diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index ec76a1a42ef0..8612a40923a8 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -27,3 +27,5 @@ ec2cc761bc7067712ecc7734502f703fe3b024c8 84ac80f1921afc243d71fd0caaa4f2838c294102 # bless mir-opt tests to add `copy` 99cb0c6bc399fb94a0ddde7e9b38e9c00d523bad +# reformat with rustfmt edition 2024 +c682aa162b0d41e21cc6748f4fecfe01efb69d1f diff --git a/Cargo.lock b/Cargo.lock index e973d7f9a2ae..0d2ae4ee15fa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,12 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + [[package]] name = "aes" version = "0.8.4" @@ -177,9 +183,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.86" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" +checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6" dependencies = [ "backtrace", ] @@ -215,7 +221,7 @@ dependencies = [ "cc", "cfg-if", "libc", - "miniz_oxide", + "miniz_oxide 0.7.4", "object 0.32.2", "rustc-demangle", ] @@ -267,12 +273,12 @@ dependencies = [ [[package]] name = "bstr" -version = "1.6.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6798148dccfbff0fae41c7574d2fa8f1ef3492fba0face179de5d8d447d67b05" +checksum = "40723b8fb387abc38f4f4a37c09073622e41dd12327033091ef8950659e6dc0c" dependencies = [ "memchr", - "regex-automata 0.3.9", + "regex-automata 0.4.7", "serde", ] @@ -332,9 +338,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.7.1" +version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" +checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" [[package]] name = "camino" @@ -464,9 +470,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.16" +version = "4.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed6719fffa43d0d87e5fd8caeab59be1554fb028cd30edc88fc4369b17971019" +checksum = "b0956a43b323ac1afaffc053ed5c4b7c1f1800bacd1683c353aabbb752515dd3" dependencies = [ "clap_builder", "clap_derive", @@ -484,9 +490,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.15" +version = "4.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "216aec2b177652e3846684cbfe25c9964d18ec45234f0f5da5157b207ed1aab6" +checksum = "4d72166dd41634086d5803a47eb71ae740e61d84709c36f3c34110173db3961b" dependencies = [ "anstream", "anstyle", @@ -497,23 +503,23 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.5.18" +version = "4.5.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ee158892bd7ce77aa15c208abbdb73e155d191c287a659b57abd5adb92feb03" +checksum = "8937760c3f4c60871870b8c3ee5f9b30771f792a7045c48bcbba999d7d6b3b8e" dependencies = [ "clap", ] [[package]] name = "clap_derive" -version = "4.5.13" +version = "4.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0" +checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.77", ] [[package]] @@ -524,7 +530,7 @@ checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" [[package]] name = "clippy" -version = "0.1.82" +version = "0.1.83" dependencies = [ "anstream", "cargo_metadata 0.18.1", @@ -542,18 +548,18 @@ dependencies = [ "rustc_tools_util", "serde", "serde_json", - "syn 2.0.75", + "syn 2.0.77", "tempfile", "termize", "tokio", "toml 0.7.8", - "ui_test 0.25.0", + "ui_test", "walkdir", ] [[package]] name = "clippy_config" -version = "0.1.82" +version = "0.1.83" dependencies = [ "itertools", "serde", @@ -576,7 +582,7 @@ dependencies = [ [[package]] name = "clippy_lints" -version = "0.1.82" +version = "0.1.83" dependencies = [ "arrayvec", "cargo_metadata 0.18.1", @@ -600,7 +606,7 @@ dependencies = [ [[package]] name = "clippy_utils" -version = "0.1.82" +version = "0.1.83" dependencies = [ "arrayvec", "clippy_config", @@ -651,7 +657,7 @@ dependencies = [ "nom", "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.77", ] [[package]] @@ -741,16 +747,16 @@ dependencies = [ "anyhow", "leb128", "md-5", - "miniz_oxide", + "miniz_oxide 0.7.4", "regex", "rustc-demangle", ] [[package]] name = "cpufeatures" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51e852e6dc9a5bed1fae92dd2375037bf2b768725bf3be87811edee3249d09ad" +checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" dependencies = [ "libc", ] @@ -835,9 +841,9 @@ dependencies = [ [[package]] name = "curl-sys" -version = "0.4.74+curl-8.9.0" +version = "0.4.76+curl-8.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8af10b986114528fcdc4b63b6f5f021b7057618411046a4de2ba0f0149a097bf" +checksum = "00462dbe9cbb9344e1b2be34d9094d74e3b8aac59a883495b335eafd02e25120" dependencies = [ "cc", "libc", @@ -869,7 +875,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.75", + "syn 2.0.77", ] [[package]] @@ -880,7 +886,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", - "syn 2.0.75", + "syn 2.0.77", ] [[package]] @@ -902,11 +908,11 @@ dependencies = [ [[package]] name = "declare_clippy_lint" -version = "0.1.82" +version = "0.1.83" dependencies = [ "itertools", "quote", - "syn 2.0.75", + "syn 2.0.77", ] [[package]] @@ -926,38 +932,38 @@ checksum = "62d671cc41a825ebabc75757b62d3d168c577f9149b2d49ece1dad1f72119d25" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.77", ] [[package]] name = "derive_builder" -version = "0.20.0" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0350b5cb0331628a5916d6c5c0b72e97393b8b6b03b47a9284f4e7f5a405ffd7" +checksum = "cd33f37ee6a119146a1781d3356a7c26028f83d779b2e04ecd45fdc75c76877b" dependencies = [ "derive_builder_macro", ] [[package]] name = "derive_builder_core" -version = "0.20.0" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d48cda787f839151732d396ac69e3473923d54312c070ee21e9effcaa8ca0b1d" +checksum = "7431fa049613920234f22c47fdc33e6cf3ee83067091ea4277a3f8c4587aae38" dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.77", ] [[package]] name = "derive_builder_macro" -version = "0.20.0" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "206868b8242f27cecce124c19fd88157fbd0dd334df2587f36417bafbc85097b" +checksum = "4abae7035bf79b9877b779505d8cf3749285b80c43941eda66604841889451dc" dependencies = [ "derive_builder_core", - "syn 2.0.75", + "syn 2.0.77", ] [[package]] @@ -968,7 +974,7 @@ checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.77", ] [[package]] @@ -980,7 +986,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.77", ] [[package]] @@ -1058,7 +1064,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.77", ] [[package]] @@ -1183,9 +1189,9 @@ checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" [[package]] name = "fastrand" -version = "2.1.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" +checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" [[package]] name = "field-offset" @@ -1199,9 +1205,9 @@ dependencies = [ [[package]] name = "filetime" -version = "0.2.24" +version = "0.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf401df4a4e3872c4fe8151134cf483738e74b67fc934d6532c882b3d24a4550" +checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586" dependencies = [ "cfg-if", "libc", @@ -1211,12 +1217,12 @@ dependencies = [ [[package]] name = "flate2" -version = "1.0.31" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f211bbe8e69bbd0cfdea405084f128ae8b4aaa6b0b522fc8f2b009084797920" +checksum = "324a1be68054ef05ad64b861cc9eaf1d623d2d8cb25b4bf2cb9cdd902b4bf253" dependencies = [ "crc32fast", - "miniz_oxide", + "miniz_oxide 0.8.0", ] [[package]] @@ -1229,7 +1235,7 @@ dependencies = [ "fluent-syntax", "intl-memoizer", "intl_pluralrules", - "rustc-hash", + "rustc-hash 1.1.0", "self_cell 0.10.3", "smallvec", "unic-langid", @@ -1349,7 +1355,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.77", ] [[package]] @@ -1461,15 +1467,15 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "globset" -version = "0.4.13" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "759c97c1e17c55525b57192c06a267cda0ac5210b222d6b82189a2338fa1c13d" +checksum = "15f1ce686646e7f1e19bf7d5533fe443a45dbfb990e00629110797578b42fb19" dependencies = [ "aho-corasick", "bstr", - "fnv", "log", - "regex", + "regex-automata 0.4.7", + "regex-syntax 0.8.4", ] [[package]] @@ -1567,7 +1573,7 @@ dependencies = [ "markup5ever", "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.77", ] [[package]] @@ -1587,9 +1593,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "iana-time-zone" -version = "0.1.60" +version = "0.1.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -1699,7 +1705,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.77", ] [[package]] @@ -1732,17 +1738,16 @@ checksum = "cb56e1aa765b4b4f3aadfab769793b7087bb03a4ea4920644a6d238e2df5b9ed" [[package]] name = "ignore" -version = "0.4.20" +version = "0.4.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbe7873dab538a9a44ad79ede1faf5f30d49f9a5c883ddbab48bce81b64b7492" +checksum = "6d89fd380afde86567dfba715db065673989d6253f42b88179abd3eae47bda4b" dependencies = [ + "crossbeam-deque", "globset", - "lazy_static", "log", "memchr", - "regex", + "regex-automata 0.4.7", "same-file", - "thread_local", "walkdir", "winapi-util", ] @@ -1755,9 +1760,9 @@ checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" [[package]] name = "indexmap" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93ead53efc7ea8ed3cfb0c79fc8023fbb782a5432b52830b6518941cebe6505c" +checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" dependencies = [ "equivalent", "hashbrown", @@ -1902,7 +1907,7 @@ dependencies = [ "anyhow", "clap", "fs-err", - "rustc-hash", + "rustc-hash 1.1.0", "rustdoc-json-types", "serde", "serde_json", @@ -1945,9 +1950,9 @@ checksum = "baff4b617f7df3d896f97fe922b64817f6cd9a756bb81d40f8883f2f66dcb401" [[package]] name = "libc" -version = "0.2.157" +version = "0.2.159" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "374af5f94e54fa97cf75e945cce8a6b201e88a1a07e688b47dfd2a59c66dbd86" +checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" [[package]] name = "libdbus-sys" @@ -2007,9 +2012,9 @@ dependencies = [ [[package]] name = "libz-sys" -version = "1.1.19" +version = "1.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdc53a7799a7496ebc9fd29f31f7df80e83c9bda5299768af5f9e59eeea74647" +checksum = "d2d16453e800a8cf6dd2fc3eb4bc99b786a9b90c663b8559a5b1a041bf89e472" dependencies = [ "cc", "libc", @@ -2171,15 +2176,15 @@ dependencies = [ "memmap2", "parking_lot", "perf-event-open-sys", - "rustc-hash", + "rustc-hash 1.1.0", "smallvec", ] [[package]] name = "memchr" -version = "2.5.0" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "memmap2" @@ -2239,6 +2244,15 @@ dependencies = [ "adler", ] +[[package]] +name = "miniz_oxide" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +dependencies = [ + "adler2", +] + [[package]] name = "miow" version = "0.6.0" @@ -2269,7 +2283,7 @@ dependencies = [ "rustc_version", "smallvec", "tempfile", - "ui_test 0.26.5", + "ui_test", "windows-sys 0.52.0", ] @@ -2453,8 +2467,8 @@ dependencies = [ "hashbrown", "indexmap", "memchr", - "ruzstd 0.7.0", - "wasmparser", + "ruzstd 0.7.2", + "wasmparser 0.216.0", ] [[package]] @@ -2474,9 +2488,9 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "once_map" -version = "0.4.18" +version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa7085055bbe9c8edbd982048dbcf8181794d4a81cb04a11931673e63cc18dc6" +checksum = "30c7f82d6d446dd295845094f3a76bcdc5e6183b66667334e169f019cd05e5a0" dependencies = [ "ahash", "hashbrown", @@ -2642,9 +2656,9 @@ dependencies = [ [[package]] name = "pest" -version = "2.7.11" +version = "2.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd53dff83f26735fdc1ca837098ccf133605d794cdae66acfc2bfac3ec809d95" +checksum = "fdbef9d1d47087a895abd220ed25eb4ad973a5e26f6a4367b038c25e28dfc2d9" dependencies = [ "memchr", "thiserror", @@ -2653,9 +2667,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.7.11" +version = "2.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a548d2beca6773b1c244554d36fcf8548a8a58e74156968211567250e48e49a" +checksum = "4d3a6e3394ec80feb3b6393c725571754c6188490265c61aaf260810d6b95aa0" dependencies = [ "pest", "pest_generator", @@ -2663,22 +2677,22 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.7.11" +version = "2.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c93a82e8d145725dcbaf44e5ea887c8a869efdcc28706df2d08c69e17077183" +checksum = "94429506bde1ca69d1b5601962c73f4172ab4726571a59ea95931218cb0e930e" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.77", ] [[package]] name = "pest_meta" -version = "2.7.11" +version = "2.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a941429fea7e08bedec25e4f6785b6ffaacc6b755da98df5ef3e7dcf4a124c4f" +checksum = "ac8a071862e93690b6e34e9a5fb8e33ff3734473ac0245b27232222c4906a33f" dependencies = [ "once_cell", "pest", @@ -2756,9 +2770,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" +checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" [[package]] name = "polonius-engine" @@ -2768,14 +2782,14 @@ checksum = "c4e8e505342045d397d0b6674dcb82d6faf5cf40484d30eeb88fc82ef14e903f" dependencies = [ "datafrog", "log", - "rustc-hash", + "rustc-hash 1.1.0", ] [[package]] name = "portable-atomic" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da544ee218f0d287a911e9c99a39a8c9bc8fcad3cb8db5959940044ecfc67265" +checksum = "d30538d42559de6b034bc76fd6dd4c38961b1ee5c6c56e3808c50128fdbc22ce" [[package]] name = "powerfmt" @@ -2825,9 +2839,9 @@ dependencies = [ [[package]] name = "psm" -version = "0.1.21" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5787f7cda34e3033a72192c018bc5883100330f362ef279a8cbccfce8bb4e874" +checksum = "aa37f80ca58604976033fae9515a8a2989fc13797d953f7c04fb8fa36a11f205" dependencies = [ "cc", ] @@ -2857,9 +2871,9 @@ dependencies = [ [[package]] name = "pulldown-cmark" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb4e75767fbc9d92b90e4d0c011f61358cde9513b31ef07ea3631b15ffc3b4fd" +checksum = "679341d22c78c6c649893cbd6c3278dcbe9fc4faa62fea3a9296ae2b50c14625" dependencies = [ "bitflags 2.6.0", "memchr", @@ -2893,9 +2907,9 @@ checksum = "07589615d719a60c8dd8a4622e7946465dfef20d1a428f969e3443e7386d5f45" [[package]] name = "quote" -version = "1.0.36" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] @@ -2961,18 +2975,18 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.3" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" +checksum = "355ae415ccd3a04315d3f8246e86d67689ea74d88d915576e1589a351062a13b" dependencies = [ "bitflags 2.6.0", ] [[package]] name = "redox_users" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ "getrandom", "libredox", @@ -2981,13 +2995,14 @@ dependencies = [ [[package]] name = "regex" -version = "1.8.4" +version = "1.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0ab3ca65655bb1e41f2a8c8cd662eb4fb035e67c3f78da1d61dffe89d07300f" +checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.7.5", + "regex-automata 0.4.7", + "regex-syntax 0.8.4", ] [[package]] @@ -3010,9 +3025,14 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.3.9" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59b23e92ee4318893fa3fe3e6fb365258efbfe6ac6ab30f090cdcbb7aa37efa9" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.8.4", +] [[package]] name = "regex-lite" @@ -3026,12 +3046,6 @@ version = "0.6.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" -[[package]] -name = "regex-syntax" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" - [[package]] name = "regex-syntax" version = "0.8.4" @@ -3056,9 +3070,9 @@ dependencies = [ [[package]] name = "rinja" -version = "0.3.0" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d3762e3740cdbf2fd2be465cc2c26d643ad17353cc2e0223d211c1b096118bd" +checksum = "f28580fecce391f3c0e65a692e5f2b5db258ba2346ee04f355ae56473ab973dc" dependencies = [ "humansize", "itoa", @@ -3069,9 +3083,9 @@ dependencies = [ [[package]] name = "rinja_derive" -version = "0.3.0" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd01fd8e15e7d19c8b8052c1d428325131e02ff1633cdcf695190c2e56ab682c" +checksum = "1f1ae91455a4c82892d9513fcfa1ac8faff6c523602d0041536341882714aede" dependencies = [ "basic-toml", "memchr", @@ -3081,18 +3095,20 @@ dependencies = [ "proc-macro2", "quote", "rinja_parser", + "rustc-hash 2.0.0", "serde", - "syn 2.0.75", + "syn 2.0.77", ] [[package]] name = "rinja_parser" -version = "0.3.0" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2f6bf7cef118c6de21206edf0b3f19f5ede60006be674a58ca21b6e003a1b57" +checksum = "06ea17639e1f35032e1c67539856e498c04cd65fe2a45f55ec437ec55e4be941" dependencies = [ "memchr", "nom", + "serde", ] [[package]] @@ -3114,14 +3130,14 @@ dependencies = [ "regex", "serde_json", "similar", - "wasmparser", + "wasmparser 0.216.0", ] [[package]] name = "rustc-build-sysroot" -version = "0.5.3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2471f8f296262437d7e848e527b4210b44a96e53a3b4435b890227ce3e6da106" +checksum = "d6d984a9db43148467059309bd1e5ad577085162f695d9fe2cf3543aeb25cd38" dependencies = [ "anyhow", "rustc_version", @@ -3141,6 +3157,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +[[package]] +name = "rustc-hash" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" + [[package]] name = "rustc-main" version = "0.0.0" @@ -3458,7 +3480,7 @@ dependencies = [ "thin-vec", "thorin-dwp", "tracing", - "wasm-encoder", + "wasm-encoder 0.216.0", "windows 0.52.0", ] @@ -3503,7 +3525,7 @@ dependencies = [ "memmap2", "parking_lot", "portable-atomic", - "rustc-hash", + "rustc-hash 1.1.0", "rustc-rayon", "rustc-stable-hash", "rustc_arena", @@ -3673,7 +3695,7 @@ dependencies = [ "fluent-syntax", "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.77", "unic-langid", ] @@ -3807,7 +3829,7 @@ version = "0.0.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.77", ] [[package]] @@ -3956,7 +3978,7 @@ version = "0.0.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.77", "synstructure", ] @@ -4197,7 +4219,7 @@ dependencies = [ name = "rustc_pattern_analysis" version = "0.0.0" dependencies = [ - "rustc-hash", + "rustc-hash 1.1.0", "rustc_apfloat", "rustc_arena", "rustc_data_structures", @@ -4281,7 +4303,7 @@ name = "rustc_resolve" version = "0.0.0" dependencies = [ "bitflags 2.6.0", - "pulldown-cmark 0.11.2", + "pulldown-cmark 0.11.3", "rustc_arena", "rustc_ast", "rustc_ast_pretty", @@ -4519,7 +4541,7 @@ dependencies = [ "bitflags 2.6.0", "derive-where", "indexmap", - "rustc-hash", + "rustc-hash 1.1.0", "rustc_ast_ir", "rustc_data_structures", "rustc_index", @@ -4537,15 +4559,15 @@ version = "0.0.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.77", "synstructure", ] [[package]] name = "rustc_version" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ "semver", ] @@ -4590,7 +4612,7 @@ name = "rustdoc-json-types" version = "0.1.0" dependencies = [ "bincode", - "rustc-hash", + "rustc-hash 1.1.0", "serde", "serde_json", ] @@ -4625,7 +4647,7 @@ dependencies = [ "proc-macro2", "quote", "serde", - "syn 2.0.75", + "syn 2.0.77", ] [[package]] @@ -4659,9 +4681,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.34" +version = "0.38.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" dependencies = [ "bitflags 2.6.0", "errno", @@ -4689,11 +4711,10 @@ dependencies = [ [[package]] name = "ruzstd" -version = "0.7.0" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5022b253619b1ba797f243056276bed8ed1a73b0f5a7ce7225d524067644bf8f" +checksum = "99c3938e133aac070997ddc684d4b393777d293ba170f2988c8fd5ea2ad4ce21" dependencies = [ - "byteorder", "twox-hash", ] @@ -4714,11 +4735,11 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" +checksum = "e9aaafd5a2b6e3d657ff009d82fbd630b6bd54dd4eb06f21693925cdf80f9b8b" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -4759,29 +4780,29 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.208" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cff085d2cb684faa248efb494c39b68e522822ac0de72ccf08109abde717cfb2" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.208" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24008e81ff7613ed8e5ba0cfaf24e2c2f1e5b8a0495711e44fcd4882fca62bcf" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.77", ] [[package]] name = "serde_json" -version = "1.0.125" +version = "1.0.128" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83c8e735a073ccf5be70aa8066aa984eaf2fa000db6c8d0100ae605b366d31ed" +checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" dependencies = [ "indexmap", "itoa", @@ -4792,9 +4813,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.7" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" dependencies = [ "serde", ] @@ -5038,9 +5059,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.75" +version = "2.0.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6af063034fc1935ede7be0122941bafa9bacb949334d090b77ca98b5817c7d9" +checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" dependencies = [ "proc-macro2", "quote", @@ -5055,14 +5076,14 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.77", ] [[package]] name = "sysinfo" -version = "0.31.2" +version = "0.31.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4115055da5f572fff541dd0c4e61b0262977f453cc9fe04be83aba25a89bdab" +checksum = "355dbe4f8799b304b05e1b0f05fc59b2a18d36645cf169607da45bde2f69a1be" dependencies = [ "core-foundation-sys", "libc", @@ -5081,9 +5102,9 @@ dependencies = [ [[package]] name = "tar" -version = "0.4.41" +version = "0.4.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb797dad5fb5b76fcf519e702f4a589483b5ef06567f160c392832c1f5e44909" +checksum = "4ff6c40d3aedb5e06b57c6f669ad17ab063dd1e63d977c6a88e7f4dfa4f04020" dependencies = [ "filetime", "libc", @@ -5173,22 +5194,22 @@ checksum = "a38c90d48152c236a3ab59271da4f4ae63d678c5d7ad6b7714d7cb9760be5e4b" [[package]] name = "thiserror" -version = "1.0.63" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" +checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.63" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" +checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.77", ] [[package]] @@ -5232,7 +5253,7 @@ dependencies = [ "ignore", "miropt-test-tools", "regex", - "rustc-hash", + "rustc-hash 1.1.0", "semver", "similar", "termcolor", @@ -5301,9 +5322,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.39.3" +version = "1.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9babc99b9923bfa4804bd74722ff02c0381021eafa4db9949217e3be8e84fff5" +checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" dependencies = [ "backtrace", "bytes", @@ -5379,7 +5400,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.77", ] [[package]] @@ -5461,7 +5482,7 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "deb68604048ff8fa93347f02441e4487594adc20bb8a084f9e564d2b827a0a9f" dependencies = [ - "rustc-hash", + "rustc-hash 1.1.0", ] [[package]] @@ -5485,33 +5506,6 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" -[[package]] -name = "ui_test" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7e4f339f62edc873975c47115f9e71c5454ddaa37c1142b42fc0b2672c8dacb" -dependencies = [ - "annotate-snippets 0.11.4", - "anyhow", - "bstr", - "cargo-platform", - "cargo_metadata 0.18.1", - "color-eyre", - "colored", - "comma", - "crossbeam-channel", - "indicatif", - "lazy_static", - "levenshtein", - "prettydiff", - "regex", - "rustc_version", - "rustfix", - "serde", - "serde_json", - "spanned", -] - [[package]] name = "ui_test" version = "0.26.5" @@ -5577,7 +5571,7 @@ checksum = "1ed7f4237ba393424195053097c1516bd4590dc82b84f2f97c5c69e12704555b" dependencies = [ "proc-macro-hack", "quote", - "syn 2.0.75", + "syn 2.0.77", "unic-langid-impl", ] @@ -5605,36 +5599,36 @@ checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" [[package]] name = "unicode-normalization" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" dependencies = [ "tinyvec", ] [[package]] name = "unicode-properties" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4259d9d4425d9f0661581b804cb85fe66a4c631cadd8f490d1c13a35d5d9291" +checksum = "52ea75f83c0137a9b98608359a5f1af8144876eb67bcb1ce837368e906a9f524" [[package]] name = "unicode-script" -version = "0.5.6" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad8d71f5726e5f285a935e9fe8edfd53f0491eb6e9a5774097fdabee7cd8c9cd" +checksum = "9fb421b350c9aff471779e262955939f565ec18b86c15364e6bdf0d662ca7c1f" [[package]] name = "unicode-security" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee9e13753df674873f3c4693b240ae5c03245ddc157dfccf7c26db9329af3a11" +checksum = "2e4ddba1535dd35ed8b61c52166b7155d7f4e4b8847cec6f48e71dc66d8b5e50" dependencies = [ "unicode-normalization", "unicode-script", @@ -5642,21 +5636,21 @@ dependencies = [ [[package]] name = "unicode-segmentation" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" [[package]] name = "unicode-width" -version = "0.1.13" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" [[package]] name = "unicode-xid" -version = "0.2.4" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" [[package]] name = "unified-diff" @@ -5775,7 +5769,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.77", "wasm-bindgen-shared", ] @@ -5797,7 +5791,7 @@ checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.77", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -5810,16 +5804,16 @@ checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" [[package]] name = "wasm-component-ld" -version = "0.5.7" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13261270d3ac58ffae0219ae34f297a7e24f9ee3b13b29be579132c588a83519" +checksum = "cb17cdbc91766d4ea0bcd6026c36ba77a21b5c8199aeb1f0993461fe6a6bec2b" dependencies = [ "anyhow", "clap", "lexopt", "tempfile", "wasi-preview1-component-adapter-provider", - "wasmparser", + "wasmparser 0.217.0", "wat", "wit-component", "wit-parser", @@ -5839,14 +5833,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04c23aebea22c8a75833ae08ed31ccc020835b12a41999e58c31464271b94a88" dependencies = [ "leb128", - "wasmparser", +] + +[[package]] +name = "wasm-encoder" +version = "0.217.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b88b0814c9a2b323a9b46c687e726996c255ac8b64aa237dd11c81ed4854760" +dependencies = [ + "leb128", + "wasmparser 0.217.0", ] [[package]] name = "wasm-metadata" -version = "0.216.0" +version = "0.217.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47c8154d703a6b0e45acf6bd172fa002fc3c7058a9f7615e517220aeca27c638" +checksum = "65a146bf9a60e9264f0548a2599aa9656dba9a641eff9ab88299dc2a637e483c" dependencies = [ "anyhow", "indexmap", @@ -5854,8 +5857,8 @@ dependencies = [ "serde_derive", "serde_json", "spdx", - "wasm-encoder", - "wasmparser", + "wasm-encoder 0.217.0", + "wasmparser 0.217.0", ] [[package]] @@ -5863,6 +5866,16 @@ name = "wasmparser" version = "0.216.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bcdee6bea3619d311fb4b299721e89a986c3470f804b6d534340e412589028e3" +dependencies = [ + "bitflags 2.6.0", + "indexmap", +] + +[[package]] +name = "wasmparser" +version = "0.217.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca917a21307d3adf2b9857b94dd05ebf8496bdcff4437a9b9fb3899d3e6c74e7" dependencies = [ "ahash", "bitflags 2.6.0", @@ -5874,22 +5887,22 @@ dependencies = [ [[package]] name = "wast" -version = "216.0.0" +version = "217.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7eb1f2eecd913fdde0dc6c3439d0f24530a98ac6db6cb3d14d92a5328554a08" +checksum = "79004ecebded92d3c710d4841383368c7f04b63d0992ddd6b0c7d5029b7629b7" dependencies = [ "bumpalo", "leb128", "memchr", "unicode-width", - "wasm-encoder", + "wasm-encoder 0.217.0", ] [[package]] name = "wat" -version = "1.216.0" +version = "1.217.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac0409090fb5154f95fb5ba3235675fd9e579e731524d63b6a2f653e1280c82a" +checksum = "c126271c3d92ca0f7c63e4e462e40c69cca52fd4245fcda730d1cf558fb55088" dependencies = [ "wast", ] @@ -5955,7 +5968,7 @@ dependencies = [ "rayon", "serde", "serde_json", - "syn 2.0.75", + "syn 2.0.77", "windows-metadata", ] @@ -5988,7 +6001,7 @@ checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.77", ] [[package]] @@ -5999,7 +6012,7 @@ checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.77", ] [[package]] @@ -6176,9 +6189,9 @@ dependencies = [ [[package]] name = "wit-component" -version = "0.216.0" +version = "0.217.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e2ca3ece38ea2447a9069b43074ba73d96dde1944cba276c54e41371745f9dc" +checksum = "d7117809905e49db716d81e794f79590c052bf2fdbbcda1731ca0fb28f6f3ddf" dependencies = [ "anyhow", "bitflags 2.6.0", @@ -6187,17 +6200,17 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "wasm-encoder", + "wasm-encoder 0.217.0", "wasm-metadata", - "wasmparser", + "wasmparser 0.217.0", "wit-parser", ] [[package]] name = "wit-parser" -version = "0.216.0" +version = "0.217.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4d108165c1167a4ccc8a803dcf5c28e0a51d6739fd228cc7adce768632c764c" +checksum = "fb893dcd6d370cfdf19a0d9adfcd403efb8e544e1a0ea3a8b81a21fe392eaa78" dependencies = [ "anyhow", "id-arena", @@ -6208,7 +6221,7 @@ dependencies = [ "serde_derive", "serde_json", "unicode-xid", - "wasmparser", + "wasmparser 0.217.0", ] [[package]] @@ -6266,7 +6279,7 @@ checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.77", "synstructure", ] @@ -6288,7 +6301,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.77", ] [[package]] @@ -6308,7 +6321,7 @@ checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.77", "synstructure", ] @@ -6331,5 +6344,5 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.77", ] diff --git a/compiler/rustc_ast/Cargo.toml b/compiler/rustc_ast/Cargo.toml index d33f9666b484..34c612dac692 100644 --- a/compiler/rustc_ast/Cargo.toml +++ b/compiler/rustc_ast/Cargo.toml @@ -4,10 +4,9 @@ version = "0.0.0" edition = "2021" [dependencies] -# FIXME: bumping memchr to 2.7.1 causes linker errors in MSVC thin-lto # tidy-alphabetical-start bitflags = "2.4.1" -memchr = "=2.5.0" +memchr = "2.7.4" rustc_ast_ir = { path = "../rustc_ast_ir" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_index = { path = "../rustc_index" } diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs index de58df381416..1d6abbef06c9 100644 --- a/compiler/rustc_ast/src/token.rs +++ b/compiler/rustc_ast/src/token.rs @@ -385,35 +385,41 @@ impl TokenKind { Literal(Lit::new(kind, symbol, suffix)) } - /// An approximation to proc-macro-style single-character operators used by rustc parser. - /// If the operator token can be broken into two tokens, the first of which is single-character, - /// then this function performs that operation, otherwise it returns `None`. - pub fn break_two_token_op(&self) -> Option<(TokenKind, TokenKind)> { - Some(match *self { - Le => (Lt, Eq), - EqEq => (Eq, Eq), - Ne => (Not, Eq), - Ge => (Gt, Eq), - AndAnd => (BinOp(And), BinOp(And)), - OrOr => (BinOp(Or), BinOp(Or)), - BinOp(Shl) => (Lt, Lt), - BinOp(Shr) => (Gt, Gt), - BinOpEq(Plus) => (BinOp(Plus), Eq), - BinOpEq(Minus) => (BinOp(Minus), Eq), - BinOpEq(Star) => (BinOp(Star), Eq), - BinOpEq(Slash) => (BinOp(Slash), Eq), - BinOpEq(Percent) => (BinOp(Percent), Eq), - BinOpEq(Caret) => (BinOp(Caret), Eq), - BinOpEq(And) => (BinOp(And), Eq), - BinOpEq(Or) => (BinOp(Or), Eq), - BinOpEq(Shl) => (Lt, Le), - BinOpEq(Shr) => (Gt, Ge), - DotDot => (Dot, Dot), - DotDotDot => (Dot, DotDot), - PathSep => (Colon, Colon), - RArrow => (BinOp(Minus), Gt), - LArrow => (Lt, BinOp(Minus)), - FatArrow => (Eq, Gt), + /// An approximation to proc-macro-style single-character operators used by + /// rustc parser. If the operator token can be broken into two tokens, the + /// first of which has `n` (1 or 2) chars, then this function performs that + /// operation, otherwise it returns `None`. + pub fn break_two_token_op(&self, n: u32) -> Option<(TokenKind, TokenKind)> { + assert!(n == 1 || n == 2); + Some(match (self, n) { + (Le, 1) => (Lt, Eq), + (EqEq, 1) => (Eq, Eq), + (Ne, 1) => (Not, Eq), + (Ge, 1) => (Gt, Eq), + (AndAnd, 1) => (BinOp(And), BinOp(And)), + (OrOr, 1) => (BinOp(Or), BinOp(Or)), + (BinOp(Shl), 1) => (Lt, Lt), + (BinOp(Shr), 1) => (Gt, Gt), + (BinOpEq(Plus), 1) => (BinOp(Plus), Eq), + (BinOpEq(Minus), 1) => (BinOp(Minus), Eq), + (BinOpEq(Star), 1) => (BinOp(Star), Eq), + (BinOpEq(Slash), 1) => (BinOp(Slash), Eq), + (BinOpEq(Percent), 1) => (BinOp(Percent), Eq), + (BinOpEq(Caret), 1) => (BinOp(Caret), Eq), + (BinOpEq(And), 1) => (BinOp(And), Eq), + (BinOpEq(Or), 1) => (BinOp(Or), Eq), + (BinOpEq(Shl), 1) => (Lt, Le), // `<` + `<=` + (BinOpEq(Shl), 2) => (BinOp(Shl), Eq), // `<<` + `=` + (BinOpEq(Shr), 1) => (Gt, Ge), // `>` + `>=` + (BinOpEq(Shr), 2) => (BinOp(Shr), Eq), // `>>` + `=` + (DotDot, 1) => (Dot, Dot), + (DotDotDot, 1) => (Dot, DotDot), // `.` + `..` + (DotDotDot, 2) => (DotDot, Dot), // `..` + `.` + (DotDotEq, 2) => (DotDot, Eq), + (PathSep, 1) => (Colon, Colon), + (RArrow, 1) => (BinOp(Minus), Gt), + (LArrow, 1) => (Lt, BinOp(Minus)), + (FatArrow, 1) => (Eq, Gt), _ => return None, }) } diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 614a99a6e196..83931a8c1a96 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -4,9 +4,9 @@ use rustc_ast::{NodeId, PatKind, attr, token}; use rustc_feature::{AttributeGate, BUILTIN_ATTRIBUTE_MAP, BuiltinAttribute, Features, GateIssue}; use rustc_session::Session; use rustc_session::parse::{feature_err, feature_err_issue, feature_warn}; -use rustc_span::Span; use rustc_span::source_map::Spanned; use rustc_span::symbol::sym; +use rustc_span::{Span, Symbol}; use rustc_target::spec::abi; use thin_vec::ThinVec; @@ -483,6 +483,8 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { maybe_stage_features(sess, features, krate); check_incompatible_features(sess, features); + check_new_solver_banned_features(sess, features); + let mut visitor = PostExpansionVisitor { sess, features }; let spans = sess.psess.gated_spans.spans.borrow(); @@ -662,3 +664,22 @@ fn check_incompatible_features(sess: &Session, features: &Features) { } } } + +fn check_new_solver_banned_features(sess: &Session, features: &Features) { + if !sess.opts.unstable_opts.next_solver.is_some_and(|n| n.globally) { + return; + } + + // Ban GCE with the new solver, because it does not implement GCE correctly. + if let Some(&(_, gce_span, _)) = features + .declared_lang_features + .iter() + .find(|&&(feat, _, _)| feat == sym::generic_const_exprs) + { + sess.dcx().emit_err(errors::IncompatibleFeatures { + spans: vec![gce_span], + f1: Symbol::intern("-Znext-solver=globally"), + f2: sym::generic_const_exprs, + }); + } +} diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs index dcc4cf2478f3..762ebc47ca9d 100644 --- a/compiler/rustc_attr/src/builtin.rs +++ b/compiler/rustc_attr/src/builtin.rs @@ -153,7 +153,7 @@ pub enum StabilityLevel { } /// Rust release in which a feature is stabilized. -#[derive(Encodable, Decodable, PartialEq, Copy, Clone, Debug, Eq, Hash)] +#[derive(Encodable, Decodable, PartialEq, Copy, Clone, Debug, Eq, PartialOrd, Ord, Hash)] #[derive(HashStable_Generic)] pub enum StableSince { Version(RustcVersion), diff --git a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs index 7077d78f4594..31a5d451ff6b 100644 --- a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs +++ b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs @@ -333,7 +333,11 @@ impl<'tcx> BorrowExplanation<'tcx> { } } - if let ConstraintCategory::Cast { unsize_to: Some(unsize_ty) } = category { + if let ConstraintCategory::Cast { + is_implicit_coercion: true, + unsize_to: Some(unsize_ty), + } = category + { self.add_object_lifetime_default_note(tcx, err, unsize_ty); } self.add_lifetime_bound_suggestion_to_diagnostic(err, &category, span, region_name); @@ -740,7 +744,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { // If we see an unsized cast, then if it is our data we should check // whether it is being cast to a trait object. Rvalue::Cast( - CastKind::PointerCoercion(PointerCoercion::Unsize), + CastKind::PointerCoercion(PointerCoercion::Unsize, _), operand, ty, ) => { diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index ca0a3c661305..39175b406a4a 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -47,7 +47,8 @@ impl<'tcx> ConstraintDescription for ConstraintCategory<'tcx> { ConstraintCategory::Yield => "yielding this value ", ConstraintCategory::UseAsConst => "using this value as a constant ", ConstraintCategory::UseAsStatic => "using this value as a static ", - ConstraintCategory::Cast { .. } => "cast ", + ConstraintCategory::Cast { is_implicit_coercion: false, .. } => "cast ", + ConstraintCategory::Cast { is_implicit_coercion: true, .. } => "coercion ", ConstraintCategory::CallArgument(_) => "argument ", ConstraintCategory::TypeAnnotation => "type annotation ", ConstraintCategory::ClosureBounds => "closure body ", diff --git a/compiler/rustc_borrowck/src/facts.rs b/compiler/rustc_borrowck/src/facts.rs index 713452796c60..ef8e757a5478 100644 --- a/compiler/rustc_borrowck/src/facts.rs +++ b/compiler/rustc_borrowck/src/facts.rs @@ -1,7 +1,7 @@ use std::error::Error; use std::fmt::Debug; use std::fs::{self, File}; -use std::io::{BufWriter, Write}; +use std::io::Write; use std::path::Path; use polonius_engine::{AllFacts as PoloniusFacts, Atom}; @@ -127,7 +127,7 @@ impl<'w> FactWriter<'w> { T: FactRow, { let file = &self.dir.join(file_name); - let mut file = BufWriter::new(File::create(file)?); + let mut file = File::create_buffered(file)?; for row in rows { row.write(&mut file, self.location_table)?; } diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 733fabe557e6..a11eca0b9c74 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -6,6 +6,7 @@ #![feature(assert_matches)] #![feature(box_patterns)] #![feature(control_flow_enum)] +#![feature(file_buffered)] #![feature(let_chains)] #![feature(never_type)] #![feature(rustc_attrs)] diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 4056b4235489..16e51e82f85e 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -1975,8 +1975,9 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { Rvalue::Cast(cast_kind, op, ty) => { self.check_operand(op, location); - match cast_kind { - CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer) => { + match *cast_kind { + CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer, coercion_source) => { + let is_implicit_coercion = coercion_source == CoercionSource::Implicit; let src_sig = op.ty(body, tcx).fn_sig(tcx); // HACK: This shouldn't be necessary... We can remove this when we actually @@ -2007,7 +2008,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { self.prove_predicate( ty::ClauseKind::WellFormed(src_ty.into()), location.to_locations(), - ConstraintCategory::Cast { unsize_to: None }, + ConstraintCategory::Cast { is_implicit_coercion, unsize_to: None }, ); let src_ty = self.normalize(src_ty, location); @@ -2015,7 +2016,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { src_ty, *ty, location.to_locations(), - ConstraintCategory::Cast { unsize_to: None }, + ConstraintCategory::Cast { is_implicit_coercion, unsize_to: None }, ) { span_mirbug!( self, @@ -2036,7 +2037,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { self.prove_predicate( ty::ClauseKind::WellFormed(src_ty.into()), location.to_locations(), - ConstraintCategory::Cast { unsize_to: None }, + ConstraintCategory::Cast { is_implicit_coercion, unsize_to: None }, ); // The type that we see in the fcx is like @@ -2049,7 +2050,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { src_ty, *ty, location.to_locations(), - ConstraintCategory::Cast { unsize_to: None }, + ConstraintCategory::Cast { is_implicit_coercion, unsize_to: None }, ) { span_mirbug!( self, @@ -2062,19 +2063,23 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } } - CastKind::PointerCoercion(PointerCoercion::ClosureFnPointer(safety)) => { + CastKind::PointerCoercion( + PointerCoercion::ClosureFnPointer(safety), + coercion_source, + ) => { let sig = match op.ty(body, tcx).kind() { ty::Closure(_, args) => args.as_closure().sig(), _ => bug!(), }; let ty_fn_ptr_from = - Ty::new_fn_ptr(tcx, tcx.signature_unclosure(sig, *safety)); + Ty::new_fn_ptr(tcx, tcx.signature_unclosure(sig, safety)); + let is_implicit_coercion = coercion_source == CoercionSource::Implicit; if let Err(terr) = self.sub_types( ty_fn_ptr_from, *ty, location.to_locations(), - ConstraintCategory::Cast { unsize_to: None }, + ConstraintCategory::Cast { is_implicit_coercion, unsize_to: None }, ) { span_mirbug!( self, @@ -2087,7 +2092,10 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } } - CastKind::PointerCoercion(PointerCoercion::UnsafeFnPointer) => { + CastKind::PointerCoercion( + PointerCoercion::UnsafeFnPointer, + coercion_source, + ) => { let fn_sig = op.ty(body, tcx).fn_sig(tcx); // The type that we see in the fcx is like @@ -2099,11 +2107,12 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { let ty_fn_ptr_from = tcx.safe_to_unsafe_fn_ty(fn_sig); + let is_implicit_coercion = coercion_source == CoercionSource::Implicit; if let Err(terr) = self.sub_types( ty_fn_ptr_from, *ty, location.to_locations(), - ConstraintCategory::Cast { unsize_to: None }, + ConstraintCategory::Cast { is_implicit_coercion, unsize_to: None }, ) { span_mirbug!( self, @@ -2116,7 +2125,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } } - CastKind::PointerCoercion(PointerCoercion::Unsize) => { + CastKind::PointerCoercion(PointerCoercion::Unsize, coercion_source) => { let &ty = ty; let trait_ref = ty::TraitRef::new( tcx, @@ -2124,22 +2133,21 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { [op.ty(body, tcx), ty], ); + let is_implicit_coercion = coercion_source == CoercionSource::Implicit; + let unsize_to = tcx.fold_regions(ty, |r, _| { + if let ty::ReVar(_) = r.kind() { tcx.lifetimes.re_erased } else { r } + }); self.prove_trait_ref( trait_ref, location.to_locations(), ConstraintCategory::Cast { - unsize_to: Some(tcx.fold_regions(ty, |r, _| { - if let ty::ReVar(_) = r.kind() { - tcx.lifetimes.re_erased - } else { - r - } - })), + is_implicit_coercion, + unsize_to: Some(unsize_to), }, ); } - CastKind::DynStar => { + CastKind::PointerCoercion(PointerCoercion::DynStar, coercion_source) => { // get the constraints from the target type (`dyn* Clone`) // // apply them to prove that the source type `Foo` implements `Clone` etc @@ -2150,12 +2158,13 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { let self_ty = op.ty(body, tcx); + let is_implicit_coercion = coercion_source == CoercionSource::Implicit; self.prove_predicates( existential_predicates .iter() .map(|predicate| predicate.with_self_ty(tcx, self_ty)), location.to_locations(), - ConstraintCategory::Cast { unsize_to: None }, + ConstraintCategory::Cast { is_implicit_coercion, unsize_to: None }, ); let outlives_predicate = tcx.mk_predicate(Binder::dummy( @@ -2166,11 +2175,14 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { self.prove_predicate( outlives_predicate, location.to_locations(), - ConstraintCategory::Cast { unsize_to: None }, + ConstraintCategory::Cast { is_implicit_coercion, unsize_to: None }, ); } - CastKind::PointerCoercion(PointerCoercion::MutToConstPointer) => { + CastKind::PointerCoercion( + PointerCoercion::MutToConstPointer, + coercion_source, + ) => { let ty::RawPtr(ty_from, hir::Mutability::Mut) = op.ty(body, tcx).kind() else { span_mirbug!(self, rvalue, "unexpected base type for cast {:?}", ty,); @@ -2180,11 +2192,12 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { span_mirbug!(self, rvalue, "unexpected target type for cast {:?}", ty,); return; }; + let is_implicit_coercion = coercion_source == CoercionSource::Implicit; if let Err(terr) = self.sub_types( *ty_from, *ty_to, location.to_locations(), - ConstraintCategory::Cast { unsize_to: None }, + ConstraintCategory::Cast { is_implicit_coercion, unsize_to: None }, ) { span_mirbug!( self, @@ -2197,7 +2210,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } } - CastKind::PointerCoercion(PointerCoercion::ArrayToPointer) => { + CastKind::PointerCoercion(PointerCoercion::ArrayToPointer, coercion_source) => { let ty_from = op.ty(body, tcx); let opt_ty_elem_mut = match ty_from.kind() { @@ -2242,11 +2255,12 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { return; } + let is_implicit_coercion = coercion_source == CoercionSource::Implicit; if let Err(terr) = self.sub_types( *ty_elem, *ty_to, location.to_locations(), - ConstraintCategory::Cast { unsize_to: None }, + ConstraintCategory::Cast { is_implicit_coercion, unsize_to: None }, ) { span_mirbug!( self, @@ -2427,7 +2441,10 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { src_obj, dst_obj, location.to_locations(), - ConstraintCategory::Cast { unsize_to: None }, + ConstraintCategory::Cast { + is_implicit_coercion: false, + unsize_to: None, + }, ) .unwrap(); } diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index dc342e151f33..1ce0aacab499 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -652,7 +652,7 @@ fn codegen_stmt<'tcx>( lval.write_cvalue(fx, res); } Rvalue::Cast( - CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer), + CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer, _), ref operand, to_ty, ) => { @@ -677,7 +677,7 @@ fn codegen_stmt<'tcx>( } } Rvalue::Cast( - CastKind::PointerCoercion(PointerCoercion::UnsafeFnPointer), + CastKind::PointerCoercion(PointerCoercion::UnsafeFnPointer, _), ref operand, to_ty, ) => { @@ -688,6 +688,7 @@ fn codegen_stmt<'tcx>( Rvalue::Cast( CastKind::PointerCoercion( PointerCoercion::MutToConstPointer | PointerCoercion::ArrayToPointer, + _, ), .., ) => { @@ -741,7 +742,7 @@ fn codegen_stmt<'tcx>( } } Rvalue::Cast( - CastKind::PointerCoercion(PointerCoercion::ClosureFnPointer(_)), + CastKind::PointerCoercion(PointerCoercion::ClosureFnPointer(_), _), ref operand, _to_ty, ) => { @@ -763,14 +764,18 @@ fn codegen_stmt<'tcx>( } } Rvalue::Cast( - CastKind::PointerCoercion(PointerCoercion::Unsize), + CastKind::PointerCoercion(PointerCoercion::Unsize, _), ref operand, _to_ty, ) => { let operand = codegen_operand(fx, operand); crate::unsize::coerce_unsized_into(fx, operand, lval); } - Rvalue::Cast(CastKind::DynStar, ref operand, _) => { + Rvalue::Cast( + CastKind::PointerCoercion(PointerCoercion::DynStar, _), + ref operand, + _, + ) => { let operand = codegen_operand(fx, operand); crate::unsize::coerce_dyn_star(fx, operand, lval); } diff --git a/compiler/rustc_codegen_cranelift/src/constant.rs b/compiler/rustc_codegen_cranelift/src/constant.rs index 6ba6a64c5448..ab78584332a0 100644 --- a/compiler/rustc_codegen_cranelift/src/constant.rs +++ b/compiler/rustc_codegen_cranelift/src/constant.rs @@ -161,13 +161,13 @@ pub(crate) fn codegen_const_value<'tcx>( fx.module.declare_func_in_func(func_id, &mut fx.bcx.func); fx.bcx.ins().func_addr(fx.pointer_type, local_func_id) } - GlobalAlloc::VTable(ty, trait_ref) => { + GlobalAlloc::VTable(ty, dyn_ty) => { let data_id = data_id_for_vtable( fx.tcx, &mut fx.constants_cx, fx.module, ty, - trait_ref, + dyn_ty.principal(), ); let local_data_id = fx.module.declare_data_in_func(data_id, &mut fx.bcx.func); @@ -456,8 +456,8 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut dyn Module, cx: &mut Constant GlobalAlloc::Memory(target_alloc) => { data_id_for_alloc_id(cx, module, alloc_id, target_alloc.inner().mutability) } - GlobalAlloc::VTable(ty, trait_ref) => { - data_id_for_vtable(tcx, cx, module, ty, trait_ref) + GlobalAlloc::VTable(ty, dyn_ty) => { + data_id_for_vtable(tcx, cx, module, ty, dyn_ty.principal()) } GlobalAlloc::Static(def_id) => { if tcx.codegen_fn_attrs(def_id).flags.contains(CodegenFnAttrFlags::THREAD_LOCAL) diff --git a/compiler/rustc_codegen_cranelift/src/unsize.rs b/compiler/rustc_codegen_cranelift/src/unsize.rs index 8cfe93b4d9c9..339628053a95 100644 --- a/compiler/rustc_codegen_cranelift/src/unsize.rs +++ b/compiler/rustc_codegen_cranelift/src/unsize.rs @@ -34,7 +34,22 @@ pub(crate) fn unsized_info<'tcx>( let old_info = old_info.expect("unsized_info: missing old info for trait upcasting coercion"); if data_a.principal_def_id() == data_b.principal_def_id() { - // A NOP cast that doesn't actually change anything, should be allowed even with invalid vtables. + // Codegen takes advantage of the additional assumption, where if the + // principal trait def id of what's being casted doesn't change, + // then we don't need to adjust the vtable at all. This + // corresponds to the fact that `dyn Tr: Unsize>` + // requires that `A = B`; we don't allow *upcasting* objects + // between the same trait with different args. If we, for + // some reason, were to relax the `Unsize` trait, it could become + // unsound, so let's assert here that the trait refs are *equal*. + // + // We can use `assert_eq` because the binders should have been anonymized, + // and because higher-ranked equality now requires the binders are equal. + debug_assert_eq!( + data_a.principal(), + data_b.principal(), + "NOP unsize vtable changed principal trait ref: {data_a} -> {data_b}" + ); return old_info; } diff --git a/compiler/rustc_codegen_gcc/src/builder.rs b/compiler/rustc_codegen_gcc/src/builder.rs index 2b5c39a4d168..b68733043823 100644 --- a/compiler/rustc_codegen_gcc/src/builder.rs +++ b/compiler/rustc_codegen_gcc/src/builder.rs @@ -503,6 +503,7 @@ impl<'a, 'gcc, 'tcx> Deref for Builder<'a, 'gcc, 'tcx> { impl<'gcc, 'tcx> BackendTypes for Builder<'_, 'gcc, 'tcx> { type Value = as BackendTypes>::Value; + type Metadata = as BackendTypes>::Metadata; type Function = as BackendTypes>::Function; type BasicBlock = as BackendTypes>::BasicBlock; type Type = as BackendTypes>::Type; diff --git a/compiler/rustc_codegen_gcc/src/common.rs b/compiler/rustc_codegen_gcc/src/common.rs index c7cf73f1992c..726b126e727d 100644 --- a/compiler/rustc_codegen_gcc/src/common.rs +++ b/compiler/rustc_codegen_gcc/src/common.rs @@ -224,10 +224,10 @@ impl<'gcc, 'tcx> ConstCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { value } GlobalAlloc::Function { instance, .. } => self.get_fn_addr(instance), - GlobalAlloc::VTable(ty, trait_ref) => { + GlobalAlloc::VTable(ty, dyn_ty) => { let alloc = self .tcx - .global_alloc(self.tcx.vtable_allocation((ty, trait_ref))) + .global_alloc(self.tcx.vtable_allocation((ty, dyn_ty.principal()))) .unwrap_memory(); let init = const_alloc_to_gcc(self, alloc); self.static_addr_of(init, alloc.inner().align, None) diff --git a/compiler/rustc_codegen_gcc/src/context.rs b/compiler/rustc_codegen_gcc/src/context.rs index 6542677af677..caccf8296a29 100644 --- a/compiler/rustc_codegen_gcc/src/context.rs +++ b/compiler/rustc_codegen_gcc/src/context.rs @@ -414,6 +414,7 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { impl<'gcc, 'tcx> BackendTypes for CodegenCx<'gcc, 'tcx> { type Value = RValue<'gcc>; + type Metadata = RValue<'gcc>; type Function = RValue<'gcc>; type BasicBlock = Block<'gcc>; diff --git a/compiler/rustc_codegen_llvm/src/asm.rs b/compiler/rustc_codegen_llvm/src/asm.rs index fabe7e56b8ff..7621f111fe25 100644 --- a/compiler/rustc_codegen_llvm/src/asm.rs +++ b/compiler/rustc_codegen_llvm/src/asm.rs @@ -504,10 +504,15 @@ pub(crate) fn inline_asm_call<'ll>( // due to the asm template string coming from a macro. LLVM will // default to the first srcloc for lines that don't have an // associated srcloc. - srcloc.push(bx.const_i32(0)); + srcloc.push(llvm::LLVMValueAsMetadata(bx.const_i32(0))); } - srcloc.extend(line_spans.iter().map(|span| bx.const_i32(span.lo().to_u32() as i32))); - let md = llvm::LLVMMDNodeInContext(bx.llcx, srcloc.as_ptr(), srcloc.len() as u32); + srcloc.extend( + line_spans + .iter() + .map(|span| llvm::LLVMValueAsMetadata(bx.const_i32(span.lo().to_u32() as i32))), + ); + let md = llvm::LLVMMDNodeInContext2(bx.llcx, srcloc.as_ptr(), srcloc.len()); + let md = llvm::LLVMMetadataAsValue(&bx.llcx, md); llvm::LLVMSetMetadata(call, kind, md); Some(call) diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index 5e510177818b..1f7a923dd2c6 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -808,8 +808,7 @@ struct ThinLTOKeysMap { impl ThinLTOKeysMap { fn save_to_file(&self, path: &Path) -> io::Result<()> { use std::io::Write; - let file = File::create(path)?; - let mut writer = io::BufWriter::new(file); + let mut writer = File::create_buffered(path)?; // The entries are loaded back into a hash map in `load_from_file()`, so // the order in which we write them to file here does not matter. for (module, key) in &self.keys { @@ -821,8 +820,8 @@ impl ThinLTOKeysMap { fn load_from_file(path: &Path) -> io::Result { use std::io::BufRead; let mut keys = BTreeMap::default(); - let file = File::open(path)?; - for line in io::BufReader::new(file).lines() { + let file = File::open_buffered(path)?; + for line in file.lines() { let line = line?; let mut split = line.split(' '); let module = split.next().unwrap(); diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index a8af2de1ddd3..4e0c62c8bf8c 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -56,6 +56,7 @@ const UNNAMED: *const c_char = c"".as_ptr(); impl<'ll, 'tcx> BackendTypes for Builder<'_, 'll, 'tcx> { type Value = as BackendTypes>::Value; + type Metadata = as BackendTypes>::Metadata; type Function = as BackendTypes>::Function; type BasicBlock = as BackendTypes>::BasicBlock; type Type = as BackendTypes>::Type; @@ -623,26 +624,19 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { unsafe { let llty = self.cx.val_ty(load); - let v = [ - self.cx.const_uint_big(llty, range.start), - self.cx.const_uint_big(llty, range.end.wrapping_add(1)), + let md = [ + llvm::LLVMValueAsMetadata(self.cx.const_uint_big(llty, range.start)), + llvm::LLVMValueAsMetadata(self.cx.const_uint_big(llty, range.end.wrapping_add(1))), ]; - - llvm::LLVMSetMetadata( - load, - llvm::MD_range as c_uint, - llvm::LLVMMDNodeInContext(self.cx.llcx, v.as_ptr(), v.len() as c_uint), - ); + let md = llvm::LLVMMDNodeInContext2(self.cx.llcx, md.as_ptr(), md.len()); + self.set_metadata(load, llvm::MD_range, md); } } fn nonnull_metadata(&mut self, load: &'ll Value) { unsafe { - llvm::LLVMSetMetadata( - load, - llvm::MD_nonnull as c_uint, - llvm::LLVMMDNodeInContext(self.cx.llcx, ptr::null(), 0), - ); + let md = llvm::LLVMMDNodeInContext2(self.cx.llcx, ptr::null(), 0); + self.set_metadata(load, llvm::MD_nonnull, md); } } @@ -690,9 +684,9 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { // *always* point to a metadata value of the integer 1. // // [1]: https://llvm.org/docs/LangRef.html#store-instruction - let one = self.cx.const_i32(1); - let node = llvm::LLVMMDNodeInContext(self.cx.llcx, &one, 1); - llvm::LLVMSetMetadata(store, llvm::MD_nontemporal as c_uint, node); + let one = llvm::LLVMValueAsMetadata(self.cx.const_i32(1)); + let md = llvm::LLVMMDNodeInContext2(self.cx.llcx, &one, 1); + self.set_metadata(store, llvm::MD_nontemporal, md); } } store @@ -1157,11 +1151,8 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { fn set_invariant_load(&mut self, load: &'ll Value) { unsafe { - llvm::LLVMSetMetadata( - load, - llvm::MD_invariant_load as c_uint, - llvm::LLVMMDNodeInContext(self.cx.llcx, ptr::null(), 0), - ); + let md = llvm::LLVMMDNodeInContext2(self.cx.llcx, ptr::null(), 0); + self.set_metadata(load, llvm::MD_invariant_load, md); } } @@ -1290,33 +1281,23 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { fn align_metadata(&mut self, load: &'ll Value, align: Align) { unsafe { - let v = [self.cx.const_u64(align.bytes())]; - - llvm::LLVMSetMetadata( - load, - llvm::MD_align as c_uint, - llvm::LLVMMDNodeInContext(self.cx.llcx, v.as_ptr(), v.len() as c_uint), - ); + let md = [llvm::LLVMValueAsMetadata(self.cx.const_u64(align.bytes()))]; + let md = llvm::LLVMMDNodeInContext2(self.cx.llcx, md.as_ptr(), md.len()); + self.set_metadata(load, llvm::MD_align, md); } } fn noundef_metadata(&mut self, load: &'ll Value) { unsafe { - llvm::LLVMSetMetadata( - load, - llvm::MD_noundef as c_uint, - llvm::LLVMMDNodeInContext(self.cx.llcx, ptr::null(), 0), - ); + let md = llvm::LLVMMDNodeInContext2(self.cx.llcx, ptr::null(), 0); + self.set_metadata(load, llvm::MD_noundef, md); } } pub(crate) fn set_unpredictable(&mut self, inst: &'ll Value) { unsafe { - llvm::LLVMSetMetadata( - inst, - llvm::MD_unpredictable as c_uint, - llvm::LLVMMDNodeInContext(self.cx.llcx, ptr::null(), 0), - ); + let md = llvm::LLVMMDNodeInContext2(self.cx.llcx, ptr::null(), 0); + self.set_metadata(inst, llvm::MD_unpredictable, md); } } diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs index 508c2d1a820e..31d59905446f 100644 --- a/compiler/rustc_codegen_llvm/src/common.rs +++ b/compiler/rustc_codegen_llvm/src/common.rs @@ -14,7 +14,7 @@ use tracing::debug; use crate::consts::const_alloc_to_llvm; pub(crate) use crate::context::CodegenCx; -use crate::llvm::{self, BasicBlock, Bool, ConstantInt, False, OperandBundleDef, True}; +use crate::llvm::{self, BasicBlock, Bool, ConstantInt, False, Metadata, OperandBundleDef, True}; use crate::type_::Type; use crate::value::Value; @@ -79,6 +79,7 @@ impl<'ll> Funclet<'ll> { impl<'ll> BackendTypes for CodegenCx<'ll, '_> { type Value = &'ll Value; + type Metadata = &'ll Metadata; // FIXME(eddyb) replace this with a `Function` "subclass" of `Value`. type Function = &'ll Value; @@ -290,10 +291,10 @@ impl<'ll, 'tcx> ConstCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { self.get_fn_addr(instance.polymorphize(self.tcx)), self.data_layout().instruction_address_space, ), - GlobalAlloc::VTable(ty, trait_ref) => { + GlobalAlloc::VTable(ty, dyn_ty) => { let alloc = self .tcx - .global_alloc(self.tcx.vtable_allocation((ty, trait_ref))) + .global_alloc(self.tcx.vtable_allocation((ty, dyn_ty.principal()))) .unwrap_memory(); let init = const_alloc_to_llvm(self, alloc, /*static*/ false); let value = self.static_addr_of(init, alloc.inner().align, None); diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index 2f9a7d77a3b7..2b8912d1db2a 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -1,9 +1,8 @@ use std::borrow::Borrow; use std::cell::{Cell, RefCell}; -use std::ffi::CStr; +use std::ffi::{CStr, c_uint}; use std::str; -use libc::c_uint; use rustc_codegen_ssa::base::{wants_msvc_seh, wants_wasm_eh}; use rustc_codegen_ssa::errors as ssa_errors; use rustc_codegen_ssa::traits::*; @@ -31,6 +30,7 @@ use smallvec::SmallVec; use crate::back::write::to_llvm_code_model; use crate::callee::get_fn; use crate::debuginfo::metadata::apply_vcall_visibility_metadata; +use crate::llvm::{Metadata, MetadataType}; use crate::type_::Type; use crate::value::Value; use crate::{attributes, coverageinfo, debuginfo, llvm, llvm_util}; @@ -404,17 +404,17 @@ pub(crate) unsafe fn create_module<'ll>( let rustc_producer = format!("rustc version {}", option_env!("CFG_VERSION").expect("CFG_VERSION")); let name_metadata = unsafe { - llvm::LLVMMDStringInContext( + llvm::LLVMMDStringInContext2( llcx, rustc_producer.as_ptr().cast(), - rustc_producer.as_bytes().len() as c_uint, + rustc_producer.as_bytes().len(), ) }; unsafe { llvm::LLVMAddNamedMetadataOperand( llmod, c"llvm.ident".as_ptr(), - llvm::LLVMMDNodeInContext(llcx, &name_metadata, 1), + &llvm::LLVMMetadataAsValue(llcx, llvm::LLVMMDNodeInContext2(llcx, &name_metadata, 1)), ); } @@ -1119,6 +1119,14 @@ impl CodegenCx<'_, '_> { name.push_str(&(idx as u64).to_base(ALPHANUMERIC_ONLY)); name } + + /// A wrapper for [`llvm::LLVMSetMetadata`], but it takes `Metadata` as a parameter instead of `Value`. + pub(crate) fn set_metadata<'a>(&self, val: &'a Value, kind_id: MetadataType, md: &'a Metadata) { + unsafe { + let node = llvm::LLVMMetadataAsValue(&self.llcx, md); + llvm::LLVMSetMetadata(val, kind_id as c_uint, node); + } + } } impl HasDataLayout for CodegenCx<'_, '_> { diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index d429d5730dd0..964b83c0fa07 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -1547,20 +1547,16 @@ pub(crate) fn apply_vcall_visibility_metadata<'ll, 'tcx>( let trait_ref_typeid = typeid_for_trait_ref(cx.tcx, trait_ref); unsafe { - let typeid = llvm::LLVMMDStringInContext( + let typeid = llvm::LLVMMDStringInContext2( cx.llcx, trait_ref_typeid.as_ptr() as *const c_char, - trait_ref_typeid.as_bytes().len() as c_uint, + trait_ref_typeid.as_bytes().len(), ); - let v = [cx.const_usize(0), typeid]; + let v = [llvm::LLVMValueAsMetadata(cx.const_usize(0)), typeid]; llvm::LLVMRustGlobalAddMetadata( vtable, llvm::MD_type as c_uint, - llvm::LLVMValueAsMetadata(llvm::LLVMMDNodeInContext( - cx.llcx, - v.as_ptr(), - v.len() as c_uint, - )), + llvm::LLVMMDNodeInContext2(cx.llcx, v.as_ptr(), v.len()), ); let vcall_visibility = llvm::LLVMValueAsMetadata(cx.const_u64(vcall_visibility as u64)); let vcall_visibility_metadata = llvm::LLVMMDNodeInContext2(cx.llcx, &vcall_visibility, 1); diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 6ee5ca73eef6..c66c80da9fcc 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -20,7 +20,7 @@ use tracing::debug; use crate::abi::{Abi, FnAbi, FnAbiLlvmExt, LlvmType, PassMode}; use crate::builder::Builder; use crate::context::CodegenCx; -use crate::llvm; +use crate::llvm::{self, Metadata}; use crate::type_::Type; use crate::type_of::LayoutLlvmExt; use crate::va_arg::emit_va_arg; @@ -613,9 +613,10 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { } } - fn type_test(&mut self, pointer: Self::Value, typeid: Self::Value) -> Self::Value { + fn type_test(&mut self, pointer: Self::Value, typeid: Self::Metadata) -> Self::Value { // Test the called operand using llvm.type.test intrinsic. The LowerTypeTests link-time // optimization pass replaces calls to this intrinsic with code to test type membership. + let typeid = unsafe { llvm::LLVMMetadataAsValue(&self.llcx, typeid) }; self.call_intrinsic("llvm.type.test", &[pointer, typeid]) } @@ -623,8 +624,9 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { &mut self, llvtable: &'ll Value, vtable_byte_offset: u64, - typeid: &'ll Value, + typeid: &'ll Metadata, ) -> Self::Value { + let typeid = unsafe { llvm::LLVMMetadataAsValue(&self.llcx, typeid) }; let vtable_byte_offset = self.const_i32(vtable_byte_offset as i32); let type_checked_load = self.call_intrinsic("llvm.type.checked.load", &[llvtable, vtable_byte_offset, typeid]); diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index d69112612ba6..bdfc0f626f80 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -11,6 +11,7 @@ #![feature(assert_matches)] #![feature(exact_size_is_empty)] #![feature(extern_types)] +#![feature(file_buffered)] #![feature(hash_raw_entry)] #![feature(impl_trait_in_assoc_type)] #![feature(iter_intersperse)] diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 4b71cb8963b0..9aabfd794bad 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -885,17 +885,7 @@ unsafe extern "C" { pub fn LLVMGetPoison(Ty: &Type) -> &Value; // Operations on metadata - // FIXME: deprecated, replace with LLVMMDStringInContext2 - pub fn LLVMMDStringInContext(C: &Context, Str: *const c_char, SLen: c_uint) -> &Value; - pub fn LLVMMDStringInContext2(C: &Context, Str: *const c_char, SLen: size_t) -> &Metadata; - - // FIXME: deprecated, replace with LLVMMDNodeInContext2 - pub fn LLVMMDNodeInContext<'a>( - C: &'a Context, - Vals: *const &'a Value, - Count: c_uint, - ) -> &'a Value; pub fn LLVMMDNodeInContext2<'a>( C: &'a Context, Vals: *const &'a Metadata, diff --git a/compiler/rustc_codegen_llvm/src/type_.rs b/compiler/rustc_codegen_llvm/src/type_.rs index 2c2b9030b7c9..f1efc7a3dacb 100644 --- a/compiler/rustc_codegen_llvm/src/type_.rs +++ b/compiler/rustc_codegen_llvm/src/type_.rs @@ -13,7 +13,7 @@ use rustc_target::abi::{AddressSpace, Align, Integer, Size}; use crate::abi::{FnAbiLlvmExt, LlvmType}; use crate::context::CodegenCx; pub(crate) use crate::llvm::Type; -use crate::llvm::{Bool, False, True}; +use crate::llvm::{Bool, False, Metadata, True}; use crate::type_of::LayoutLlvmExt; use crate::value::Value; use crate::{common, llvm}; @@ -283,43 +283,31 @@ impl<'ll, 'tcx> LayoutTypeCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { impl<'ll, 'tcx> TypeMembershipCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { fn add_type_metadata(&self, function: &'ll Value, typeid: String) { let typeid_metadata = self.typeid_metadata(typeid).unwrap(); - let v = [self.const_usize(0), typeid_metadata]; unsafe { + let v = [llvm::LLVMValueAsMetadata(self.const_usize(0)), typeid_metadata]; llvm::LLVMRustGlobalAddMetadata( function, llvm::MD_type as c_uint, - llvm::LLVMValueAsMetadata(llvm::LLVMMDNodeInContext( - self.llcx, - v.as_ptr(), - v.len() as c_uint, - )), + llvm::LLVMMDNodeInContext2(self.llcx, v.as_ptr(), v.len()), ) } } fn set_type_metadata(&self, function: &'ll Value, typeid: String) { let typeid_metadata = self.typeid_metadata(typeid).unwrap(); - let v = [self.const_usize(0), typeid_metadata]; unsafe { + let v = [llvm::LLVMValueAsMetadata(self.const_usize(0)), typeid_metadata]; llvm::LLVMGlobalSetMetadata( function, llvm::MD_type as c_uint, - llvm::LLVMValueAsMetadata(llvm::LLVMMDNodeInContext( - self.llcx, - v.as_ptr(), - v.len() as c_uint, - )), + llvm::LLVMMDNodeInContext2(self.llcx, v.as_ptr(), v.len()), ) } } - fn typeid_metadata(&self, typeid: String) -> Option<&'ll Value> { + fn typeid_metadata(&self, typeid: String) -> Option<&'ll Metadata> { Some(unsafe { - llvm::LLVMMDStringInContext( - self.llcx, - typeid.as_ptr() as *const c_char, - typeid.len() as c_uint, - ) + llvm::LLVMMDStringInContext2(self.llcx, typeid.as_ptr() as *const c_char, typeid.len()) }) } diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 892dfb91201e..69693230ce07 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -1087,16 +1087,17 @@ fn link_natively( let strip = sess.opts.cg.strip; if sess.target.is_like_osx { + let stripcmd = "/usr/bin/strip"; match (strip, crate_type) { (Strip::Debuginfo, _) => { - strip_symbols_with_external_utility(sess, "strip", out_filename, Some("-S")) + strip_symbols_with_external_utility(sess, stripcmd, out_filename, Some("-S")) } // Per the manpage, `-x` is the maximum safe strip level for dynamic libraries. (#93988) (Strip::Symbols, CrateType::Dylib | CrateType::Cdylib | CrateType::ProcMacro) => { - strip_symbols_with_external_utility(sess, "strip", out_filename, Some("-x")) + strip_symbols_with_external_utility(sess, stripcmd, out_filename, Some("-x")) } (Strip::Symbols, _) => { - strip_symbols_with_external_utility(sess, "strip", out_filename, None) + strip_symbols_with_external_utility(sess, stripcmd, out_filename, None) } (Strip::None, _) => {} } diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index 84817e198445..a73ec83ee62c 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -1,9 +1,8 @@ use std::ffi::{OsStr, OsString}; use std::fs::{self, File}; use std::io::prelude::*; -use std::io::{self, BufWriter}; use std::path::{Path, PathBuf}; -use std::{env, iter, mem, str}; +use std::{env, io, iter, mem, str}; use cc::windows_registry; use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; @@ -754,7 +753,7 @@ impl<'a> Linker for GccLinker<'a> { if self.sess.target.is_like_osx { // Write a plain, newline-separated list of symbols let res: io::Result<()> = try { - let mut f = BufWriter::new(File::create(&path)?); + let mut f = File::create_buffered(&path)?; for sym in symbols { debug!(" _{sym}"); writeln!(f, "_{sym}")?; @@ -765,7 +764,7 @@ impl<'a> Linker for GccLinker<'a> { } } else if is_windows { let res: io::Result<()> = try { - let mut f = BufWriter::new(File::create(&path)?); + let mut f = File::create_buffered(&path)?; // .def file similar to MSVC one but without LIBRARY section // because LD doesn't like when it's empty @@ -781,7 +780,7 @@ impl<'a> Linker for GccLinker<'a> { } else { // Write an LD version script let res: io::Result<()> = try { - let mut f = BufWriter::new(File::create(&path)?); + let mut f = File::create_buffered(&path)?; writeln!(f, "{{")?; if !symbols.is_empty() { writeln!(f, " global:")?; @@ -1059,7 +1058,7 @@ impl<'a> Linker for MsvcLinker<'a> { let path = tmpdir.join("lib.def"); let res: io::Result<()> = try { - let mut f = BufWriter::new(File::create(&path)?); + let mut f = File::create_buffered(&path)?; // Start off with the standard module name header and then go // straight to exports. @@ -1648,7 +1647,7 @@ impl<'a> Linker for AixLinker<'a> { fn export_symbols(&mut self, tmpdir: &Path, _crate_type: CrateType, symbols: &[String]) { let path = tmpdir.join("list.exp"); let res: io::Result<()> = try { - let mut f = BufWriter::new(File::create(&path)?); + let mut f = File::create_buffered(&path)?; // FIXME: use llvm-nm to generate export list. for symbol in symbols { debug!(" _{symbol}"); @@ -1961,7 +1960,7 @@ impl<'a> Linker for BpfLinker<'a> { fn export_symbols(&mut self, tmpdir: &Path, _crate_type: CrateType, symbols: &[String]) { let path = tmpdir.join("symbols"); let res: io::Result<()> = try { - let mut f = BufWriter::new(File::create(&path)?); + let mut f = File::create_buffered(&path)?; for sym in symbols { writeln!(f, "{sym}")?; } diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 8e4385bee1aa..5c67600e4eec 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -37,6 +37,7 @@ use crate::back::write::{ submit_codegened_module_to_llvm, submit_post_lto_module_to_llvm, submit_pre_lto_module_to_llvm, }; use crate::common::{self, IntPredicate, RealPredicate, TypeKind}; +use crate::meth::load_vtable; use crate::mir::operand::OperandValue; use crate::mir::place::PlaceRef; use crate::traits::*; @@ -124,8 +125,28 @@ fn unsized_info<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let old_info = old_info.expect("unsized_info: missing old info for trait upcasting coercion"); if data_a.principal_def_id() == data_b.principal_def_id() { - // A NOP cast that doesn't actually change anything, should be allowed even with - // invalid vtables. + // Codegen takes advantage of the additional assumption, where if the + // principal trait def id of what's being casted doesn't change, + // then we don't need to adjust the vtable at all. This + // corresponds to the fact that `dyn Tr: Unsize>` + // requires that `A = B`; we don't allow *upcasting* objects + // between the same trait with different args. If we, for + // some reason, were to relax the `Unsize` trait, it could become + // unsound, so let's assert here that the trait refs are *equal*. + // + // We can use `assert_eq` because the binders should have been anonymized, + // and because higher-ranked equality now requires the binders are equal. + debug_assert_eq!( + data_a.principal(), + data_b.principal(), + "NOP unsize vtable changed principal trait ref: {data_a} -> {data_b}" + ); + + // A NOP cast that doesn't actually change anything, let's avoid any + // unnecessary work. This relies on the assumption that if the principal + // traits are equal, then the associated type bounds (`dyn Trait`) + // are also equal, which is ensured by the fact that normalization is + // a function and we do not allow overlapping impls. return old_info; } @@ -135,14 +156,8 @@ fn unsized_info<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( if let Some(entry_idx) = vptr_entry_idx { let ptr_size = bx.data_layout().pointer_size; - let ptr_align = bx.data_layout().pointer_align.abi; let vtable_byte_offset = u64::try_from(entry_idx).unwrap() * ptr_size.bytes(); - let gep = bx.inbounds_ptradd(old_info, bx.const_usize(vtable_byte_offset)); - let new_vptr = bx.load(bx.type_ptr(), gep, ptr_align); - bx.nonnull_metadata(new_vptr); - // VTable loads are invariant. - bx.set_invariant_load(new_vptr); - new_vptr + load_vtable(bx, old_info, bx.type_ptr(), vtable_byte_offset, source, true) } else { old_info } diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index ec0520fbb09d..162d14272a55 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -6,6 +6,7 @@ #![doc(rust_logo)] #![feature(assert_matches)] #![feature(box_patterns)] +#![feature(file_buffered)] #![feature(if_let_guard)] #![feature(let_chains)] #![feature(negative_impls)] diff --git a/compiler/rustc_codegen_ssa/src/meth.rs b/compiler/rustc_codegen_ssa/src/meth.rs index ecc3b2b24f1c..7eb0ecd12ffe 100644 --- a/compiler/rustc_codegen_ssa/src/meth.rs +++ b/compiler/rustc_codegen_ssa/src/meth.rs @@ -28,27 +28,9 @@ impl<'a, 'tcx> VirtualIndex { let llty = bx.fn_ptr_backend_type(fn_abi); let ptr_size = bx.data_layout().pointer_size; - let ptr_align = bx.data_layout().pointer_align.abi; let vtable_byte_offset = self.0 * ptr_size.bytes(); - if bx.cx().sess().opts.unstable_opts.virtual_function_elimination - && bx.cx().sess().lto() == Lto::Fat - { - let typeid = bx - .typeid_metadata(typeid_for_trait_ref(bx.tcx(), expect_dyn_trait_in_self(ty))) - .unwrap(); - let func = bx.type_checked_load(llvtable, vtable_byte_offset, typeid); - func - } else { - let gep = bx.inbounds_ptradd(llvtable, bx.const_usize(vtable_byte_offset)); - let ptr = bx.load(llty, gep, ptr_align); - // VTable loads are invariant. - bx.set_invariant_load(ptr); - if nonnull { - bx.nonnull_metadata(ptr); - } - ptr - } + load_vtable(bx, llvtable, llty, vtable_byte_offset, ty, nonnull) } pub(crate) fn get_optional_fn>( @@ -75,31 +57,27 @@ impl<'a, 'tcx> VirtualIndex { self, bx: &mut Bx, llvtable: Bx::Value, + ty: Ty<'tcx>, ) -> Bx::Value { // Load the data pointer from the object. debug!("get_int({:?}, {:?})", llvtable, self); let llty = bx.type_isize(); let ptr_size = bx.data_layout().pointer_size; - let ptr_align = bx.data_layout().pointer_align.abi; let vtable_byte_offset = self.0 * ptr_size.bytes(); - let gep = bx.inbounds_ptradd(llvtable, bx.const_usize(vtable_byte_offset)); - let ptr = bx.load(llty, gep, ptr_align); - // VTable loads are invariant. - bx.set_invariant_load(ptr); - ptr + load_vtable(bx, llvtable, llty, vtable_byte_offset, ty, false) } } /// This takes a valid `self` receiver type and extracts the principal trait -/// ref of the type. -fn expect_dyn_trait_in_self(ty: Ty<'_>) -> ty::PolyExistentialTraitRef<'_> { +/// ref of the type. Return `None` if there is no principal trait. +fn dyn_trait_in_self(ty: Ty<'_>) -> Option> { for arg in ty.peel_refs().walk() { if let GenericArgKind::Type(ty) = arg.unpack() && let ty::Dynamic(data, _, _) = ty.kind() { - return data.principal().expect("expected principal trait object"); + return data.principal(); } } @@ -138,3 +116,36 @@ pub(crate) fn get_vtable<'tcx, Cx: CodegenMethods<'tcx>>( cx.vtables().borrow_mut().insert((ty, trait_ref), vtable); vtable } + +/// Call this function whenever you need to load a vtable. +pub(crate) fn load_vtable<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( + bx: &mut Bx, + llvtable: Bx::Value, + llty: Bx::Type, + vtable_byte_offset: u64, + ty: Ty<'tcx>, + nonnull: bool, +) -> Bx::Value { + let ptr_align = bx.data_layout().pointer_align.abi; + + if bx.cx().sess().opts.unstable_opts.virtual_function_elimination + && bx.cx().sess().lto() == Lto::Fat + { + if let Some(trait_ref) = dyn_trait_in_self(ty) { + let typeid = bx.typeid_metadata(typeid_for_trait_ref(bx.tcx(), trait_ref)).unwrap(); + let func = bx.type_checked_load(llvtable, vtable_byte_offset, typeid); + return func; + } else if nonnull { + bug!("load nonnull value from a vtable without a principal trait") + } + } + + let gep = bx.inbounds_ptradd(llvtable, bx.const_usize(vtable_byte_offset)); + let ptr = bx.load(llty, gep, ptr_align); + // VTable loads are invariant. + bx.set_invariant_load(ptr); + if nonnull { + bx.nonnull_metadata(ptr); + } + ptr +} diff --git a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs index 9874809ea2bb..146f55f95c21 100644 --- a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs +++ b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs @@ -1,4 +1,5 @@ use std::collections::hash_map::Entry; +use std::marker::PhantomData; use std::ops::Range; use rustc_data_structures::fx::FxHashMap; @@ -14,7 +15,7 @@ use rustc_target::abi::{Abi, FieldIdx, FieldsShape, Size, VariantIdx}; use super::operand::{OperandRef, OperandValue}; use super::place::{PlaceRef, PlaceValue}; -use super::{FunctionCx, LocalRef}; +use super::{FunctionCx, LocalRef, PerLocalVarDebugInfoIndexVec}; use crate::traits::*; pub struct FunctionDebugContext<'tcx, S, L> { @@ -48,6 +49,17 @@ pub struct PerLocalVarDebugInfo<'tcx, D> { pub projection: &'tcx ty::List>, } +/// Information needed to emit a constant. +pub struct ConstDebugInfo<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> { + pub name: String, + pub source_info: mir::SourceInfo, + pub operand: OperandRef<'tcx, Bx::Value>, + pub dbg_var: Bx::DIVariable, + pub dbg_loc: Bx::DILocation, + pub fragment: Option>, + pub _phantom: PhantomData<&'a ()>, +} + #[derive(Clone, Copy, Debug)] pub struct DebugScope { pub dbg_scope: S, @@ -427,11 +439,25 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } } - pub(crate) fn debug_introduce_locals(&self, bx: &mut Bx) { + pub(crate) fn debug_introduce_locals( + &self, + bx: &mut Bx, + consts: Vec>, + ) { if bx.sess().opts.debuginfo == DebugInfo::Full || !bx.sess().fewer_names() { for local in self.locals.indices() { self.debug_introduce_local(bx, local); } + + for ConstDebugInfo { name, source_info, operand, dbg_var, dbg_loc, fragment, .. } in + consts.into_iter() + { + self.set_debug_loc(bx, source_info); + let base = FunctionCx::spill_operand_to_stack(operand, Some(name), bx); + bx.clear_dbg_loc(); + + bx.dbg_var_addr(dbg_var, dbg_loc, base.val.llval, Size::ZERO, &[], fragment); + } } } @@ -439,7 +465,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { pub(crate) fn compute_per_local_var_debug_info( &self, bx: &mut Bx, - ) -> Option>>> { + ) -> Option<( + PerLocalVarDebugInfoIndexVec<'tcx, Bx::DIVariable>, + Vec>, + )> { let full_debug_info = self.cx.sess().opts.debuginfo == DebugInfo::Full; let target_is_msvc = self.cx.sess().target.is_like_msvc; @@ -449,6 +478,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } let mut per_local = IndexVec::from_elem(vec![], &self.mir.local_decls); + let mut constants = vec![]; let mut params_seen: FxHashMap<_, Bx::DIVariable> = Default::default(); for var in &self.mir.var_debug_info { let dbg_scope_and_span = if full_debug_info { @@ -545,23 +575,19 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let Some(dbg_loc) = self.dbg_loc(var.source_info) else { continue }; let operand = self.eval_mir_constant_to_operand(bx, &c); - self.set_debug_loc(bx, var.source_info); - let base = - Self::spill_operand_to_stack(operand, Some(var.name.to_string()), bx); - bx.clear_dbg_loc(); - - bx.dbg_var_addr( + constants.push(ConstDebugInfo { + name: var.name.to_string(), + source_info: var.source_info, + operand, dbg_var, dbg_loc, - base.val.llval, - Size::ZERO, - &[], fragment, - ); + _phantom: PhantomData, + }); } } } } - Some(per_local) + Some((per_local, constants)) } } diff --git a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs index 71541e6ab41a..32cc78187b9e 100644 --- a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs @@ -126,7 +126,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { sym::vtable_align => ty::COMMON_VTABLE_ENTRIES_ALIGN, _ => bug!(), }; - let value = meth::VirtualIndex::from_index(idx).get_usize(bx, vtable); + let value = meth::VirtualIndex::from_index(idx).get_usize(bx, vtable, callee_ty); match name { // Size is always <= isize::MAX. sym::vtable_size => { diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs index 202baddec723..c4fb24aa625d 100644 --- a/compiler/rustc_codegen_ssa/src/mir/mod.rs +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs @@ -41,6 +41,9 @@ enum CachedLlbb { Skip, } +type PerLocalVarDebugInfoIndexVec<'tcx, V> = + IndexVec>>; + /// Master context for codegenning from MIR. pub struct FunctionCx<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> { instance: Instance<'tcx>, @@ -107,8 +110,7 @@ pub struct FunctionCx<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> { /// All `VarDebugInfo` from the MIR body, partitioned by `Local`. /// This is `None` if no variable debuginfo/names are needed. - per_local_var_debug_info: - Option>>>, + per_local_var_debug_info: Option>, /// Caller location propagated if this function has `#[track_caller]`. caller_location: Option>, @@ -216,7 +218,9 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( // monomorphization, and if there is an error during collection then codegen never starts -- so // we don't have to do it again. - fx.per_local_var_debug_info = fx.compute_per_local_var_debug_info(&mut start_bx); + let (per_local_var_debug_info, consts_debug_info) = + fx.compute_per_local_var_debug_info(&mut start_bx).unzip(); + fx.per_local_var_debug_info = per_local_var_debug_info; let traversal_order = traversal::mono_reachable_reverse_postorder(mir, cx.tcx(), instance); let memory_locals = analyze::non_ssa_locals(&fx, &traversal_order); @@ -268,7 +272,7 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( fx.initialize_locals(local_values); // Apply debuginfo to the newly allocated locals. - fx.debug_introduce_locals(&mut start_bx); + fx.debug_introduce_locals(&mut start_bx, consts_debug_info.unwrap_or_default()); // If the backend supports coverage, and coverage is enabled for this function, // do any necessary start-of-function codegen (e.g. locals for MC/DC bitmaps). diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index ad0e03fc2630..f9c0f3ce9414 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -34,7 +34,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } mir::Rvalue::Cast( - mir::CastKind::PointerCoercion(PointerCoercion::Unsize), + mir::CastKind::PointerCoercion(PointerCoercion::Unsize, _), ref source, _, ) => { @@ -465,7 +465,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let lladdr = bx.ptrtoint(llptr, llcast_ty); OperandValue::Immediate(lladdr) } - mir::CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer) => { + mir::CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer, _) => { match *operand.layout.ty.kind() { ty::FnDef(def_id, args) => { let instance = ty::Instance::resolve_for_fn_ptr( @@ -481,7 +481,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { _ => bug!("{} cannot be reified to a fn ptr", operand.layout.ty), } } - mir::CastKind::PointerCoercion(PointerCoercion::ClosureFnPointer(_)) => { + mir::CastKind::PointerCoercion(PointerCoercion::ClosureFnPointer(_), _) => { match *operand.layout.ty.kind() { ty::Closure(def_id, args) => { let instance = Instance::resolve_closure( @@ -496,11 +496,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { _ => bug!("{} cannot be cast to a fn ptr", operand.layout.ty), } } - mir::CastKind::PointerCoercion(PointerCoercion::UnsafeFnPointer) => { + mir::CastKind::PointerCoercion(PointerCoercion::UnsafeFnPointer, _) => { // This is a no-op at the LLVM level. operand.val } - mir::CastKind::PointerCoercion(PointerCoercion::Unsize) => { + mir::CastKind::PointerCoercion(PointerCoercion::Unsize, _) => { assert!(bx.cx().is_backend_scalar_pair(cast)); let (lldata, llextra) = operand.val.pointer_parts(); let (lldata, llextra) = @@ -508,7 +508,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { OperandValue::Pair(lldata, llextra) } mir::CastKind::PointerCoercion( - PointerCoercion::MutToConstPointer | PointerCoercion::ArrayToPointer, + PointerCoercion::MutToConstPointer | PointerCoercion::ArrayToPointer, _ ) => { bug!("{kind:?} is for borrowck, and should never appear in codegen"); } @@ -526,7 +526,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { bug!("unexpected non-pair operand"); } } - mir::CastKind::DynStar => { + mir::CastKind::PointerCoercion(PointerCoercion::DynStar, _) => { let (lldata, llextra) = operand.val.pointer_parts(); let (lldata, llextra) = base::cast_to_dyn_star(bx, lldata, operand.layout, cast.ty, llextra); diff --git a/compiler/rustc_codegen_ssa/src/size_of_val.rs b/compiler/rustc_codegen_ssa/src/size_of_val.rs index 3f3ae21035d5..827b939217e8 100644 --- a/compiler/rustc_codegen_ssa/src/size_of_val.rs +++ b/compiler/rustc_codegen_ssa/src/size_of_val.rs @@ -28,9 +28,9 @@ pub fn size_and_align_of_dst<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( // Load size/align from vtable. let vtable = info.unwrap(); let size = meth::VirtualIndex::from_index(ty::COMMON_VTABLE_ENTRIES_SIZE) - .get_usize(bx, vtable); + .get_usize(bx, vtable, t); let align = meth::VirtualIndex::from_index(ty::COMMON_VTABLE_ENTRIES_ALIGN) - .get_usize(bx, vtable); + .get_usize(bx, vtable, t); // Size is always <= isize::MAX. let size_bound = bx.data_layout().ptr_sized_integer().signed_max() as u128; diff --git a/compiler/rustc_codegen_ssa/src/traits/backend.rs b/compiler/rustc_codegen_ssa/src/traits/backend.rs index 3df85a618d11..676fb181d67c 100644 --- a/compiler/rustc_codegen_ssa/src/traits/backend.rs +++ b/compiler/rustc_codegen_ssa/src/traits/backend.rs @@ -21,6 +21,7 @@ use crate::{CodegenResults, ModuleCodegen}; pub trait BackendTypes { type Value: CodegenObject; + type Metadata: CodegenObject; type Function: CodegenObject; type BasicBlock: Copy; diff --git a/compiler/rustc_codegen_ssa/src/traits/builder.rs b/compiler/rustc_codegen_ssa/src/traits/builder.rs index 71187df37ccc..c0c1085e949e 100644 --- a/compiler/rustc_codegen_ssa/src/traits/builder.rs +++ b/compiler/rustc_codegen_ssa/src/traits/builder.rs @@ -51,6 +51,7 @@ pub trait BuilderMethods<'a, 'tcx>: type CodegenCx: CodegenMethods< 'tcx, Value = Self::Value, + Metadata = Self::Metadata, Function = Self::Function, BasicBlock = Self::BasicBlock, Type = Self::Type, diff --git a/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs b/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs index e721cfb71342..5b9274b48248 100644 --- a/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs @@ -24,14 +24,14 @@ pub trait IntrinsicCallBuilderMethods<'tcx>: BackendTypes { fn assume(&mut self, val: Self::Value); fn expect(&mut self, cond: Self::Value, expected: bool) -> Self::Value; /// Trait method used to test whether a given pointer is associated with a type identifier. - fn type_test(&mut self, pointer: Self::Value, typeid: Self::Value) -> Self::Value; + fn type_test(&mut self, pointer: Self::Value, typeid: Self::Metadata) -> Self::Value; /// Trait method used to load a function while testing if it is associated with a type /// identifier. fn type_checked_load( &mut self, llvtable: Self::Value, vtable_byte_offset: u64, - typeid: Self::Value, + typeid: Self::Metadata, ) -> Self::Value; /// Trait method used to inject `va_start` on the "spoofed" `VaListImpl` in /// Rust defined C-variadic functions. diff --git a/compiler/rustc_codegen_ssa/src/traits/type_.rs b/compiler/rustc_codegen_ssa/src/traits/type_.rs index 50534ec8030b..f862434c8ef7 100644 --- a/compiler/rustc_codegen_ssa/src/traits/type_.rs +++ b/compiler/rustc_codegen_ssa/src/traits/type_.rs @@ -152,7 +152,7 @@ pub trait LayoutTypeCodegenMethods<'tcx>: BackendTypes { pub trait TypeMembershipCodegenMethods<'tcx>: BackendTypes { fn add_type_metadata(&self, _function: Self::Function, _typeid: String) {} fn set_type_metadata(&self, _function: Self::Function, _typeid: String) {} - fn typeid_metadata(&self, _typeid: String) -> Option { + fn typeid_metadata(&self, _typeid: String) -> Option { None } fn add_kcfi_type_metadata(&self, _function: Self::Function, _typeid: u32) {} diff --git a/compiler/rustc_const_eval/messages.ftl b/compiler/rustc_const_eval/messages.ftl index 2dc9d57e3b5c..73a9a1569f64 100644 --- a/compiler/rustc_const_eval/messages.ftl +++ b/compiler/rustc_const_eval/messages.ftl @@ -198,7 +198,7 @@ const_eval_invalid_vtable_pointer = using {$pointer} as vtable pointer but it does not point to a vtable const_eval_invalid_vtable_trait = - using vtable for trait `{$vtable_trait}` but trait `{$expected_trait}` was expected + using vtable for `{$vtable_dyn_type}` but `{$expected_dyn_type}` was expected const_eval_lazy_lock = consider wrapping this expression in `std::sync::LazyLock::new(|| ...)` @@ -459,7 +459,7 @@ const_eval_validation_invalid_fn_ptr = {$front_matter}: encountered {$value}, bu const_eval_validation_invalid_ref_meta = {$front_matter}: encountered invalid reference metadata: total size is bigger than largest supported object const_eval_validation_invalid_ref_slice_meta = {$front_matter}: encountered invalid reference metadata: slice is bigger than largest supported object const_eval_validation_invalid_vtable_ptr = {$front_matter}: encountered {$value}, but expected a vtable pointer -const_eval_validation_invalid_vtable_trait = {$front_matter}: wrong trait in wide pointer vtable: expected `{$ref_trait}`, but encountered `{$vtable_trait}` +const_eval_validation_invalid_vtable_trait = {$front_matter}: wrong trait in wide pointer vtable: expected `{$expected_dyn_type}`, but encountered `{$vtable_dyn_type}` const_eval_validation_mutable_ref_to_immutable = {$front_matter}: encountered mutable reference or box pointing to read-only memory const_eval_validation_never_val = {$front_matter}: encountered a value of the never type `!` const_eval_validation_null_box = {$front_matter}: encountered a null box diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs index 2c33ff9d219f..2cbf242fcf28 100644 --- a/compiler/rustc_const_eval/src/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/check_consts/check.rs @@ -317,7 +317,6 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> { { self.error_emitted = Some(guar); } - self.check_op_spanned(ops::StaticAccess, span) } /// Returns whether this place can possibly escape the evaluation of the current const/static @@ -440,6 +439,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { | PointerCoercion::UnsafeFnPointer | PointerCoercion::ClosureFnPointer(_) | PointerCoercion::ReifyFnPointer, + _, ), _, _, @@ -447,8 +447,12 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { // These are all okay; they only change the type, not the data. } - Rvalue::Cast(CastKind::PointerCoercion(PointerCoercion::Unsize), _, _) => { - // Unsizing is implemented for CTFE. + Rvalue::Cast( + CastKind::PointerCoercion(PointerCoercion::Unsize | PointerCoercion::DynStar, _), + _, + _, + ) => { + // Unsizing and `dyn*` coercions are implemented for CTFE. } Rvalue::Cast(CastKind::PointerExposeProvenance, _, _) => { @@ -458,10 +462,6 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { // Since no pointer can ever get exposed (rejected above), this is easy to support. } - Rvalue::Cast(CastKind::DynStar, _, _) => { - // `dyn*` coercion is implemented for CTFE. - } - Rvalue::Cast(_, _, _) => {} Rvalue::NullaryOp( diff --git a/compiler/rustc_const_eval/src/check_consts/ops.rs b/compiler/rustc_const_eval/src/check_consts/ops.rs index df8313d0e703..6eb33c29e1d8 100644 --- a/compiler/rustc_const_eval/src/check_consts/ops.rs +++ b/compiler/rustc_const_eval/src/check_consts/ops.rs @@ -16,7 +16,6 @@ use rustc_middle::ty::{ suggest_constraining_type_param, }; use rustc_middle::util::{CallDesugaringKind, CallKind, call_kind}; -use rustc_session::parse::feature_err; use rustc_span::symbol::sym; use rustc_span::{BytePos, Pos, Span, Symbol}; use rustc_trait_selection::traits::SelectionContext; @@ -477,33 +476,6 @@ impl<'tcx> NonConstOp<'tcx> for RawPtrToIntCast { } } -/// An access to a (non-thread-local) `static`. -#[derive(Debug)] -pub(crate) struct StaticAccess; -impl<'tcx> NonConstOp<'tcx> for StaticAccess { - fn status_in_item(&self, ccx: &ConstCx<'_, 'tcx>) -> Status { - if let hir::ConstContext::Static(_) = ccx.const_kind() { - Status::Allowed - } else { - Status::Unstable(sym::const_refs_to_static) - } - } - - #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable - fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> { - let mut err = feature_err( - &ccx.tcx.sess, - sym::const_refs_to_static, - span, - format!("referencing statics in {}s is unstable", ccx.const_kind(),), - ); - err - .note("`static` and `const` variables can refer to other `const` variables. A `const` variable, however, cannot refer to a `static` variable.") - .help("to fix this, the value can be extracted to a `const` and then used."); - err - } -} - /// An access to a thread-local `static`. #[derive(Debug)] pub(crate) struct ThreadLocalAccess; diff --git a/compiler/rustc_const_eval/src/const_eval/mod.rs b/compiler/rustc_const_eval/src/const_eval/mod.rs index 284eb912c208..48aeee2e6f05 100644 --- a/compiler/rustc_const_eval/src/const_eval/mod.rs +++ b/compiler/rustc_const_eval/src/const_eval/mod.rs @@ -16,12 +16,12 @@ mod fn_queries; mod machine; mod valtrees; -pub use dummy_machine::*; -pub use error::*; -pub use eval_queries::*; -pub use fn_queries::*; -pub use machine::*; -pub(crate) use valtrees::{eval_to_valtree, valtree_to_const_value}; +pub use self::dummy_machine::*; +pub use self::error::*; +pub use self::eval_queries::*; +pub use self::fn_queries::*; +pub use self::machine::*; +pub(crate) use self::valtrees::{eval_to_valtree, valtree_to_const_value}; // We forbid type-level constants that contain more than `VALTREE_MAX_NODES` nodes. const VALTREE_MAX_NODES: usize = 100000; diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs index c38d7f3d03c6..c60bacb85067 100644 --- a/compiler/rustc_const_eval/src/errors.rs +++ b/compiler/rustc_const_eval/src/errors.rs @@ -522,12 +522,9 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> { UnterminatedCString(ptr) | InvalidFunctionPointer(ptr) | InvalidVTablePointer(ptr) => { diag.arg("pointer", ptr); } - InvalidVTableTrait { expected_trait, vtable_trait } => { - diag.arg("expected_trait", expected_trait.to_string()); - diag.arg( - "vtable_trait", - vtable_trait.map(|t| t.to_string()).unwrap_or_else(|| format!("")), - ); + InvalidVTableTrait { expected_dyn_type, vtable_dyn_type } => { + diag.arg("expected_dyn_type", expected_dyn_type.to_string()); + diag.arg("vtable_dyn_type", vtable_dyn_type.to_string()); } PointerUseAfterFree(alloc_id, msg) => { diag.arg("alloc_id", alloc_id) @@ -777,12 +774,9 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> { DanglingPtrNoProvenance { pointer, .. } => { err.arg("pointer", pointer); } - InvalidMetaWrongTrait { expected_trait: ref_trait, vtable_trait } => { - err.arg("ref_trait", ref_trait.to_string()); - err.arg( - "vtable_trait", - vtable_trait.map(|t| t.to_string()).unwrap_or_else(|| format!("")), - ); + InvalidMetaWrongTrait { vtable_dyn_type, expected_dyn_type } => { + err.arg("vtable_dyn_type", vtable_dyn_type.to_string()); + err.arg("expected_dyn_type", expected_dyn_type.to_string()); } NullPtr { .. } | ConstRefToMutable diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs index 198aa1bbd5bd..85e7d91c2ad5 100644 --- a/compiler/rustc_const_eval/src/interpret/cast.rs +++ b/compiler/rustc_const_eval/src/interpret/cast.rs @@ -32,7 +32,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { if cast_ty == dest.layout.ty { dest.layout } else { self.layout_of(cast_ty)? }; // FIXME: In which cases should we trigger UB when the source is uninit? match cast_kind { - CastKind::PointerCoercion(PointerCoercion::Unsize) => { + CastKind::PointerCoercion(PointerCoercion::Unsize, _) => { self.unsize_into(src, cast_layout, dest)?; } @@ -68,11 +68,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { CastKind::PointerCoercion( PointerCoercion::MutToConstPointer | PointerCoercion::ArrayToPointer, + _, ) => { bug!("{cast_kind:?} casts are for borrowck only, not runtime MIR"); } - CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer) => { + CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer, _) => { // All reifications must be monomorphic, bail out otherwise. ensure_monomorphic_enough(*self.tcx, src.layout.ty)?; @@ -94,7 +95,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } } - CastKind::PointerCoercion(PointerCoercion::UnsafeFnPointer) => { + CastKind::PointerCoercion(PointerCoercion::UnsafeFnPointer, _) => { let src = self.read_immediate(src)?; match cast_ty.kind() { ty::FnPtr(..) => { @@ -105,7 +106,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } } - CastKind::PointerCoercion(PointerCoercion::ClosureFnPointer(_)) => { + CastKind::PointerCoercion(PointerCoercion::ClosureFnPointer(_), _) => { // All reifications must be monomorphic, bail out otherwise. ensure_monomorphic_enough(*self.tcx, src.layout.ty)?; @@ -125,10 +126,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } } - CastKind::DynStar => { + CastKind::PointerCoercion(PointerCoercion::DynStar, _) => { if let ty::Dynamic(data, _, ty::DynStar) = cast_ty.kind() { // Initial cast from sized to dyn trait - let vtable = self.get_vtable_ptr(src.layout.ty, data.principal())?; + let vtable = self.get_vtable_ptr(src.layout.ty, data)?; let vtable = Scalar::from_maybe_pointer(vtable, self); let data = self.read_immediate(src)?.to_scalar(); let _assert_pointer_like = data.to_pointer(self)?; @@ -446,12 +447,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } // Get the destination trait vtable and return that. - let new_vptr = self.get_vtable_ptr(ty, data_b.principal())?; + let new_vptr = self.get_vtable_ptr(ty, data_b)?; self.write_immediate(Immediate::new_dyn_trait(old_data, new_vptr, self), dest) } (_, &ty::Dynamic(data, _, ty::Dyn)) => { // Initial cast from sized to dyn trait - let vtable = self.get_vtable_ptr(src_pointee_ty, data.principal())?; + let vtable = self.get_vtable_ptr(src_pointee_ty, data)?; let ptr = self.read_pointer(src)?; let val = Immediate::new_dyn_trait(ptr, vtable, &*self.tcx); self.write_immediate(val, dest) diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index e5fdf592ec9f..c3b506d848cd 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -943,12 +943,13 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { if offset.bytes() != 0 { throw_ub!(InvalidVTablePointer(Pointer::new(alloc_id, offset))) } - let Some(GlobalAlloc::VTable(ty, vtable_trait)) = self.tcx.try_get_global_alloc(alloc_id) + let Some(GlobalAlloc::VTable(ty, vtable_dyn_type)) = + self.tcx.try_get_global_alloc(alloc_id) else { throw_ub!(InvalidVTablePointer(Pointer::new(alloc_id, offset))) }; - if let Some(expected_trait) = expected_trait { - self.check_vtable_for_type(vtable_trait, expected_trait)?; + if let Some(expected_dyn_type) = expected_trait { + self.check_vtable_for_type(vtable_dyn_type, expected_dyn_type)?; } Ok(ty) } @@ -1113,11 +1114,8 @@ impl<'a, 'tcx, M: Machine<'tcx>> std::fmt::Debug for DumpAllocs<'a, 'tcx, M> { Some(GlobalAlloc::Function { instance, .. }) => { write!(fmt, " (fn: {instance})")?; } - Some(GlobalAlloc::VTable(ty, Some(trait_ref))) => { - write!(fmt, " (vtable: impl {trait_ref} for {ty})")?; - } - Some(GlobalAlloc::VTable(ty, None)) => { - write!(fmt, " (vtable: impl for {ty})")?; + Some(GlobalAlloc::VTable(ty, dyn_ty)) => { + write!(fmt, " (vtable: impl {dyn_ty} for {ty})")?; } Some(GlobalAlloc::Static(did)) => { write!(fmt, " (static: {})", self.ecx.tcx.def_path_str(did))?; diff --git a/compiler/rustc_const_eval/src/interpret/mod.rs b/compiler/rustc_const_eval/src/interpret/mod.rs index 669f5a597f80..5e84626f77e7 100644 --- a/compiler/rustc_const_eval/src/interpret/mod.rs +++ b/compiler/rustc_const_eval/src/interpret/mod.rs @@ -19,12 +19,12 @@ mod util; mod validity; mod visitor; -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::call::FnArg; pub use self::eval_context::{InterpCx, format_interp_error}; +use self::eval_context::{from_known_layout, mir_assign_valid_types}; pub use self::intern::{ HasStaticRootDefId, InternKind, InternResult, intern_const_alloc_for_constprop, intern_const_alloc_recursive, diff --git a/compiler/rustc_const_eval/src/interpret/traits.rs b/compiler/rustc_const_eval/src/interpret/traits.rs index b5eef0fd8c9b..8eead6018ac4 100644 --- a/compiler/rustc_const_eval/src/interpret/traits.rs +++ b/compiler/rustc_const_eval/src/interpret/traits.rs @@ -1,6 +1,6 @@ use rustc_middle::mir::interpret::{InterpResult, Pointer}; use rustc_middle::ty::layout::LayoutOf; -use rustc_middle::ty::{self, Ty, TyCtxt, VtblEntry}; +use rustc_middle::ty::{self, ExistentialPredicateStableCmpExt, Ty, TyCtxt, VtblEntry}; use rustc_target::abi::{Align, Size}; use tracing::trace; @@ -11,26 +11,25 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { /// Creates a dynamic vtable for the given type and vtable origin. This is used only for /// objects. /// - /// The `trait_ref` encodes the erased self type. Hence, if we are making an object `Foo` - /// from a value of type `Foo`, then `trait_ref` would map `T: Trait`. `None` here means that - /// this is an auto trait without any methods, so we only need the basic vtable (drop, size, - /// align). + /// The `dyn_ty` encodes the erased self type. Hence, if we are making an object + /// `Foo + Send>` from a value of type `Foo`, then `dyn_ty` + /// would be `Trait + Send`. If this list doesn't have a principal trait ref, + /// we only need the basic vtable prefix (drop, size, align). pub fn get_vtable_ptr( &self, ty: Ty<'tcx>, - poly_trait_ref: Option>, + dyn_ty: &'tcx ty::List>, ) -> InterpResult<'tcx, Pointer>> { - trace!("get_vtable(trait_ref={:?})", poly_trait_ref); + trace!("get_vtable(ty={ty:?}, dyn_ty={dyn_ty:?})"); - let (ty, poly_trait_ref) = self.tcx.erase_regions((ty, poly_trait_ref)); + let (ty, dyn_ty) = self.tcx.erase_regions((ty, dyn_ty)); // All vtables must be monomorphic, bail out otherwise. ensure_monomorphic_enough(*self.tcx, ty)?; - ensure_monomorphic_enough(*self.tcx, poly_trait_ref)?; + ensure_monomorphic_enough(*self.tcx, dyn_ty)?; let salt = M::get_global_alloc_salt(self, None); - let vtable_symbolic_allocation = - self.tcx.reserve_and_set_vtable_alloc(ty, poly_trait_ref, salt); + let vtable_symbolic_allocation = self.tcx.reserve_and_set_vtable_alloc(ty, dyn_ty, salt); let vtable_ptr = self.global_root_pointer(Pointer::from(vtable_symbolic_allocation))?; Ok(vtable_ptr.into()) } @@ -64,17 +63,45 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { /// expected trait type. pub(super) fn check_vtable_for_type( &self, - vtable_trait: Option>, - expected_trait: &'tcx ty::List>, + vtable_dyn_type: &'tcx ty::List>, + expected_dyn_type: &'tcx ty::List>, ) -> InterpResult<'tcx> { - let eq = match (expected_trait.principal(), vtable_trait) { - (Some(a), Some(b)) => self.eq_in_param_env(a, b), - (None, None) => true, - _ => false, - }; - if !eq { - throw_ub!(InvalidVTableTrait { expected_trait, vtable_trait }); + // We check validity by comparing the lists of predicates for equality. We *could* instead + // check that the dynamic type to which the vtable belongs satisfies all the expected + // predicates, but that would likely be a lot slower and seems unnecessarily permissive. + + // FIXME: we are skipping auto traits for now, but might revisit this in the future. + let mut sorted_vtable: Vec<_> = vtable_dyn_type.without_auto_traits().collect(); + let mut sorted_expected: Vec<_> = expected_dyn_type.without_auto_traits().collect(); + // `skip_binder` here is okay because `stable_cmp` doesn't look at binders + sorted_vtable.sort_by(|a, b| a.skip_binder().stable_cmp(*self.tcx, &b.skip_binder())); + sorted_vtable.dedup(); + sorted_expected.sort_by(|a, b| a.skip_binder().stable_cmp(*self.tcx, &b.skip_binder())); + sorted_expected.dedup(); + + if sorted_vtable.len() != sorted_expected.len() { + throw_ub!(InvalidVTableTrait { vtable_dyn_type, expected_dyn_type }); + } + + for (a_pred, b_pred) in std::iter::zip(sorted_vtable, sorted_expected) { + let is_eq = match (a_pred.skip_binder(), b_pred.skip_binder()) { + ( + ty::ExistentialPredicate::Trait(a_data), + ty::ExistentialPredicate::Trait(b_data), + ) => self.eq_in_param_env(a_pred.rebind(a_data), b_pred.rebind(b_data)), + + ( + ty::ExistentialPredicate::Projection(a_data), + ty::ExistentialPredicate::Projection(b_data), + ) => self.eq_in_param_env(a_pred.rebind(a_data), b_pred.rebind(b_data)), + + _ => false, + }; + if !is_eq { + throw_ub!(InvalidVTableTrait { vtable_dyn_type, expected_dyn_type }); + } } + Ok(()) } diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index ff3c6120f0ca..203cceccd9d1 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -452,8 +452,8 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { self.path, Ub(DanglingIntPointer{ .. } | InvalidVTablePointer(..)) => InvalidVTablePtr { value: format!("{vtable}") }, - Ub(InvalidVTableTrait { expected_trait, vtable_trait }) => { - InvalidMetaWrongTrait { expected_trait, vtable_trait: *vtable_trait } + Ub(InvalidVTableTrait { vtable_dyn_type, expected_dyn_type }) => { + InvalidMetaWrongTrait { vtable_dyn_type, expected_dyn_type } }, ); } @@ -1281,8 +1281,8 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt, self.path, // It's not great to catch errors here, since we can't give a very good path, // but it's better than ICEing. - Ub(InvalidVTableTrait { expected_trait, vtable_trait }) => { - InvalidMetaWrongTrait { expected_trait, vtable_trait: *vtable_trait } + Ub(InvalidVTableTrait { vtable_dyn_type, expected_dyn_type }) => { + InvalidMetaWrongTrait { vtable_dyn_type, expected_dyn_type: *expected_dyn_type } }, ); } diff --git a/compiler/rustc_const_eval/src/lib.rs b/compiler/rustc_const_eval/src/lib.rs index 73dda81bd298..cbe8a043fba0 100644 --- a/compiler/rustc_const_eval/src/lib.rs +++ b/compiler/rustc_const_eval/src/lib.rs @@ -1,6 +1,8 @@ // tidy-alphabetical-start #![allow(internal_features)] #![allow(rustc::diagnostic_outside_of_impl)] +#![cfg_attr(not(bootstrap), feature(unqualified_local_imports))] +#![cfg_attr(not(bootstrap), warn(unqualified_local_imports))] #![doc(rust_logo)] #![feature(assert_matches)] #![feature(box_patterns)] @@ -25,10 +27,11 @@ pub mod util; use std::sync::atomic::AtomicBool; -pub use errors::ReportErrorExt; use rustc_middle::ty; use rustc_middle::util::Providers; +pub use self::errors::ReportErrorExt; + rustc_fluent_macro::fluent_messages! { "../messages.ftl" } pub fn provide(providers: &mut Providers) { diff --git a/compiler/rustc_data_structures/src/lib.rs b/compiler/rustc_data_structures/src/lib.rs index a35f5b1f17db..f225684d99ff 100644 --- a/compiler/rustc_data_structures/src/lib.rs +++ b/compiler/rustc_data_structures/src/lib.rs @@ -23,6 +23,7 @@ #![feature(cfg_match)] #![feature(core_intrinsics)] #![feature(extend_one)] +#![feature(file_buffered)] #![feature(hash_raw_entry)] #![feature(macro_metavar_expr)] #![feature(map_try_insert)] diff --git a/compiler/rustc_data_structures/src/obligation_forest/graphviz.rs b/compiler/rustc_data_structures/src/obligation_forest/graphviz.rs index 60cde9a52b41..65a24366db83 100644 --- a/compiler/rustc_data_structures/src/obligation_forest/graphviz.rs +++ b/compiler/rustc_data_structures/src/obligation_forest/graphviz.rs @@ -1,6 +1,5 @@ use std::env::var_os; use std::fs::File; -use std::io::BufWriter; use std::path::Path; use std::sync::atomic::{AtomicUsize, Ordering}; @@ -33,7 +32,7 @@ impl ObligationForest { let file_path = dir.as_ref().join(format!("{counter:010}_{description}.gv")); - let mut gv_file = BufWriter::new(File::create(file_path).unwrap()); + let mut gv_file = File::create_buffered(file_path).unwrap(); dot::render(&self, &mut gv_file).unwrap(); } diff --git a/compiler/rustc_error_codes/src/error_codes/E0013.md b/compiler/rustc_error_codes/src/error_codes/E0013.md index 9f4848343ff1..c4d65225ece9 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0013.md +++ b/compiler/rustc_error_codes/src/error_codes/E0013.md @@ -5,7 +5,7 @@ variable cannot refer to a static variable. Erroneous code example: -```compile_fail,E0658 +``` static X: i32 = 42; const Y: i32 = X; ``` diff --git a/compiler/rustc_feature/src/accepted.rs b/compiler/rustc_feature/src/accepted.rs index 70e92f545c63..5ff002dd7d2b 100644 --- a/compiler/rustc_feature/src/accepted.rs +++ b/compiler/rustc_feature/src/accepted.rs @@ -151,6 +151,8 @@ declare_features! ( (accepted, const_raw_ptr_deref, "1.58.0", Some(51911)), /// Allows references to types with interior mutability within constants (accepted, const_refs_to_cell, "CURRENT_RUSTC_VERSION", Some(80384)), + /// Allows creating pointers and references to `static` items in constants. + (accepted, const_refs_to_static, "CURRENT_RUSTC_VERSION", Some(119618)), /// Allows implementing `Copy` for closures where possible (RFC 2132). (accepted, copy_closures, "1.26.0", Some(44490)), /// Allows `crate` in paths. diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 0b09e9fbb853..91b072b56db2 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -227,6 +227,8 @@ declare_features! ( (internal, staged_api, "1.0.0", None), /// Added for testing unstable lints; perma-unstable. (internal, test_unstable_lint, "1.60.0", None), + /// Helps with formatting for `group_imports = "StdExternalCrate"`. + (unstable, unqualified_local_imports, "CURRENT_RUSTC_VERSION", None), /// Use for stable + negative coherence and strict coherence depending on trait's /// rustc_strict_coherence value. (unstable, with_negative_coherence, "1.60.0", None), @@ -405,8 +407,6 @@ declare_features! ( (unstable, const_for, "1.56.0", Some(87575)), /// Be more precise when looking for live drops in a const context. (unstable, const_precise_live_drops, "1.46.0", Some(73255)), - /// Allows creating pointers and references to `static` items in constants. - (unstable, const_refs_to_static, "1.78.0", Some(119618)), /// Allows `impl const Trait for T` syntax. (unstable, const_trait_impl, "1.42.0", Some(67792)), /// Allows the `?` operator in const contexts. diff --git a/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs b/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs index c07d8009aa56..dfb3c088afb0 100644 --- a/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs +++ b/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs @@ -22,36 +22,38 @@ use crate::errors; pub(crate) fn crate_inherent_impls( tcx: TyCtxt<'_>, (): (), -) -> Result<&'_ CrateInherentImpls, ErrorGuaranteed> { +) -> (&'_ CrateInherentImpls, Result<(), ErrorGuaranteed>) { let mut collect = InherentCollect { tcx, impls_map: Default::default() }; + let mut res = Ok(()); for id in tcx.hir().items() { res = res.and(collect.check_item(id)); } - res?; - Ok(tcx.arena.alloc(collect.impls_map)) + + (tcx.arena.alloc(collect.impls_map), res) } -pub(crate) fn crate_incoherent_impls( +pub(crate) fn crate_inherent_impls_validity_check( tcx: TyCtxt<'_>, - simp: SimplifiedType, -) -> Result<&[DefId], ErrorGuaranteed> { - let crate_map = tcx.crate_inherent_impls(())?; - Ok(tcx.arena.alloc_from_iter( + (): (), +) -> Result<(), ErrorGuaranteed> { + tcx.crate_inherent_impls(()).1 +} + +pub(crate) fn crate_incoherent_impls(tcx: TyCtxt<'_>, simp: SimplifiedType) -> &[DefId] { + let (crate_map, _) = tcx.crate_inherent_impls(()); + tcx.arena.alloc_from_iter( crate_map.incoherent_impls.get(&simp).unwrap_or(&Vec::new()).iter().map(|d| d.to_def_id()), - )) + ) } /// On-demand query: yields a vector of the inherent impls for a specific type. -pub(crate) fn inherent_impls( - tcx: TyCtxt<'_>, - ty_def_id: LocalDefId, -) -> Result<&[DefId], ErrorGuaranteed> { - let crate_map = tcx.crate_inherent_impls(())?; - Ok(match crate_map.inherent_impls.get(&ty_def_id) { +pub(crate) fn inherent_impls(tcx: TyCtxt<'_>, ty_def_id: LocalDefId) -> &[DefId] { + let (crate_map, _) = tcx.crate_inherent_impls(()); + match crate_map.inherent_impls.get(&ty_def_id) { Some(v) => &v[..], None => &[], - }) + } } struct InherentCollect<'tcx> { diff --git a/compiler/rustc_hir_analysis/src/coherence/inherent_impls_overlap.rs b/compiler/rustc_hir_analysis/src/coherence/inherent_impls_overlap.rs index 43b9093ecac8..b8066b4b47d9 100644 --- a/compiler/rustc_hir_analysis/src/coherence/inherent_impls_overlap.rs +++ b/compiler/rustc_hir_analysis/src/coherence/inherent_impls_overlap.rs @@ -177,8 +177,7 @@ impl<'tcx> InherentOverlapChecker<'tcx> { return Ok(()); } - let impls = self.tcx.inherent_impls(id.owner_id)?; - + let impls = self.tcx.inherent_impls(id.owner_id); let overlap_mode = OverlapMode::get(self.tcx, id.owner_id.to_def_id()); let impls_items = impls diff --git a/compiler/rustc_hir_analysis/src/coherence/mod.rs b/compiler/rustc_hir_analysis/src/coherence/mod.rs index 185f3176f078..b25406583f6c 100644 --- a/compiler/rustc_hir_analysis/src/coherence/mod.rs +++ b/compiler/rustc_hir_analysis/src/coherence/mod.rs @@ -124,7 +124,10 @@ fn enforce_empty_impls_for_marker_traits( pub(crate) fn provide(providers: &mut Providers) { use self::builtin::coerce_unsized_info; - use self::inherent_impls::{crate_incoherent_impls, crate_inherent_impls, inherent_impls}; + use self::inherent_impls::{ + crate_incoherent_impls, crate_inherent_impls, crate_inherent_impls_validity_check, + inherent_impls, + }; use self::inherent_impls_overlap::crate_inherent_impls_overlap_check; use self::orphan::orphan_check_impl; @@ -133,6 +136,7 @@ pub(crate) fn provide(providers: &mut Providers) { crate_inherent_impls, crate_incoherent_impls, inherent_impls, + crate_inherent_impls_validity_check, crate_inherent_impls_overlap_check, coerce_unsized_info, orphan_check_impl, diff --git a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs index c64741625a4e..7557219aaa69 100644 --- a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs +++ b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs @@ -1,8 +1,9 @@ -use rustc_data_structures::fx::FxIndexSet; +use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_hir as hir; use rustc_infer::traits::util; +use rustc_middle::ty::fold::shift_vars; use rustc_middle::ty::{ - self, GenericArgs, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, + self, GenericArgs, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, }; use rustc_middle::{bug, span_bug}; use rustc_span::Span; @@ -42,14 +43,18 @@ fn associated_type_bounds<'tcx>( let trait_def_id = tcx.local_parent(assoc_item_def_id); let trait_predicates = tcx.trait_explicit_predicates_and_bounds(trait_def_id); - let bounds_from_parent = trait_predicates.predicates.iter().copied().filter(|(pred, _)| { - match pred.kind().skip_binder() { - ty::ClauseKind::Trait(tr) => tr.self_ty() == item_ty, - ty::ClauseKind::Projection(proj) => proj.projection_term.self_ty() == item_ty, - ty::ClauseKind::TypeOutlives(outlives) => outlives.0 == item_ty, - _ => false, - } - }); + let item_trait_ref = ty::TraitRef::identity(tcx, tcx.parent(assoc_item_def_id.to_def_id())); + let bounds_from_parent = + trait_predicates.predicates.iter().copied().filter_map(|(clause, span)| { + remap_gat_vars_and_recurse_into_nested_projections( + tcx, + filter, + item_trait_ref, + assoc_item_def_id, + span, + clause, + ) + }); let all_bounds = tcx.arena.alloc_from_iter(bounds.clauses(tcx).chain(bounds_from_parent)); debug!( @@ -63,6 +68,226 @@ fn associated_type_bounds<'tcx>( all_bounds } +/// The code below is quite involved, so let me explain. +/// +/// We loop here, because we also want to collect vars for nested associated items as +/// well. For example, given a clause like `Self::A::B`, we want to add that to the +/// item bounds for `A`, so that we may use that bound in the case that `Self::A::B` is +/// rigid. +/// +/// Secondly, regarding bound vars, when we see a where clause that mentions a GAT +/// like `for<'a, ...> Self::Assoc<'a, ...>: Bound<'b, ...>`, we want to turn that into +/// an item bound on the GAT, where all of the GAT args are substituted with the GAT's +/// param regions, and then keep all of the other late-bound vars in the bound around. +/// We need to "compress" the binder so that it doesn't mention any of those vars that +/// were mapped to params. +fn remap_gat_vars_and_recurse_into_nested_projections<'tcx>( + tcx: TyCtxt<'tcx>, + filter: PredicateFilter, + item_trait_ref: ty::TraitRef<'tcx>, + assoc_item_def_id: LocalDefId, + span: Span, + clause: ty::Clause<'tcx>, +) -> Option<(ty::Clause<'tcx>, Span)> { + let mut clause_ty = match clause.kind().skip_binder() { + ty::ClauseKind::Trait(tr) => tr.self_ty(), + ty::ClauseKind::Projection(proj) => proj.projection_term.self_ty(), + ty::ClauseKind::TypeOutlives(outlives) => outlives.0, + _ => return None, + }; + + let gat_vars = loop { + if let ty::Alias(ty::Projection, alias_ty) = *clause_ty.kind() { + if alias_ty.trait_ref(tcx) == item_trait_ref + && alias_ty.def_id == assoc_item_def_id.to_def_id() + { + // We have found the GAT in question... + // Return the vars, since we may need to remap them. + break &alias_ty.args[item_trait_ref.args.len()..]; + } else { + // Only collect *self* type bounds if the filter is for self. + match filter { + PredicateFilter::SelfOnly | PredicateFilter::SelfThatDefines(_) => { + return None; + } + PredicateFilter::All | PredicateFilter::SelfAndAssociatedTypeBounds => {} + } + + clause_ty = alias_ty.self_ty(); + continue; + } + } + + return None; + }; + + // Special-case: No GAT vars, no mapping needed. + if gat_vars.is_empty() { + return Some((clause, span)); + } + + // First, check that all of the GAT args are substituted with a unique late-bound arg. + // If we find a duplicate, then it can't be mapped to the definition's params. + let mut mapping = FxIndexMap::default(); + let generics = tcx.generics_of(assoc_item_def_id); + for (param, var) in std::iter::zip(&generics.own_params, gat_vars) { + let existing = match var.unpack() { + ty::GenericArgKind::Lifetime(re) => { + if let ty::RegionKind::ReBound(ty::INNERMOST, bv) = re.kind() { + mapping.insert(bv.var, tcx.mk_param_from_def(param)) + } else { + return None; + } + } + ty::GenericArgKind::Type(ty) => { + if let ty::Bound(ty::INNERMOST, bv) = *ty.kind() { + mapping.insert(bv.var, tcx.mk_param_from_def(param)) + } else { + return None; + } + } + ty::GenericArgKind::Const(ct) => { + if let ty::ConstKind::Bound(ty::INNERMOST, bv) = ct.kind() { + mapping.insert(bv, tcx.mk_param_from_def(param)) + } else { + return None; + } + } + }; + + if existing.is_some() { + return None; + } + } + + // Finally, map all of the args in the GAT to the params we expect, and compress + // the remaining late-bound vars so that they count up from var 0. + let mut folder = + MapAndCompressBoundVars { tcx, binder: ty::INNERMOST, still_bound_vars: vec![], mapping }; + let pred = clause.kind().skip_binder().fold_with(&mut folder); + + Some(( + ty::Binder::bind_with_vars(pred, tcx.mk_bound_variable_kinds(&folder.still_bound_vars)) + .upcast(tcx), + span, + )) +} + +/// Given some where clause like `for<'b, 'c> >::Gat<'b>: Bound<'c>`, +/// the mapping will map `'b` back to the GAT's `'b_identity`. Then we need to compress the +/// remaining bound var `'c` to index 0. +/// +/// This folder gives us: `for<'c> >::Gat<'b_identity>: Bound<'c>`, +/// which is sufficient for an item bound for `Gat`, since all of the GAT's args are identity. +struct MapAndCompressBoundVars<'tcx> { + tcx: TyCtxt<'tcx>, + /// How deep are we? Makes sure we don't touch the vars of nested binders. + binder: ty::DebruijnIndex, + /// List of bound vars that remain unsubstituted because they were not + /// mentioned in the GAT's args. + still_bound_vars: Vec, + /// Subtle invariant: If the `GenericArg` is bound, then it should be + /// stored with the debruijn index of `INNERMOST` so it can be shifted + /// correctly during substitution. + mapping: FxIndexMap>, +} + +impl<'tcx> TypeFolder> for MapAndCompressBoundVars<'tcx> { + fn cx(&self) -> TyCtxt<'tcx> { + self.tcx + } + + fn fold_binder(&mut self, t: ty::Binder<'tcx, T>) -> ty::Binder<'tcx, T> + where + ty::Binder<'tcx, T>: TypeSuperFoldable>, + { + self.binder.shift_in(1); + let out = t.super_fold_with(self); + self.binder.shift_out(1); + out + } + + fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { + if !ty.has_bound_vars() { + return ty; + } + + if let ty::Bound(binder, old_bound) = *ty.kind() + && self.binder == binder + { + let mapped = if let Some(mapped) = self.mapping.get(&old_bound.var) { + mapped.expect_ty() + } else { + // If we didn't find a mapped generic, then make a new one. + // Allocate a new var idx, and insert a new bound ty. + let var = ty::BoundVar::from_usize(self.still_bound_vars.len()); + self.still_bound_vars.push(ty::BoundVariableKind::Ty(old_bound.kind)); + let mapped = Ty::new_bound(self.tcx, ty::INNERMOST, ty::BoundTy { + var, + kind: old_bound.kind, + }); + self.mapping.insert(old_bound.var, mapped.into()); + mapped + }; + + shift_vars(self.tcx, mapped, self.binder.as_u32()) + } else { + ty.super_fold_with(self) + } + } + + fn fold_region(&mut self, re: ty::Region<'tcx>) -> ty::Region<'tcx> { + if let ty::ReBound(binder, old_bound) = re.kind() + && self.binder == binder + { + let mapped = if let Some(mapped) = self.mapping.get(&old_bound.var) { + mapped.expect_region() + } else { + let var = ty::BoundVar::from_usize(self.still_bound_vars.len()); + self.still_bound_vars.push(ty::BoundVariableKind::Region(old_bound.kind)); + let mapped = ty::Region::new_bound(self.tcx, ty::INNERMOST, ty::BoundRegion { + var, + kind: old_bound.kind, + }); + self.mapping.insert(old_bound.var, mapped.into()); + mapped + }; + + shift_vars(self.tcx, mapped, self.binder.as_u32()) + } else { + re + } + } + + fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { + if !ct.has_bound_vars() { + return ct; + } + + if let ty::ConstKind::Bound(binder, old_var) = ct.kind() + && self.binder == binder + { + let mapped = if let Some(mapped) = self.mapping.get(&old_var) { + mapped.expect_const() + } else { + let var = ty::BoundVar::from_usize(self.still_bound_vars.len()); + self.still_bound_vars.push(ty::BoundVariableKind::Const); + let mapped = ty::Const::new_bound(self.tcx, ty::INNERMOST, var); + self.mapping.insert(old_var, mapped.into()); + mapped + }; + + shift_vars(self.tcx, mapped, self.binder.as_u32()) + } else { + ct.super_fold_with(self) + } + } + + fn fold_predicate(&mut self, p: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> { + if !p.has_bound_vars() { p } else { p.super_fold_with(self) } + } +} + /// Opaque types don't inherit bounds from their parent: for return position /// impl trait it isn't possible to write a suitable predicate on the /// containing function and for type-alias impl trait we don't have a backwards diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs index 5e3203e84736..5775a8867b16 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs @@ -1028,7 +1028,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { .. }) = node && let Some(ty_def_id) = qself_ty.ty_def_id() - && let Ok([inherent_impl]) = tcx.inherent_impls(ty_def_id) + && let [inherent_impl] = tcx.inherent_impls(ty_def_id) && let name = format!("{ident2}_{ident3}") && let Some(ty::AssocItem { kind: ty::AssocKind::Fn, .. }) = tcx .associated_items(inherent_impl) diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index e95b5142559f..d7d4a98e63fe 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -1272,7 +1272,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } let candidates: Vec<_> = tcx - .inherent_impls(adt_did)? + .inherent_impls(adt_did) .iter() .filter_map(|&impl_| { let (item, scope) = diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs index 1dc85c4903e3..92d85d48a42c 100644 --- a/compiler/rustc_hir_analysis/src/lib.rs +++ b/compiler/rustc_hir_analysis/src/lib.rs @@ -170,7 +170,7 @@ pub fn check_crate(tcx: TyCtxt<'_>) { let _ = tcx.ensure().coherent_trait(trait_def_id); } // these queries are executed for side-effects (error reporting): - let _ = tcx.ensure().crate_inherent_impls(()); + let _ = tcx.ensure().crate_inherent_impls_validity_check(()); let _ = tcx.ensure().crate_inherent_impls_overlap_check(()); }); diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index aab0c4c116af..fcd2940b83ae 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -32,13 +32,14 @@ use rustc_data_structures::fx::FxHashSet; use rustc_errors::codes::*; use rustc_errors::{Applicability, Diag, ErrorGuaranteed}; use rustc_hir::{self as hir, ExprKind}; +use rustc_infer::infer::DefineOpaqueTypes; use rustc_macros::{TypeFoldable, TypeVisitable}; -use rustc_middle::bug; use rustc_middle::mir::Mutability; use rustc_middle::ty::adjustment::AllowTwoPhase; use rustc_middle::ty::cast::{CastKind, CastTy}; use rustc_middle::ty::error::TypeError; use rustc_middle::ty::{self, Ty, TyCtxt, TypeAndMut, TypeVisitableExt, VariantDef}; +use rustc_middle::{bug, span_bug}; use rustc_session::lint; use rustc_span::def_id::LOCAL_CRATE; use rustc_span::symbol::sym; @@ -152,12 +153,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } #[derive(Copy, Clone, Debug)] -pub enum CastError { +enum CastError<'tcx> { ErrorGuaranteed(ErrorGuaranteed), CastToBool, CastToChar, - DifferingKinds, + DifferingKinds { + src_kind: PointerKind<'tcx>, + dst_kind: PointerKind<'tcx>, + }, /// Cast of thin to fat raw ptr (e.g., `*const () as *const [u8]`). SizedUnsizedCast, IllegalCast, @@ -177,7 +181,7 @@ pub enum CastError { ForeignNonExhaustiveAdt, } -impl From for CastError { +impl From for CastError<'_> { fn from(err: ErrorGuaranteed) -> Self { CastError::ErrorGuaranteed(err) } @@ -251,7 +255,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { } } - fn report_cast_error(&self, fcx: &FnCtxt<'a, 'tcx>, e: CastError) { + fn report_cast_error(&self, fcx: &FnCtxt<'a, 'tcx>, e: CastError<'tcx>) { match e { CastError::ErrorGuaranteed(_) => { // an error has already been reported @@ -303,10 +307,52 @@ impl<'a, 'tcx> CastCheck<'tcx> { CastError::IllegalCast => { make_invalid_casting_error(self.span, self.expr_ty, self.cast_ty, fcx).emit(); } - CastError::DifferingKinds => { - make_invalid_casting_error(self.span, self.expr_ty, self.cast_ty, fcx) - .with_note("vtable kinds may not match") - .emit(); + CastError::DifferingKinds { src_kind, dst_kind } => { + let mut err = + make_invalid_casting_error(self.span, self.expr_ty, self.cast_ty, fcx); + + match (src_kind, dst_kind) { + (PointerKind::VTable(_), PointerKind::VTable(_)) => { + err.note("the trait objects may have different vtables"); + } + ( + PointerKind::OfParam(_) | PointerKind::OfAlias(_), + PointerKind::OfParam(_) + | PointerKind::OfAlias(_) + | PointerKind::VTable(_) + | PointerKind::Length, + ) + | ( + PointerKind::VTable(_) | PointerKind::Length, + PointerKind::OfParam(_) | PointerKind::OfAlias(_), + ) => { + err.note("the pointers may have different metadata"); + } + (PointerKind::VTable(_), PointerKind::Length) + | (PointerKind::Length, PointerKind::VTable(_)) => { + err.note("the pointers have different metadata"); + } + ( + PointerKind::Thin, + PointerKind::Thin + | PointerKind::VTable(_) + | PointerKind::Length + | PointerKind::OfParam(_) + | PointerKind::OfAlias(_), + ) + | ( + PointerKind::VTable(_) + | PointerKind::Length + | PointerKind::OfParam(_) + | PointerKind::OfAlias(_), + PointerKind::Thin, + ) + | (PointerKind::Length, PointerKind::Length) => { + span_bug!(self.span, "unexpected cast error: {e:?}") + } + } + + err.emit(); } CastError::CastToBool => { let expr_ty = fcx.resolve_vars_if_possible(self.expr_ty); @@ -670,10 +716,20 @@ impl<'a, 'tcx> CastCheck<'tcx> { /// Checks a cast, and report an error if one exists. In some cases, this /// can return Ok and create type errors in the fcx rather than returning /// directly. coercion-cast is handled in check instead of here. - fn do_check(&self, fcx: &FnCtxt<'a, 'tcx>) -> Result { + fn do_check(&self, fcx: &FnCtxt<'a, 'tcx>) -> Result> { use rustc_middle::ty::cast::CastTy::*; use rustc_middle::ty::cast::IntTy::*; + if self.cast_ty.is_dyn_star() { + if fcx.tcx.features().dyn_star { + span_bug!(self.span, "should be handled by `coerce`"); + } else { + // Report "casting is invalid" rather than "non-primitive cast" + // if the feature is not enabled. + return Err(CastError::IllegalCast); + } + } + let (t_from, t_cast) = match (CastTy::from_ty(self.expr_ty), CastTy::from_ty(self.cast_ty)) { (Some(t_from), Some(t_cast)) => (t_from, t_cast), @@ -780,16 +836,6 @@ impl<'a, 'tcx> CastCheck<'tcx> { (Int(Char) | Int(Bool), Int(_)) => Ok(CastKind::PrimIntCast), (Int(_) | Float, Int(_) | Float) => Ok(CastKind::NumericCast), - - (_, DynStar) => { - if fcx.tcx.features().dyn_star { - bug!("should be handled by `coerce`") - } else { - Err(CastError::IllegalCast) - } - } - - (DynStar, _) => Err(CastError::IllegalCast), } } @@ -798,27 +844,34 @@ impl<'a, 'tcx> CastCheck<'tcx> { fcx: &FnCtxt<'a, 'tcx>, m_src: ty::TypeAndMut<'tcx>, m_dst: ty::TypeAndMut<'tcx>, - ) -> Result { + ) -> Result> { debug!("check_ptr_ptr_cast m_src={m_src:?} m_dst={m_dst:?}"); - // ptr-ptr cast. vtables must match. + // ptr-ptr cast. metadata must match. let src_kind = fcx.tcx.erase_regions(fcx.pointer_kind(m_src.ty, self.span)?); let dst_kind = fcx.tcx.erase_regions(fcx.pointer_kind(m_dst.ty, self.span)?); - match (src_kind, dst_kind) { - // We can't cast if target pointer kind is unknown - (_, None) => Err(CastError::UnknownCastPtrKind), - // Cast to thin pointer is OK - (_, Some(PointerKind::Thin)) => Ok(CastKind::PtrPtrCast), + // We can't cast if target pointer kind is unknown + let Some(dst_kind) = dst_kind else { + return Err(CastError::UnknownCastPtrKind); + }; + + // Cast to thin pointer is OK + if dst_kind == PointerKind::Thin { + return Ok(CastKind::PtrPtrCast); + } - // We can't cast to fat pointer if source pointer kind is unknown - (None, _) => Err(CastError::UnknownExprPtrKind), + // We can't cast to fat pointer if source pointer kind is unknown + let Some(src_kind) = src_kind else { + return Err(CastError::UnknownCastPtrKind); + }; + match (src_kind, dst_kind) { // thin -> fat? report invalid cast (don't complain about vtable kinds) - (Some(PointerKind::Thin), _) => Err(CastError::SizedUnsizedCast), + (PointerKind::Thin, _) => Err(CastError::SizedUnsizedCast), // trait object -> trait object? need to do additional checks - (Some(PointerKind::VTable(src_tty)), Some(PointerKind::VTable(dst_tty))) => { + (PointerKind::VTable(src_tty), PointerKind::VTable(dst_tty)) => { match (src_tty.principal(), dst_tty.principal()) { // A + SrcAuto> -> B + DstAuto>. need to make sure // - `Src` and `Dst` traits are the same @@ -834,7 +887,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { // Note that trait upcasting goes through a different mechanism (`coerce_unsized`) // and is unaffected by this check. if src_principal.def_id() != dst_principal.def_id() { - return Err(CastError::DifferingKinds); + return Err(CastError::DifferingKinds { src_kind, dst_kind }); } // We need to reconstruct trait object types. @@ -860,7 +913,16 @@ impl<'a, 'tcx> CastCheck<'tcx> { )); // `dyn Src = dyn Dst`, this checks for matching traits/generics - fcx.demand_eqtype(self.span, src_obj, dst_obj); + // This is `demand_eqtype`, but inlined to give a better error. + let cause = fcx.misc(self.span); + if fcx + .at(&cause, fcx.param_env) + .eq(DefineOpaqueTypes::Yes, src_obj, dst_obj) + .map(|infer_ok| fcx.register_infer_ok_obligations(infer_ok)) + .is_err() + { + return Err(CastError::DifferingKinds { src_kind, dst_kind }); + } // Check that `SrcAuto` (+auto traits implied by `Src`) is a superset of `DstAuto`. // Emit an FCW otherwise. @@ -905,17 +967,17 @@ impl<'a, 'tcx> CastCheck<'tcx> { // dyn Trait -> dyn Auto? should be ok, but we used to not allow it. // FIXME: allow this - (Some(_), None) => Err(CastError::DifferingKinds), + (Some(_), None) => Err(CastError::DifferingKinds { src_kind, dst_kind }), // dyn Auto -> dyn Trait? not ok. - (None, Some(_)) => Err(CastError::DifferingKinds), + (None, Some(_)) => Err(CastError::DifferingKinds { src_kind, dst_kind }), } } // fat -> fat? metadata kinds must match - (Some(src_kind), Some(dst_kind)) if src_kind == dst_kind => Ok(CastKind::PtrPtrCast), + (src_kind, dst_kind) if src_kind == dst_kind => Ok(CastKind::PtrPtrCast), - (_, _) => Err(CastError::DifferingKinds), + (_, _) => Err(CastError::DifferingKinds { src_kind, dst_kind }), } } @@ -923,7 +985,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { &self, fcx: &FnCtxt<'a, 'tcx>, m_cast: ty::TypeAndMut<'tcx>, - ) -> Result { + ) -> Result> { // fptr-ptr cast. must be to thin ptr match fcx.pointer_kind(m_cast.ty, self.span)? { @@ -937,7 +999,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { &self, fcx: &FnCtxt<'a, 'tcx>, m_expr: ty::TypeAndMut<'tcx>, - ) -> Result { + ) -> Result> { // ptr-addr cast. must be from thin ptr match fcx.pointer_kind(m_expr.ty, self.span)? { @@ -952,7 +1014,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { fcx: &FnCtxt<'a, 'tcx>, m_expr: ty::TypeAndMut<'tcx>, m_cast: ty::TypeAndMut<'tcx>, - ) -> Result { + ) -> Result> { // array-ptr-cast: allow mut-to-mut, mut-to-const, const-to-const if m_expr.mutbl >= m_cast.mutbl { if let ty::Array(ety, _) = m_expr.ty.kind() { @@ -987,7 +1049,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { &self, fcx: &FnCtxt<'a, 'tcx>, m_cast: TypeAndMut<'tcx>, - ) -> Result { + ) -> Result> { // ptr-addr cast. pointer must be thin. match fcx.pointer_kind(m_cast.ty, self.span)? { None => Err(CastError::UnknownCastPtrKind), diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 7be91d78e19f..b08c5bc6d9a3 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -769,7 +769,10 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { )); Ok(InferOk { - value: (vec![Adjustment { kind: Adjust::DynStar, target: b }], b), + value: ( + vec![Adjustment { kind: Adjust::Pointer(PointerCoercion::DynStar), target: b }], + b, + ), obligations, }) } diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 015a72832509..b34ed4640db1 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -2126,7 +2126,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .tcx .inherent_impls(def_id) .into_iter() - .flatten() .flat_map(|i| self.tcx.associated_items(i).in_definition_order()) // Only assoc fn with no receivers. .filter(|item| { diff --git a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs index 29feed56290b..bb5f35113730 100644 --- a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs @@ -759,9 +759,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx for adjustment in adjustments { debug!("walk_adjustment expr={:?} adj={:?}", expr, adjustment); match adjustment.kind { - adjustment::Adjust::NeverToAny - | adjustment::Adjust::Pointer(_) - | adjustment::Adjust::DynStar => { + adjustment::Adjust::NeverToAny | adjustment::Adjust::Pointer(_) => { // Creating a closure/fn-pointer or unsizing consumes // the input and stores it into the resulting rvalue. self.consume_or_copy(&place_with_id, place_with_id.hir_id); @@ -1296,8 +1294,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx adjustment::Adjust::NeverToAny | adjustment::Adjust::Pointer(_) | adjustment::Adjust::Borrow(_) - | adjustment::Adjust::ReborrowPin(..) - | adjustment::Adjust::DynStar => { + | adjustment::Adjust::ReborrowPin(..) => { // Result is an rvalue. Ok(self.cat_rvalue(expr.hir_id, target)) } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 5f89f7dedc2c..487cc7e55cd9 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -882,7 +882,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let ty = self.lowerer().lower_ty(hir_ty); debug!(?ty, "return type (lowered)"); debug!(?expected, "expected type"); - let bound_vars = self.tcx.late_bound_vars(hir_ty.hir_id.owner.into()); + let bound_vars = + self.tcx.late_bound_vars(self.tcx.local_def_id_to_hir_id(fn_id)); let ty = Binder::bind_with_vars(ty, bound_vars); let ty = self.normalize(hir_ty.span, ty); let ty = self.tcx.instantiate_bound_regions_with_erased(ty); diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index 3828b40b8857..1a8afcf003d2 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -728,13 +728,13 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { let Some(simp) = simplify_type(self.tcx, self_ty, TreatParams::InstantiateWithInfer) else { bug!("unexpected incoherent type: {:?}", self_ty) }; - for &impl_def_id in self.tcx.incoherent_impls(simp).into_iter().flatten() { + for &impl_def_id in self.tcx.incoherent_impls(simp).into_iter() { self.assemble_inherent_impl_probe(impl_def_id); } } fn assemble_inherent_impl_candidates_for_type(&mut self, def_id: DefId) { - let impl_def_ids = self.tcx.at(self.span).inherent_impls(def_id).into_iter().flatten(); + let impl_def_ids = self.tcx.at(self.span).inherent_impls(def_id).into_iter(); for &impl_def_id in impl_def_ids { self.assemble_inherent_impl_probe(impl_def_id); } diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index feb47209f5e7..e03be4f43f73 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -680,7 +680,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.tcx .inherent_impls(adt_def.did()) .into_iter() - .flatten() .any(|def_id| self.associated_value(*def_id, item_name).is_some()) } else { false @@ -1438,7 +1437,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .tcx .inherent_impls(adt.did()) .into_iter() - .flatten() .copied() .filter(|def_id| { if let Some(assoc) = self.associated_value(*def_id, item_name) { @@ -1900,7 +1898,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { call_args: Option>>, ) -> Option { if let ty::Adt(adt, adt_args) = rcvr_ty.kind() { - for inherent_impl_did in self.tcx.inherent_impls(adt.did()).into_iter().flatten() { + for inherent_impl_did in self.tcx.inherent_impls(adt.did()).into_iter() { for inherent_method in self.tcx.associated_items(inherent_impl_did).in_definition_order() { @@ -2114,9 +2112,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let ty::Adt(adt_def, _) = rcvr_ty.kind() else { return; }; - // FIXME(oli-obk): try out bubbling this error up one level and cancelling the other error in that case. - let Ok(impls) = self.tcx.inherent_impls(adt_def.did()) else { return }; - let mut items = impls + let mut items = self + .tcx + .inherent_impls(adt_def.did()) .iter() .flat_map(|i| self.tcx.associated_items(i).in_definition_order()) // Only assoc fn with no receivers and only if @@ -2495,7 +2493,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .and_then(|simp| { tcx.incoherent_impls(simp) .into_iter() - .flatten() .find_map(|&id| self.associated_value(id, item_name)) }) .is_some() diff --git a/compiler/rustc_incremental/src/assert_dep_graph.rs b/compiler/rustc_incremental/src/assert_dep_graph.rs index 646b9dbe1331..a006786aa75b 100644 --- a/compiler/rustc_incremental/src/assert_dep_graph.rs +++ b/compiler/rustc_incremental/src/assert_dep_graph.rs @@ -35,7 +35,7 @@ use std::env; use std::fs::{self, File}; -use std::io::{BufWriter, Write}; +use std::io::Write; use rustc_data_structures::fx::FxIndexSet; use rustc_data_structures::graph::implementation::{Direction, INCOMING, NodeIndex, OUTGOING}; @@ -245,7 +245,7 @@ fn dump_graph(query: &DepGraphQuery) { { // dump a .txt file with just the edges: let txt_path = format!("{path}.txt"); - let mut file = BufWriter::new(File::create(&txt_path).unwrap()); + let mut file = File::create_buffered(&txt_path).unwrap(); for (source, target) in &edges { write!(file, "{source:?} -> {target:?}\n").unwrap(); } diff --git a/compiler/rustc_incremental/src/lib.rs b/compiler/rustc_incremental/src/lib.rs index dda1232b80bf..e8735cba9bd2 100644 --- a/compiler/rustc_incremental/src/lib.rs +++ b/compiler/rustc_incremental/src/lib.rs @@ -5,6 +5,7 @@ #![deny(missing_docs)] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] +#![feature(file_buffered)] #![feature(rustdoc_internals)] #![warn(unreachable_pub)] // tidy-alphabetical-end diff --git a/compiler/rustc_interface/src/lib.rs b/compiler/rustc_interface/src/lib.rs index 94ebe51f213f..b81a74027016 100644 --- a/compiler/rustc_interface/src/lib.rs +++ b/compiler/rustc_interface/src/lib.rs @@ -1,5 +1,6 @@ // tidy-alphabetical-start #![feature(decl_macro)] +#![feature(file_buffered)] #![feature(let_chains)] #![feature(try_blocks)] #![warn(unreachable_pub)] diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 617581cf667f..bba517915a2d 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -519,7 +519,7 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P write_deps_to_file(&mut file)?; } OutFileName::Real(ref path) => { - let mut file = BufWriter::new(fs::File::create(path)?); + let mut file = fs::File::create_buffered(path)?; write_deps_to_file(&mut file)?; } } diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index e71c5676ce4f..a286ccb22c7a 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -395,8 +395,6 @@ lint_improper_ctypes_opaque = opaque types have no C equivalent lint_improper_ctypes_pat_help = consider using the base type instead lint_improper_ctypes_pat_reason = pattern types have no C equivalent - -lint_improper_ctypes_recursion_limit_reached = type is infinitely recursive lint_improper_ctypes_slice_help = consider using a raw pointer instead lint_improper_ctypes_slice_reason = slices have no C equivalent @@ -533,7 +531,7 @@ lint_non_binding_let_multi_suggestion = consider immediately dropping the value lint_non_binding_let_on_drop_type = - non-binding let on a type that implements `Drop` + non-binding let on a type that has a destructor 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 @@ -592,10 +590,7 @@ lint_non_local_definitions_cargo_update = the {$macro_kind} `{$macro_name}` may lint_non_local_definitions_deprecation = this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue lint_non_local_definitions_impl = non-local `impl` definition, `impl` blocks should be written at the same level as their item - .remove_help = remove `{$may_remove_part}` to make the `impl` local - .without_trait = methods and associated constants are still usable outside the current expression, only `impl Local` and `impl dyn Local` can ever be private, and only if the type is nested in the same item as the `impl` - .with_trait = an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` - .bounds = `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type + .non_local = an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` .doctest = make this doc-test a standalone test with its own `fn main() {"{"} ... {"}"}` .exception = items in an anonymous const item (`const _: () = {"{"} ... {"}"}`) are treated as in the same scope as the anonymous const's declaration for the purpose of this lint .const_anon = use a const-anon item to suppress this lint @@ -617,12 +612,6 @@ lint_non_local_definitions_macro_rules = non-local `macro_rules!` definition, `# remove the `#[macro_export]` or make this doc-test a standalone test with its own `fn main() {"{"} ... {"}"}` .non_local = a `macro_rules!` definition is non-local if it is nested inside an item and has a `#[macro_export]` attribute -lint_non_local_definitions_may_move = may need to be moved as well - -lint_non_local_definitions_of_trait_not_local = `{$of_trait_str}` is not local - -lint_non_local_definitions_self_ty_not_local = `{$self_ty_str}` is not local - lint_non_snake_case = {$sort} `{$name}` should have a snake case name .rename_or_convert_suggestion = rename the identifier or convert it to a snake case raw identifier .cannot_convert_note = `{$sc}` cannot be used as a raw identifier @@ -899,6 +888,8 @@ lint_unnameable_test_items = cannot test inner items lint_unnecessary_qualification = unnecessary qualification .suggestion = remove the unnecessary path segments +lint_unqualified_local_imports = `use` of a local item without leading `self::`, `super::`, or `crate::` + lint_unsafe_attr_outside_unsafe = unsafe attribute used without unsafe .label = usage of unsafe attribute lint_unsafe_attr_outside_unsafe_suggestion = wrap the attribute in `unsafe(...)` diff --git a/compiler/rustc_lint/src/let_underscore.rs b/compiler/rustc_lint/src/let_underscore.rs index a12a97ee5730..abee9ee78699 100644 --- a/compiler/rustc_lint/src/let_underscore.rs +++ b/compiler/rustc_lint/src/let_underscore.rs @@ -51,7 +51,7 @@ declare_lint! { /// intent. pub LET_UNDERSCORE_DROP, Allow, - "non-binding let on a type that implements `Drop`" + "non-binding let on a type that has a destructor" } declare_lint! { diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 652a40dada82..c74cb866f21f 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -86,6 +86,7 @@ mod tail_expr_drop_order; mod traits; mod types; mod unit_bindings; +mod unqualified_local_imports; mod unused; use async_closures::AsyncClosureUsage; @@ -126,6 +127,7 @@ use tail_expr_drop_order::TailExprDropOrder; use traits::*; use types::*; use unit_bindings::*; +use unqualified_local_imports::*; use unused::*; #[rustfmt::skip] @@ -249,6 +251,7 @@ late_lint_methods!( TailExprDropOrder: TailExprDropOrder, IfLetRescope: IfLetRescope::default(), StaticMutRefs: StaticMutRefs, + UnqualifiedLocalImports: UnqualifiedLocalImports, ] ] ); diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 76002cc84258..0045cfcaa56a 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -1375,12 +1375,7 @@ pub(crate) enum NonLocalDefinitionsDiag { body_name: String, cargo_update: Option, const_anon: Option>, - move_to: Option<(Span, Vec)>, doctest: bool, - may_remove: Option<(Span, String)>, - has_trait: bool, - self_ty_str: String, - of_trait_str: Option, macro_to_change: Option<(String, &'static str)>, }, MacroRules { @@ -1401,22 +1396,13 @@ impl<'a> LintDiagnostic<'a, ()> for NonLocalDefinitionsDiag { body_name, cargo_update, const_anon, - move_to, doctest, - may_remove, - has_trait, - self_ty_str, - of_trait_str, macro_to_change, } => { diag.primary_message(fluent::lint_non_local_definitions_impl); diag.arg("depth", depth); diag.arg("body_kind_descr", body_kind_descr); diag.arg("body_name", body_name); - diag.arg("self_ty_str", self_ty_str); - if let Some(of_trait_str) = of_trait_str { - diag.arg("of_trait_str", of_trait_str); - } if let Some((macro_to_change, macro_kind)) = macro_to_change { diag.arg("macro_to_change", macro_to_change); @@ -1427,34 +1413,12 @@ impl<'a> LintDiagnostic<'a, ()> for NonLocalDefinitionsDiag { diag.subdiagnostic(cargo_update); } - if has_trait { - diag.note(fluent::lint_bounds); - diag.note(fluent::lint_with_trait); - } else { - diag.note(fluent::lint_without_trait); - } + diag.note(fluent::lint_non_local); - if let Some((move_help, may_move)) = move_to { - let mut ms = MultiSpan::from_span(move_help); - for sp in may_move { - ms.push_span_label(sp, fluent::lint_non_local_definitions_may_move); - } - diag.span_help(ms, fluent::lint_non_local_definitions_impl_move_help); - } if doctest { diag.help(fluent::lint_doctest); } - if let Some((span, part)) = may_remove { - diag.arg("may_remove_part", part); - diag.span_suggestion( - span, - fluent::lint_remove_help, - "", - Applicability::MaybeIncorrect, - ); - } - if let Some(const_anon) = const_anon { diag.note(fluent::lint_exception); if let Some(const_anon) = const_anon { @@ -3093,3 +3057,7 @@ pub(crate) enum MutRefSugg { span: Span, }, } + +#[derive(LintDiagnostic)] +#[diag(lint_unqualified_local_imports)] +pub(crate) struct UnqualifiedLocalImportsDiag {} diff --git a/compiler/rustc_lint/src/non_local_def.rs b/compiler/rustc_lint/src/non_local_def.rs index ef7ab7efd544..56f930ea7f62 100644 --- a/compiler/rustc_lint/src/non_local_def.rs +++ b/compiler/rustc_lint/src/non_local_def.rs @@ -1,20 +1,12 @@ use rustc_errors::MultiSpan; -use rustc_hir::def::DefKind; +use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{Body, HirId, Item, ItemKind, Node, Path, QPath, TyKind}; -use rustc_infer::infer::InferCtxt; -use rustc_infer::traits::{Obligation, ObligationCause}; -use rustc_middle::ty::{ - self, Binder, EarlyBinder, TraitRef, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, -}; +use rustc_hir::{Body, HirId, Item, ItemKind, Node, Path, TyKind}; +use rustc_middle::ty::TyCtxt; use rustc_session::{declare_lint, impl_lint_pass}; use rustc_span::def_id::{DefId, LOCAL_CRATE}; use rustc_span::symbol::kw; -use rustc_span::{ExpnKind, MacroKind, Span, Symbol, sym}; -use rustc_trait_selection::error_reporting::traits::ambiguity::{ - CandidateSource, compute_applicable_impls_for_diagnostics, -}; -use rustc_trait_selection::infer::TyCtxtInferExt; +use rustc_span::{ExpnKind, MacroKind, Span, sym}; use crate::lints::{NonLocalDefinitionsCargoUpdateNote, NonLocalDefinitionsDiag}; use crate::{LateContext, LateLintPass, LintContext, fluent_generated as fluent}; @@ -49,7 +41,7 @@ declare_lint! { /// All nested bodies (functions, enum discriminant, array length, consts) (expect for /// `const _: Ty = { ... }` in top-level module, which is still undecided) are checked. pub NON_LOCAL_DEFINITIONS, - Allow, + Warn, "checks for non-local definitions", report_in_external_macro } @@ -142,42 +134,28 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions { None }; - // Part 1: Is the Self type local? - let self_ty_has_local_parent = - ty_has_local_parent(&impl_.self_ty.kind, cx, parent, parent_parent); - - if self_ty_has_local_parent { - return; - } - - // Part 2: Is the Trait local? - let of_trait_has_local_parent = impl_ - .of_trait - .map(|of_trait| path_has_local_parent(of_trait.path, cx, parent, parent_parent)) - .unwrap_or(false); - - if of_trait_has_local_parent { - return; + // 1. We collect all the `hir::Path` from the `Self` type and `Trait` ref + // of the `impl` definition + let mut collector = PathCollector { paths: Vec::new() }; + collector.visit_ty(&impl_.self_ty); + if let Some(of_trait) = &impl_.of_trait { + collector.visit_trait_ref(of_trait); } - // Part 3: Is the impl definition leaking outside it's defining scope? - // - // We always consider inherent impls to be leaking. - let impl_has_enough_non_local_candidates = cx - .tcx - .impl_trait_ref(def_id) - .map(|binder| { - impl_trait_ref_has_enough_non_local_candidates( - cx.tcx, - item.span, - def_id, - binder, - |did| did_has_local_parent(did, cx.tcx, parent, parent_parent), - ) - }) - .unwrap_or(false); + // 1.5. Remove any path that doesn't resolve to a `DefId` or if it resolve to a + // type-param (e.g. `T`). + collector.paths.retain( + |p| matches!(p.res, Res::Def(def_kind, _) if def_kind != DefKind::TyParam), + ); - if impl_has_enough_non_local_candidates { + // 2. We check if any of path reference a "local" parent and if that the case + // we bail out as asked by T-lang, even though this isn't correct from a + // type-system point of view, as inference exists and could still leak the impl. + if collector + .paths + .iter() + .any(|path| path_has_local_parent(path, cx, parent, parent_parent)) + { return; } @@ -199,76 +177,28 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions { let const_anon = matches!(parent_def_kind, DefKind::Const | DefKind::Static { .. }) .then_some(span_for_const_anon_suggestion); - let may_remove = match &impl_.self_ty.kind { - TyKind::Ptr(mut_ty) | TyKind::Ref(_, mut_ty) - if ty_has_local_parent(&mut_ty.ty.kind, cx, parent, parent_parent) => - { - let type_ = - if matches!(impl_.self_ty.kind, TyKind::Ptr(_)) { "*" } else { "&" }; - let part = format!("{}{}", type_, mut_ty.mutbl.prefix_str()); - Some((impl_.self_ty.span.shrink_to_lo().until(mut_ty.ty.span), part)) - } - _ => None, - }; - let impl_span = item.span.shrink_to_lo().to(impl_.self_ty.span); let mut ms = MultiSpan::from_span(impl_span); - let (self_ty_span, self_ty_str) = - self_ty_kind_for_diagnostic(&impl_.self_ty, cx.tcx); - - ms.push_span_label( - self_ty_span, - fluent::lint_non_local_definitions_self_ty_not_local, - ); - let of_trait_str = if let Some(of_trait) = &impl_.of_trait { + for path in &collector.paths { + // FIXME: While a translatable diagnostic message can have an argument + // we (currently) have no way to set different args per diag msg with + // `MultiSpan::push_span_label`. + #[allow(rustc::untranslatable_diagnostic)] ms.push_span_label( - path_span_without_args(&of_trait.path), - fluent::lint_non_local_definitions_of_trait_not_local, + path_span_without_args(path), + format!("`{}` is not local", path_name_to_string(path)), ); - Some(path_name_to_string(&of_trait.path)) - } else { - None - }; - - let (doctest, move_to) = if is_at_toplevel_doctest() { - (true, None) - } else { - let mut collector = PathCollector { paths: Vec::new() }; - collector.visit_ty(&impl_.self_ty); - if let Some(of_trait) = &impl_.of_trait { - collector.visit_trait_ref(of_trait); - } - collector.visit_generics(&impl_.generics); - - let mut may_move: Vec = collector - .paths - .into_iter() - .filter_map(|path| { - if let Some(did) = path.res.opt_def_id() - && did_has_local_parent(did, cx.tcx, parent, parent_parent) - { - Some(cx.tcx.def_span(did)) - } else { - None - } - }) - .collect(); - may_move.sort(); - may_move.dedup(); + } - let move_to = if may_move.is_empty() { - ms.push_span_label( - cx.tcx.def_span(parent), - fluent::lint_non_local_definitions_impl_move_help, - ); - None - } else { - Some((cx.tcx.def_span(parent), may_move)) - }; + let doctest = is_at_toplevel_doctest(); - (false, move_to) - }; + if !doctest { + ms.push_span_label( + cx.tcx.def_span(parent), + fluent::lint_non_local_definitions_impl_move_help, + ); + } let macro_to_change = if let ExpnKind::Macro(kind, name) = item.span.ctxt().outer_expn_data().kind { @@ -285,12 +215,7 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions { .unwrap_or_else(|| "".to_string()), cargo_update: cargo_update(), const_anon, - self_ty_str, - of_trait_str, - move_to, doctest, - may_remove, - has_trait: impl_.of_trait.is_some(), macro_to_change, }) } @@ -316,90 +241,6 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions { } } -// Detecting if the impl definition is leaking outside of its defining scope. -// -// Rule: for each impl, instantiate all local types with inference vars and -// then assemble candidates for that goal, if there are more than 1 (non-private -// impls), it does not leak. -// -// https://github.com/rust-lang/rust/issues/121621#issuecomment-1976826895 -fn impl_trait_ref_has_enough_non_local_candidates<'tcx>( - tcx: TyCtxt<'tcx>, - infer_span: Span, - trait_def_id: DefId, - binder: EarlyBinder<'tcx, TraitRef<'tcx>>, - mut did_has_local_parent: impl FnMut(DefId) -> bool, -) -> bool { - let infcx = tcx - .infer_ctxt() - // We use the new trait solver since the obligation we are trying to - // prove here may overflow and those are fatal in the old trait solver. - // Which is unacceptable for a lint. - // - // Thanksfully the part we use here are very similar to the - // new-trait-solver-as-coherence, which is in stabilization. - // - // https://github.com/rust-lang/rust/issues/123573 - .with_next_trait_solver(true) - .build(); - - let trait_ref = binder.instantiate(tcx, infcx.fresh_args_for_item(infer_span, trait_def_id)); - - let trait_ref = trait_ref.fold_with(&mut ReplaceLocalTypesWithInfer { - infcx: &infcx, - infer_span, - did_has_local_parent: &mut did_has_local_parent, - }); - - let poly_trait_obligation = Obligation::new( - tcx, - ObligationCause::dummy(), - ty::ParamEnv::empty(), - Binder::dummy(trait_ref), - ); - - let ambiguities = compute_applicable_impls_for_diagnostics(&infcx, &poly_trait_obligation); - - let mut it = ambiguities.iter().filter(|ambi| match ambi { - CandidateSource::DefId(did) => !did_has_local_parent(*did), - CandidateSource::ParamEnv(_) => unreachable!(), - }); - - let _ = it.next(); - it.next().is_some() -} - -/// Replace every local type by inference variable. -/// -/// ```text -/// as std::cmp::PartialEq>> -/// to -/// as std::cmp::PartialEq>> -/// ``` -struct ReplaceLocalTypesWithInfer<'a, 'tcx, F: FnMut(DefId) -> bool> { - infcx: &'a InferCtxt<'tcx>, - did_has_local_parent: F, - infer_span: Span, -} - -impl<'a, 'tcx, F: FnMut(DefId) -> bool> TypeFolder> - for ReplaceLocalTypesWithInfer<'a, 'tcx, F> -{ - fn cx(&self) -> TyCtxt<'tcx> { - self.infcx.tcx - } - - fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { - if let Some(def) = t.ty_adt_def() - && (self.did_has_local_parent)(def.did()) - { - self.infcx.next_ty_var(self.infer_span) - } else { - t.super_fold_with(self) - } - } -} - /// Simple hir::Path collector struct PathCollector<'tcx> { paths: Vec>, @@ -412,42 +253,6 @@ impl<'tcx> Visitor<'tcx> for PathCollector<'tcx> { } } -/// Given a `Ty` we check if the (outermost) type is local. -fn ty_has_local_parent( - ty_kind: &TyKind<'_>, - cx: &LateContext<'_>, - impl_parent: DefId, - impl_parent_parent: Option, -) -> bool { - match ty_kind { - TyKind::Path(QPath::Resolved(_, ty_path)) => { - path_has_local_parent(ty_path, cx, impl_parent, impl_parent_parent) - } - TyKind::TraitObject([principle_poly_trait_ref, ..], _, _) => path_has_local_parent( - principle_poly_trait_ref.0.trait_ref.path, - cx, - impl_parent, - impl_parent_parent, - ), - TyKind::TraitObject([], _, _) - | TyKind::InferDelegation(_, _) - | TyKind::Slice(_) - | TyKind::Array(_, _) - | TyKind::Ptr(_) - | TyKind::Ref(_, _) - | TyKind::BareFn(_) - | TyKind::Never - | TyKind::Tup(_) - | TyKind::Path(_) - | TyKind::Pat(..) - | TyKind::AnonAdt(_) - | TyKind::OpaqueDef(_, _, _) - | TyKind::Typeof(_) - | TyKind::Infer - | TyKind::Err(_) => false, - } -} - /// Given a path and a parent impl def id, this checks if the if parent resolution /// def id correspond to the def id of the parent impl definition. /// @@ -503,38 +308,3 @@ fn path_span_without_args(path: &Path<'_>) -> Span { fn path_name_to_string(path: &Path<'_>) -> String { path.segments.last().unwrap().ident.name.to_ident_string() } - -/// Compute the `Span` and visual representation for the `Self` we want to point at; -/// It follows part of the actual logic of non-local, and if possible return the least -/// amount possible for the span and representation. -fn self_ty_kind_for_diagnostic(ty: &rustc_hir::Ty<'_>, tcx: TyCtxt<'_>) -> (Span, String) { - match ty.kind { - TyKind::Path(QPath::Resolved(_, ty_path)) => ( - path_span_without_args(ty_path), - ty_path - .res - .opt_def_id() - .map(|did| tcx.opt_item_name(did)) - .flatten() - .as_ref() - .map(|s| Symbol::as_str(s)) - .unwrap_or("") - .to_string(), - ), - TyKind::TraitObject([principle_poly_trait_ref, ..], _, _) => { - let path = &principle_poly_trait_ref.0.trait_ref.path; - ( - path_span_without_args(path), - path.res - .opt_def_id() - .map(|did| tcx.opt_item_name(did)) - .flatten() - .as_ref() - .map(|s| Symbol::as_str(s)) - .unwrap_or("") - .to_string(), - ) - } - _ => (ty.span, rustc_hir_pretty::ty_to_string(&tcx, ty)), - } -} diff --git a/compiler/rustc_lint/src/passes.rs b/compiler/rustc_lint/src/passes.rs index 17ec58c7957f..a1d436e0d3db 100644 --- a/compiler/rustc_lint/src/passes.rs +++ b/compiler/rustc_lint/src/passes.rs @@ -14,6 +14,8 @@ macro_rules! late_lint_methods { fn check_mod(a: &'tcx rustc_hir::Mod<'tcx>, b: rustc_hir::HirId); fn check_foreign_item(a: &'tcx rustc_hir::ForeignItem<'tcx>); fn check_item(a: &'tcx rustc_hir::Item<'tcx>); + /// This is called *after* recursing into the item + /// (in contrast to `check_item`, which is checked before). fn check_item_post(a: &'tcx rustc_hir::Item<'tcx>); fn check_local(a: &'tcx rustc_hir::LetStmt<'tcx>); fn check_block(a: &'tcx rustc_hir::Block<'tcx>); @@ -135,6 +137,8 @@ macro_rules! early_lint_methods { fn check_crate(a: &rustc_ast::Crate); fn check_crate_post(a: &rustc_ast::Crate); fn check_item(a: &rustc_ast::Item); + /// This is called *after* recursing into the item + /// (in contrast to `check_item`, which is checked before). fn check_item_post(a: &rustc_ast::Item); fn check_local(a: &rustc_ast::Local); fn check_block(a: &rustc_ast::Block); diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index f5a24f9808d2..15fe18adbfb1 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -592,8 +592,6 @@ struct CTypesVisitorState<'tcx> { /// The original type being checked, before we recursed /// to any other types it contains. base_ty: Ty<'tcx>, - /// Number of times we recursed while checking the type - recursion_depth: usize, } enum FfiResult<'tcx> { @@ -899,23 +897,12 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { // Protect against infinite recursion, for example // `struct S(*mut S);`. + // FIXME: A recursion limit is necessary as well, for irregular + // recursive types. if !acc.cache.insert(ty) { return FfiSafe; } - // Additional recursion check for more complex types like - // `struct A { v: *const A>, ... }` for which the - // cache check above won't be enough (fixes #130310) - if !tcx.recursion_limit().value_within_limit(acc.recursion_depth) { - return FfiUnsafe { - ty: acc.base_ty, - reason: fluent::lint_improper_ctypes_recursion_limit_reached, - help: None, - }; - } - - acc.recursion_depth += 1; - match *ty.kind() { ty::Adt(def, args) => { if let Some(boxed) = ty.boxed_ty() @@ -1261,8 +1248,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { return; } - let mut acc = - CTypesVisitorState { cache: FxHashSet::default(), base_ty: ty, recursion_depth: 0 }; + let mut acc = CTypesVisitorState { cache: FxHashSet::default(), base_ty: ty }; match self.check_type_for_ffi(&mut acc, ty) { FfiResult::FfiSafe => {} FfiResult::FfiPhantom(ty) => { diff --git a/compiler/rustc_lint/src/unqualified_local_imports.rs b/compiler/rustc_lint/src/unqualified_local_imports.rs new file mode 100644 index 000000000000..bea01a33bd6c --- /dev/null +++ b/compiler/rustc_lint/src/unqualified_local_imports.rs @@ -0,0 +1,85 @@ +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::{self as hir}; +use rustc_session::{declare_lint, declare_lint_pass}; +use rustc_span::symbol::kw; + +use crate::{LateContext, LateLintPass, LintContext, lints}; + +declare_lint! { + /// The `unqualified_local_imports` lint checks for `use` items that import a local item using a + /// path that does not start with `self::`, `super::`, or `crate::`. + /// + /// ### Example + /// + /// ```rust,edition2018 + /// #![warn(unqualified_local_imports)] + /// + /// mod localmod { + /// pub struct S; + /// } + /// + /// use localmod::S; + /// # // We have to actually use `S`, or else the `unused` warnings suppress the lint we care about. + /// # pub fn main() { + /// # let _x = S; + /// # } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// This lint is meant to be used with the (unstable) rustfmt setting `group_imports = "StdExternalCrate"`. + /// That setting makes rustfmt group `self::`, `super::`, and `crate::` imports separately from those + /// refering to other crates. However, rustfmt cannot know whether `use c::S;` refers to a local module `c` + /// or an external crate `c`, so it always gets categorized as an import from another crate. + /// To ensure consistent grouping of imports from the local crate, all local imports must + /// start with `self::`, `super::`, or `crate::`. This lint can be used to enforce that style. + pub UNQUALIFIED_LOCAL_IMPORTS, + Allow, + "`use` of a local item without leading `self::`, `super::`, or `crate::`", + @feature_gate = unqualified_local_imports; +} + +declare_lint_pass!(UnqualifiedLocalImports => [UNQUALIFIED_LOCAL_IMPORTS]); + +impl<'tcx> LateLintPass<'tcx> for UnqualifiedLocalImports { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) { + let hir::ItemKind::Use(path, _kind) = item.kind else { return }; + // `path` has three resolutions for the type, module, value namespaces. + // Check if any of them qualifies: local crate, and not a macro. + // (Macros can't be imported any other way so we don't complain about them.) + let is_local_import = |res: &Res| { + matches!( + res, + hir::def::Res::Def(def_kind, def_id) + if def_id.is_local() && !matches!(def_kind, DefKind::Macro(_)), + ) + }; + if !path.res.iter().any(is_local_import) { + return; + } + // So this does refer to something local. Let's check whether it starts with `self`, + // `super`, or `crate`. If the path is empty, that means we have a `use *`, which is + // equivalent to `use crate::*` so we don't fire the lint in that case. + let Some(first_seg) = path.segments.first() else { return }; + if matches!(first_seg.ident.name, kw::SelfLower | kw::Super | kw::Crate) { + return; + } + + let encl_item_id = cx.tcx.hir().get_parent_item(item.hir_id()); + let encl_item = cx.tcx.hir_node_by_def_id(encl_item_id.def_id); + if encl_item.fn_kind().is_some() { + // `use` in a method -- don't lint, that leads to too many undesirable lints + // when a function imports all variants of an enum. + return; + } + + // This `use` qualifies for our lint! + cx.emit_span_lint( + UNQUALIFIED_LOCAL_IMPORTS, + first_seg.ident.span, + lints::UnqualifiedLocalImportsDiag {}, + ); + } +} diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index 165fb7aa6c3d..8f0b1b812765 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -732,12 +732,7 @@ extern "C" LLVMRustResult LLVMRustOptimize( PTO.SLPVectorization = SLPVectorize; PTO.MergeFunctions = MergeFunctions; - // FIXME: We may want to expose this as an option. - bool DebugPassManager = false; - PassInstrumentationCallbacks PIC; - StandardInstrumentations SI(TheModule->getContext(), DebugPassManager); - SI.registerCallbacks(PIC); if (LlvmSelfProfiler) { LLVMSelfProfileInitializeCallbacks(PIC, LlvmSelfProfiler, @@ -784,6 +779,12 @@ extern "C" LLVMRustResult LLVMRustOptimize( CGSCCAnalysisManager CGAM; ModuleAnalysisManager MAM; + // FIXME: We may want to expose this as an option. + bool DebugPassManager = false; + + StandardInstrumentations SI(TheModule->getContext(), DebugPassManager); + SI.registerCallbacks(PIC, &MAM); + if (LLVMPluginsLen) { auto PluginsStr = StringRef(LLVMPlugins, LLVMPluginsLen); SmallVector Plugins; diff --git a/compiler/rustc_metadata/src/fs.rs b/compiler/rustc_metadata/src/fs.rs index a58f3c6b5caa..4450d050c8e1 100644 --- a/compiler/rustc_metadata/src/fs.rs +++ b/compiler/rustc_metadata/src/fs.rs @@ -128,8 +128,7 @@ pub fn non_durable_rename(src: &Path, dst: &Path) -> std::io::Result<()> { } pub fn copy_to_stdout(from: &Path) -> io::Result<()> { - let file = fs::File::open(from)?; - let mut reader = io::BufReader::new(file); + let mut reader = fs::File::open_buffered(from)?; let mut stdout = io::stdout(); io::copy(&mut reader, &mut stdout)?; Ok(()) diff --git a/compiler/rustc_metadata/src/lib.rs b/compiler/rustc_metadata/src/lib.rs index 1759ea7d4415..10f2087d1e6f 100644 --- a/compiler/rustc_metadata/src/lib.rs +++ b/compiler/rustc_metadata/src/lib.rs @@ -8,6 +8,7 @@ #![feature(decl_macro)] #![feature(error_iter)] #![feature(extract_if)] +#![feature(file_buffered)] #![feature(if_let_guard)] #![feature(iter_from_coroutine)] #![feature(let_chains)] diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index 53d2089296d9..69707fdbe8fa 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -347,7 +347,7 @@ provide! { tcx, def_id, other, cdata, tcx.arena.alloc_from_iter(cdata.get_associated_item_or_field_def_ids(def_id.index)) } associated_item => { cdata.get_associated_item(def_id.index, tcx.sess) } - inherent_impls => { Ok(cdata.get_inherent_implementations_for_type(tcx, def_id.index)) } + inherent_impls => { cdata.get_inherent_implementations_for_type(tcx, def_id.index) } item_attrs => { tcx.arena.alloc_from_iter(cdata.get_item_attrs(def_id.index, tcx.sess)) } is_mir_available => { cdata.is_item_mir_available(def_id.index) } is_ctfe_mir_available => { cdata.is_ctfe_mir_available(def_id.index) } @@ -393,7 +393,7 @@ provide! { tcx, def_id, other, cdata, traits => { tcx.arena.alloc_from_iter(cdata.get_traits()) } trait_impls_in_crate => { tcx.arena.alloc_from_iter(cdata.get_trait_impls()) } implementations_of_trait => { cdata.get_implementations_of_trait(tcx, other) } - crate_incoherent_impls => { Ok(cdata.get_incoherent_impls(tcx, other)) } + crate_incoherent_impls => { cdata.get_incoherent_impls(tcx, other) } dep_kind => { cdata.dep_kind } module_children => { diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 891f17934418..5f756672b049 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -1540,7 +1540,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { } } - for (def_id, impls) in &tcx.crate_inherent_impls(()).unwrap().inherent_impls { + for (def_id, impls) in &tcx.crate_inherent_impls(()).0.inherent_impls { record_defaulted_array!(self.tables.inherent_impls[def_id.to_def_id()] <- impls.iter().map(|def_id| { assert!(def_id.is_local()); def_id.index @@ -2089,7 +2089,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { let all_impls: Vec<_> = tcx .crate_inherent_impls(()) - .unwrap() + .0 .incoherent_impls .iter() .map(|(&simp, impls)| IncoherentImpls { diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs index 167b5aef68b7..e11361a615f4 100644 --- a/compiler/rustc_middle/src/hir/map/mod.rs +++ b/compiler/rustc_middle/src/hir/map/mod.rs @@ -598,7 +598,10 @@ impl<'hir> Map<'hir> { /// in the HIR which is recorded by the map and is an item, either an item /// in a module, trait, or impl. pub fn get_parent_item(self, hir_id: HirId) -> OwnerId { - if let Some((def_id, _node)) = self.parent_owner_iter(hir_id).next() { + if hir_id.local_id != ItemLocalId::ZERO { + // If this is a child of a HIR owner, return the owner. + hir_id.owner + } else if let Some((def_id, _node)) = self.parent_owner_iter(hir_id).next() { def_id } else { CRATE_OWNER_ID diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs index 23cd247e8841..a32b19b067a5 100644 --- a/compiler/rustc_middle/src/lib.rs +++ b/compiler/rustc_middle/src/lib.rs @@ -45,6 +45,7 @@ #![feature(discriminant_kind)] #![feature(extern_types)] #![feature(extract_if)] +#![feature(file_buffered)] #![feature(if_let_guard)] #![feature(intra_doc_pointers)] #![feature(iter_from_coroutine)] diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index 89507f1049ac..46646e759d59 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -365,8 +365,10 @@ pub enum UndefinedBehaviorInfo<'tcx> { InvalidVTablePointer(Pointer), /// Using a vtable for the wrong trait. InvalidVTableTrait { - expected_trait: &'tcx ty::List>, - vtable_trait: Option>, + /// The vtable that was actually referenced by the wide pointer metadata. + vtable_dyn_type: &'tcx ty::List>, + /// The vtable that was expected at the point in MIR that it was accessed. + expected_dyn_type: &'tcx ty::List>, }, /// Using a string that is not valid UTF-8, InvalidStr(std::str::Utf8Error), @@ -479,8 +481,10 @@ pub enum ValidationErrorKind<'tcx> { value: String, }, InvalidMetaWrongTrait { - expected_trait: &'tcx ty::List>, - vtable_trait: Option>, + /// The vtable that was actually referenced by the wide pointer metadata. + vtable_dyn_type: &'tcx ty::List>, + /// The vtable that was expected at the point in MIR that it was accessed. + expected_dyn_type: &'tcx ty::List>, }, InvalidMetaSliceTooLarge { ptr_kind: PointerKind, diff --git a/compiler/rustc_middle/src/mir/interpret/mod.rs b/compiler/rustc_middle/src/mir/interpret/mod.rs index 3af8f0db3f7f..d7809cc4343d 100644 --- a/compiler/rustc_middle/src/mir/interpret/mod.rs +++ b/compiler/rustc_middle/src/mir/interpret/mod.rs @@ -232,9 +232,8 @@ impl<'s> AllocDecodingSession<'s> { } AllocDiscriminant::VTable => { trace!("creating vtable alloc ID"); - let ty = as Decodable>::decode(decoder); - let poly_trait_ref = - > as Decodable>::decode(decoder); + let ty = Decodable::decode(decoder); + let poly_trait_ref = Decodable::decode(decoder); trace!("decoded vtable alloc instance: {ty:?}, {poly_trait_ref:?}"); decoder.interner().reserve_and_set_vtable_alloc(ty, poly_trait_ref, CTFE_ALLOC_SALT) } @@ -259,7 +258,10 @@ pub enum GlobalAlloc<'tcx> { /// The alloc ID is used as a function pointer. Function { instance: Instance<'tcx> }, /// This alloc ID points to a symbolic (not-reified) vtable. - VTable(Ty<'tcx>, Option>), + /// We remember the full dyn type, not just the principal trait, so that + /// const-eval and Miri can detect UB due to invalid transmutes of + /// `dyn Trait` types. + VTable(Ty<'tcx>, &'tcx ty::List>), /// The alloc ID points to a "lazy" static variable that did not get computed (yet). /// This is also used to break the cycle in recursive statics. Static(DefId), @@ -293,7 +295,7 @@ impl<'tcx> GlobalAlloc<'tcx> { #[inline] pub fn unwrap_vtable(&self) -> (Ty<'tcx>, Option>) { match *self { - GlobalAlloc::VTable(ty, poly_trait_ref) => (ty, poly_trait_ref), + GlobalAlloc::VTable(ty, dyn_ty) => (ty, dyn_ty.principal()), _ => bug!("expected vtable, got {:?}", self), } } @@ -398,10 +400,10 @@ impl<'tcx> TyCtxt<'tcx> { pub fn reserve_and_set_vtable_alloc( self, ty: Ty<'tcx>, - poly_trait_ref: Option>, + dyn_ty: &'tcx ty::List>, salt: usize, ) -> AllocId { - self.reserve_and_set_dedup(GlobalAlloc::VTable(ty, poly_trait_ref), salt) + self.reserve_and_set_dedup(GlobalAlloc::VTable(ty, dyn_ty), salt) } /// Interns the `Allocation` and return a new `AllocId`, even if there's already an identical diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index 612055eb879f..487895652183 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -277,9 +277,9 @@ pub fn create_dump_file<'tcx>( ) })?; } - Ok(io::BufWriter::new(fs::File::create(&file_path).map_err(|e| { + Ok(fs::File::create_buffered(&file_path).map_err(|e| { io::Error::new(e.kind(), format!("IO error creating MIR dump file: {file_path:?}; {e}")) - })?)) + })?) } /////////////////////////////////////////////////////////////////////////// @@ -1536,11 +1536,8 @@ pub fn write_allocations<'tcx>( // gracefully handle it and allow buggy rustc to be debugged via allocation printing. None => write!(w, " (deallocated)")?, Some(GlobalAlloc::Function { instance, .. }) => write!(w, " (fn: {instance})")?, - Some(GlobalAlloc::VTable(ty, Some(trait_ref))) => { - write!(w, " (vtable: impl {trait_ref} for {ty})")? - } - Some(GlobalAlloc::VTable(ty, None)) => { - write!(w, " (vtable: impl for {ty})")? + Some(GlobalAlloc::VTable(ty, dyn_ty)) => { + write!(w, " (vtable: impl {dyn_ty} for {ty})")? } Some(GlobalAlloc::Static(did)) if !tcx.is_foreign_item(did) => { match tcx.eval_static_initializer(did) { diff --git a/compiler/rustc_middle/src/mir/query.rs b/compiler/rustc_middle/src/mir/query.rs index 1002f64f849a..70331214ac5a 100644 --- a/compiler/rustc_middle/src/mir/query.rs +++ b/compiler/rustc_middle/src/mir/query.rs @@ -234,7 +234,9 @@ pub enum ConstraintCategory<'tcx> { UseAsStatic, TypeAnnotation, Cast { - /// Whether this is an unsizing cast and if yes, this contains the target type. + /// Whether this cast is a coercion that was automatically inserted by the compiler. + is_implicit_coercion: bool, + /// Whether this is an unsizing coercion and if yes, this contains the target type. /// Region variables are erased to ReErased. #[derive_where(skip)] unsize_to: Option>, diff --git a/compiler/rustc_middle/src/mir/statement.rs b/compiler/rustc_middle/src/mir/statement.rs index bc7dfa6205e7..88ed90c31146 100644 --- a/compiler/rustc_middle/src/mir/statement.rs +++ b/compiler/rustc_middle/src/mir/statement.rs @@ -432,9 +432,8 @@ impl<'tcx> Rvalue<'tcx> { | CastKind::IntToFloat | CastKind::FnPtrToPtr | CastKind::PtrToPtr - | CastKind::PointerCoercion(_) + | CastKind::PointerCoercion(_, _) | CastKind::PointerWithExposedProvenance - | CastKind::DynStar | CastKind::Transmute, _, _, diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index 231eb1989834..ae75f2d4187b 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -579,7 +579,8 @@ pub struct CopyNonOverlapping<'tcx> { pub count: Operand<'tcx>, } -/// Represents how a `TerminatorKind::Call` was constructed, used for diagnostics +/// Represents how a [`TerminatorKind::Call`] was constructed. +/// Used only for diagnostics. #[derive(Clone, Copy, TyEncodable, TyDecodable, Debug, PartialEq, Hash, HashStable)] #[derive(TypeFoldable, TypeVisitable)] pub enum CallSource { @@ -1403,9 +1404,7 @@ pub enum CastKind { /// * [`PointerCoercion::MutToConstPointer`] /// /// Both are runtime nops, so should be [`CastKind::PtrToPtr`] instead in runtime MIR. - PointerCoercion(PointerCoercion), - /// Cast into a dyn* object. - DynStar, + PointerCoercion(PointerCoercion, CoercionSource), IntToInt, FloatToInt, FloatToFloat, @@ -1421,6 +1420,16 @@ pub enum CastKind { Transmute, } +/// Represents how a [`CastKind::PointerCoercion`] was constructed. +/// Used only for diagnostics. +#[derive(Clone, Copy, Debug, PartialEq, Eq, TyEncodable, TyDecodable, Hash, HashStable)] +pub enum CoercionSource { + /// The coercion was manually written by the user with an `as` cast. + AsCast, + /// The coercion was automatically inserted by the compiler. + Implicit, +} + #[derive(Clone, Debug, PartialEq, Eq, TyEncodable, TyDecodable, Hash, HashStable)] #[derive(TypeFoldable, TypeVisitable)] pub enum AggregateKind<'tcx> { diff --git a/compiler/rustc_middle/src/query/erase.rs b/compiler/rustc_middle/src/query/erase.rs index bd20e6aa0053..48bf4ffced0c 100644 --- a/compiler/rustc_middle/src/query/erase.rs +++ b/compiler/rustc_middle/src/query/erase.rs @@ -1,6 +1,8 @@ use std::intrinsics::transmute_unchecked; use std::mem::MaybeUninit; +use rustc_span::ErrorGuaranteed; + use crate::query::CyclePlaceholder; use crate::ty::adjustment::CoerceUnsizedInfo; use crate::ty::{self, Ty}; @@ -216,6 +218,10 @@ impl EraseType for (&'_ T0, &'_ [T1]) { type Result = [u8; size_of::<(&'static (), &'static [()])>()]; } +impl EraseType for (&'_ T0, Result<(), ErrorGuaranteed>) { + type Result = [u8; size_of::<(&'static (), Result<(), ErrorGuaranteed>)>()]; +} + macro_rules! trivial { ($($ty:ty),+ $(,)?) => { $( diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 00036097ef50..9609ef46d5c3 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -881,13 +881,13 @@ rustc_queries! { /// Maps a `DefId` of a type to a list of its inherent impls. /// Contains implementations of methods that are inherent to a type. /// Methods in these implementations don't need to be exported. - query inherent_impls(key: DefId) -> Result<&'tcx [DefId], ErrorGuaranteed> { + query inherent_impls(key: DefId) -> &'tcx [DefId] { desc { |tcx| "collecting inherent impls for `{}`", tcx.def_path_str(key) } cache_on_disk_if { key.is_local() } separate_provide_extern } - query incoherent_impls(key: SimplifiedType) -> Result<&'tcx [DefId], ErrorGuaranteed> { + query incoherent_impls(key: SimplifiedType) -> &'tcx [DefId] { desc { |tcx| "collecting all inherent impls for `{:?}`", key } } @@ -1017,8 +1017,14 @@ rustc_queries! { /// Gets a complete map from all types to their inherent impls. /// Not meant to be used directly outside of coherence. - query crate_inherent_impls(k: ()) -> Result<&'tcx CrateInherentImpls, ErrorGuaranteed> { + query crate_inherent_impls(k: ()) -> (&'tcx CrateInherentImpls, Result<(), ErrorGuaranteed>) { desc { "finding all inherent impls defined in crate" } + } + + /// Checks all types in the crate for overlap in their inherent impls. Reports errors. + /// Not meant to be used directly outside of coherence. + query crate_inherent_impls_validity_check(_: ()) -> Result<(), ErrorGuaranteed> { + desc { "check for inherent impls that should not be defined in crate" } ensure_forwards_result_if_red } @@ -1715,7 +1721,7 @@ rustc_queries! { /// /// Do not call this directly, but instead use the `incoherent_impls` query. /// This query is only used to get the data necessary for that query. - query crate_incoherent_impls(key: (CrateNum, SimplifiedType)) -> Result<&'tcx [DefId], ErrorGuaranteed> { + query crate_incoherent_impls(key: (CrateNum, SimplifiedType)) -> &'tcx [DefId] { desc { |tcx| "collecting all impls for a type in a crate" } separate_provide_extern } diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index 32234d6b55d6..e614d41899a0 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -338,6 +338,8 @@ pub enum ExprKind<'tcx> { PointerCoercion { cast: PointerCoercion, source: ExprId, + /// Whether this coercion is written with an `as` cast in the source code. + is_from_as_cast: bool, }, /// A `loop` expression. Loop { @@ -453,12 +455,14 @@ pub enum ExprKind<'tcx> { source: ExprId, /// Type that the user gave to this expression user_ty: UserTy<'tcx>, + user_ty_span: Span, }, - /// A type ascription on a value, e.g. `42: i32`. + /// A type ascription on a value, e.g. `type_ascribe!(42, i32)` or `42 as i32`. ValueTypeAscription { source: ExprId, /// Type that the user gave to this expression user_ty: UserTy<'tcx>, + user_ty_span: Span, }, /// A closure definition. Closure(Box>), diff --git a/compiler/rustc_middle/src/thir/visit.rs b/compiler/rustc_middle/src/thir/visit.rs index e246ecebbec0..58e2ebaeaf8d 100644 --- a/compiler/rustc_middle/src/thir/visit.rs +++ b/compiler/rustc_middle/src/thir/visit.rs @@ -68,7 +68,9 @@ pub fn walk_expr<'thir, 'tcx: 'thir, V: Visitor<'thir, 'tcx>>( Cast { source } => visitor.visit_expr(&visitor.thir()[source]), Use { source } => visitor.visit_expr(&visitor.thir()[source]), NeverToAny { source } => visitor.visit_expr(&visitor.thir()[source]), - PointerCoercion { source, cast: _ } => visitor.visit_expr(&visitor.thir()[source]), + PointerCoercion { source, cast: _, is_from_as_cast: _ } => { + visitor.visit_expr(&visitor.thir()[source]) + } Let { expr, ref pat } => { visitor.visit_expr(&visitor.thir()[expr]); visitor.visit_pat(pat); @@ -129,7 +131,8 @@ pub fn walk_expr<'thir, 'tcx: 'thir, V: Visitor<'thir, 'tcx>>( visitor.visit_expr(&visitor.thir()[base.base]); } } - PlaceTypeAscription { source, user_ty: _ } | ValueTypeAscription { source, user_ty: _ } => { + PlaceTypeAscription { source, user_ty: _, user_ty_span: _ } + | ValueTypeAscription { source, user_ty: _, user_ty_span: _ } => { visitor.visit_expr(&visitor.thir()[source]) } Closure(box ClosureExpr { diff --git a/compiler/rustc_middle/src/ty/adjustment.rs b/compiler/rustc_middle/src/ty/adjustment.rs index 5a32078760e5..41a20e89cbf7 100644 --- a/compiler/rustc_middle/src/ty/adjustment.rs +++ b/compiler/rustc_middle/src/ty/adjustment.rs @@ -35,6 +35,9 @@ pub enum PointerCoercion { /// type. Codegen backends and miri figure out what has to be done /// based on the precise source/target type at hand. Unsize, + + /// Go from a pointer-like type to a `dyn*` object. + DynStar, } /// Represents coercing a value to a different type of value. @@ -102,9 +105,6 @@ pub enum Adjust<'tcx> { Pointer(PointerCoercion), - /// Cast into a dyn* object. - DynStar, - /// Take a pinned reference and reborrow as a `Pin<&mut T>` or `Pin<&T>`. ReborrowPin(ty::Region<'tcx>, hir::Mutability), } diff --git a/compiler/rustc_middle/src/ty/cast.rs b/compiler/rustc_middle/src/ty/cast.rs index 46f376595367..b1316ceef5ac 100644 --- a/compiler/rustc_middle/src/ty/cast.rs +++ b/compiler/rustc_middle/src/ty/cast.rs @@ -34,15 +34,12 @@ pub enum CastTy<'tcx> { FnPtr, /// Raw pointers. Ptr(ty::TypeAndMut<'tcx>), - /// Casting into a `dyn*` value. - DynStar, } /// Cast Kind. See [RFC 401](https://rust-lang.github.io/rfcs/0401-coercions.html) /// (or rustc_hir_analysis/check/cast.rs). #[derive(Copy, Clone, Debug, TyEncodable, TyDecodable, HashStable)] pub enum CastKind { - CoercionCast, PtrPtrCast, PtrAddrCast, AddrPtrCast, @@ -53,7 +50,6 @@ pub enum CastKind { ArrayPtrCast, FnPtrPtrCast, FnPtrAddrCast, - DynStarCast, } impl<'tcx> CastTy<'tcx> { @@ -71,7 +67,6 @@ impl<'tcx> CastTy<'tcx> { ty::Adt(d, _) if d.is_enum() && d.is_payloadfree() => Some(CastTy::Int(IntTy::CEnum)), ty::RawPtr(ty, mutbl) => Some(CastTy::Ptr(ty::TypeAndMut { ty, mutbl })), ty::FnPtr(..) => Some(CastTy::FnPtr), - ty::Dynamic(_, _, ty::DynStar) => Some(CastTy::DynStar), _ => None, } } @@ -86,7 +81,6 @@ pub fn mir_cast_kind<'tcx>(from_ty: Ty<'tcx>, cast_ty: Ty<'tcx>) -> mir::CastKin mir::CastKind::PointerExposeProvenance } (Some(CastTy::Int(_)), Some(CastTy::Ptr(_))) => mir::CastKind::PointerWithExposedProvenance, - (_, Some(CastTy::DynStar)) => mir::CastKind::DynStar, (Some(CastTy::Int(_)), Some(CastTy::Int(_))) => mir::CastKind::IntToInt, (Some(CastTy::FnPtr), Some(CastTy::Ptr(_))) => mir::CastKind::FnPtrToPtr, diff --git a/compiler/rustc_middle/src/ty/trait_def.rs b/compiler/rustc_middle/src/ty/trait_def.rs index 82690f70e5f1..188107e5d549 100644 --- a/compiler/rustc_middle/src/ty/trait_def.rs +++ b/compiler/rustc_middle/src/ty/trait_def.rs @@ -253,30 +253,16 @@ pub(super) fn trait_impls_of_provider(tcx: TyCtxt<'_>, trait_id: DefId) -> Trait } /// Query provider for `incoherent_impls`. -pub(super) fn incoherent_impls_provider( - tcx: TyCtxt<'_>, - simp: SimplifiedType, -) -> Result<&[DefId], ErrorGuaranteed> { +pub(super) fn incoherent_impls_provider(tcx: TyCtxt<'_>, simp: SimplifiedType) -> &[DefId] { let mut impls = Vec::new(); - - let mut res = Ok(()); for cnum in iter::once(LOCAL_CRATE).chain(tcx.crates(()).iter().copied()) { - let incoherent_impls = match tcx.crate_incoherent_impls((cnum, simp)) { - Ok(impls) => impls, - Err(e) => { - res = Err(e); - continue; - } - }; - for &impl_def_id in incoherent_impls { + for &impl_def_id in tcx.crate_incoherent_impls((cnum, simp)) { impls.push(impl_def_id) } } - debug!(?impls); - res?; - Ok(tcx.arena.alloc_slice(&impls)) + tcx.arena.alloc_slice(&impls) } pub(super) fn traits_provider(tcx: TyCtxt<'_>, _: LocalCrate) -> &[DefId] { diff --git a/compiler/rustc_mir_build/src/build/expr/as_place.rs b/compiler/rustc_mir_build/src/build/expr/as_place.rs index 2084bcae7b79..c7298e3ddfa9 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_place.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_place.rs @@ -470,21 +470,22 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { block.and(place_builder) } - ExprKind::PlaceTypeAscription { source, ref user_ty } => { + ExprKind::PlaceTypeAscription { source, ref user_ty, user_ty_span } => { let place_builder = unpack!( block = this.expr_as_place(block, source, mutability, fake_borrow_temps,) ); if let Some(user_ty) = user_ty { + let ty_source_info = this.source_info(user_ty_span); let annotation_index = this.canonical_user_type_annotations.push(CanonicalUserTypeAnnotation { - span: source_info.span, + span: user_ty_span, user_ty: user_ty.clone(), inferred_ty: expr.ty, }); let place = place_builder.to_place(this); this.cfg.push(block, Statement { - source_info, + source_info: ty_source_info, kind: StatementKind::AscribeUserType( Box::new((place, UserTypeProjection { base: annotation_index, @@ -496,20 +497,21 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } block.and(place_builder) } - ExprKind::ValueTypeAscription { source, ref user_ty } => { + ExprKind::ValueTypeAscription { source, ref user_ty, user_ty_span } => { let source_expr = &this.thir[source]; let temp = unpack!( block = this.as_temp(block, source_expr.temp_lifetime, source, mutability) ); if let Some(user_ty) = user_ty { + let ty_source_info = this.source_info(user_ty_span); let annotation_index = this.canonical_user_type_annotations.push(CanonicalUserTypeAnnotation { - span: source_info.span, + span: user_ty_span, user_ty: user_ty.clone(), inferred_ty: expr.ty, }); this.cfg.push(block, Statement { - source_info, + source_info: ty_source_info, kind: StatementKind::AscribeUserType( Box::new((Place::from(temp), UserTypeProjection { base: annotation_index, diff --git a/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs b/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs index ffedee8a1e81..fd949a533845 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs @@ -292,7 +292,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let cast_kind = mir_cast_kind(ty, expr.ty); block.and(Rvalue::Cast(cast_kind, source, expr.ty)) } - ExprKind::PointerCoercion { cast, source } => { + ExprKind::PointerCoercion { cast, source, is_from_as_cast } => { let source = unpack!( block = this.as_operand( block, @@ -302,7 +302,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { NeedsTemporary::No ) ); - block.and(Rvalue::Cast(CastKind::PointerCoercion(cast), source, expr.ty)) + let origin = + if is_from_as_cast { CoercionSource::AsCast } else { CoercionSource::Implicit }; + block.and(Rvalue::Cast(CastKind::PointerCoercion(cast, origin), source, expr.ty)) } ExprKind::Array { ref fields } => { // (*) We would (maybe) be closer to codegen if we diff --git a/compiler/rustc_mir_build/src/build/matches/test.rs b/compiler/rustc_mir_build/src/build/matches/test.rs index 63873aad02a4..020c202f9650 100644 --- a/compiler/rustc_mir_build/src/build/matches/test.rs +++ b/compiler/rustc_mir_build/src/build/matches/test.rs @@ -407,7 +407,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { source_info, temp, Rvalue::Cast( - CastKind::PointerCoercion(PointerCoercion::Unsize), + CastKind::PointerCoercion( + PointerCoercion::Unsize, + CoercionSource::Implicit, + ), Operand::Copy(val), ty, ), @@ -421,7 +424,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { source_info, slice, Rvalue::Cast( - CastKind::PointerCoercion(PointerCoercion::Unsize), + CastKind::PointerCoercion( + PointerCoercion::Unsize, + CoercionSource::Implicit, + ), expect, ty, ), diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index 55236933922f..fbd45f59a4fb 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -104,16 +104,29 @@ impl<'tcx> Cx<'tcx> { }; let kind = match adjustment.kind { - Adjust::Pointer(PointerCoercion::Unsize) => { - adjust_span(&mut expr); + Adjust::Pointer(cast) => { + if cast == PointerCoercion::Unsize { + adjust_span(&mut expr); + } + + let is_from_as_cast = if let hir::Node::Expr(hir::Expr { + kind: hir::ExprKind::Cast(..), + span: cast_span, + .. + }) = self.tcx.parent_hir_node(hir_expr.hir_id) + { + // Use the whole span of the `x as T` expression for the coercion. + span = *cast_span; + true + } else { + false + }; ExprKind::PointerCoercion { - cast: PointerCoercion::Unsize, + cast, source: self.thir.exprs.push(expr), + is_from_as_cast, } } - Adjust::Pointer(cast) => { - ExprKind::PointerCoercion { cast, source: self.thir.exprs.push(expr) } - } Adjust::NeverToAny if adjustment.target.is_never() => return expr, Adjust::NeverToAny => ExprKind::NeverToAny { source: self.thir.exprs.push(expr) }, Adjust::Deref(None) => { @@ -146,7 +159,6 @@ impl<'tcx> Cx<'tcx> { Adjust::Borrow(AutoBorrow::RawPtr(mutability)) => { ExprKind::RawBorrow { mutability, arg: self.thir.exprs.push(expr) } } - Adjust::DynStar => ExprKind::Cast { source: self.thir.exprs.push(expr) }, Adjust::ReborrowPin(region, mutbl) => { debug!("apply ReborrowPin adjustment"); // Rewrite `$expr` as `Pin { __pointer: &(mut)? *($expr).__pointer }` @@ -236,6 +248,7 @@ impl<'tcx> Cx<'tcx> { ExprKind::PointerCoercion { source: self.mirror_expr(source), cast: PointerCoercion::ArrayToPointer, + is_from_as_cast: true, } } else if let hir::ExprKind::Path(ref qpath) = source.kind && let res = self.typeck_results().qpath_res(qpath, source.hir_id) @@ -841,6 +854,7 @@ impl<'tcx> Cx<'tcx> { ExprKind::ValueTypeAscription { source: cast_expr, user_ty: Some(Box::new(*user_ty)), + user_ty_span: cast_ty.span, } } else { cast @@ -852,9 +866,17 @@ impl<'tcx> Cx<'tcx> { debug!("make_mirror_unadjusted: (type) user_ty={:?}", user_ty); let mirrored = self.mirror_expr(source); if source.is_syntactic_place_expr() { - ExprKind::PlaceTypeAscription { source: mirrored, user_ty } + ExprKind::PlaceTypeAscription { + source: mirrored, + user_ty, + user_ty_span: ty.span, + } } else { - ExprKind::ValueTypeAscription { source: mirrored, user_ty } + ExprKind::ValueTypeAscription { + source: mirrored, + user_ty, + user_ty_span: ty.span, + } } } hir::ExprKind::DropTemps(source) => ExprKind::Use { source: self.mirror_expr(source) }, diff --git a/compiler/rustc_mir_build/src/thir/print.rs b/compiler/rustc_mir_build/src/thir/print.rs index ce7774f59488..61317925d09e 100644 --- a/compiler/rustc_mir_build/src/thir/print.rs +++ b/compiler/rustc_mir_build/src/thir/print.rs @@ -292,9 +292,14 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> { self.print_expr(*source, depth_lvl + 2); print_indented!(self, "}", depth_lvl); } - PointerCoercion { cast, source } => { + PointerCoercion { cast, is_from_as_cast, source } => { print_indented!(self, "Pointer {", depth_lvl); print_indented!(self, format!("cast: {:?}", cast), depth_lvl + 1); + print_indented!( + self, + format!("is_from_as_cast: {:?}", is_from_as_cast), + depth_lvl + 1 + ); print_indented!(self, "source:", depth_lvl + 1); self.print_expr(*source, depth_lvl + 2); print_indented!(self, "}", depth_lvl); @@ -454,16 +459,18 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> { self.print_adt_expr(&**adt_expr, depth_lvl + 1); print_indented!(self, "}", depth_lvl); } - PlaceTypeAscription { source, user_ty } => { + PlaceTypeAscription { source, user_ty, user_ty_span } => { print_indented!(self, "PlaceTypeAscription {", depth_lvl); print_indented!(self, format!("user_ty: {:?}", user_ty), depth_lvl + 1); + print_indented!(self, format!("user_ty_span: {:?}", user_ty_span), depth_lvl + 1); print_indented!(self, "source:", depth_lvl + 1); self.print_expr(*source, depth_lvl + 2); print_indented!(self, "}", depth_lvl); } - ValueTypeAscription { source, user_ty } => { + ValueTypeAscription { source, user_ty, user_ty_span } => { print_indented!(self, "ValueTypeAscription {", depth_lvl); print_indented!(self, format!("user_ty: {:?}", user_ty), depth_lvl + 1); + print_indented!(self, format!("user_ty_span: {:?}", user_ty_span), depth_lvl + 1); print_indented!(self, "source:", depth_lvl + 1); self.print_expr(*source, depth_lvl + 2); print_indented!(self, "}", depth_lvl); diff --git a/compiler/rustc_mir_dataflow/src/framework/engine.rs b/compiler/rustc_mir_dataflow/src/framework/engine.rs index 947bdf860b58..da01a9740943 100644 --- a/compiler/rustc_mir_dataflow/src/framework/engine.rs +++ b/compiler/rustc_mir_dataflow/src/framework/engine.rs @@ -266,7 +266,7 @@ where A::Domain: DebugWithContext, { use std::fs; - use std::io::{self, Write}; + use std::io::Write; let def_id = body.source.def_id(); let Ok(attrs) = RustcMirAttrs::parse(tcx, def_id) else { @@ -281,8 +281,7 @@ where if let Some(parent) = path.parent() { fs::create_dir_all(parent)?; } - let f = fs::File::create(&path)?; - io::BufWriter::new(f) + fs::File::create_buffered(&path)? } None if dump_enabled(tcx, A::NAME, def_id) => { diff --git a/compiler/rustc_mir_dataflow/src/lib.rs b/compiler/rustc_mir_dataflow/src/lib.rs index cb7c7edc413a..cd926c076412 100644 --- a/compiler/rustc_mir_dataflow/src/lib.rs +++ b/compiler/rustc_mir_dataflow/src/lib.rs @@ -3,6 +3,7 @@ #![feature(associated_type_defaults)] #![feature(box_patterns)] #![feature(exact_size_is_empty)] +#![feature(file_buffered)] #![feature(let_chains)] #![feature(try_blocks)] #![warn(unreachable_pub)] diff --git a/compiler/rustc_mir_transform/src/cleanup_post_borrowck.rs b/compiler/rustc_mir_transform/src/cleanup_post_borrowck.rs index 5395e1b0e94d..6a22a58470c9 100644 --- a/compiler/rustc_mir_transform/src/cleanup_post_borrowck.rs +++ b/compiler/rustc_mir_transform/src/cleanup_post_borrowck.rs @@ -42,6 +42,7 @@ impl<'tcx> crate::MirPass<'tcx> for CleanupPostBorrowck { ref mut cast_kind @ CastKind::PointerCoercion( PointerCoercion::ArrayToPointer | PointerCoercion::MutToConstPointer, + _, ), .., ), diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs index 2026b7893159..88dc8e74a8c2 100644 --- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs +++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs @@ -189,7 +189,7 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, 'tcx> { } } Rvalue::Cast( - CastKind::PointerCoercion(ty::adjustment::PointerCoercion::Unsize), + CastKind::PointerCoercion(ty::adjustment::PointerCoercion::Unsize, _), operand, _, ) => { diff --git a/compiler/rustc_mir_transform/src/dump_mir.rs b/compiler/rustc_mir_transform/src/dump_mir.rs index d7c473604196..5dd84975b88e 100644 --- a/compiler/rustc_mir_transform/src/dump_mir.rs +++ b/compiler/rustc_mir_transform/src/dump_mir.rs @@ -24,7 +24,7 @@ pub fn emit_mir(tcx: TyCtxt<'_>) -> io::Result<()> { write_mir_pretty(tcx, None, &mut f)?; } OutFileName::Real(path) => { - let mut f = io::BufWriter::new(File::create(&path)?); + let mut f = File::create_buffered(&path)?; write_mir_pretty(tcx, None, &mut f)?; if tcx.sess.opts.json_artifact_notifications { tcx.dcx().emit_artifact_notification(&path, "mir"); diff --git a/compiler/rustc_mir_transform/src/early_otherwise_branch.rs b/compiler/rustc_mir_transform/src/early_otherwise_branch.rs index aed7f20aaea0..704ed508b22a 100644 --- a/compiler/rustc_mir_transform/src/early_otherwise_branch.rs +++ b/compiler/rustc_mir_transform/src/early_otherwise_branch.rs @@ -133,29 +133,18 @@ impl<'tcx> crate::MirPass<'tcx> for EarlyOtherwiseBranch { let mut patch = MirPatch::new(body); - let (second_discriminant_temp, second_operand) = if opt_data.need_hoist_discriminant { - // create temp to store second discriminant in, `_s` in example above - let second_discriminant_temp = - patch.new_temp(opt_data.child_ty, opt_data.child_source.span); + // create temp to store second discriminant in, `_s` in example above + let second_discriminant_temp = + patch.new_temp(opt_data.child_ty, opt_data.child_source.span); - patch.add_statement( - parent_end, - StatementKind::StorageLive(second_discriminant_temp), - ); + patch.add_statement(parent_end, StatementKind::StorageLive(second_discriminant_temp)); - // create assignment of discriminant - patch.add_assign( - parent_end, - Place::from(second_discriminant_temp), - Rvalue::Discriminant(opt_data.child_place), - ); - ( - Some(second_discriminant_temp), - Operand::Move(Place::from(second_discriminant_temp)), - ) - } else { - (None, Operand::Copy(opt_data.child_place)) - }; + // create assignment of discriminant + patch.add_assign( + parent_end, + Place::from(second_discriminant_temp), + Rvalue::Discriminant(opt_data.child_place), + ); // create temp to store inequality comparison between the two discriminants, `_t` in // example above @@ -164,9 +153,11 @@ impl<'tcx> crate::MirPass<'tcx> for EarlyOtherwiseBranch { let comp_temp = patch.new_temp(comp_res_type, opt_data.child_source.span); patch.add_statement(parent_end, StatementKind::StorageLive(comp_temp)); - // create inequality comparison - let comp_rvalue = - Rvalue::BinaryOp(nequal, Box::new((parent_op.clone(), second_operand))); + // create inequality comparison between the two discriminants + let comp_rvalue = Rvalue::BinaryOp( + nequal, + Box::new((parent_op.clone(), Operand::Move(Place::from(second_discriminant_temp)))), + ); patch.add_statement( parent_end, StatementKind::Assign(Box::new((Place::from(comp_temp), comp_rvalue))), @@ -202,13 +193,8 @@ impl<'tcx> crate::MirPass<'tcx> for EarlyOtherwiseBranch { TerminatorKind::if_(Operand::Move(Place::from(comp_temp)), true_case, false_case), ); - if let Some(second_discriminant_temp) = second_discriminant_temp { - // generate StorageDead for the second_discriminant_temp not in use anymore - patch.add_statement( - parent_end, - StatementKind::StorageDead(second_discriminant_temp), - ); - } + // generate StorageDead for the second_discriminant_temp not in use anymore + patch.add_statement(parent_end, StatementKind::StorageDead(second_discriminant_temp)); // Generate a StorageDead for comp_temp in each of the targets, since we moved it into // the switch @@ -236,7 +222,6 @@ struct OptimizationData<'tcx> { child_place: Place<'tcx>, child_ty: Ty<'tcx>, child_source: SourceInfo, - need_hoist_discriminant: bool, } fn evaluate_candidate<'tcx>( @@ -250,12 +235,44 @@ fn evaluate_candidate<'tcx>( return None; }; let parent_ty = parent_discr.ty(body.local_decls(), tcx); + if !bbs[targets.otherwise()].is_empty_unreachable() { + // Someone could write code like this: + // ```rust + // let Q = val; + // if discriminant(P) == otherwise { + // let ptr = &mut Q as *mut _ as *mut u8; + // // It may be difficult for us to effectively determine whether values are valid. + // // Invalid values can come from all sorts of corners. + // unsafe { *ptr = 10; } + // } + // + // match P { + // A => match Q { + // A => { + // // code + // } + // _ => { + // // don't use Q + // } + // } + // _ => { + // // don't use Q + // } + // }; + // ``` + // + // Hoisting the `discriminant(Q)` out of the `A` arm causes us to compute the discriminant + // of an invalid value, which is UB. + // In order to fix this, **we would either need to show that the discriminant computation of + // `place` is computed in all branches**. + // FIXME(#95162) For the moment, we adopt a conservative approach and + // consider only the `otherwise` branch has no statements and an unreachable terminator. + return None; + } let (_, child) = targets.iter().next()?; - - let Terminator { - kind: TerminatorKind::SwitchInt { targets: child_targets, discr: child_discr }, - source_info, - } = bbs[child].terminator() + let child_terminator = &bbs[child].terminator(); + let TerminatorKind::SwitchInt { targets: child_targets, discr: child_discr } = + &child_terminator.kind else { return None; }; @@ -263,115 +280,25 @@ fn evaluate_candidate<'tcx>( if child_ty != parent_ty { return None; } - - // We only handle: - // ``` - // bb4: { - // _8 = discriminant((_3.1: Enum1)); - // switchInt(move _8) -> [2: bb7, otherwise: bb1]; - // } - // ``` - // and - // ``` - // bb2: { - // switchInt((_3.1: u64)) -> [1: bb5, otherwise: bb1]; - // } - // ``` - if bbs[child].statements.len() > 1 { + let Some(StatementKind::Assign(boxed)) = &bbs[child].statements.first().map(|x| &x.kind) else { return None; - } - - // When thie BB has exactly one statement, this statement should be discriminant. - let need_hoist_discriminant = bbs[child].statements.len() == 1; - let child_place = if need_hoist_discriminant { - if !bbs[targets.otherwise()].is_empty_unreachable() { - // Someone could write code like this: - // ```rust - // let Q = val; - // if discriminant(P) == otherwise { - // let ptr = &mut Q as *mut _ as *mut u8; - // // It may be difficult for us to effectively determine whether values are valid. - // // Invalid values can come from all sorts of corners. - // unsafe { *ptr = 10; } - // } - // - // match P { - // A => match Q { - // A => { - // // code - // } - // _ => { - // // don't use Q - // } - // } - // _ => { - // // don't use Q - // } - // }; - // ``` - // - // Hoisting the `discriminant(Q)` out of the `A` arm causes us to compute the discriminant of an - // invalid value, which is UB. - // In order to fix this, **we would either need to show that the discriminant computation of - // `place` is computed in all branches**. - // FIXME(#95162) For the moment, we adopt a conservative approach and - // consider only the `otherwise` branch has no statements and an unreachable terminator. - return None; - } - // Handle: - // ``` - // bb4: { - // _8 = discriminant((_3.1: Enum1)); - // switchInt(move _8) -> [2: bb7, otherwise: bb1]; - // } - // ``` - let [ - Statement { - kind: StatementKind::Assign(box (_, Rvalue::Discriminant(child_place))), - .. - }, - ] = bbs[child].statements.as_slice() - else { - return None; - }; - *child_place - } else { - // Handle: - // ``` - // bb2: { - // switchInt((_3.1: u64)) -> [1: bb5, otherwise: bb1]; - // } - // ``` - let Operand::Copy(child_place) = child_discr else { - return None; - }; - *child_place }; - let destination = if need_hoist_discriminant || bbs[targets.otherwise()].is_empty_unreachable() - { - child_targets.otherwise() - } else { - targets.otherwise() + let (_, Rvalue::Discriminant(child_place)) = &**boxed else { + return None; }; + let destination = child_targets.otherwise(); // Verify that the optimization is legal for each branch for (value, child) in targets.iter() { - if !verify_candidate_branch( - &bbs[child], - value, - child_place, - destination, - need_hoist_discriminant, - ) { + if !verify_candidate_branch(&bbs[child], value, *child_place, destination) { return None; } } Some(OptimizationData { destination, - child_place, + child_place: *child_place, child_ty, - child_source: *source_info, - need_hoist_discriminant, + child_source: child_terminator.source_info, }) } @@ -380,48 +307,31 @@ fn verify_candidate_branch<'tcx>( value: u128, place: Place<'tcx>, destination: BasicBlock, - need_hoist_discriminant: bool, ) -> bool { - // In order for the optimization to be correct, the terminator must be a `SwitchInt`. - let TerminatorKind::SwitchInt { discr: switch_op, targets } = &branch.terminator().kind else { - return false; - }; - if need_hoist_discriminant { - // If we need hoist discriminant, the branch must have exactly one statement. - let [statement] = branch.statements.as_slice() else { - return false; - }; - // The statement must assign the discriminant of `place`. - let StatementKind::Assign(box (discr_place, Rvalue::Discriminant(from_place))) = - statement.kind - else { - return false; - }; - if from_place != place { - return false; - } - // The assignment must invalidate a local that terminate on a `SwitchInt`. - if !discr_place.projection.is_empty() || *switch_op != Operand::Move(discr_place) { - return false; - } + // In order for the optimization to be correct, the branch must... + // ...have exactly one statement + if let [statement] = branch.statements.as_slice() + // ...assign the discriminant of `place` in that statement + && let StatementKind::Assign(boxed) = &statement.kind + && let (discr_place, Rvalue::Discriminant(from_place)) = &**boxed + && *from_place == place + // ...make that assignment to a local + && discr_place.projection.is_empty() + // ...terminate on a `SwitchInt` that invalidates that local + && let TerminatorKind::SwitchInt { discr: switch_op, targets, .. } = + &branch.terminator().kind + && *switch_op == Operand::Move(*discr_place) + // ...fall through to `destination` if the switch misses + && destination == targets.otherwise() + // ...have a branch for value `value` + && let mut iter = targets.iter() + && let Some((target_value, _)) = iter.next() + && target_value == value + // ...and have no more branches + && iter.next().is_none() + { + true } else { - // If we don't need hoist discriminant, the branch must not have any statements. - if !branch.statements.is_empty() { - return false; - } - // The place on `SwitchInt` must be the same. - if *switch_op != Operand::Copy(place) { - return false; - } + false } - // It must fall through to `destination` if the switch misses. - if destination != targets.otherwise() { - return false; - } - // It must have exactly one branch for value `value` and have no more branches. - let mut iter = targets.iter(); - let (Some((target_value, _)), None) = (iter.next(), iter.next()) else { - return false; - }; - target_value == value } diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index 4d44669fa3ec..f735d08fca57 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -576,7 +576,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } value.offset(Size::ZERO, to, &self.ecx).ok()? } - CastKind::PointerCoercion(ty::adjustment::PointerCoercion::Unsize) => { + CastKind::PointerCoercion(ty::adjustment::PointerCoercion::Unsize, _) => { let src = self.evaluated[value].as_ref()?; let to = self.ecx.layout_of(to).ok()?; let dest = self.ecx.allocate(to, MemoryKind::Stack).ok()?; @@ -593,7 +593,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { let ret = self.ecx.ptr_to_ptr(&src, to).ok()?; ret.into() } - CastKind::PointerCoercion(ty::adjustment::PointerCoercion::UnsafeFnPointer) => { + CastKind::PointerCoercion(ty::adjustment::PointerCoercion::UnsafeFnPointer, _) => { let src = self.evaluated[value].as_ref()?; let src = self.ecx.read_immediate(src).ok()?; let to = self.ecx.layout_of(to).ok()?; @@ -1138,7 +1138,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { ( UnOp::PtrMetadata, Value::Cast { - kind: CastKind::PointerCoercion(ty::adjustment::PointerCoercion::Unsize), + kind: CastKind::PointerCoercion(ty::adjustment::PointerCoercion::Unsize, _), from, to, .. @@ -1342,7 +1342,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { return Some(value); } - if let CastKind::PointerCoercion(ReifyFnPointer | ClosureFnPointer(_)) = kind { + if let CastKind::PointerCoercion(ReifyFnPointer | ClosureFnPointer(_), _) = kind { // Each reification of a generic fn may get a different pointer. // Do not try to merge them. return self.new_opaque(); @@ -1429,7 +1429,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { // We have an unsizing cast, which assigns the length to fat pointer metadata. if let Value::Cast { kind, from, to, .. } = self.get(inner) - && let CastKind::PointerCoercion(ty::adjustment::PointerCoercion::Unsize) = kind + && let CastKind::PointerCoercion(ty::adjustment::PointerCoercion::Unsize, _) = kind && let Some(from) = from.builtin_deref(true) && let ty::Array(_, len) = from.kind() && let Some(to) = to.builtin_deref(true) diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index 56268c1d201d..4c090665992b 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -3,6 +3,7 @@ #![feature(box_patterns)] #![feature(const_type_name)] #![feature(cow_is_borrowed)] +#![feature(file_buffered)] #![feature(if_let_guard)] #![feature(impl_trait_in_assoc_type)] #![feature(let_chains)] diff --git a/compiler/rustc_mir_transform/src/mentioned_items.rs b/compiler/rustc_mir_transform/src/mentioned_items.rs index f24de609e6b2..cf5c5f85a9ff 100644 --- a/compiler/rustc_mir_transform/src/mentioned_items.rs +++ b/compiler/rustc_mir_transform/src/mentioned_items.rs @@ -70,11 +70,11 @@ impl<'tcx> Visitor<'tcx> for MentionedItemsVisitor<'_, 'tcx> { match *rvalue { // We need to detect unsizing casts that required vtables. mir::Rvalue::Cast( - mir::CastKind::PointerCoercion(PointerCoercion::Unsize), + mir::CastKind::PointerCoercion(PointerCoercion::Unsize, _) + | mir::CastKind::PointerCoercion(PointerCoercion::DynStar, _), ref operand, target_ty, - ) - | mir::Rvalue::Cast(mir::CastKind::DynStar, ref operand, target_ty) => { + ) => { // This isn't monomorphized yet so we can't tell what the actual types are -- just // add everything that may involve a vtable. let source_ty = operand.ty(self.body, self.tcx); @@ -96,7 +96,7 @@ impl<'tcx> Visitor<'tcx> for MentionedItemsVisitor<'_, 'tcx> { } // Similarly, record closures that are turned into function pointers. mir::Rvalue::Cast( - mir::CastKind::PointerCoercion(PointerCoercion::ClosureFnPointer(_)), + mir::CastKind::PointerCoercion(PointerCoercion::ClosureFnPointer(_), _), ref operand, _, ) => { @@ -106,7 +106,7 @@ impl<'tcx> Visitor<'tcx> for MentionedItemsVisitor<'_, 'tcx> { } // And finally, function pointer reification casts. mir::Rvalue::Cast( - mir::CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer), + mir::CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer, _), ref operand, _, ) => { diff --git a/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs b/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs index c206252c15aa..71723f040b3b 100644 --- a/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs +++ b/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs @@ -7,9 +7,10 @@ use rustc_hir::def_id::DefId; use rustc_hir::lang_items::LangItem; use rustc_index::{Idx, IndexVec}; use rustc_middle::mir::{ - BasicBlock, BasicBlockData, Body, CallSource, CastKind, Const, ConstOperand, ConstValue, Local, - LocalDecl, MirSource, Operand, Place, PlaceElem, RETURN_PLACE, Rvalue, SourceInfo, Statement, - StatementKind, Terminator, TerminatorKind, UnwindAction, UnwindTerminateReason, + BasicBlock, BasicBlockData, Body, CallSource, CastKind, CoercionSource, Const, ConstOperand, + ConstValue, Local, LocalDecl, MirSource, Operand, Place, PlaceElem, RETURN_PLACE, Rvalue, + SourceInfo, Statement, StatementKind, Terminator, TerminatorKind, UnwindAction, + UnwindTerminateReason, }; use rustc_middle::ty::adjustment::PointerCoercion; use rustc_middle::ty::util::{AsyncDropGlueMorphology, Discr}; @@ -329,7 +330,7 @@ impl<'tcx> AsyncDestructorCtorShimBuilder<'tcx> { fn put_array_as_slice(&mut self, elem_ty: Ty<'tcx>) { let slice_ptr_ty = Ty::new_mut_ptr(self.tcx, Ty::new_slice(self.tcx, elem_ty)); self.put_temp_rvalue(Rvalue::Cast( - CastKind::PointerCoercion(PointerCoercion::Unsize), + CastKind::PointerCoercion(PointerCoercion::Unsize, CoercionSource::Implicit), Operand::Copy(Self::SELF_PTR.into()), slice_ptr_ty, )) diff --git a/compiler/rustc_mir_transform/src/validate.rs b/compiler/rustc_mir_transform/src/validate.rs index 914dddc1a56e..e353be6a105f 100644 --- a/compiler/rustc_mir_transform/src/validate.rs +++ b/compiler/rustc_mir_transform/src/validate.rs @@ -4,7 +4,8 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir::LangItem; use rustc_index::IndexVec; use rustc_index::bit_set::BitSet; -use rustc_infer::traits::Reveal; +use rustc_infer::infer::TyCtxtInferExt; +use rustc_infer::traits::{Obligation, ObligationCause, Reveal}; use rustc_middle::mir::coverage::CoverageKind; use rustc_middle::mir::visit::{NonUseContext, PlaceContext, Visitor}; use rustc_middle::mir::*; @@ -16,6 +17,8 @@ use rustc_middle::ty::{ use rustc_middle::{bug, span_bug}; use rustc_target::abi::{FIRST_VARIANT, Size}; use rustc_target::spec::abi::Abi; +use rustc_trait_selection::traits::ObligationCtxt; +use rustc_type_ir::Upcast; use crate::util::{is_within_packed, relate_types}; @@ -586,6 +589,22 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { crate::util::relate_types(self.tcx, self.param_env, variance, src, dest) } + + /// Check that the given predicate definitely holds in the param-env of this MIR body. + fn predicate_must_hold_modulo_regions( + &self, + pred: impl Upcast, ty::Predicate<'tcx>>, + ) -> bool { + let infcx = self.tcx.infer_ctxt().build(); + let ocx = ObligationCtxt::new(&infcx); + ocx.register_obligation(Obligation::new( + self.tcx, + ObligationCause::dummy(), + self.param_env, + pred, + )); + ocx.select_all_or_error().is_empty() + } } impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { @@ -1128,12 +1147,9 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { Rvalue::Cast(kind, operand, target_type) => { let op_ty = operand.ty(self.body, self.tcx); match kind { - CastKind::DynStar => { - // FIXME(dyn-star): make sure nothing needs to be done here. - } // FIXME: Add Checks for these CastKind::PointerWithExposedProvenance | CastKind::PointerExposeProvenance => {} - CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer) => { + CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer, _) => { // FIXME: check signature compatibility. check_kinds!( op_ty, @@ -1146,7 +1162,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { ty::FnPtr(..) ); } - CastKind::PointerCoercion(PointerCoercion::UnsafeFnPointer) => { + CastKind::PointerCoercion(PointerCoercion::UnsafeFnPointer, _) => { // FIXME: check safety and signature compatibility. check_kinds!( op_ty, @@ -1159,7 +1175,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { ty::FnPtr(..) ); } - CastKind::PointerCoercion(PointerCoercion::ClosureFnPointer(..)) => { + CastKind::PointerCoercion(PointerCoercion::ClosureFnPointer(..), _) => { // FIXME: check safety, captures, and signature compatibility. check_kinds!( op_ty, @@ -1172,7 +1188,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { ty::FnPtr(..) ); } - CastKind::PointerCoercion(PointerCoercion::MutToConstPointer) => { + CastKind::PointerCoercion(PointerCoercion::MutToConstPointer, _) => { // FIXME: check same pointee? check_kinds!( op_ty, @@ -1188,7 +1204,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { self.fail(location, format!("After borrowck, MIR disallows {kind:?}")); } } - CastKind::PointerCoercion(PointerCoercion::ArrayToPointer) => { + CastKind::PointerCoercion(PointerCoercion::ArrayToPointer, _) => { // FIXME: Check pointee types check_kinds!( op_ty, @@ -1204,9 +1220,22 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { self.fail(location, format!("After borrowck, MIR disallows {kind:?}")); } } - CastKind::PointerCoercion(PointerCoercion::Unsize) => { - // This is used for all `CoerceUnsized` types, - // not just pointers/references, so is hard to check. + CastKind::PointerCoercion(PointerCoercion::Unsize, _) => { + // Pointers being unsize coerced should at least implement + // `CoerceUnsized`. + if !self.predicate_must_hold_modulo_regions(ty::TraitRef::new( + self.tcx, + self.tcx.require_lang_item( + LangItem::CoerceUnsized, + Some(self.body.source_info(location).span), + ), + [op_ty, *target_type], + )) { + self.fail(location, format!("Unsize coercion, but `{op_ty}` isn't coercible to `{target_type}`")); + } + } + CastKind::PointerCoercion(PointerCoercion::DynStar, _) => { + // FIXME(dyn-star): make sure nothing needs to be done here. } CastKind::IntToInt | CastKind::IntToFloat => { let input_valid = op_ty.is_integral() || op_ty.is_char() || op_ty.is_bool(); diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 0cdd237dc003..276098a84007 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -665,11 +665,11 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> { // have to instantiate all methods of the trait being cast to, so we // can build the appropriate vtable. mir::Rvalue::Cast( - mir::CastKind::PointerCoercion(PointerCoercion::Unsize), + mir::CastKind::PointerCoercion(PointerCoercion::Unsize, _) + | mir::CastKind::PointerCoercion(PointerCoercion::DynStar, _), ref operand, target_ty, - ) - | mir::Rvalue::Cast(mir::CastKind::DynStar, ref operand, target_ty) => { + ) => { let source_ty = operand.ty(self.body, self.tcx); // *Before* monomorphizing, record that we already handled this mention. self.used_mentioned_items @@ -694,7 +694,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> { } } mir::Rvalue::Cast( - mir::CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer), + mir::CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer, _), ref operand, _, ) => { @@ -705,7 +705,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> { visit_fn_use(self.tcx, fn_ty, false, span, self.used_items); } mir::Rvalue::Cast( - mir::CastKind::PointerCoercion(PointerCoercion::ClosureFnPointer(_)), + mir::CastKind::PointerCoercion(PointerCoercion::ClosureFnPointer(_), _), ref operand, _, ) => { @@ -1175,15 +1175,15 @@ fn collect_alloc<'tcx>(tcx: TyCtxt<'tcx>, alloc_id: AllocId, output: &mut MonoIt output.push(create_fn_mono_item(tcx, instance, DUMMY_SP)); } } - GlobalAlloc::VTable(ty, trait_ref) => { - let alloc_id = tcx.vtable_allocation((ty, trait_ref)); + GlobalAlloc::VTable(ty, dyn_ty) => { + let alloc_id = tcx.vtable_allocation((ty, dyn_ty.principal())); collect_alloc(tcx, alloc_id, output) } } } fn assoc_fn_of_type<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, fn_ident: Ident) -> Option { - for impl_def_id in tcx.inherent_impls(def_id).ok()? { + for impl_def_id in tcx.inherent_impls(def_id) { if let Some(new) = tcx.associated_items(impl_def_id).find_by_name_and_kind( tcx, fn_ident, diff --git a/compiler/rustc_monomorphize/src/lib.rs b/compiler/rustc_monomorphize/src/lib.rs index 91101ddd5902..e92e6978d0f4 100644 --- a/compiler/rustc_monomorphize/src/lib.rs +++ b/compiler/rustc_monomorphize/src/lib.rs @@ -1,5 +1,6 @@ // tidy-alphabetical-start #![feature(array_windows)] +#![feature(file_buffered)] #![feature(if_let_guard)] #![feature(let_chains)] #![warn(unreachable_pub)] diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs index 1c1e6164f2eb..ad05cca66cab 100644 --- a/compiler/rustc_monomorphize/src/partitioning.rs +++ b/compiler/rustc_monomorphize/src/partitioning.rs @@ -95,7 +95,7 @@ use std::cmp; use std::collections::hash_map::Entry; use std::fs::{self, File}; -use std::io::{BufWriter, Write}; +use std::io::Write; use std::path::{Path, PathBuf}; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; @@ -1243,8 +1243,7 @@ fn dump_mono_items_stats<'tcx>( let ext = format.extension(); let filename = format!("{crate_name}.mono_items.{ext}"); let output_path = output_directory.join(&filename); - let file = File::create(&output_path)?; - let mut file = BufWriter::new(file); + let mut file = File::create_buffered(&output_path)?; // Gather instantiated mono items grouped by def_id let mut items_per_def_id: FxIndexMap<_, Vec<_>> = Default::default(); diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs index eb1c1a645785..8bd615e6d791 100644 --- a/compiler/rustc_parse/src/parser/attr_wrapper.rs +++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs @@ -108,7 +108,7 @@ struct LazyAttrTokenStreamImpl { start_token: (Token, Spacing), cursor_snapshot: TokenCursor, num_calls: u32, - break_last_token: bool, + break_last_token: u32, node_replacements: Box<[NodeReplacement]>, } @@ -339,17 +339,20 @@ impl<'a> Parser<'a> { let parser_replacements_end = self.capture_state.parser_replacements.len(); assert!( - !(self.break_last_token && matches!(capture_trailing, Trailing::Yes)), - "Cannot set break_last_token and have trailing token" + !(self.break_last_token > 0 && matches!(capture_trailing, Trailing::Yes)), + "Cannot have break_last_token > 0 and have trailing token" ); + assert!(self.break_last_token <= 2, "cannot break token more than twice"); let end_pos = self.num_bump_calls + capture_trailing as u32 - // If we 'broke' the last token (e.g. breaking a '>>' token to two '>' tokens), then - // extend the range of captured tokens to include it, since the parser was not actually - // bumped past it. When the `LazyAttrTokenStream` gets converted into an - // `AttrTokenStream`, we will create the proper token. - + self.break_last_token as u32; + // If we "broke" the last token (e.g. breaking a `>>` token once into `>` + `>`, or + // breaking a `>>=` token twice into `>` + `>` + `=`), then extend the range of + // captured tokens to include it, because the parser was not actually bumped past it. + // (Even if we broke twice, it was still just one token originally, hence the `1`.) + // When the `LazyAttrTokenStream` gets converted into an `AttrTokenStream`, we will + // rebreak that final token once or twice. + + if self.break_last_token == 0 { 0 } else { 1 }; let num_calls = end_pos - collect_pos.start_pos; @@ -425,7 +428,7 @@ impl<'a> Parser<'a> { // for the `#[cfg]` and/or `#[cfg_attr]` attrs. This allows us to run // eager cfg-expansion on the captured token stream. if definite_capture_mode { - assert!(!self.break_last_token, "Should not have unglued last token with cfg attr"); + assert!(self.break_last_token == 0, "Should not have unglued last token with cfg attr"); // What is the status here when parsing the example code at the top of this method? // @@ -471,7 +474,7 @@ impl<'a> Parser<'a> { /// close delims. fn make_attr_token_stream( iter: impl Iterator, - break_last_token: bool, + break_last_token: u32, ) -> AttrTokenStream { #[derive(Debug)] struct FrameData { @@ -513,18 +516,17 @@ fn make_attr_token_stream( } } - if break_last_token { + if break_last_token > 0 { let last_token = stack_top.inner.pop().unwrap(); if let AttrTokenTree::Token(last_token, spacing) = last_token { - let unglued_first = last_token.kind.break_two_token_op().unwrap().0; + let (unglued, _) = last_token.kind.break_two_token_op(break_last_token).unwrap(); - // An 'unglued' token is always two ASCII characters + // Tokens are always ASCII chars, so we can use byte arithmetic here. let mut first_span = last_token.span.shrink_to_lo(); - first_span = first_span.with_hi(first_span.lo() + rustc_span::BytePos(1)); + first_span = + first_span.with_hi(first_span.lo() + rustc_span::BytePos(break_last_token)); - stack_top - .inner - .push(AttrTokenTree::Token(Token::new(unglued_first, first_span), spacing)); + stack_top.inner.push(AttrTokenTree::Token(Token::new(unglued, first_span), spacing)); } else { panic!("Unexpected last token {last_token:?}") } diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 186828baf01e..77ad4fdeeb17 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -146,21 +146,25 @@ pub struct Parser<'a> { token_cursor: TokenCursor, // The number of calls to `bump`, i.e. the position in the token stream. num_bump_calls: u32, - // During parsing we may sometimes need to 'unglue' a glued token into two - // component tokens (e.g. '>>' into '>' and '>), so the parser can consume - // them one at a time. This process bypasses the normal capturing mechanism - // (e.g. `num_bump_calls` will not be incremented), since the 'unglued' - // tokens due not exist in the original `TokenStream`. + // During parsing we may sometimes need to "unglue" a glued token into two + // or three component tokens (e.g. `>>` into `>` and `>`, or `>>=` into `>` + // and `>` and `=`), so the parser can consume them one at a time. This + // process bypasses the normal capturing mechanism (e.g. `num_bump_calls` + // will not be incremented), since the "unglued" tokens due not exist in + // the original `TokenStream`. // - // If we end up consuming both unglued tokens, this is not an issue. We'll - // end up capturing the single 'glued' token. + // If we end up consuming all the component tokens, this is not an issue, + // because we'll end up capturing the single "glued" token. // - // However, sometimes we may want to capture just the first 'unglued' + // However, sometimes we may want to capture not all of the original // token. For example, capturing the `Vec` in `Option>` // requires us to unglue the trailing `>>` token. The `break_last_token` - // field is used to track this token. It gets appended to the captured + // field is used to track these tokens. They get appended to the captured // stream when we evaluate a `LazyAttrTokenStream`. - break_last_token: bool, + // + // This value is always 0, 1, or 2. It can only reach 2 when splitting + // `>>=` or `<<=`. + break_last_token: u32, /// This field is used to keep track of how many left angle brackets we have seen. This is /// required in order to detect extra leading left angle brackets (`<` characters) and error /// appropriately. @@ -453,7 +457,7 @@ impl<'a> Parser<'a> { expected_tokens: Vec::new(), token_cursor: TokenCursor { tree_cursor: stream.into_trees(), stack: Vec::new() }, num_bump_calls: 0, - break_last_token: false, + break_last_token: 0, unmatched_angle_bracket_count: 0, angle_bracket_nesting: 0, last_unexpected_token_span: None, @@ -773,7 +777,7 @@ impl<'a> Parser<'a> { self.bump(); return true; } - match self.token.kind.break_two_token_op() { + match self.token.kind.break_two_token_op(1) { Some((first, second)) if first == expected => { let first_span = self.psess.source_map().start_point(self.token.span); let second_span = self.token.span.with_lo(first_span.hi()); @@ -783,8 +787,8 @@ impl<'a> Parser<'a> { // // If we consume any additional tokens, then this token // is not needed (we'll capture the entire 'glued' token), - // and `bump` will set this field to `None` - self.break_last_token = true; + // and `bump` will set this field to 0. + self.break_last_token += 1; // Use the spacing of the glued token as the spacing of the // unglued second token. self.bump_with((Token::new(second, second_span), self.token_spacing)); @@ -1148,10 +1152,9 @@ impl<'a> Parser<'a> { // than `.0`/`.1` access. let mut next = self.token_cursor.inlined_next(); self.num_bump_calls += 1; - // We've retrieved an token from the underlying - // cursor, so we no longer need to worry about - // an unglued token. See `break_and_eat` for more details - self.break_last_token = false; + // We got a token from the underlying cursor and no longer need to + // worry about an unglued token. See `break_and_eat` for more details. + self.break_last_token = 0; if next.0.span.is_dummy() { // Tweak the location for better diagnostics, but keep syntactic context intact. let fallback_span = self.token.span; diff --git a/compiler/rustc_passes/src/reachable.rs b/compiler/rustc_passes/src/reachable.rs index d3e852b6b8f7..925ee2620228 100644 --- a/compiler/rustc_passes/src/reachable.rs +++ b/compiler/rustc_passes/src/reachable.rs @@ -318,10 +318,10 @@ impl<'tcx> ReachableContext<'tcx> { )); self.visit(instance.args); } - GlobalAlloc::VTable(ty, trait_ref) => { + GlobalAlloc::VTable(ty, dyn_ty) => { self.visit(ty); // Manually visit to actually see the trait's `DefId`. Type visitors won't see it - if let Some(trait_ref) = trait_ref { + if let Some(trait_ref) = dyn_ty.principal() { let ExistentialTraitRef { def_id, args } = trait_ref.skip_binder(); self.visit_def_id(def_id, "", &""); self.visit(args); diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 7ea057c7b4bc..35d166e8b4ab 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -1825,10 +1825,12 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { // Doing analysis on local `DefId`s would cause infinite recursion. return; } - let Ok(impls) = self.r.tcx.inherent_impls(def_id) else { return }; // Look at all the associated functions without receivers in the type's // inherent impls to look for builders that return `Self` - let mut items = impls + let mut items = self + .r + .tcx + .inherent_impls(def_id) .iter() .flat_map(|i| self.r.tcx.associated_items(i).in_definition_order()) // Only assoc fn with no receivers. diff --git a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs index d228bbf51c24..0dbbc338e738 100644 --- a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs +++ b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs @@ -282,11 +282,12 @@ impl<'tcx> Stable<'tcx> for mir::CastKind { type T = stable_mir::mir::CastKind; fn stable(&self, tables: &mut Tables<'_>) -> Self::T { use rustc_middle::mir::CastKind::*; + use rustc_middle::ty::adjustment::PointerCoercion; match self { PointerExposeProvenance => stable_mir::mir::CastKind::PointerExposeAddress, PointerWithExposedProvenance => stable_mir::mir::CastKind::PointerWithExposedProvenance, - PointerCoercion(c) => stable_mir::mir::CastKind::PointerCoercion(c.stable(tables)), - DynStar => stable_mir::mir::CastKind::DynStar, + PointerCoercion(PointerCoercion::DynStar, _) => stable_mir::mir::CastKind::DynStar, + PointerCoercion(c, _) => stable_mir::mir::CastKind::PointerCoercion(c.stable(tables)), IntToInt => stable_mir::mir::CastKind::IntToInt, FloatToInt => stable_mir::mir::CastKind::FloatToInt, FloatToFloat => stable_mir::mir::CastKind::FloatToFloat, @@ -712,8 +713,9 @@ impl<'tcx> Stable<'tcx> for mir::interpret::GlobalAlloc<'tcx> { mir::interpret::GlobalAlloc::Function { instance, .. } => { GlobalAlloc::Function(instance.stable(tables)) } - mir::interpret::GlobalAlloc::VTable(ty, trait_ref) => { - GlobalAlloc::VTable(ty.stable(tables), trait_ref.stable(tables)) + mir::interpret::GlobalAlloc::VTable(ty, dyn_ty) => { + // FIXME: Should we record the whole vtable? + GlobalAlloc::VTable(ty.stable(tables), dyn_ty.principal().stable(tables)) } mir::interpret::GlobalAlloc::Static(def) => { GlobalAlloc::Static(tables.static_def(*def)) diff --git a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs index e54ab1f7e24c..ac9a02357626 100644 --- a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs +++ b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs @@ -119,6 +119,7 @@ impl<'tcx> Stable<'tcx> for ty::adjustment::PointerCoercion { } PointerCoercion::ArrayToPointer => stable_mir::mir::PointerCoercion::ArrayToPointer, PointerCoercion::Unsize => stable_mir::mir::PointerCoercion::Unsize, + PointerCoercion::DynStar => unreachable!("represented as `CastKind::DynStar` in smir"), } } } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index cfe990a225fe..8f226b26befd 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -2058,6 +2058,7 @@ symbols! { unmarked_api, unnamed_fields, unpin, + unqualified_local_imports, unreachable, unreachable_2015, unreachable_2015_macro, diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 012962dab425..f327c1fd1790 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -1690,12 +1690,8 @@ supported_targets! { ("x86_64h-apple-darwin", x86_64h_apple_darwin), ("i686-apple-darwin", i686_apple_darwin), - // FIXME(#106649): Remove aarch64-fuchsia in favor of aarch64-unknown-fuchsia - ("aarch64-fuchsia", aarch64_fuchsia), ("aarch64-unknown-fuchsia", aarch64_unknown_fuchsia), ("riscv64gc-unknown-fuchsia", riscv64gc_unknown_fuchsia), - // FIXME(#106649): Remove x86_64-fuchsia in favor of x86_64-unknown-fuchsia - ("x86_64-fuchsia", x86_64_fuchsia), ("x86_64-unknown-fuchsia", x86_64_unknown_fuchsia), ("avr-unknown-gnu-atmega328", avr_unknown_gnu_atmega328), @@ -1844,6 +1840,8 @@ supported_targets! { ("powerpc-wrs-vxworks", powerpc_wrs_vxworks), ("powerpc-wrs-vxworks-spe", powerpc_wrs_vxworks_spe), ("powerpc64-wrs-vxworks", powerpc64_wrs_vxworks), + ("riscv32-wrs-vxworks", riscv32_wrs_vxworks), + ("riscv64-wrs-vxworks", riscv64_wrs_vxworks), ("aarch64-kmc-solid_asp3", aarch64_kmc_solid_asp3), ("armv7a-kmc-solid_asp3-eabi", armv7a_kmc_solid_asp3_eabi), @@ -1886,6 +1884,7 @@ supported_targets! { ("aarch64-unknown-linux-ohos", aarch64_unknown_linux_ohos), ("armv7-unknown-linux-ohos", armv7_unknown_linux_ohos), + ("loongarch64-unknown-linux-ohos", loongarch64_unknown_linux_ohos), ("x86_64-unknown-linux-ohos", x86_64_unknown_linux_ohos), ("x86_64-unknown-linux-none", x86_64_unknown_linux_none), diff --git a/compiler/rustc_target/src/spec/targets/aarch64_fuchsia.rs b/compiler/rustc_target/src/spec/targets/aarch64_fuchsia.rs deleted file mode 100644 index 144ac85622ee..000000000000 --- a/compiler/rustc_target/src/spec/targets/aarch64_fuchsia.rs +++ /dev/null @@ -1 +0,0 @@ -pub(crate) use crate::spec::targets::aarch64_unknown_fuchsia::target; diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_ohos.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_ohos.rs index 0942f5d896bb..9d5bce053500 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_ohos.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_ohos.rs @@ -5,8 +5,7 @@ pub(crate) fn target() -> Target { base.max_atomic_width = Some(128); Target { - // LLVM 15 doesn't support OpenHarmony yet, use a linux target instead. - llvm_target: "aarch64-unknown-linux-musl".into(), + llvm_target: "aarch64-unknown-linux-ohos".into(), metadata: crate::spec::TargetMetadata { description: Some("ARM64 OpenHarmony".into()), tier: Some(2), diff --git a/compiler/rustc_target/src/spec/targets/aarch64_wrs_vxworks.rs b/compiler/rustc_target/src/spec/targets/aarch64_wrs_vxworks.rs index b74c49f0a090..dd5298944e0d 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_wrs_vxworks.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_wrs_vxworks.rs @@ -7,7 +7,7 @@ pub(crate) fn target() -> Target { description: None, tier: Some(3), host_tools: Some(false), - std: None, // ? + std: Some(true), }, pointer_width: 64, data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(), diff --git a/compiler/rustc_target/src/spec/targets/armv7_unknown_linux_ohos.rs b/compiler/rustc_target/src/spec/targets/armv7_unknown_linux_ohos.rs index 04ea9e3468d2..92b09bcc45c4 100644 --- a/compiler/rustc_target/src/spec/targets/armv7_unknown_linux_ohos.rs +++ b/compiler/rustc_target/src/spec/targets/armv7_unknown_linux_ohos.rs @@ -7,8 +7,7 @@ pub(crate) fn target() -> Target { // Most of these settings are copied from the armv7_unknown_linux_musleabi // target. Target { - // LLVM 15 doesn't support OpenHarmony yet, use a linux target instead. - llvm_target: "armv7-unknown-linux-gnueabi".into(), + llvm_target: "armv7-unknown-linux-ohos".into(), metadata: crate::spec::TargetMetadata { description: Some("Armv7-A OpenHarmony".into()), tier: Some(2), diff --git a/compiler/rustc_target/src/spec/targets/i686_wrs_vxworks.rs b/compiler/rustc_target/src/spec/targets/i686_wrs_vxworks.rs index c2b9bc14ca43..dfffa6e138f8 100644 --- a/compiler/rustc_target/src/spec/targets/i686_wrs_vxworks.rs +++ b/compiler/rustc_target/src/spec/targets/i686_wrs_vxworks.rs @@ -13,7 +13,7 @@ pub(crate) fn target() -> Target { description: None, tier: Some(3), host_tools: Some(false), - std: None, // ? + std: Some(true), }, pointer_width: 32, data_layout: "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-\ diff --git a/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_ohos.rs b/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_ohos.rs new file mode 100644 index 000000000000..32a856be6690 --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_ohos.rs @@ -0,0 +1,23 @@ +use crate::spec::{Target, TargetOptions, base}; + +pub(crate) fn target() -> Target { + Target { + llvm_target: "loongarch64-unknown-linux-ohos".into(), + metadata: crate::spec::TargetMetadata { + description: Some("LoongArch64 OpenHarmony".into()), + tier: Some(3), + host_tools: Some(false), + std: Some(true), + }, + pointer_width: 64, + data_layout: "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128".into(), + arch: "loongarch64".into(), + options: TargetOptions { + cpu: "generic".into(), + features: "+f,+d".into(), + llvm_abiname: "lp64d".into(), + max_atomic_width: Some(64), + ..base::linux_ohos::opts() + }, + } +} diff --git a/compiler/rustc_target/src/spec/targets/powerpc64_wrs_vxworks.rs b/compiler/rustc_target/src/spec/targets/powerpc64_wrs_vxworks.rs index 11cf957cc185..1d3d9f1b77df 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc64_wrs_vxworks.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc64_wrs_vxworks.rs @@ -14,7 +14,7 @@ pub(crate) fn target() -> Target { description: None, tier: Some(3), host_tools: Some(false), - std: None, // ? + std: Some(true), }, pointer_width: 64, data_layout: "E-m:e-Fi64-i64:64-n32:64-S128-v256:256:256-v512:512:512".into(), diff --git a/compiler/rustc_target/src/spec/targets/powerpc_wrs_vxworks.rs b/compiler/rustc_target/src/spec/targets/powerpc_wrs_vxworks.rs index 24cad76b0004..22b38208b108 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc_wrs_vxworks.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc_wrs_vxworks.rs @@ -13,7 +13,7 @@ pub(crate) fn target() -> Target { description: None, tier: Some(3), host_tools: Some(false), - std: None, // ? + std: Some(true), }, pointer_width: 32, data_layout: "E-m:e-p:32:32-Fn32-i64:64-n32".into(), diff --git a/compiler/rustc_target/src/spec/targets/riscv32_wrs_vxworks.rs b/compiler/rustc_target/src/spec/targets/riscv32_wrs_vxworks.rs new file mode 100644 index 000000000000..4d3df78a5636 --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/riscv32_wrs_vxworks.rs @@ -0,0 +1,24 @@ +use crate::spec::{StackProbeType, Target, TargetOptions, base}; + +pub(crate) fn target() -> Target { + Target { + llvm_target: "riscv32".into(), + metadata: crate::spec::TargetMetadata { + description: None, + tier: Some(3), + host_tools: Some(false), + std: Some(true), + }, + pointer_width: 32, + data_layout: "e-m:e-p:32:32-i64:64-n32-S128".into(), + arch: "riscv32".into(), + options: TargetOptions { + cpu: "generic-rv32".into(), + llvm_abiname: "ilp32d".into(), + max_atomic_width: Some(32), + features: "+m,+a,+f,+d,+c".into(), + stack_probes: StackProbeType::Inline, + ..base::vxworks::opts() + }, + } +} diff --git a/compiler/rustc_target/src/spec/targets/riscv64_wrs_vxworks.rs b/compiler/rustc_target/src/spec/targets/riscv64_wrs_vxworks.rs new file mode 100644 index 000000000000..720549e6a017 --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/riscv64_wrs_vxworks.rs @@ -0,0 +1,24 @@ +use crate::spec::{StackProbeType, Target, TargetOptions, base}; + +pub(crate) fn target() -> Target { + Target { + llvm_target: "riscv64".into(), + metadata: crate::spec::TargetMetadata { + description: None, + tier: Some(3), + host_tools: Some(false), + std: Some(true), + }, + pointer_width: 64, + data_layout: "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128".into(), + arch: "riscv64".into(), + options: TargetOptions { + cpu: "generic-rv64".into(), + llvm_abiname: "lp64d".into(), + max_atomic_width: Some(64), + features: "+m,+a,+f,+d,+c".into(), + stack_probes: StackProbeType::Inline, + ..base::vxworks::opts() + }, + } +} diff --git a/compiler/rustc_target/src/spec/targets/s390x_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/s390x_unknown_linux_gnu.rs index a8163e228e69..3efbb4648361 100644 --- a/compiler/rustc_target/src/spec/targets/s390x_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/s390x_unknown_linux_gnu.rs @@ -6,9 +6,8 @@ pub(crate) fn target() -> Target { base.endian = Endian::Big; // z10 is the oldest CPU supported by LLVM base.cpu = "z10".into(); - // FIXME: The ABI implementation in cabi_s390x.rs is for now hard-coded to assume the no-vector - // ABI. Pass the -vector feature string to LLVM to respect this assumption. On LLVM < 16, we - // also strip v128 from the data_layout below to match the older LLVM's expectation. + // FIXME: The ABI implementation in abi/call/s390x.rs is for now hard-coded to assume the no-vector + // ABI. Pass the -vector feature string to LLVM to respect this assumption. base.features = "-vector".into(); base.max_atomic_width = Some(128); base.min_global_align = Some(16); diff --git a/compiler/rustc_target/src/spec/targets/s390x_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/s390x_unknown_linux_musl.rs index e52bcc987f9b..65b5c1167bdd 100644 --- a/compiler/rustc_target/src/spec/targets/s390x_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/s390x_unknown_linux_musl.rs @@ -6,9 +6,8 @@ pub(crate) fn target() -> Target { base.endian = Endian::Big; // z10 is the oldest CPU supported by LLVM base.cpu = "z10".into(); - // FIXME: The ABI implementation in cabi_s390x.rs is for now hard-coded to assume the no-vector - // ABI. Pass the -vector feature string to LLVM to respect this assumption. On LLVM < 16, we - // also strip v128 from the data_layout below to match the older LLVM's expectation. + // FIXME: The ABI implementation in abi/call/s390x.rs is for now hard-coded to assume the no-vector + // ABI. Pass the -vector feature string to LLVM to respect this assumption. base.features = "-vector".into(); base.max_atomic_width = Some(128); base.min_global_align = Some(16); diff --git a/compiler/rustc_target/src/spec/targets/x86_64_fuchsia.rs b/compiler/rustc_target/src/spec/targets/x86_64_fuchsia.rs deleted file mode 100644 index ce3e1e159b7d..000000000000 --- a/compiler/rustc_target/src/spec/targets/x86_64_fuchsia.rs +++ /dev/null @@ -1 +0,0 @@ -pub(crate) use crate::spec::targets::x86_64_unknown_fuchsia::target; diff --git a/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_ohos.rs b/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_ohos.rs index e455aad5f51a..522943c91a5e 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_ohos.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_ohos.rs @@ -15,8 +15,7 @@ pub(crate) fn target() -> Target { base.supports_xray = true; Target { - // LLVM 15 doesn't support OpenHarmony yet, use a linux target instead. - llvm_target: "x86_64-unknown-linux-musl".into(), + llvm_target: "x86_64-unknown-linux-ohos".into(), metadata: crate::spec::TargetMetadata { description: Some("x86_64 OpenHarmony".into()), tier: Some(2), diff --git a/compiler/rustc_target/src/spec/targets/x86_64_wrs_vxworks.rs b/compiler/rustc_target/src/spec/targets/x86_64_wrs_vxworks.rs index 8fbdd8f57f65..f003f939ad11 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_wrs_vxworks.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_wrs_vxworks.rs @@ -15,7 +15,7 @@ pub(crate) fn target() -> Target { description: None, tier: Some(3), host_tools: Some(false), - std: None, // ? + std: Some(true), }, pointer_width: 64, data_layout: 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 19e2679ae4da..969f2528836d 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 @@ -2635,49 +2635,47 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // This shouldn't be common unless manually implementing one of the // traits manually, but don't make it more confusing when it does // happen. - Ok( - if Some(expected_trait_ref.def_id) != self.tcx.lang_items().coroutine_trait() - && not_tupled - { - self.report_and_explain_type_error( - TypeTrace::trait_refs( - &obligation.cause, - true, - expected_trait_ref, - found_trait_ref, - ), - ty::error::TypeError::Mismatch, - ) - } else if found.len() == expected.len() { - self.report_closure_arg_mismatch( - span, - found_span, - found_trait_ref, - expected_trait_ref, - obligation.cause.code(), - found_node, - obligation.param_env, - ) - } else { - let (closure_span, closure_arg_span, found) = found_did - .and_then(|did| { - let node = self.tcx.hir().get_if_local(did)?; - let (found_span, closure_arg_span, found) = - self.get_fn_like_arguments(node)?; - Some((Some(found_span), closure_arg_span, found)) - }) - .unwrap_or((found_span, None, found)); - - self.report_arg_count_mismatch( + if Some(expected_trait_ref.def_id) != self.tcx.lang_items().coroutine_trait() && not_tupled + { + return Ok(self.report_and_explain_type_error( + TypeTrace::trait_refs(&obligation.cause, true, expected_trait_ref, found_trait_ref), + ty::error::TypeError::Mismatch, + )); + } + if found.len() != expected.len() { + let (closure_span, closure_arg_span, found) = found_did + .and_then(|did| { + let node = self.tcx.hir().get_if_local(did)?; + let (found_span, closure_arg_span, found) = self.get_fn_like_arguments(node)?; + Some((Some(found_span), closure_arg_span, found)) + }) + .unwrap_or((found_span, None, found)); + + // If the coroutine take a single () as its argument, + // the trait argument would found the coroutine take 0 arguments, + // but get_fn_like_arguments would give 1 argument. + // This would result in "Expected to take 1 argument, but it takes 1 argument". + // Check again to avoid this. + if found.len() != expected.len() { + return Ok(self.report_arg_count_mismatch( span, closure_span, expected, found, found_trait_ty.is_closure(), closure_arg_span, - ) - }, - ) + )); + } + } + Ok(self.report_closure_arg_mismatch( + span, + found_span, + found_trait_ref, + expected_trait_ref, + obligation.cause.code(), + found_node, + obligation.param_env, + )) } /// Given some node representing a fn-like thing in the HIR map, diff --git a/compiler/stable_mir/src/mir/body.rs b/compiler/stable_mir/src/mir/body.rs index 7c09fe1a0854..ab9fc218d19f 100644 --- a/compiler/stable_mir/src/mir/body.rs +++ b/compiler/stable_mir/src/mir/body.rs @@ -960,6 +960,7 @@ pub enum CastKind { PointerExposeAddress, PointerWithExposedProvenance, PointerCoercion(PointerCoercion), + // FIXME(smir-rename): change this to PointerCoercion(DynStar) DynStar, IntToInt, FloatToInt, diff --git a/library/Cargo.lock b/library/Cargo.lock index ded30dd82f7b..2343b2baf834 100644 --- a/library/Cargo.lock +++ b/library/Cargo.lock @@ -326,6 +326,7 @@ dependencies = [ "hashbrown", "hermit-abi", "libc", + "memchr", "miniz_oxide", "object", "panic_abort", diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index f61a484499f9..6421504b8964 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -199,7 +199,7 @@ use core::ops::{ DerefPure, DispatchFromDyn, Receiver, }; use core::pin::{Pin, PinCoerceUnsized}; -use core::ptr::{self, NonNull, Unique, addr_of_mut}; +use core::ptr::{self, NonNull, Unique}; use core::task::{Context, Poll}; use core::{borrow, fmt, slice}; @@ -1277,7 +1277,7 @@ impl Box { #[inline] pub fn into_raw(b: Self) -> *mut T { // Make sure Miri realizes that we transition from a noalias pointer to a raw pointer here. - unsafe { addr_of_mut!(*&mut *Self::into_raw_with_allocator(b).0) } + unsafe { &raw mut *&mut *Self::into_raw_with_allocator(b).0 } } /// Consumes the `Box`, returning a wrapped `NonNull` pointer. @@ -1396,7 +1396,7 @@ impl Box { // want *no* aliasing requirements here! // In case `A` *is* `Global`, this does not quite have the right behavior; `into_raw` // works around that. - let ptr = addr_of_mut!(**b); + let ptr = &raw mut **b; let alloc = unsafe { ptr::read(&b.1) }; (ptr, alloc) } @@ -1506,7 +1506,7 @@ impl Box { pub fn as_mut_ptr(b: &mut Self) -> *mut T { // This is a primitive deref, not going through `DerefMut`, and therefore not materializing // any references. - ptr::addr_of_mut!(**b) + &raw mut **b } /// Returns a raw pointer to the `Box`'s contents. @@ -1554,7 +1554,7 @@ impl Box { pub fn as_ptr(b: &Self) -> *const T { // This is a primitive deref, not going through `DerefMut`, and therefore not materializing // any references. - ptr::addr_of!(**b) + &raw const **b } /// Returns a reference to the underlying allocator. diff --git a/library/alloc/src/boxed/thin.rs b/library/alloc/src/boxed/thin.rs index 9baded3a5214..78e5aec09b18 100644 --- a/library/alloc/src/boxed/thin.rs +++ b/library/alloc/src/boxed/thin.rs @@ -186,7 +186,7 @@ impl ThinBox { fn with_header(&self) -> &WithHeader<::Metadata> { // SAFETY: both types are transparent to `NonNull` - unsafe { &*(core::ptr::addr_of!(self.ptr) as *const WithHeader<_>) } + unsafe { &*((&raw const self.ptr) as *const WithHeader<_>) } } } diff --git a/library/alloc/src/collections/btree/node.rs b/library/alloc/src/collections/btree/node.rs index 78ccb3af66db..5c513d34fc9d 100644 --- a/library/alloc/src/collections/btree/node.rs +++ b/library/alloc/src/collections/btree/node.rs @@ -72,8 +72,8 @@ impl LeafNode { // be both slightly faster and easier to track in Valgrind. unsafe { // parent_idx, keys, and vals are all MaybeUninit - ptr::addr_of_mut!((*this).parent).write(None); - ptr::addr_of_mut!((*this).len).write(0); + (&raw mut (*this).parent).write(None); + (&raw mut (*this).len).write(0); } } @@ -114,7 +114,7 @@ impl InternalNode { unsafe { let mut node = Box::::new_uninit_in(alloc); // We only need to initialize the data; the edges are MaybeUninit. - LeafNode::init(ptr::addr_of_mut!((*node.as_mut_ptr()).data)); + LeafNode::init(&raw mut (*node.as_mut_ptr()).data); node.assume_init() } } @@ -525,8 +525,8 @@ impl<'a, K, V, Type> NodeRef, K, V, Type> { // to avoid aliasing with outstanding references to other elements, // in particular, those returned to the caller in earlier iterations. let leaf = Self::as_leaf_ptr(&mut self); - let keys = unsafe { ptr::addr_of!((*leaf).keys) }; - let vals = unsafe { ptr::addr_of_mut!((*leaf).vals) }; + let keys = unsafe { &raw const (*leaf).keys }; + let vals = unsafe { &raw mut (*leaf).vals }; // We must coerce to unsized array pointers because of Rust issue #74679. let keys: *const [_] = keys; let vals: *mut [_] = vals; diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index 54669bd310a6..0e2c35845e8f 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -787,7 +787,7 @@ impl Rc { let strong = unsafe { let inner = init_ptr.as_ptr(); - ptr::write(ptr::addr_of_mut!((*inner).value), data); + ptr::write(&raw mut (*inner).value, data); let prev_value = (*inner).strong.get(); debug_assert_eq!(prev_value, 0, "No prior strong references should exist"); @@ -1442,7 +1442,7 @@ impl Rc { // SAFETY: This cannot go through Deref::deref or Rc::inner because // this is required to retain raw/mut provenance such that e.g. `get_mut` can // write through the pointer after the Rc is recovered through `from_raw`. - unsafe { ptr::addr_of_mut!((*ptr).value) } + unsafe { &raw mut (*ptr).value } } /// Constructs an `Rc` from a raw pointer in the provided allocator. @@ -2042,8 +2042,8 @@ impl Rc { unsafe { debug_assert_eq!(Layout::for_value_raw(inner), layout); - ptr::addr_of_mut!((*inner).strong).write(Cell::new(1)); - ptr::addr_of_mut!((*inner).weak).write(Cell::new(1)); + (&raw mut (*inner).strong).write(Cell::new(1)); + (&raw mut (*inner).weak).write(Cell::new(1)); } Ok(inner) @@ -2072,8 +2072,8 @@ impl Rc { // Copy value as bytes ptr::copy_nonoverlapping( - core::ptr::addr_of!(*src) as *const u8, - ptr::addr_of_mut!((*ptr).value) as *mut u8, + (&raw const *src) as *const u8, + (&raw mut (*ptr).value) as *mut u8, value_size, ); @@ -2107,11 +2107,7 @@ impl Rc<[T]> { unsafe fn copy_from_slice(v: &[T]) -> Rc<[T]> { unsafe { let ptr = Self::allocate_for_slice(v.len()); - ptr::copy_nonoverlapping( - v.as_ptr(), - ptr::addr_of_mut!((*ptr).value) as *mut T, - v.len(), - ); + ptr::copy_nonoverlapping(v.as_ptr(), (&raw mut (*ptr).value) as *mut T, v.len()); Self::from_ptr(ptr) } } @@ -2149,7 +2145,7 @@ impl Rc<[T]> { let layout = Layout::for_value_raw(ptr); // Pointer to first element - let elems = ptr::addr_of_mut!((*ptr).value) as *mut T; + let elems = (&raw mut (*ptr).value) as *mut T; let mut guard = Guard { mem: NonNull::new_unchecked(mem), elems, layout, n_elems: 0 }; @@ -2577,7 +2573,7 @@ impl fmt::Debug for Rc { #[stable(feature = "rust1", since = "1.0.0")] impl fmt::Pointer for Rc { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Pointer::fmt(&core::ptr::addr_of!(**self), f) + fmt::Pointer::fmt(&(&raw const **self), f) } } @@ -2718,7 +2714,7 @@ impl From> for Rc<[T], A> { let (vec_ptr, len, cap, alloc) = v.into_raw_parts_with_alloc(); let rc_ptr = Self::allocate_for_slice_in(len, &alloc); - ptr::copy_nonoverlapping(vec_ptr, ptr::addr_of_mut!((*rc_ptr).value) as *mut T, len); + ptr::copy_nonoverlapping(vec_ptr, (&raw mut (*rc_ptr).value) as *mut T, len); // Create a `Vec` with length 0, to deallocate the buffer // without dropping its contents or the allocator @@ -3084,7 +3080,7 @@ impl Weak { // SAFETY: if is_dangling returns false, then the pointer is dereferenceable. // The payload may be dropped at this point, and we have to maintain provenance, // so use raw pointer manipulation. - unsafe { ptr::addr_of_mut!((*ptr).value) } + unsafe { &raw mut (*ptr).value } } } diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index 50886244d582..ced789c4f924 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -797,7 +797,7 @@ impl Arc { // reference into a strong reference. let strong = unsafe { let inner = init_ptr.as_ptr(); - ptr::write(ptr::addr_of_mut!((*inner).data), data); + ptr::write(&raw mut (*inner).data, data); // The above write to the data field must be visible to any threads which // observe a non-zero strong count. Therefore we need at least "Release" ordering @@ -1583,7 +1583,7 @@ impl Arc { // SAFETY: This cannot go through Deref::deref or RcBoxPtr::inner because // this is required to retain raw/mut provenance such that e.g. `get_mut` can // write through the pointer after the Rc is recovered through `from_raw`. - unsafe { ptr::addr_of_mut!((*ptr).data) } + unsafe { &raw mut (*ptr).data } } /// Constructs an `Arc` from a raw pointer. @@ -1955,8 +1955,8 @@ impl Arc { debug_assert_eq!(unsafe { Layout::for_value_raw(inner) }, layout); unsafe { - ptr::addr_of_mut!((*inner).strong).write(atomic::AtomicUsize::new(1)); - ptr::addr_of_mut!((*inner).weak).write(atomic::AtomicUsize::new(1)); + (&raw mut (*inner).strong).write(atomic::AtomicUsize::new(1)); + (&raw mut (*inner).weak).write(atomic::AtomicUsize::new(1)); } inner @@ -1986,8 +1986,8 @@ impl Arc { // Copy value as bytes ptr::copy_nonoverlapping( - core::ptr::addr_of!(*src) as *const u8, - ptr::addr_of_mut!((*ptr).data) as *mut u8, + (&raw const *src) as *const u8, + (&raw mut (*ptr).data) as *mut u8, value_size, ); @@ -2022,7 +2022,7 @@ impl Arc<[T]> { unsafe { let ptr = Self::allocate_for_slice(v.len()); - ptr::copy_nonoverlapping(v.as_ptr(), ptr::addr_of_mut!((*ptr).data) as *mut T, v.len()); + ptr::copy_nonoverlapping(v.as_ptr(), (&raw mut (*ptr).data) as *mut T, v.len()); Self::from_ptr(ptr) } @@ -2061,7 +2061,7 @@ impl Arc<[T]> { let layout = Layout::for_value_raw(ptr); // Pointer to first element - let elems = ptr::addr_of_mut!((*ptr).data) as *mut T; + let elems = (&raw mut (*ptr).data) as *mut T; let mut guard = Guard { mem: NonNull::new_unchecked(mem), elems, layout, n_elems: 0 }; @@ -2805,7 +2805,7 @@ impl Weak { // SAFETY: if is_dangling returns false, then the pointer is dereferenceable. // The payload may be dropped at this point, and we have to maintain provenance, // so use raw pointer manipulation. - unsafe { ptr::addr_of_mut!((*ptr).data) } + unsafe { &raw mut (*ptr).data } } } @@ -3428,7 +3428,7 @@ impl fmt::Debug for Arc { #[stable(feature = "rust1", since = "1.0.0")] impl fmt::Pointer for Arc { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Pointer::fmt(&core::ptr::addr_of!(**self), f) + fmt::Pointer::fmt(&(&raw const **self), f) } } @@ -3678,7 +3678,7 @@ impl From> for Arc<[T], A> { let (vec_ptr, len, cap, alloc) = v.into_raw_parts_with_alloc(); let rc_ptr = Self::allocate_for_slice_in(len, &alloc); - ptr::copy_nonoverlapping(vec_ptr, ptr::addr_of_mut!((*rc_ptr).data) as *mut T, len); + ptr::copy_nonoverlapping(vec_ptr, (&raw mut (*rc_ptr).data) as *mut T, len); // Create a `Vec` with length 0, to deallocate the buffer // without dropping its contents or the allocator diff --git a/library/alloc/src/vec/into_iter.rs b/library/alloc/src/vec/into_iter.rs index f7aad56695d5..9a6745fdbc0a 100644 --- a/library/alloc/src/vec/into_iter.rs +++ b/library/alloc/src/vec/into_iter.rs @@ -21,11 +21,11 @@ use crate::raw_vec::RawVec; macro non_null { (mut $place:expr, $t:ident) => {{ #![allow(unused_unsafe)] // we're sometimes used within an unsafe block - unsafe { &mut *(ptr::addr_of_mut!($place) as *mut NonNull<$t>) } + unsafe { &mut *((&raw mut $place) as *mut NonNull<$t>) } }}, ($place:expr, $t:ident) => {{ #![allow(unused_unsafe)] // we're sometimes used within an unsafe block - unsafe { *(ptr::addr_of!($place) as *const NonNull<$t>) } + unsafe { *((&raw const $place) as *const NonNull<$t>) } }}, } diff --git a/library/core/src/char/methods.rs b/library/core/src/char/methods.rs index 3dcaab6a12be..55e67e5c255e 100644 --- a/library/core/src/char/methods.rs +++ b/library/core/src/char/methods.rs @@ -606,6 +606,7 @@ impl char { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_char_len_utf", since = "1.52.0")] #[inline] + #[must_use] pub const fn len_utf8(self) -> usize { len_utf8(self as u32) } @@ -637,6 +638,7 @@ impl char { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_char_len_utf", since = "1.52.0")] #[inline] + #[must_use] pub const fn len_utf16(self) -> usize { len_utf16(self as u32) } @@ -1738,6 +1740,7 @@ impl EscapeDebugExtArgs { } #[inline] +#[must_use] const fn len_utf8(code: u32) -> usize { match code { ..MAX_ONE_B => 1, @@ -1748,6 +1751,7 @@ const fn len_utf8(code: u32) -> usize { } #[inline] +#[must_use] const fn len_utf16(code: u32) -> usize { if (code & 0xFFFF) == code { 1 } else { 2 } } diff --git a/library/core/src/ffi/c_str.rs b/library/core/src/ffi/c_str.rs index 930f8a766207..15b00b9aa442 100644 --- a/library/core/src/ffi/c_str.rs +++ b/library/core/src/ffi/c_str.rs @@ -5,7 +5,7 @@ use crate::error::Error; use crate::ffi::c_char; use crate::iter::FusedIterator; use crate::marker::PhantomData; -use crate::ptr::{NonNull, addr_of}; +use crate::ptr::NonNull; use crate::slice::memchr; use crate::{fmt, intrinsics, ops, slice, str}; @@ -623,7 +623,7 @@ impl CStr { pub const fn to_bytes_with_nul(&self) -> &[u8] { // SAFETY: Transmuting a slice of `c_char`s to a slice of `u8`s // is safe on all supported targets. - unsafe { &*(addr_of!(self.inner) as *const [u8]) } + unsafe { &*((&raw const self.inner) as *const [u8]) } } /// Iterates over the bytes in this C string. diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index 3b2a6e820c64..881a89f4d109 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -3286,13 +3286,13 @@ pub const fn ptr_metadata + ?Sized, M>(_ptr: *cons #[doc(alias = "memcpy")] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_allowed_through_unstable_modules] -#[rustc_const_unstable(feature = "const_intrinsic_copy", issue = "80697")] +#[rustc_const_stable(feature = "const_intrinsic_copy", since = "CURRENT_RUSTC_VERSION")] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces #[rustc_diagnostic_item = "ptr_copy_nonoverlapping"] pub const unsafe fn copy_nonoverlapping(src: *const T, dst: *mut T, count: usize) { extern "rust-intrinsic" { - #[rustc_const_unstable(feature = "const_intrinsic_copy", issue = "80697")] + #[rustc_const_stable(feature = "const_intrinsic_copy", since = "CURRENT_RUSTC_VERSION")] #[rustc_nounwind] pub fn copy_nonoverlapping(src: *const T, dst: *mut T, count: usize); } @@ -3388,13 +3388,13 @@ pub const unsafe fn copy_nonoverlapping(src: *const T, dst: *mut T, count: us #[doc(alias = "memmove")] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_allowed_through_unstable_modules] -#[rustc_const_unstable(feature = "const_intrinsic_copy", issue = "80697")] +#[rustc_const_stable(feature = "const_intrinsic_copy", since = "CURRENT_RUSTC_VERSION")] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces #[rustc_diagnostic_item = "ptr_copy"] pub const unsafe fn copy(src: *const T, dst: *mut T, count: usize) { extern "rust-intrinsic" { - #[rustc_const_unstable(feature = "const_intrinsic_copy", issue = "80697")] + #[rustc_const_stable(feature = "const_intrinsic_copy", since = "CURRENT_RUSTC_VERSION")] #[rustc_nounwind] fn copy(src: *const T, dst: *mut T, count: usize); } diff --git a/library/core/src/iter/adapters/filter_map.rs b/library/core/src/iter/adapters/filter_map.rs index 914ef6131771..cc64ceb13f76 100644 --- a/library/core/src/iter/adapters/filter_map.rs +++ b/library/core/src/iter/adapters/filter_map.rs @@ -3,7 +3,6 @@ use crate::iter::{FusedIterator, InPlaceIterable, TrustedFused}; use crate::mem::{ManuallyDrop, MaybeUninit}; use crate::num::NonZero; use crate::ops::{ControlFlow, Try}; -use crate::ptr::addr_of; use crate::{array, fmt}; /// An iterator that uses `f` to both filter and map elements from `iter`. @@ -101,7 +100,7 @@ where unsafe { let opt_payload_at: *const MaybeUninit = - addr_of!(val).byte_add(core::mem::offset_of!(Option, Some.0)).cast(); + (&raw const val).byte_add(core::mem::offset_of!(Option, Some.0)).cast(); let dst = guard.array.as_mut_ptr().add(idx); crate::ptr::copy_nonoverlapping(opt_payload_at, dst, 1); crate::mem::forget(val); diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index a30b57c19d40..01cadd78cc09 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -128,11 +128,11 @@ #![feature(const_hash)] #![feature(const_heap)] #![feature(const_index_range_slice_index)] -#![feature(const_intrinsic_copy)] #![feature(const_intrinsic_forget)] #![feature(const_ipv4)] #![feature(const_ipv6)] #![feature(const_likely)] +#![feature(const_make_ascii)] #![feature(const_maybe_uninit_assume_init)] #![feature(const_nonnull_new)] #![feature(const_num_midpoint)] @@ -151,6 +151,7 @@ #![feature(const_slice_from_raw_parts_mut)] #![feature(const_slice_from_ref)] #![feature(const_slice_split_at_mut)] +#![feature(const_str_as_mut)] #![feature(const_str_from_utf8_unchecked_mut)] #![feature(const_strict_overflow_ops)] #![feature(const_swap)] @@ -394,6 +395,8 @@ pub mod panicking; #[unstable(feature = "core_pattern_types", issue = "123646")] pub mod pat; pub mod pin; +#[unstable(feature = "random", issue = "130703")] +pub mod random; #[unstable(feature = "new_range_api", issue = "125687")] pub mod range; pub mod result; diff --git a/library/core/src/mem/manually_drop.rs b/library/core/src/mem/manually_drop.rs index 3e47785ee488..7d519384e373 100644 --- a/library/core/src/mem/manually_drop.rs +++ b/library/core/src/mem/manually_drop.rs @@ -1,22 +1,21 @@ use crate::ops::{Deref, DerefMut, DerefPure}; use crate::ptr; -/// A wrapper to inhibit the compiler from automatically calling `T`’s destructor. -/// This wrapper is 0-cost. +/// A wrapper to inhibit the compiler from automatically calling `T`’s +/// destructor. This wrapper is 0-cost. /// /// `ManuallyDrop` is guaranteed to have the same layout and bit validity as -/// `T`, and is subject to the same layout optimizations as `T`. As a consequence, -/// it has *no effect* on the assumptions that the compiler makes about its -/// contents. For example, initializing a `ManuallyDrop<&mut T>` with [`mem::zeroed`] -/// is undefined behavior. If you need to handle uninitialized data, use -/// [`MaybeUninit`] instead. +/// `T`, and is subject to the same layout optimizations as `T`. As a +/// consequence, it has *no effect* on the assumptions that the compiler makes +/// about its contents. For example, initializing a `ManuallyDrop<&mut T>` with +/// [`mem::zeroed`] is undefined behavior. If you need to handle uninitialized +/// data, use [`MaybeUninit`] instead. /// -/// Note that accessing the value inside a `ManuallyDrop` is safe. -/// This means that a `ManuallyDrop` whose content has been dropped must not -/// be exposed through a public safe API. -/// Correspondingly, `ManuallyDrop::drop` is unsafe. +/// Note that accessing the value inside a `ManuallyDrop` is safe. This means +/// that a `ManuallyDrop` whose content has been dropped must not be exposed +/// through a public safe API. Correspondingly, `ManuallyDrop::drop` is unsafe. /// -/// # `ManuallyDrop` and drop order. +/// # `ManuallyDrop` and drop order /// /// Rust has a well-defined [drop order] of values. To make sure that fields or /// locals are dropped in a specific order, reorder the declarations such that @@ -40,9 +39,116 @@ use crate::ptr; /// } /// ``` /// +/// # Interaction with `Box` +/// +/// Currently, if you have a `ManuallyDrop`, where the type `T` is a `Box` or +/// contains a `Box` inside, then dropping the `T` followed by moving the +/// `ManuallyDrop` is [considered to be undefined +/// behavior](https://github.com/rust-lang/unsafe-code-guidelines/issues/245). +/// That is, the following code causes undefined behavior: +/// +/// ```no_run +/// use std::mem::ManuallyDrop; +/// +/// let mut x = ManuallyDrop::new(Box::new(42)); +/// unsafe { +/// ManuallyDrop::drop(&mut x); +/// } +/// let y = x; // Undefined behavior! +/// ``` +/// +/// This is [likely to change in the +/// future](https://rust-lang.github.io/rfcs/3336-maybe-dangling.html). In the +/// meantime, consider using [`MaybeUninit`] instead. +/// +/// # Safety hazards when storing `ManuallyDrop` in a struct or an enum. +/// +/// Special care is needed when all of the conditions below are met: +/// * A struct or enum contains a `ManuallyDrop`. +/// * The `ManuallyDrop` is not inside a `union`. +/// * The struct or enum is part of public API, or is stored in a struct or an +/// enum that is part of public API. +/// * There is code that drops the contents of the `ManuallyDrop` field, and +/// this code is outside the struct or enum's `Drop` implementation. +/// +/// In particular, the following hazards may occur: +/// +/// #### Storing generic types +/// +/// If the `ManuallyDrop` contains a client-supplied generic type, the client +/// might provide a `Box` as that type. This would cause undefined behavior when +/// the struct or enum is later moved, as mentioned in the previous section. For +/// example, the following code causes undefined behavior: +/// +/// ```no_run +/// use std::mem::ManuallyDrop; +/// +/// pub struct BadOption { +/// // Invariant: Has been dropped iff `is_some` is false. +/// value: ManuallyDrop, +/// is_some: bool, +/// } +/// impl BadOption { +/// pub fn new(value: T) -> Self { +/// Self { value: ManuallyDrop::new(value), is_some: true } +/// } +/// pub fn change_to_none(&mut self) { +/// if self.is_some { +/// self.is_some = false; +/// unsafe { +/// // SAFETY: `value` hasn't been dropped yet, as per the invariant +/// // (This is actually unsound!) +/// ManuallyDrop::drop(&mut self.value); +/// } +/// } +/// } +/// } +/// +/// // In another crate: +/// +/// let mut option = BadOption::new(Box::new(42)); +/// option.change_to_none(); +/// let option2 = option; // Undefined behavior! +/// ``` +/// +/// #### Deriving traits +/// +/// Deriving `Debug`, `Clone`, `PartialEq`, `PartialOrd`, `Ord`, or `Hash` on +/// the struct or enum could be unsound, since the derived implementations of +/// these traits would access the `ManuallyDrop` field. For example, the +/// following code causes undefined behavior: +/// +/// ```no_run +/// use std::mem::ManuallyDrop; +/// +/// // This derive is unsound in combination with the `ManuallyDrop::drop` call. +/// #[derive(Debug)] +/// pub struct Foo { +/// value: ManuallyDrop, +/// } +/// impl Foo { +/// pub fn new() -> Self { +/// let mut temp = Self { +/// value: ManuallyDrop::new(String::from("Unsafe rust is hard.")) +/// }; +/// unsafe { +/// // SAFETY: `value` hasn't been dropped yet. +/// ManuallyDrop::drop(&mut temp.value); +/// } +/// temp +/// } +/// } +/// +/// // In another crate: +/// +/// let foo = Foo::new(); +/// println!("{:?}", foo); // Undefined behavior! +/// ``` +/// /// [drop order]: https://doc.rust-lang.org/reference/destructors.html /// [`mem::zeroed`]: crate::mem::zeroed /// [`MaybeUninit`]: crate::mem::MaybeUninit +/// [`MaybeUninit`]: crate::mem::MaybeUninit #[stable(feature = "manually_drop", since = "1.20.0")] #[lang = "manually_drop"] #[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] diff --git a/library/core/src/ptr/alignment.rs b/library/core/src/ptr/alignment.rs index ceb5906d226e..50706fca5b0f 100644 --- a/library/core/src/ptr/alignment.rs +++ b/library/core/src/ptr/alignment.rs @@ -155,7 +155,7 @@ impl Alignment { !(unsafe { self.as_usize().unchecked_sub(1) }) } - // Remove me once `Ord::max` is usable in const + // FIXME(const-hack) Remove me once `Ord::max` is usable in const pub(crate) const fn max(a: Self, b: Self) -> Self { if a.as_usize() > b.as_usize() { a } else { b } } diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs index 3b45d46b31d5..1146ca6ef430 100644 --- a/library/core/src/ptr/const_ptr.rs +++ b/library/core/src/ptr/const_ptr.rs @@ -1192,7 +1192,7 @@ impl *const T { /// See [`ptr::copy`] for safety concerns and examples. /// /// [`ptr::copy`]: crate::ptr::copy() - #[rustc_const_unstable(feature = "const_intrinsic_copy", issue = "80697")] + #[rustc_const_stable(feature = "const_intrinsic_copy", since = "CURRENT_RUSTC_VERSION")] #[stable(feature = "pointer_methods", since = "1.26.0")] #[inline] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces @@ -1212,7 +1212,7 @@ impl *const T { /// See [`ptr::copy_nonoverlapping`] for safety concerns and examples. /// /// [`ptr::copy_nonoverlapping`]: crate::ptr::copy_nonoverlapping() - #[rustc_const_unstable(feature = "const_intrinsic_copy", issue = "80697")] + #[rustc_const_stable(feature = "const_intrinsic_copy", since = "CURRENT_RUSTC_VERSION")] #[stable(feature = "pointer_methods", since = "1.26.0")] #[inline] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index d4b505c6ae37..4f97048eab10 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -1516,11 +1516,7 @@ pub const unsafe fn read(src: *const T) -> T { #[inline] #[stable(feature = "ptr_unaligned", since = "1.17.0")] #[rustc_const_stable(feature = "const_ptr_read", since = "1.71.0")] -#[rustc_allow_const_fn_unstable( - const_mut_refs, - const_maybe_uninit_as_mut_ptr, - const_intrinsic_copy -)] +#[cfg_attr(bootstrap, rustc_allow_const_fn_unstable(const_mut_refs))] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces #[rustc_diagnostic_item = "ptr_read_unaligned"] pub const unsafe fn read_unaligned(src: *const T) -> T { @@ -1734,7 +1730,7 @@ pub const unsafe fn write_unaligned(dst: *mut T, src: T) { // `dst` cannot overlap `src` because the caller has mutable access // to `dst` while `src` is owned by this function. unsafe { - copy_nonoverlapping(addr_of!(src) as *const u8, dst as *mut u8, mem::size_of::()); + copy_nonoverlapping((&raw const src) as *const u8, dst as *mut u8, mem::size_of::()); // We are calling the intrinsic directly to avoid function calls in the generated code. intrinsics::forget(src); } @@ -2352,7 +2348,6 @@ impl fmt::Debug for F { /// no difference whether the pointer is null or dangling.) #[stable(feature = "raw_ref_macros", since = "1.51.0")] #[rustc_macro_transparency = "semitransparent"] -#[allow_internal_unstable(raw_ref_op)] pub macro addr_of($place:expr) { &raw const $place } @@ -2443,7 +2438,6 @@ pub macro addr_of($place:expr) { /// makes no difference whether the pointer is null or dangling.) #[stable(feature = "raw_ref_macros", since = "1.51.0")] #[rustc_macro_transparency = "semitransparent"] -#[allow_internal_unstable(raw_ref_op)] pub macro addr_of_mut($place:expr) { &raw mut $place } diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs index 5fa3b9bf61f7..8e33cf081ba6 100644 --- a/library/core/src/ptr/mut_ptr.rs +++ b/library/core/src/ptr/mut_ptr.rs @@ -1269,7 +1269,7 @@ impl *mut T { /// See [`ptr::copy`] for safety concerns and examples. /// /// [`ptr::copy`]: crate::ptr::copy() - #[rustc_const_unstable(feature = "const_intrinsic_copy", issue = "80697")] + #[rustc_const_stable(feature = "const_intrinsic_copy", since = "CURRENT_RUSTC_VERSION")] #[stable(feature = "pointer_methods", since = "1.26.0")] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces @@ -1289,7 +1289,7 @@ impl *mut T { /// See [`ptr::copy_nonoverlapping`] for safety concerns and examples. /// /// [`ptr::copy_nonoverlapping`]: crate::ptr::copy_nonoverlapping() - #[rustc_const_unstable(feature = "const_intrinsic_copy", issue = "80697")] + #[rustc_const_stable(feature = "const_intrinsic_copy", since = "CURRENT_RUSTC_VERSION")] #[stable(feature = "pointer_methods", since = "1.26.0")] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces @@ -1309,7 +1309,7 @@ impl *mut T { /// See [`ptr::copy`] for safety concerns and examples. /// /// [`ptr::copy`]: crate::ptr::copy() - #[rustc_const_unstable(feature = "const_intrinsic_copy", issue = "80697")] + #[rustc_const_stable(feature = "const_intrinsic_copy", since = "CURRENT_RUSTC_VERSION")] #[stable(feature = "pointer_methods", since = "1.26.0")] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces @@ -1329,7 +1329,7 @@ impl *mut T { /// See [`ptr::copy_nonoverlapping`] for safety concerns and examples. /// /// [`ptr::copy_nonoverlapping`]: crate::ptr::copy_nonoverlapping() - #[rustc_const_unstable(feature = "const_intrinsic_copy", issue = "80697")] + #[rustc_const_stable(feature = "const_intrinsic_copy", since = "CURRENT_RUSTC_VERSION")] #[stable(feature = "pointer_methods", since = "1.26.0")] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces diff --git a/library/core/src/ptr/non_null.rs b/library/core/src/ptr/non_null.rs index 673acc2972fe..daa40b3c9d2e 100644 --- a/library/core/src/ptr/non_null.rs +++ b/library/core/src/ptr/non_null.rs @@ -924,7 +924,7 @@ impl NonNull { #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces #[stable(feature = "non_null_convenience", since = "1.80.0")] - #[rustc_const_unstable(feature = "const_intrinsic_copy", issue = "80697")] + #[rustc_const_stable(feature = "const_intrinsic_copy", since = "CURRENT_RUSTC_VERSION")] pub const unsafe fn copy_to(self, dest: NonNull, count: usize) where T: Sized, @@ -944,7 +944,7 @@ impl NonNull { #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces #[stable(feature = "non_null_convenience", since = "1.80.0")] - #[rustc_const_unstable(feature = "const_intrinsic_copy", issue = "80697")] + #[rustc_const_stable(feature = "const_intrinsic_copy", since = "CURRENT_RUSTC_VERSION")] pub const unsafe fn copy_to_nonoverlapping(self, dest: NonNull, count: usize) where T: Sized, @@ -964,7 +964,7 @@ impl NonNull { #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces #[stable(feature = "non_null_convenience", since = "1.80.0")] - #[rustc_const_unstable(feature = "const_intrinsic_copy", issue = "80697")] + #[rustc_const_stable(feature = "const_intrinsic_copy", since = "CURRENT_RUSTC_VERSION")] pub const unsafe fn copy_from(self, src: NonNull, count: usize) where T: Sized, @@ -984,7 +984,7 @@ impl NonNull { #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces #[stable(feature = "non_null_convenience", since = "1.80.0")] - #[rustc_const_unstable(feature = "const_intrinsic_copy", issue = "80697")] + #[rustc_const_stable(feature = "const_intrinsic_copy", since = "CURRENT_RUSTC_VERSION")] pub const unsafe fn copy_from_nonoverlapping(self, src: NonNull, count: usize) where T: Sized, diff --git a/library/core/src/random.rs b/library/core/src/random.rs new file mode 100644 index 000000000000..051fe2608638 --- /dev/null +++ b/library/core/src/random.rs @@ -0,0 +1,62 @@ +//! Random value generation. +//! +//! The [`Random`] trait allows generating a random value for a type using a +//! given [`RandomSource`]. + +/// A source of randomness. +#[unstable(feature = "random", issue = "130703")] +pub trait RandomSource { + /// Fills `bytes` with random bytes. + fn fill_bytes(&mut self, bytes: &mut [u8]); +} + +/// A trait for getting a random value for a type. +/// +/// **Warning:** Be careful when manipulating random values! The +/// [`random`](Random::random) method on integers samples them with a uniform +/// distribution, so a value of 1 is just as likely as [`i32::MAX`]. By using +/// modulo operations, some of the resulting values can become more likely than +/// others. Use audited crates when in doubt. +#[unstable(feature = "random", issue = "130703")] +pub trait Random: Sized { + /// Generates a random value. + fn random(source: &mut (impl RandomSource + ?Sized)) -> Self; +} + +impl Random for bool { + fn random(source: &mut (impl RandomSource + ?Sized)) -> Self { + u8::random(source) & 1 == 1 + } +} + +macro_rules! impl_primitive { + ($t:ty) => { + impl Random for $t { + /// Generates a random value. + /// + /// **Warning:** Be careful when manipulating the resulting value! This + /// method samples according to a uniform distribution, so a value of 1 is + /// just as likely as [`MAX`](Self::MAX). By using modulo operations, some + /// values can become more likely than others. Use audited crates when in + /// doubt. + fn random(source: &mut (impl RandomSource + ?Sized)) -> Self { + let mut bytes = (0 as Self).to_ne_bytes(); + source.fill_bytes(&mut bytes); + Self::from_ne_bytes(bytes) + } + } + }; +} + +impl_primitive!(u8); +impl_primitive!(i8); +impl_primitive!(u16); +impl_primitive!(i16); +impl_primitive!(u32); +impl_primitive!(i32); +impl_primitive!(u64); +impl_primitive!(i64); +impl_primitive!(u128); +impl_primitive!(i128); +impl_primitive!(usize); +impl_primitive!(isize); diff --git a/library/core/src/slice/ascii.rs b/library/core/src/slice/ascii.rs index d1ea52fab6b8..8f8050fdc3aa 100644 --- a/library/core/src/slice/ascii.rs +++ b/library/core/src/slice/ascii.rs @@ -67,10 +67,15 @@ impl [u8] { /// /// [`to_ascii_uppercase`]: #method.to_ascii_uppercase #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] + #[rustc_const_unstable(feature = "const_make_ascii", issue = "130698")] #[inline] - pub fn make_ascii_uppercase(&mut self) { - for byte in self { + pub const fn make_ascii_uppercase(&mut self) { + // FIXME(const-hack): We would like to simply iterate using `for` loops but this isn't currently allowed in constant expressions. + let mut i = 0; + while i < self.len() { + let byte = &mut self[i]; byte.make_ascii_uppercase(); + i += 1; } } @@ -84,10 +89,15 @@ impl [u8] { /// /// [`to_ascii_lowercase`]: #method.to_ascii_lowercase #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] + #[rustc_const_unstable(feature = "const_make_ascii", issue = "130698")] #[inline] - pub fn make_ascii_lowercase(&mut self) { - for byte in self { + pub const fn make_ascii_lowercase(&mut self) { + // FIXME(const-hack): We would like to simply iterate using `for` loops but this isn't currently allowed in constant expressions. + let mut i = 0; + while i < self.len() { + let byte = &mut self[i]; byte.make_ascii_lowercase(); + i += 1; } } diff --git a/library/core/src/slice/iter.rs b/library/core/src/slice/iter.rs index 1168f36da154..c5746157d01b 100644 --- a/library/core/src/slice/iter.rs +++ b/library/core/src/slice/iter.rs @@ -11,7 +11,7 @@ use crate::iter::{ use crate::marker::PhantomData; use crate::mem::{self, SizedTypeProperties}; use crate::num::NonZero; -use crate::ptr::{self, NonNull, without_provenance, without_provenance_mut}; +use crate::ptr::{NonNull, without_provenance, without_provenance_mut}; use crate::{cmp, fmt}; #[stable(feature = "boxed_slice_into_iter", since = "1.80.0")] diff --git a/library/core/src/slice/iter/macros.rs b/library/core/src/slice/iter/macros.rs index c2a381946441..830debe02ea2 100644 --- a/library/core/src/slice/iter/macros.rs +++ b/library/core/src/slice/iter/macros.rs @@ -14,11 +14,11 @@ macro_rules! if_zst { if T::IS_ZST { // SAFETY: for ZSTs, the pointer is storing a provenance-free length, // so consuming and updating it as a `usize` is fine. - let $len = unsafe { &mut *ptr::addr_of_mut!($this.end_or_len).cast::() }; + let $len = unsafe { &mut *(&raw mut $this.end_or_len).cast::() }; $zst_body } else { // SAFETY: for non-ZSTs, the type invariant ensures it cannot be null - let $end = unsafe { &mut *ptr::addr_of_mut!($this.end_or_len).cast::>() }; + let $end = unsafe { &mut *(&raw mut $this.end_or_len).cast::>() }; $other_body } }}; @@ -30,7 +30,7 @@ macro_rules! if_zst { $zst_body } else { // SAFETY: for non-ZSTs, the type invariant ensures it cannot be null - let $end = unsafe { *ptr::addr_of!($this.end_or_len).cast::>() }; + let $end = unsafe { *(&raw const $this.end_or_len).cast::>() }; $other_body } }}; diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index 11c9f483f369..dd8fa1ae343a 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -883,8 +883,8 @@ impl [T] { pub const fn swap(&mut self, a: usize, b: usize) { // FIXME: use swap_unchecked here (https://github.com/rust-lang/rust/pull/88540#issuecomment-944344343) // Can't take two mutable loans from one vector, so instead use raw pointers. - let pa = ptr::addr_of_mut!(self[a]); - let pb = ptr::addr_of_mut!(self[b]); + let pa = &raw mut self[a]; + let pb = &raw mut self[b]; // SAFETY: `pa` and `pb` have been created from safe mutable references and refer // to elements in the slice and therefore are guaranteed to be valid and aligned. // Note that accessing the elements behind `a` and `b` is checked and will diff --git a/library/core/src/slice/sort/select.rs b/library/core/src/slice/sort/select.rs index f6529f23bcb3..3358c03d30a9 100644 --- a/library/core/src/slice/sort/select.rs +++ b/library/core/src/slice/sort/select.rs @@ -7,6 +7,7 @@ //! better performance than one would get using heapsort as fallback. use crate::mem::{self, SizedTypeProperties}; +#[cfg(not(feature = "optimize_for_size"))] use crate::slice::sort::shared::pivot::choose_pivot; use crate::slice::sort::shared::smallsort::insertion_sort_shift_left; use crate::slice::sort::unstable::quicksort::partition; @@ -40,7 +41,13 @@ where let min_idx = min_index(v, &mut is_less).unwrap(); v.swap(min_idx, index); } else { - partition_at_index_loop(v, index, None, &mut is_less); + cfg_if! { + if #[cfg(feature = "optimize_for_size")] { + median_of_medians(v, &mut is_less, index); + } else { + partition_at_index_loop(v, index, None, &mut is_less); + } + } } let (left, right) = v.split_at_mut(index); @@ -53,6 +60,7 @@ where // most once, it doesn't make sense to use something more sophisticated than insertion-sort. const INSERTION_SORT_THRESHOLD: usize = 16; +#[cfg(not(feature = "optimize_for_size"))] fn partition_at_index_loop<'a, T, F>( mut v: &'a mut [T], mut index: usize, @@ -169,6 +177,7 @@ fn median_of_medians bool>(mut v: &mut [T], is_less: &mut if v.len() >= 2 { insertion_sort_shift_left(v, 1, is_less); } + return; } diff --git a/library/core/src/slice/sort/shared/mod.rs b/library/core/src/slice/sort/shared/mod.rs index ad1171bfc6a0..e2cdcb3dd511 100644 --- a/library/core/src/slice/sort/shared/mod.rs +++ b/library/core/src/slice/sort/shared/mod.rs @@ -1,3 +1,5 @@ +#![cfg_attr(any(feature = "optimize_for_size", target_pointer_width = "16"), allow(dead_code))] + use crate::marker::Freeze; pub(crate) mod pivot; diff --git a/library/core/src/slice/sort/shared/smallsort.rs b/library/core/src/slice/sort/shared/smallsort.rs index fae628a7c147..6adf779a72f0 100644 --- a/library/core/src/slice/sort/shared/smallsort.rs +++ b/library/core/src/slice/sort/shared/smallsort.rs @@ -378,7 +378,12 @@ where /// Swap two values in the slice pointed to by `v_base` at the position `a_pos` and `b_pos` if the /// value at position `b_pos` is less than the one at position `a_pos`. -pub unsafe fn swap_if_less(v_base: *mut T, a_pos: usize, b_pos: usize, is_less: &mut F) +/// +/// Purposefully not marked `#[inline]`, despite us wanting it to be inlined for integers like +/// types. `is_less` could be a huge function and we want to give the compiler an option to +/// not inline this function. For the same reasons that this function is very perf critical +/// it should be in the same module as the functions that use it. +unsafe fn swap_if_less(v_base: *mut T, a_pos: usize, b_pos: usize, is_less: &mut F) where F: FnMut(&T, &T) -> bool, { diff --git a/library/core/src/slice/sort/stable/mod.rs b/library/core/src/slice/sort/stable/mod.rs index c6f637b3d279..7adcc83b818d 100644 --- a/library/core/src/slice/sort/stable/mod.rs +++ b/library/core/src/slice/sort/stable/mod.rs @@ -1,15 +1,24 @@ //! This module contains the entry points for `slice::sort`. +#[cfg(not(any(feature = "optimize_for_size", target_pointer_width = "16")))] +use crate::cmp; +use crate::intrinsics; use crate::mem::{self, MaybeUninit, SizedTypeProperties}; +#[cfg(not(any(feature = "optimize_for_size", target_pointer_width = "16")))] use crate::slice::sort::shared::smallsort::{ SMALL_SORT_GENERAL_SCRATCH_LEN, StableSmallSortTypeImpl, insertion_sort_shift_left, }; -use crate::{cmp, intrinsics}; -pub(crate) mod drift; pub(crate) mod merge; + +#[cfg(not(any(feature = "optimize_for_size", target_pointer_width = "16")))] +pub(crate) mod drift; +#[cfg(not(any(feature = "optimize_for_size", target_pointer_width = "16")))] pub(crate) mod quicksort; +#[cfg(any(feature = "optimize_for_size", target_pointer_width = "16"))] +pub(crate) mod tiny; + /// Stable sort called driftsort by Orson Peters and Lukas Bergdoll. /// Design document: /// @@ -30,25 +39,53 @@ pub fn sort bool, BufT: BufGuard>(v: &mut [T], is_less return; } - // More advanced sorting methods than insertion sort are faster if called in - // a hot loop for small inputs, but for general-purpose code the small - // binary size of insertion sort is more important. The instruction cache in - // modern processors is very valuable, and for a single sort call in general - // purpose code any gains from an advanced method are cancelled by i-cache - // misses during the sort, and thrashing the i-cache for surrounding code. - const MAX_LEN_ALWAYS_INSERTION_SORT: usize = 20; - if intrinsics::likely(len <= MAX_LEN_ALWAYS_INSERTION_SORT) { - insertion_sort_shift_left(v, 1, is_less); - return; - } + cfg_if! { + if #[cfg(any(feature = "optimize_for_size", target_pointer_width = "16"))] { + let alloc_len = len / 2; + + cfg_if! { + if #[cfg(target_pointer_width = "16")] { + let mut heap_buf = BufT::with_capacity(alloc_len); + let scratch = heap_buf.as_uninit_slice_mut(); + } else { + // For small inputs 4KiB of stack storage suffices, which allows us to avoid + // calling the (de-)allocator. Benchmarks showed this was quite beneficial. + let mut stack_buf = AlignedStorage::::new(); + let stack_scratch = stack_buf.as_uninit_slice_mut(); + let mut heap_buf; + let scratch = if stack_scratch.len() >= alloc_len { + stack_scratch + } else { + heap_buf = BufT::with_capacity(alloc_len); + heap_buf.as_uninit_slice_mut() + }; + } + } - driftsort_main::(v, is_less); + tiny::mergesort(v, scratch, is_less); + } else { + // More advanced sorting methods than insertion sort are faster if called in + // a hot loop for small inputs, but for general-purpose code the small + // binary size of insertion sort is more important. The instruction cache in + // modern processors is very valuable, and for a single sort call in general + // purpose code any gains from an advanced method are cancelled by i-cache + // misses during the sort, and thrashing the i-cache for surrounding code. + const MAX_LEN_ALWAYS_INSERTION_SORT: usize = 20; + if intrinsics::likely(len <= MAX_LEN_ALWAYS_INSERTION_SORT) { + insertion_sort_shift_left(v, 1, is_less); + return; + } + + driftsort_main::(v, is_less); + } + } } /// See [`sort`] /// /// Deliberately don't inline the main sorting routine entrypoint to ensure the /// inlined insertion sort i-cache footprint remains minimal. +#[cfg(not(any(feature = "optimize_for_size", target_pointer_width = "16")))] #[inline(never)] fn driftsort_main bool, BufT: BufGuard>(v: &mut [T], is_less: &mut F) { // By allocating n elements of memory we can ensure the entire input can diff --git a/library/core/src/slice/sort/stable/tiny.rs b/library/core/src/slice/sort/stable/tiny.rs new file mode 100644 index 000000000000..071ab8e107fe --- /dev/null +++ b/library/core/src/slice/sort/stable/tiny.rs @@ -0,0 +1,41 @@ +//! Binary-size optimized mergesort inspired by https://github.com/voultapher/tiny-sort-rs. + +use crate::mem::MaybeUninit; +use crate::ptr; +use crate::slice::sort::stable::merge; + +/// Tiny recursive top-down merge sort optimized for binary size. It has no adaptiveness whatsoever, +/// no run detection, etc. +#[inline(always)] +pub fn mergesort bool>( + v: &mut [T], + scratch: &mut [MaybeUninit], + is_less: &mut F, +) { + let len = v.len(); + + if len > 2 { + let mid = len / 2; + + // SAFETY: mid is in-bounds. + unsafe { + // Sort the left half recursively. + mergesort(v.get_unchecked_mut(..mid), scratch, is_less); + // Sort the right half recursively. + mergesort(v.get_unchecked_mut(mid..), scratch, is_less); + } + + merge::merge(v, scratch, mid, is_less); + } else if len == 2 { + // SAFETY: We checked the len, the pointers we create are valid and don't overlap. + unsafe { + let v_base = v.as_mut_ptr(); + let v_a = v_base; + let v_b = v_base.add(1); + + if is_less(&*v_b, &*v_a) { + ptr::swap_nonoverlapping(v_a, v_b, 1); + } + } + } +} diff --git a/library/core/src/slice/sort/unstable/heapsort.rs b/library/core/src/slice/sort/unstable/heapsort.rs index 27e2ad588ea0..85231779d031 100644 --- a/library/core/src/slice/sort/unstable/heapsort.rs +++ b/library/core/src/slice/sort/unstable/heapsort.rs @@ -1,46 +1,46 @@ //! This module contains a branchless heapsort as fallback for unstable quicksort. -use crate::{intrinsics, ptr}; +use crate::{cmp, intrinsics, ptr}; /// Sorts `v` using heapsort, which guarantees *O*(*n* \* log(*n*)) worst-case. /// /// Never inline this, it sits the main hot-loop in `recurse` and is meant as unlikely algorithmic /// fallback. -/// -/// SAFETY: The caller has to guarantee that `v.len()` >= 2. #[inline(never)] -pub(crate) unsafe fn heapsort(v: &mut [T], is_less: &mut F) +pub(crate) fn heapsort(v: &mut [T], is_less: &mut F) where F: FnMut(&T, &T) -> bool, { - // SAFETY: See function safety. - unsafe { - intrinsics::assume(v.len() >= 2); - - // Build the heap in linear time. - for i in (0..v.len() / 2).rev() { - sift_down(v, i, is_less); - } + let len = v.len(); - // Pop maximal elements from the heap. - for i in (1..v.len()).rev() { + for i in (0..len + len / 2).rev() { + let sift_idx = if i >= len { + i - len + } else { v.swap(0, i); - sift_down(&mut v[..i], 0, is_less); + 0 + }; + + // SAFETY: The above calculation ensures that `sift_idx` is either 0 or + // `(len..(len + (len / 2))) - len`, which simplifies to `0..(len / 2)`. + // This guarantees the required `sift_idx <= len`. + unsafe { + sift_down(&mut v[..cmp::min(i, len)], sift_idx, is_less); } } } // This binary heap respects the invariant `parent >= child`. // -// SAFETY: The caller has to guarantee that node < `v.len()`. -#[inline(never)] +// SAFETY: The caller has to guarantee that `node <= v.len()`. +#[inline(always)] unsafe fn sift_down(v: &mut [T], mut node: usize, is_less: &mut F) where F: FnMut(&T, &T) -> bool, { // SAFETY: See function safety. unsafe { - intrinsics::assume(node < v.len()); + intrinsics::assume(node <= v.len()); } let len = v.len(); @@ -69,9 +69,7 @@ where break; } - // Swap `node` with the greater child, move one step down, and continue sifting. This - // could be ptr::swap_nonoverlapping but that adds a significant amount of binary-size. - ptr::swap(v_base.add(node), v_base.add(child)); + ptr::swap_nonoverlapping(v_base.add(node), v_base.add(child), 1); } node = child; diff --git a/library/core/src/slice/sort/unstable/mod.rs b/library/core/src/slice/sort/unstable/mod.rs index 932e01f4401e..2eb653c4601a 100644 --- a/library/core/src/slice/sort/unstable/mod.rs +++ b/library/core/src/slice/sort/unstable/mod.rs @@ -2,7 +2,9 @@ use crate::intrinsics; use crate::mem::SizedTypeProperties; +#[cfg(not(any(feature = "optimize_for_size", target_pointer_width = "16")))] use crate::slice::sort::shared::find_existing_run; +#[cfg(not(any(feature = "optimize_for_size", target_pointer_width = "16")))] use crate::slice::sort::shared::smallsort::insertion_sort_shift_left; pub(crate) mod heapsort; @@ -28,25 +30,32 @@ pub fn sort bool>(v: &mut [T], is_less: &mut F) { return; } - // More advanced sorting methods than insertion sort are faster if called in - // a hot loop for small inputs, but for general-purpose code the small - // binary size of insertion sort is more important. The instruction cache in - // modern processors is very valuable, and for a single sort call in general - // purpose code any gains from an advanced method are cancelled by i-cache - // misses during the sort, and thrashing the i-cache for surrounding code. - const MAX_LEN_ALWAYS_INSERTION_SORT: usize = 20; - if intrinsics::likely(len <= MAX_LEN_ALWAYS_INSERTION_SORT) { - insertion_sort_shift_left(v, 1, is_less); - return; - } + cfg_if! { + if #[cfg(any(feature = "optimize_for_size", target_pointer_width = "16"))] { + heapsort::heapsort(v, is_less); + } else { + // More advanced sorting methods than insertion sort are faster if called in + // a hot loop for small inputs, but for general-purpose code the small + // binary size of insertion sort is more important. The instruction cache in + // modern processors is very valuable, and for a single sort call in general + // purpose code any gains from an advanced method are cancelled by i-cache + // misses during the sort, and thrashing the i-cache for surrounding code. + const MAX_LEN_ALWAYS_INSERTION_SORT: usize = 20; + if intrinsics::likely(len <= MAX_LEN_ALWAYS_INSERTION_SORT) { + insertion_sort_shift_left(v, 1, is_less); + return; + } - ipnsort(v, is_less); + ipnsort(v, is_less); + } + } } /// See [`sort`] /// /// Deliberately don't inline the main sorting routine entrypoint to ensure the /// inlined insertion sort i-cache footprint remains minimal. +#[cfg(not(any(feature = "optimize_for_size", target_pointer_width = "16")))] #[inline(never)] fn ipnsort(v: &mut [T], is_less: &mut F) where diff --git a/library/core/src/slice/sort/unstable/quicksort.rs b/library/core/src/slice/sort/unstable/quicksort.rs index cd53656e9b4b..4feef5deeb0f 100644 --- a/library/core/src/slice/sort/unstable/quicksort.rs +++ b/library/core/src/slice/sort/unstable/quicksort.rs @@ -1,8 +1,12 @@ //! This module contains an unstable quicksort and two partition implementations. use crate::mem::{self, ManuallyDrop}; +#[cfg(not(feature = "optimize_for_size"))] use crate::slice::sort::shared::pivot::choose_pivot; +#[cfg(not(feature = "optimize_for_size"))] use crate::slice::sort::shared::smallsort::UnstableSmallSortTypeImpl; +#[cfg(not(feature = "optimize_for_size"))] +use crate::slice::sort::unstable::heapsort; use crate::{intrinsics, ptr}; /// Sorts `v` recursively. @@ -11,6 +15,7 @@ use crate::{intrinsics, ptr}; /// /// `limit` is the number of allowed imbalanced partitions before switching to `heapsort`. If zero, /// this function will immediately switch to heapsort. +#[cfg(not(feature = "optimize_for_size"))] pub(crate) fn quicksort<'a, T, F>( mut v: &'a mut [T], mut ancestor_pivot: Option<&'a T>, @@ -28,10 +33,7 @@ pub(crate) fn quicksort<'a, T, F>( // If too many bad pivot choices were made, simply fall back to heapsort in order to // guarantee `O(N x log(N))` worst-case. if limit == 0 { - // SAFETY: We assume the `small_sort` threshold is at least 1. - unsafe { - crate::slice::sort::unstable::heapsort::heapsort(v, is_less); - } + heapsort::heapsort(v, is_less); return; } @@ -98,13 +100,15 @@ where return 0; } - // Allows for panic-free code-gen by proving this property to the compiler. if pivot >= len { intrinsics::abort(); } - // Place the pivot at the beginning of slice. - v.swap(0, pivot); + // SAFETY: We checked that `pivot` is in-bounds. + unsafe { + // Place the pivot at the beginning of slice. + v.swap_unchecked(0, pivot); + } let (pivot, v_without_pivot) = v.split_at_mut(1); // Assuming that Rust generates noalias LLVM IR we can be sure that a partition function @@ -118,8 +122,15 @@ where // compile-time by only instantiating the code that is needed. Idea by Frank Steffahn. let num_lt = (const { inst_partition::() })(v_without_pivot, pivot, is_less); - // Place the pivot between the two partitions. - v.swap(0, num_lt); + if num_lt >= len { + intrinsics::abort(); + } + + // SAFETY: We checked that `num_lt` is in-bounds. + unsafe { + // Place the pivot between the two partitions. + v.swap_unchecked(0, num_lt); + } num_lt } @@ -129,7 +140,13 @@ const fn inst_partition bool>() -> fn(&mut [T], &T, &mut if mem::size_of::() <= MAX_BRANCHLESS_PARTITION_SIZE { // Specialize for types that are relatively cheap to copy, where branchless optimizations // have large leverage e.g. `u64` and `String`. - partition_lomuto_branchless_cyclic:: + cfg_if! { + if #[cfg(feature = "optimize_for_size")] { + partition_lomuto_branchless_simple:: + } else { + partition_lomuto_branchless_cyclic:: + } + } } else { partition_hoare_branchy_cyclic:: } @@ -215,6 +232,7 @@ where } } +#[cfg(not(feature = "optimize_for_size"))] struct PartitionState { // The current element that is being looked at, scans left to right through slice. right: *mut T, @@ -225,6 +243,7 @@ struct PartitionState { gap: GapGuardRaw, } +#[cfg(not(feature = "optimize_for_size"))] fn partition_lomuto_branchless_cyclic(v: &mut [T], pivot: &T, is_less: &mut F) -> usize where F: FnMut(&T, &T) -> bool, @@ -316,6 +335,27 @@ where } } +#[cfg(feature = "optimize_for_size")] +fn partition_lomuto_branchless_simple bool>( + v: &mut [T], + pivot: &T, + is_less: &mut F, +) -> usize { + let mut left = 0; + + for right in 0..v.len() { + // SAFETY: `left` can at max be incremented by 1 each loop iteration, which implies that + // left <= right and that both are in-bounds. + unsafe { + let right_is_lt = is_less(v.get_unchecked(right), pivot); + v.swap_unchecked(left, right); + left += right_is_lt as usize; + } + } + + left +} + struct GapGuard { pos: *mut T, value: ManuallyDrop, @@ -333,11 +373,13 @@ impl Drop for GapGuard { /// Ideally this wouldn't be needed and we could just use the regular GapGuard. /// See comment in [`partition_lomuto_branchless_cyclic`]. +#[cfg(not(feature = "optimize_for_size"))] struct GapGuardRaw { pos: *mut T, value: *mut T, } +#[cfg(not(feature = "optimize_for_size"))] impl Drop for GapGuardRaw { fn drop(&mut self) { // SAFETY: `self` MUST be constructed in a way that makes copying the gap value into diff --git a/library/core/src/str/lossy.rs b/library/core/src/str/lossy.rs index 3f31107acf05..e7677c8317a9 100644 --- a/library/core/src/str/lossy.rs +++ b/library/core/src/str/lossy.rs @@ -8,6 +8,8 @@ impl [u8] { /// Creates an iterator over the contiguous valid UTF-8 ranges of this /// slice, and the non-UTF-8 fragments in between. /// + /// See the [`Utf8Chunk`] type for documenation of the items yielded by this iterator. + /// /// # Examples /// /// This function formats arbitrary but mostly-UTF-8 bytes into Rust source @@ -148,6 +150,8 @@ impl fmt::Debug for Debug<'_> { /// If you want a simple conversion from UTF-8 byte slices to string slices, /// [`from_utf8`] is easier to use. /// +/// See the [`Utf8Chunk`] type for documenation of the items yielded by this iterator. +/// /// [byteslice]: slice /// [`from_utf8`]: super::from_utf8 /// diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs index 300dde3dd439..3d535214637f 100644 --- a/library/core/src/str/mod.rs +++ b/library/core/src/str/mod.rs @@ -2473,8 +2473,9 @@ impl str { /// assert_eq!("GRüßE, JüRGEN ❤", s); /// ``` #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] + #[rustc_const_unstable(feature = "const_make_ascii", issue = "130698")] #[inline] - pub fn make_ascii_uppercase(&mut self) { + pub const fn make_ascii_uppercase(&mut self) { // SAFETY: changing ASCII letters only does not invalidate UTF-8. let me = unsafe { self.as_bytes_mut() }; me.make_ascii_uppercase() @@ -2500,8 +2501,9 @@ impl str { /// assert_eq!("grÜße, jÜrgen ❤", s); /// ``` #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] + #[rustc_const_unstable(feature = "const_make_ascii", issue = "130698")] #[inline] - pub fn make_ascii_lowercase(&mut self) { + pub const fn make_ascii_lowercase(&mut self) { // SAFETY: changing ASCII letters only does not invalidate UTF-8. let me = unsafe { self.as_bytes_mut() }; me.make_ascii_lowercase() diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index 5315ac856f69..604c0d487436 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -21,7 +21,6 @@ #![feature(const_cell_into_inner)] #![feature(const_hash)] #![feature(const_heap)] -#![feature(const_intrinsic_copy)] #![feature(const_ip)] #![feature(const_ipv4)] #![feature(const_ipv6)] diff --git a/library/panic_unwind/src/emcc.rs b/library/panic_unwind/src/emcc.rs index a4cbb1875d53..b986fc1c2a82 100644 --- a/library/panic_unwind/src/emcc.rs +++ b/library/panic_unwind/src/emcc.rs @@ -78,7 +78,7 @@ pub unsafe fn cleanup(ptr: *mut u8) -> Box { super::__rust_foreign_exception(); } - let canary = ptr::addr_of!((*adjusted_ptr).canary).read(); + let canary = (&raw const (*adjusted_ptr).canary).read(); if !ptr::eq(canary, &EXCEPTION_TYPE_INFO) { super::__rust_foreign_exception(); } diff --git a/library/panic_unwind/src/gcc.rs b/library/panic_unwind/src/gcc.rs index d8c1dcaaefe7..89b44338d6b4 100644 --- a/library/panic_unwind/src/gcc.rs +++ b/library/panic_unwind/src/gcc.rs @@ -92,7 +92,7 @@ pub unsafe fn cleanup(ptr: *mut u8) -> Box { let exception = exception.cast::(); // Just access the canary field, avoid accessing the entire `Exception` as // it can be a foreign Rust exception. - let canary = ptr::addr_of!((*exception).canary).read(); + let canary = (&raw const (*exception).canary).read(); if !ptr::eq(canary, &CANARY) { // A foreign Rust exception, treat it slightly differently from other // foreign exceptions, because call into `_Unwind_DeleteException` will diff --git a/library/panic_unwind/src/lib.rs b/library/panic_unwind/src/lib.rs index 4552fb68d26d..6cd4dffb8aa3 100644 --- a/library/panic_unwind/src/lib.rs +++ b/library/panic_unwind/src/lib.rs @@ -48,7 +48,7 @@ cfg_if::cfg_if! { target_os = "psp", target_os = "xous", target_os = "solid_asp3", - all(target_family = "unix", not(any(target_os = "espidf", target_os = "rtems"))), + all(target_family = "unix", not(any(target_os = "espidf", target_os = "rtems", target_os = "nuttx"))), all(target_vendor = "fortanix", target_env = "sgx"), target_family = "wasm", ))] { diff --git a/library/panic_unwind/src/seh.rs b/library/panic_unwind/src/seh.rs index 9e74a45a0e26..565a2b8c573b 100644 --- a/library/panic_unwind/src/seh.rs +++ b/library/panic_unwind/src/seh.rs @@ -50,7 +50,6 @@ use alloc::boxed::Box; use core::any::Any; use core::ffi::{c_int, c_uint, c_void}; use core::mem::{self, ManuallyDrop}; -use core::ptr::{addr_of, addr_of_mut}; // NOTE(nbdd0121): The `canary` field is part of stable ABI. #[repr(C)] @@ -131,8 +130,6 @@ mod imp { #[cfg(not(target_arch = "x86"))] mod imp { - use core::ptr::addr_of; - // On 64-bit systems, SEH represents pointers as 32-bit offsets from `__ImageBase`. #[repr(transparent)] #[derive(Copy, Clone)] @@ -157,7 +154,7 @@ mod imp { // going to be cross-lang LTOed anyway. However, using expose is shorter and // requires less unsafe. let addr: usize = ptr.expose_provenance(); - let image_base = addr_of!(__ImageBase).addr(); + let image_base = (&raw const __ImageBase).addr(); let offset: usize = addr - image_base; Self(offset as u32) } @@ -250,7 +247,7 @@ extern "C" { // This is fine since the MSVC runtime uses string comparison on the type name // to match TypeDescriptors rather than pointer equality. static mut TYPE_DESCRIPTOR: _TypeDescriptor = _TypeDescriptor { - pVFTable: addr_of!(TYPE_INFO_VTABLE) as *const _, + pVFTable: (&raw const TYPE_INFO_VTABLE) as *const _, spare: core::ptr::null_mut(), name: TYPE_NAME, }; @@ -304,8 +301,8 @@ pub unsafe fn panic(data: Box) -> u32 { // dropped when unwinding. Instead it will be dropped by exception_cleanup // which is invoked by the C++ runtime. let mut exception = - ManuallyDrop::new(Exception { canary: addr_of!(TYPE_DESCRIPTOR), data: Some(data) }); - let throw_ptr = addr_of_mut!(exception) as *mut _; + ManuallyDrop::new(Exception { canary: (&raw const TYPE_DESCRIPTOR), data: Some(data) }); + let throw_ptr = (&raw mut exception) as *mut _; // This... may seems surprising, and justifiably so. On 32-bit MSVC the // pointers between these structure are just that, pointers. On 64-bit MSVC, @@ -328,23 +325,23 @@ pub unsafe fn panic(data: Box) -> u32 { // In any case, we basically need to do something like this until we can // express more operations in statics (and we may never be able to). atomic_store_seqcst( - addr_of_mut!(THROW_INFO.pmfnUnwind).cast(), + (&raw mut THROW_INFO.pmfnUnwind).cast(), ptr_t::new(exception_cleanup as *mut u8).raw(), ); atomic_store_seqcst( - addr_of_mut!(THROW_INFO.pCatchableTypeArray).cast(), - ptr_t::new(addr_of_mut!(CATCHABLE_TYPE_ARRAY).cast()).raw(), + (&raw mut THROW_INFO.pCatchableTypeArray).cast(), + ptr_t::new((&raw mut CATCHABLE_TYPE_ARRAY).cast()).raw(), ); atomic_store_seqcst( - addr_of_mut!(CATCHABLE_TYPE_ARRAY.arrayOfCatchableTypes[0]).cast(), - ptr_t::new(addr_of_mut!(CATCHABLE_TYPE).cast()).raw(), + (&raw mut CATCHABLE_TYPE_ARRAY.arrayOfCatchableTypes[0]).cast(), + ptr_t::new((&raw mut CATCHABLE_TYPE).cast()).raw(), ); atomic_store_seqcst( - addr_of_mut!(CATCHABLE_TYPE.pType).cast(), - ptr_t::new(addr_of_mut!(TYPE_DESCRIPTOR).cast()).raw(), + (&raw mut CATCHABLE_TYPE.pType).cast(), + ptr_t::new((&raw mut TYPE_DESCRIPTOR).cast()).raw(), ); atomic_store_seqcst( - addr_of_mut!(CATCHABLE_TYPE.copyFunction).cast(), + (&raw mut CATCHABLE_TYPE.copyFunction).cast(), ptr_t::new(exception_copy as *mut u8).raw(), ); @@ -352,7 +349,7 @@ pub unsafe fn panic(data: Box) -> u32 { fn _CxxThrowException(pExceptionObject: *mut c_void, pThrowInfo: *mut u8) -> !; } - _CxxThrowException(throw_ptr, addr_of_mut!(THROW_INFO) as *mut _); + _CxxThrowException(throw_ptr, (&raw mut THROW_INFO) as *mut _); } pub unsafe fn cleanup(payload: *mut u8) -> Box { @@ -362,8 +359,8 @@ pub unsafe fn cleanup(payload: *mut u8) -> Box { super::__rust_foreign_exception(); } let exception = payload as *mut Exception; - let canary = addr_of!((*exception).canary).read(); - if !core::ptr::eq(canary, addr_of!(TYPE_DESCRIPTOR)) { + let canary = (&raw const (*exception).canary).read(); + if !core::ptr::eq(canary, &raw const TYPE_DESCRIPTOR) { // A foreign Rust exception. super::__rust_foreign_exception(); } diff --git a/library/portable-simd/crates/core_simd/src/lib.rs b/library/portable-simd/crates/core_simd/src/lib.rs index 331b66262490..cc6246b4a0d4 100644 --- a/library/portable-simd/crates/core_simd/src/lib.rs +++ b/library/portable-simd/crates/core_simd/src/lib.rs @@ -1,8 +1,6 @@ #![no_std] #![feature( - const_intrinsic_copy, const_refs_to_cell, - const_maybe_uninit_as_mut_ptr, const_mut_refs, convert_float_to_int, core_intrinsics, diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index 9a31fd21dc71..ea11586f9a51 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -23,6 +23,10 @@ unwind = { path = "../unwind" } hashbrown = { version = "0.14", default-features = false, features = [ 'rustc-dep-of-std', ] } +# FIXME(#127890): `object` depends on `memchr`, but `memchr` > v2.5 causes +# issues with LTO. This dependency is not used directly, but pin it here so +# it resolves to 2.5. To be removed once rust-lang/rust#127890 is fixed. +memchr = { version = "=2.5.0", default-features = false, features = ["rustc-dep-of-std"] } std_detect = { path = "../stdarch/crates/std_detect", default-features = false, features = [ 'rustc-dep-of-std', ] } diff --git a/library/std/build.rs b/library/std/build.rs index ba1eece46f3c..359ae4f20eea 100644 --- a/library/std/build.rs +++ b/library/std/build.rs @@ -54,6 +54,7 @@ fn main() { || target_os == "teeos" || target_os == "zkvm" || target_os == "rtems" + || target_os == "nuttx" // See src/bootstrap/src/core/build_steps/synthetic_targets.rs || env::var("RUSTC_BOOTSTRAP_SYNTHETIC_TARGET").is_ok() diff --git a/library/std/src/ffi/os_str.rs b/library/std/src/ffi/os_str.rs index 0f905803bb8b..2243f100643d 100644 --- a/library/std/src/ffi/os_str.rs +++ b/library/std/src/ffi/os_str.rs @@ -9,7 +9,6 @@ use crate::borrow::{Borrow, Cow}; use crate::collections::TryReserveError; use crate::hash::{Hash, Hasher}; use crate::ops::{self, Range}; -use crate::ptr::addr_of_mut; use crate::rc::Rc; use crate::str::FromStr; use crate::sync::Arc; @@ -1272,7 +1271,7 @@ unsafe impl CloneToUninit for OsStr { #[cfg_attr(debug_assertions, track_caller)] unsafe fn clone_to_uninit(&self, dst: *mut Self) { // SAFETY: we're just a wrapper around a platform-specific Slice - unsafe { self.inner.clone_to_uninit(addr_of_mut!((*dst).inner)) } + unsafe { self.inner.clone_to_uninit(&raw mut (*dst).inner) } } } diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 55f3b628ce8e..db7867337dd5 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -375,6 +375,44 @@ impl File { OpenOptions::new().read(true).open(path.as_ref()) } + /// Attempts to open a file in read-only mode with buffering. + /// + /// See the [`OpenOptions::open`] method, the [`BufReader`][io::BufReader] type, + /// and the [`BufRead`][io::BufRead] trait for more details. + /// + /// If you only need to read the entire file contents, + /// consider [`std::fs::read()`][self::read] or + /// [`std::fs::read_to_string()`][self::read_to_string] instead. + /// + /// # Errors + /// + /// This function will return an error if `path` does not already exist. + /// Other errors may also be returned according to [`OpenOptions::open`]. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(file_buffered)] + /// use std::fs::File; + /// use std::io::BufRead; + /// + /// fn main() -> std::io::Result<()> { + /// let mut f = File::open_buffered("foo.txt")?; + /// assert!(f.capacity() > 0); + /// for (line, i) in f.lines().zip(1..) { + /// println!("{i:6}: {}", line?); + /// } + /// Ok(()) + /// } + /// ``` + #[unstable(feature = "file_buffered", issue = "130804")] + pub fn open_buffered>(path: P) -> io::Result> { + // Allocate the buffer *first* so we don't affect the filesystem otherwise. + let buffer = io::BufReader::::try_new_buffer()?; + let file = File::open(path)?; + Ok(io::BufReader::with_buffer(file, buffer)) + } + /// Opens a file in write-only mode. /// /// This function will create a file if it does not exist, @@ -404,6 +442,45 @@ impl File { OpenOptions::new().write(true).create(true).truncate(true).open(path.as_ref()) } + /// Opens a file in write-only mode with buffering. + /// + /// This function will create a file if it does not exist, + /// and will truncate it if it does. + /// + /// Depending on the platform, this function may fail if the + /// full directory path does not exist. + /// + /// See the [`OpenOptions::open`] method and the + /// [`BufWriter`][io::BufWriter] type for more details. + /// + /// See also [`std::fs::write()`][self::write] for a simple function to + /// create a file with some given data. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(file_buffered)] + /// use std::fs::File; + /// use std::io::Write; + /// + /// fn main() -> std::io::Result<()> { + /// let mut f = File::create_buffered("foo.txt")?; + /// assert!(f.capacity() > 0); + /// for i in 0..100 { + /// writeln!(&mut f, "{i}")?; + /// } + /// f.flush()?; + /// Ok(()) + /// } + /// ``` + #[unstable(feature = "file_buffered", issue = "130804")] + pub fn create_buffered>(path: P) -> io::Result> { + // Allocate the buffer *first* so we don't affect the filesystem otherwise. + let buffer = io::BufWriter::::try_new_buffer()?; + let file = File::create(path)?; + Ok(io::BufWriter::with_buffer(file, buffer)) + } + /// Creates a new file in read-write mode; error if the file exists. /// /// This function will create a file if it does not exist, or return an error if it does. This diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs index 412603ddea3d..0672fe6f7718 100644 --- a/library/std/src/fs/tests.rs +++ b/library/std/src/fs/tests.rs @@ -1732,7 +1732,7 @@ fn windows_unix_socket_exists() { let bytes = core::slice::from_raw_parts(bytes.as_ptr().cast::(), bytes.len()); addr.sun_path[..bytes.len()].copy_from_slice(bytes); let len = mem::size_of_val(&addr) as i32; - let result = c::bind(socket, ptr::addr_of!(addr).cast::(), len); + let result = c::bind(socket, (&raw const addr).cast::(), len); c::closesocket(socket); assert_eq!(result, 0); } diff --git a/library/std/src/hash/random.rs b/library/std/src/hash/random.rs index 8ef45172eac4..40f3a90f60c8 100644 --- a/library/std/src/hash/random.rs +++ b/library/std/src/hash/random.rs @@ -10,7 +10,8 @@ #[allow(deprecated)] use super::{BuildHasher, Hasher, SipHasher13}; use crate::cell::Cell; -use crate::{fmt, sys}; +use crate::fmt; +use crate::sys::random::hashmap_random_keys; /// `RandomState` is the default state for [`HashMap`] types. /// @@ -65,7 +66,7 @@ impl RandomState { // increment one of the seeds on every RandomState creation, giving // every corresponding HashMap a different iteration order. thread_local!(static KEYS: Cell<(u64, u64)> = { - Cell::new(sys::hashmap_random_keys()) + Cell::new(hashmap_random_keys()) }); KEYS.with(|keys| { diff --git a/library/std/src/io/buffered/bufreader.rs b/library/std/src/io/buffered/bufreader.rs index e51dde994de4..fcb3e36027ba 100644 --- a/library/std/src/io/buffered/bufreader.rs +++ b/library/std/src/io/buffered/bufreader.rs @@ -74,6 +74,14 @@ impl BufReader { BufReader::with_capacity(DEFAULT_BUF_SIZE, inner) } + pub(crate) fn try_new_buffer() -> io::Result { + Buffer::try_with_capacity(DEFAULT_BUF_SIZE) + } + + pub(crate) fn with_buffer(inner: R, buf: Buffer) -> Self { + Self { inner, buf } + } + /// Creates a new `BufReader` with the specified buffer capacity. /// /// # Examples diff --git a/library/std/src/io/buffered/bufreader/buffer.rs b/library/std/src/io/buffered/bufreader/buffer.rs index 1bf84d8bef31..3df7e3971dac 100644 --- a/library/std/src/io/buffered/bufreader/buffer.rs +++ b/library/std/src/io/buffered/bufreader/buffer.rs @@ -10,7 +10,7 @@ //! without encountering any runtime bounds checks. use crate::cmp; -use crate::io::{self, BorrowedBuf, Read}; +use crate::io::{self, BorrowedBuf, ErrorKind, Read}; use crate::mem::MaybeUninit; pub struct Buffer { @@ -36,6 +36,16 @@ impl Buffer { Self { buf, pos: 0, filled: 0, initialized: 0 } } + #[inline] + pub fn try_with_capacity(capacity: usize) -> io::Result { + match Box::try_new_uninit_slice(capacity) { + Ok(buf) => Ok(Self { buf, pos: 0, filled: 0, initialized: 0 }), + Err(_) => { + Err(io::const_io_error!(ErrorKind::OutOfMemory, "failed to allocate read buffer")) + } + } + } + #[inline] pub fn buffer(&self) -> &[u8] { // SAFETY: self.pos and self.cap are valid, and self.cap => self.pos, and diff --git a/library/std/src/io/buffered/bufwriter.rs b/library/std/src/io/buffered/bufwriter.rs index 13516d3b961f..c41bae2aa4e8 100644 --- a/library/std/src/io/buffered/bufwriter.rs +++ b/library/std/src/io/buffered/bufwriter.rs @@ -94,6 +94,16 @@ impl BufWriter { BufWriter::with_capacity(DEFAULT_BUF_SIZE, inner) } + pub(crate) fn try_new_buffer() -> io::Result> { + Vec::try_with_capacity(DEFAULT_BUF_SIZE).map_err(|_| { + io::const_io_error!(ErrorKind::OutOfMemory, "failed to allocate write buffer") + }) + } + + pub(crate) fn with_buffer(inner: W, buf: Vec) -> Self { + Self { inner, buf, panicked: false } + } + /// Creates a new `BufWriter` with at least the specified buffer capacity. /// /// # Examples diff --git a/library/std/src/io/error.rs b/library/std/src/io/error.rs index 6ecd9469c174..f20814dd95cc 100644 --- a/library/std/src/io/error.rs +++ b/library/std/src/io/error.rs @@ -400,6 +400,11 @@ pub enum ErrorKind { #[stable(feature = "out_of_memory_error", since = "1.54.0")] OutOfMemory, + /// The operation was partially successful and needs to be checked + /// later on due to not blocking. + #[unstable(feature = "io_error_inprogress", issue = "130840")] + InProgress, + // "Unusual" error kinds which do not correspond simply to (sets // of) OS error codes, should be added just above this comment. // `Other` and `Uncategorized` should remain at the end: @@ -449,6 +454,7 @@ impl ErrorKind { FilesystemQuotaExceeded => "filesystem quota exceeded", HostUnreachable => "host unreachable", Interrupted => "operation interrupted", + InProgress => "in progress", InvalidData => "invalid data", InvalidFilename => "invalid filename", InvalidInput => "invalid input parameter", diff --git a/library/std/src/io/error/repr_bitpacked.rs b/library/std/src/io/error/repr_bitpacked.rs index 9d3ade46bd92..80ba8455df34 100644 --- a/library/std/src/io/error/repr_bitpacked.rs +++ b/library/std/src/io/error/repr_bitpacked.rs @@ -348,6 +348,7 @@ fn kind_from_prim(ek: u32) -> Option { UnexpectedEof, Unsupported, OutOfMemory, + InProgress, Uncategorized, }) } diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 2add88da9a7a..b81e7c18abbb 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -32,13 +32,17 @@ //! //! Once you are familiar with the contents of the standard library you may //! begin to find the verbosity of the prose distracting. At this stage in your -//! development you may want to press the `[-]` button near the top of the -//! page to collapse it into a more skimmable view. -//! -//! While you are looking at that `[-]` button also notice the `source` -//! link. Rust's API documentation comes with the source code and you are -//! encouraged to read it. The standard library source is generally high -//! quality and a peek behind the curtains is often enlightening. +//! development you may want to press the +//! +//! Summary button near the +//! top of the page to collapse it into a more skimmable view. +//! +//! While you are looking at the top of the page, also notice the +//! source link. Rust's API documentation comes with the source +//! code and you are encouraged to read it. The standard library source is +//! generally high quality and a peek behind the curtains is +//! often enlightening. //! //! # What is in the standard library documentation? //! @@ -318,6 +322,7 @@ // // Library features (core): // tidy-alphabetical-start +#![feature(array_chunks)] #![feature(c_str_module)] #![feature(char_internals)] #![feature(clone_to_uninit)] @@ -348,6 +353,7 @@ #![feature(prelude_2024)] #![feature(ptr_as_uninit)] #![feature(ptr_mask)] +#![feature(random)] #![feature(slice_internals)] #![feature(slice_ptr_get)] #![feature(slice_range)] @@ -368,6 +374,7 @@ #![feature(slice_concat_trait)] #![feature(thin_box)] #![feature(try_reserve_kind)] +#![feature(try_with_capacity)] #![feature(vec_into_raw_parts)] // tidy-alphabetical-end // @@ -595,6 +602,8 @@ pub mod path; #[unstable(feature = "anonymous_pipe", issue = "127154")] pub mod pipe; pub mod process; +#[unstable(feature = "random", issue = "130703")] +pub mod random; pub mod sync; pub mod time; diff --git a/library/std/src/os/mod.rs b/library/std/src/os/mod.rs index a2496baa63fb..6701173d1e00 100644 --- a/library/std/src/os/mod.rs +++ b/library/std/src/os/mod.rs @@ -139,6 +139,8 @@ pub mod macos; pub mod netbsd; #[cfg(target_os = "nto")] pub mod nto; +#[cfg(target_os = "nuttx")] +pub mod nuttx; #[cfg(target_os = "openbsd")] pub mod openbsd; #[cfg(target_os = "redox")] diff --git a/library/std/src/os/nuttx/fs.rs b/library/std/src/os/nuttx/fs.rs new file mode 100644 index 000000000000..7d6d8d16eca7 --- /dev/null +++ b/library/std/src/os/nuttx/fs.rs @@ -0,0 +1,92 @@ +#![stable(feature = "metadata_ext", since = "1.1.0")] + +use crate::fs::Metadata; +use crate::sys_common::AsInner; + +#[stable(feature = "metadata_ext", since = "1.1.0")] +pub trait MetadataExt { + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_dev(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ino(&self) -> u64; + #[stable(feature = "metadata_ext", since = "1.1.0")] + fn st_mode(&self) -> u32; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_nlink(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_uid(&self) -> u32; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_gid(&self) -> u32; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_rdev(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_size(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_atime(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_atime_nsec(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_mtime(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_mtime_nsec(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ctime(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ctime_nsec(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_blksize(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_blocks(&self) -> u64; +} + +#[stable(feature = "metadata_ext", since = "1.1.0")] +impl MetadataExt for Metadata { + fn st_dev(&self) -> u64 { + self.as_inner().as_inner().st_dev as u64 + } + fn st_ino(&self) -> u64 { + self.as_inner().as_inner().st_ino as u64 + } + fn st_mode(&self) -> u32 { + self.as_inner().as_inner().st_mode as u32 + } + fn st_nlink(&self) -> u64 { + self.as_inner().as_inner().st_nlink as u64 + } + fn st_uid(&self) -> u32 { + self.as_inner().as_inner().st_uid as u32 + } + fn st_gid(&self) -> u32 { + self.as_inner().as_inner().st_gid as u32 + } + fn st_rdev(&self) -> u64 { + self.as_inner().as_inner().st_rdev as u64 + } + fn st_size(&self) -> u64 { + self.as_inner().as_inner().st_size as u64 + } + fn st_atime(&self) -> i64 { + self.as_inner().as_inner().st_atim.tv_sec as i64 + } + fn st_atime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_atim.tv_nsec as i64 + } + fn st_mtime(&self) -> i64 { + self.as_inner().as_inner().st_mtim.tv_sec as i64 + } + fn st_mtime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_mtim.tv_nsec as i64 + } + fn st_ctime(&self) -> i64 { + self.as_inner().as_inner().st_ctim.tv_sec as i64 + } + fn st_ctime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_ctim.tv_nsec as i64 + } + fn st_blksize(&self) -> u64 { + self.as_inner().as_inner().st_blksize as u64 + } + fn st_blocks(&self) -> u64 { + self.as_inner().as_inner().st_blocks as u64 + } +} diff --git a/library/std/src/os/nuttx/mod.rs b/library/std/src/os/nuttx/mod.rs new file mode 100644 index 000000000000..7275bfd1765d --- /dev/null +++ b/library/std/src/os/nuttx/mod.rs @@ -0,0 +1,4 @@ +#![stable(feature = "raw_ext", since = "1.1.0")] +#![forbid(unsafe_op_in_unsafe_fn)] +pub mod fs; +pub(crate) mod raw; diff --git a/library/std/src/os/nuttx/raw.rs b/library/std/src/os/nuttx/raw.rs new file mode 100644 index 000000000000..113079cf4abd --- /dev/null +++ b/library/std/src/os/nuttx/raw.rs @@ -0,0 +1,33 @@ +//! rtems raw type definitions + +#![stable(feature = "raw_ext", since = "1.1.0")] +#![deprecated( + since = "1.8.0", + note = "these type aliases are no longer supported by \ + the standard library, the `libc` crate on \ + crates.io should be used instead for the correct \ + definitions" +)] +#![allow(deprecated)] + +#[stable(feature = "pthread_t", since = "1.8.0")] +pub type pthread_t = libc::pthread_t; + +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type blkcnt_t = libc::blkcnt_t; + +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type blksize_t = libc::blksize_t; +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type dev_t = libc::dev_t; +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type ino_t = libc::ino_t; +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type mode_t = libc::mode_t; +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type nlink_t = libc::nlink_t; +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type off_t = libc::off_t; + +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type time_t = libc::time_t; diff --git a/library/std/src/os/unix/mod.rs b/library/std/src/os/unix/mod.rs index 7d2f0bd4efea..5c2ec8ef994d 100644 --- a/library/std/src/os/unix/mod.rs +++ b/library/std/src/os/unix/mod.rs @@ -69,6 +69,8 @@ mod platform { pub use crate::os::netbsd::*; #[cfg(target_os = "nto")] pub use crate::os::nto::*; + #[cfg(target_os = "nuttx")] + pub use crate::os::nuttx::*; #[cfg(target_os = "openbsd")] pub use crate::os::openbsd::*; #[cfg(target_os = "redox")] diff --git a/library/std/src/os/unix/net/addr.rs b/library/std/src/os/unix/net/addr.rs index 79f2c365025b..253e1503cf7a 100644 --- a/library/std/src/os/unix/net/addr.rs +++ b/library/std/src/os/unix/net/addr.rs @@ -15,15 +15,12 @@ mod libc { pub type socklen_t = u32; pub struct sockaddr; #[derive(Clone)] - pub struct sockaddr_un; + pub struct sockaddr_un { + pub sun_path: [u8; 1], + } } -fn sun_path_offset(addr: &libc::sockaddr_un) -> usize { - // Work with an actual instance of the type since using a null pointer is UB - let base = (addr as *const libc::sockaddr_un).addr(); - let path = core::ptr::addr_of!(addr.sun_path).addr(); - path - base -} +const SUN_PATH_OFFSET: usize = mem::offset_of!(libc::sockaddr_un, sun_path); pub(super) fn sockaddr_un(path: &Path) -> io::Result<(libc::sockaddr_un, libc::socklen_t)> { // SAFETY: All zeros is a valid representation for `sockaddr_un`. @@ -53,7 +50,7 @@ pub(super) fn sockaddr_un(path: &Path) -> io::Result<(libc::sockaddr_un, libc::s ptr::copy_nonoverlapping(bytes.as_ptr(), addr.sun_path.as_mut_ptr().cast(), bytes.len()) }; - let mut len = sun_path_offset(&addr) + bytes.len(); + let mut len = SUN_PATH_OFFSET + bytes.len(); match bytes.get(0) { Some(&0) | None => {} Some(_) => len += 1, @@ -98,7 +95,7 @@ impl SocketAddr { unsafe { let mut addr: libc::sockaddr_un = mem::zeroed(); let mut len = mem::size_of::() as libc::socklen_t; - cvt(f(core::ptr::addr_of_mut!(addr) as *mut _, &mut len))?; + cvt(f((&raw mut addr) as *mut _, &mut len))?; SocketAddr::from_parts(addr, len) } } @@ -114,13 +111,13 @@ impl SocketAddr { let sun_path: &[u8] = unsafe { mem::transmute::<&[libc::c_char], &[u8]>(&addr.sun_path) }; len = core::slice::memchr::memchr(0, sun_path) - .map_or(len, |new_len| (new_len + sun_path_offset(&addr)) as libc::socklen_t); + .map_or(len, |new_len| (new_len + SUN_PATH_OFFSET) as libc::socklen_t); } if len == 0 { // When there is a datagram from unnamed unix socket // linux returns zero bytes of address - len = sun_path_offset(&addr) as libc::socklen_t; // i.e., zero-length address + len = SUN_PATH_OFFSET as libc::socklen_t; // i.e., zero-length address } else if addr.sun_family != libc::AF_UNIX as libc::sa_family_t { return Err(io::const_io_error!( io::ErrorKind::InvalidInput, @@ -238,7 +235,7 @@ impl SocketAddr { } fn address(&self) -> AddressKind<'_> { - let len = self.len as usize - sun_path_offset(&self.addr); + let len = self.len as usize - SUN_PATH_OFFSET; let path = unsafe { mem::transmute::<&[libc::c_char], &[u8]>(&self.addr.sun_path) }; // macOS seems to return a len of 16 and a zeroed sun_path for unnamed addresses @@ -287,7 +284,7 @@ impl linux_ext::addr::SocketAddrExt for SocketAddr { addr.sun_path.as_mut_ptr().add(1) as *mut u8, name.len(), ); - let len = (sun_path_offset(&addr) + 1 + name.len()) as libc::socklen_t; + let len = (SUN_PATH_OFFSET + 1 + name.len()) as libc::socklen_t; SocketAddr::from_parts(addr, len) } } diff --git a/library/std/src/os/unix/net/ancillary.rs b/library/std/src/os/unix/net/ancillary.rs index c34a3b4e184a..36967fc3f98b 100644 --- a/library/std/src/os/unix/net/ancillary.rs +++ b/library/std/src/os/unix/net/ancillary.rs @@ -37,7 +37,7 @@ pub(super) fn recv_vectored_with_ancillary_from( unsafe { let mut msg_name: libc::sockaddr_un = zeroed(); let mut msg: libc::msghdr = zeroed(); - msg.msg_name = core::ptr::addr_of_mut!(msg_name) as *mut _; + msg.msg_name = (&raw mut msg_name) as *mut _; msg.msg_namelen = size_of::() as libc::socklen_t; msg.msg_iov = bufs.as_mut_ptr().cast(); msg.msg_iovlen = bufs.len() as _; @@ -70,7 +70,7 @@ pub(super) fn send_vectored_with_ancillary_to( if let Some(path) = path { sockaddr_un(path)? } else { (zeroed(), 0) }; let mut msg: libc::msghdr = zeroed(); - msg.msg_name = core::ptr::addr_of_mut!(msg_name) as *mut _; + msg.msg_name = (&raw mut msg_name) as *mut _; msg.msg_namelen = msg_namelen; msg.msg_iov = bufs.as_ptr() as *mut _; msg.msg_iovlen = bufs.len() as _; diff --git a/library/std/src/os/unix/net/datagram.rs b/library/std/src/os/unix/net/datagram.rs index 48aaddd2d529..82446ea107fe 100644 --- a/library/std/src/os/unix/net/datagram.rs +++ b/library/std/src/os/unix/net/datagram.rs @@ -100,7 +100,7 @@ impl UnixDatagram { let socket = UnixDatagram::unbound()?; let (addr, len) = sockaddr_un(path.as_ref())?; - cvt(libc::bind(socket.as_raw_fd(), core::ptr::addr_of!(addr) as *const _, len as _))?; + cvt(libc::bind(socket.as_raw_fd(), (&raw const addr) as *const _, len as _))?; Ok(socket) } @@ -133,7 +133,7 @@ impl UnixDatagram { let socket = UnixDatagram::unbound()?; cvt(libc::bind( socket.as_raw_fd(), - core::ptr::addr_of!(socket_addr.addr) as *const _, + (&raw const socket_addr.addr) as *const _, socket_addr.len as _, ))?; Ok(socket) @@ -215,7 +215,7 @@ impl UnixDatagram { unsafe { let (addr, len) = sockaddr_un(path.as_ref())?; - cvt(libc::connect(self.as_raw_fd(), core::ptr::addr_of!(addr) as *const _, len))?; + cvt(libc::connect(self.as_raw_fd(), (&raw const addr) as *const _, len))?; } Ok(()) } @@ -247,7 +247,7 @@ impl UnixDatagram { unsafe { cvt(libc::connect( self.as_raw_fd(), - core::ptr::addr_of!(socket_addr.addr) as *const _, + (&raw const socket_addr.addr) as *const _, socket_addr.len, ))?; } @@ -514,7 +514,7 @@ impl UnixDatagram { buf.as_ptr() as *const _, buf.len(), MSG_NOSIGNAL, - core::ptr::addr_of!(addr) as *const _, + (&raw const addr) as *const _, len, ))?; Ok(count as usize) @@ -549,7 +549,7 @@ impl UnixDatagram { buf.as_ptr() as *const _, buf.len(), MSG_NOSIGNAL, - core::ptr::addr_of!(socket_addr.addr) as *const _, + (&raw const socket_addr.addr) as *const _, socket_addr.len, ))?; Ok(count as usize) diff --git a/library/std/src/os/unix/net/listener.rs b/library/std/src/os/unix/net/listener.rs index 440408eb13fa..be236317d047 100644 --- a/library/std/src/os/unix/net/listener.rs +++ b/library/std/src/os/unix/net/listener.rs @@ -103,11 +103,7 @@ impl UnixListener { )))] const backlog: libc::c_int = libc::SOMAXCONN; - cvt(libc::bind( - inner.as_inner().as_raw_fd(), - core::ptr::addr_of!(addr) as *const _, - len as _, - ))?; + cvt(libc::bind(inner.as_inner().as_raw_fd(), (&raw const addr) as *const _, len as _))?; cvt(libc::listen(inner.as_inner().as_raw_fd(), backlog))?; Ok(UnixListener(inner)) @@ -147,7 +143,7 @@ impl UnixListener { const backlog: core::ffi::c_int = 128; cvt(libc::bind( inner.as_raw_fd(), - core::ptr::addr_of!(socket_addr.addr) as *const _, + (&raw const socket_addr.addr) as *const _, socket_addr.len as _, ))?; cvt(libc::listen(inner.as_raw_fd(), backlog))?; @@ -182,7 +178,7 @@ impl UnixListener { pub fn accept(&self) -> io::Result<(UnixStream, SocketAddr)> { let mut storage: libc::sockaddr_un = unsafe { mem::zeroed() }; let mut len = mem::size_of_val(&storage) as libc::socklen_t; - let sock = self.0.accept(core::ptr::addr_of_mut!(storage) as *mut _, &mut len)?; + let sock = self.0.accept((&raw mut storage) as *mut _, &mut len)?; let addr = SocketAddr::from_parts(storage, len)?; Ok((UnixStream(sock), addr)) } diff --git a/library/std/src/os/unix/net/stream.rs b/library/std/src/os/unix/net/stream.rs index 4967c5b89ecc..cb210b41eae1 100644 --- a/library/std/src/os/unix/net/stream.rs +++ b/library/std/src/os/unix/net/stream.rs @@ -84,7 +84,7 @@ impl UnixStream { let inner = Socket::new_raw(libc::AF_UNIX, libc::SOCK_STREAM)?; let (addr, len) = sockaddr_un(path.as_ref())?; - cvt(libc::connect(inner.as_raw_fd(), core::ptr::addr_of!(addr) as *const _, len))?; + cvt(libc::connect(inner.as_raw_fd(), (&raw const addr) as *const _, len))?; Ok(UnixStream(inner)) } } @@ -118,7 +118,7 @@ impl UnixStream { let inner = Socket::new_raw(libc::AF_UNIX, libc::SOCK_STREAM)?; cvt(libc::connect( inner.as_raw_fd(), - core::ptr::addr_of!(socket_addr.addr) as *const _, + (&raw const socket_addr.addr) as *const _, socket_addr.len, ))?; Ok(UnixStream(inner)) diff --git a/library/std/src/os/unix/net/ucred.rs b/library/std/src/os/unix/net/ucred.rs index c818bd058586..e1014a4f296d 100644 --- a/library/std/src/os/unix/net/ucred.rs +++ b/library/std/src/os/unix/net/ucred.rs @@ -60,7 +60,7 @@ mod impl_linux { socket.as_raw_fd(), SOL_SOCKET, SO_PEERCRED, - core::ptr::addr_of_mut!(ucred) as *mut c_void, + (&raw mut ucred) as *mut c_void, &mut ucred_size, ); @@ -121,7 +121,7 @@ mod impl_apple { socket.as_raw_fd(), SOL_LOCAL, LOCAL_PEERPID, - core::ptr::addr_of_mut!(pid) as *mut c_void, + (&raw mut pid) as *mut c_void, &mut pid_size, ); diff --git a/library/std/src/panicking.rs b/library/std/src/panicking.rs index 336e34d7b95e..9fe05db8126d 100644 --- a/library/std/src/panicking.rs +++ b/library/std/src/panicking.rs @@ -506,7 +506,7 @@ pub unsafe fn r#try R>(f: F) -> Result> // method of calling a catch panic whilst juggling ownership. let mut data = Data { f: ManuallyDrop::new(f) }; - let data_ptr = core::ptr::addr_of_mut!(data) as *mut u8; + let data_ptr = (&raw mut data) as *mut u8; // SAFETY: // // Access to the union's fields: this is `std` and we know that the `r#try` diff --git a/library/std/src/path.rs b/library/std/src/path.rs index e3ff7d199ccc..63edfdb82f36 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -3144,7 +3144,7 @@ unsafe impl CloneToUninit for Path { #[cfg_attr(debug_assertions, track_caller)] unsafe fn clone_to_uninit(&self, dst: *mut Self) { // SAFETY: Path is just a wrapper around OsStr - unsafe { self.inner.clone_to_uninit(core::ptr::addr_of_mut!((*dst).inner)) } + unsafe { self.inner.clone_to_uninit(&raw mut (*dst).inner) } } } diff --git a/library/std/src/random.rs b/library/std/src/random.rs new file mode 100644 index 000000000000..604fa4df1106 --- /dev/null +++ b/library/std/src/random.rs @@ -0,0 +1,105 @@ +//! Random value generation. +//! +//! The [`Random`] trait allows generating a random value for a type using a +//! given [`RandomSource`]. + +#[unstable(feature = "random", issue = "130703")] +pub use core::random::*; + +use crate::sys::random as sys; + +/// The default random source. +/// +/// This asks the system for random data suitable for cryptographic purposes +/// such as key generation. If security is a concern, consult the platform +/// documentation below for the specific guarantees your target provides. +/// +/// The high quality of randomness provided by this source means it can be quite +/// slow on some targets. If you need a large quantity of random numbers and +/// security is not a concern, consider using an alternative random number +/// generator (potentially seeded from this one). +/// +/// # Underlying sources +/// +/// Platform | Source +/// -----------------------|--------------------------------------------------------------- +/// Linux | [`getrandom`] or [`/dev/urandom`] after polling `/dev/random` +/// Windows | [`ProcessPrng`](https://learn.microsoft.com/en-us/windows/win32/seccng/processprng) +/// Apple | `CCRandomGenerateBytes` +/// DragonFly | [`arc4random_buf`](https://man.dragonflybsd.org/?command=arc4random) +/// ESP-IDF | [`esp_fill_random`](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/random.html#_CPPv415esp_fill_randomPv6size_t) +/// FreeBSD | [`arc4random_buf`](https://man.freebsd.org/cgi/man.cgi?query=arc4random) +/// Fuchsia | [`cprng_draw`](https://fuchsia.dev/reference/syscalls/cprng_draw) +/// Haiku | `arc4random_buf` +/// Illumos | [`arc4random_buf`](https://www.illumos.org/man/3C/arc4random) +/// NetBSD | [`arc4random_buf`](https://man.netbsd.org/arc4random.3) +/// OpenBSD | [`arc4random_buf`](https://man.openbsd.org/arc4random.3) +/// Solaris | [`arc4random_buf`](https://docs.oracle.com/cd/E88353_01/html/E37843/arc4random-3c.html) +/// Vita | `arc4random_buf` +/// Hermit | `read_entropy` +/// Horizon | `getrandom` shim +/// Hurd, L4Re, QNX | `/dev/urandom` +/// Redox | `/scheme/rand` +/// SGX | [`rdrand`](https://en.wikipedia.org/wiki/RDRAND) +/// SOLID | `SOLID_RNG_SampleRandomBytes` +/// TEEOS | `TEE_GenerateRandom` +/// UEFI | [`EFI_RNG_PROTOCOL`](https://uefi.org/specs/UEFI/2.10/37_Secure_Technologies.html#random-number-generator-protocol) +/// VxWorks | `randABytes` after waiting for `randSecure` to become ready +/// WASI | [`random_get`](https://github.com/WebAssembly/WASI/blob/main/legacy/preview1/docs.md#-random_getbuf-pointeru8-buf_len-size---result-errno) +/// ZKVM | `sys_rand` +/// +/// Note that the sources used might change over time. +/// +/// Consult the documentation for the underlying operations on your supported +/// targets to determine whether they provide any particular desired properties, +/// such as support for reseeding on VM fork operations. +/// +/// [`getrandom`]: https://www.man7.org/linux/man-pages/man2/getrandom.2.html +/// [`/dev/urandom`]: https://www.man7.org/linux/man-pages/man4/random.4.html +#[derive(Default, Debug, Clone, Copy)] +#[unstable(feature = "random", issue = "130703")] +pub struct DefaultRandomSource; + +#[unstable(feature = "random", issue = "130703")] +impl RandomSource for DefaultRandomSource { + fn fill_bytes(&mut self, bytes: &mut [u8]) { + sys::fill_bytes(bytes) + } +} + +/// Generates a random value with the default random source. +/// +/// This is a convenience function for `T::random(&mut DefaultRandomSource)` and +/// will sample according to the same distribution as the underlying [`Random`] +/// trait implementation. See [`DefaultRandomSource`] for more information about +/// how randomness is sourced. +/// +/// **Warning:** Be careful when manipulating random values! The +/// [`random`](Random::random) method on integers samples them with a uniform +/// distribution, so a value of 1 is just as likely as [`i32::MAX`]. By using +/// modulo operations, some of the resulting values can become more likely than +/// others. Use audited crates when in doubt. +/// +/// # Examples +/// +/// Generating a [version 4/variant 1 UUID] represented as text: +/// ``` +/// #![feature(random)] +/// +/// use std::random::random; +/// +/// let bits: u128 = random(); +/// let g1 = (bits >> 96) as u32; +/// let g2 = (bits >> 80) as u16; +/// let g3 = (0x4000 | (bits >> 64) & 0x0fff) as u16; +/// let g4 = (0x8000 | (bits >> 48) & 0x3fff) as u16; +/// let g5 = (bits & 0xffffffffffff) as u64; +/// let uuid = format!("{g1:08x}-{g2:04x}-{g3:04x}-{g4:04x}-{g5:012x}"); +/// println!("{uuid}"); +/// ``` +/// +/// [version 4/variant 1 UUID]: https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_4_(random) +#[unstable(feature = "random", issue = "130703")] +pub fn random() -> T { + T::random(&mut DefaultRandomSource) +} diff --git a/library/std/src/sync/mpmc/zero.rs b/library/std/src/sync/mpmc/zero.rs index 2451d7b79d19..446881291e68 100644 --- a/library/std/src/sync/mpmc/zero.rs +++ b/library/std/src/sync/mpmc/zero.rs @@ -185,11 +185,7 @@ impl Channel { // Prepare for blocking until a receiver wakes us up. let oper = Operation::hook(token); let mut packet = Packet::::message_on_stack(msg); - inner.senders.register_with_packet( - oper, - core::ptr::addr_of_mut!(packet) as *mut (), - cx, - ); + inner.senders.register_with_packet(oper, (&raw mut packet) as *mut (), cx); inner.receivers.notify(); drop(inner); @@ -256,11 +252,7 @@ impl Channel { // Prepare for blocking until a sender wakes us up. let oper = Operation::hook(token); let mut packet = Packet::::empty_on_stack(); - inner.receivers.register_with_packet( - oper, - core::ptr::addr_of_mut!(packet) as *mut (), - cx, - ); + inner.receivers.register_with_packet(oper, (&raw mut packet) as *mut (), cx); inner.senders.notify(); drop(inner); diff --git a/library/std/src/sys/alloc/unix.rs b/library/std/src/sys/alloc/unix.rs index 266b69cdc1e3..1af9d7662901 100644 --- a/library/std/src/sys/alloc/unix.rs +++ b/library/std/src/sys/alloc/unix.rs @@ -71,6 +71,7 @@ cfg_if::cfg_if! { } } else { #[inline] + #[cfg_attr(target_os = "vxworks", allow(unused_unsafe))] unsafe fn aligned_malloc(layout: &Layout) -> *mut u8 { let mut out = ptr::null_mut(); // We prefer posix_memalign over aligned_alloc since it is more widely available, and diff --git a/library/std/src/sys/dbg.rs b/library/std/src/sys/dbg.rs index 383ed84cfa25..7266a739e785 100644 --- a/library/std/src/sys/dbg.rs +++ b/library/std/src/sys/dbg.rs @@ -105,84 +105,7 @@ mod os { } } -#[cfg(target_os = "linux")] -mod os { - use super::DebuggerPresence; - use crate::fs::File; - use crate::io::Read; - - pub(super) fn is_debugger_present() -> Option { - // This function is crafted with the following goals: - // * Memory efficiency: It avoids crashing the panicking process due to - // out-of-memory (OOM) conditions by not using large heap buffers or - // allocating significant stack space, which could lead to stack overflow. - // * Minimal binary size: The function uses a minimal set of facilities - // from the standard library to avoid increasing the resulting binary size. - // - // To achieve these goals, the function does not use `[std::io::BufReader]` - // and instead reads the file byte by byte using a sliding window approach. - // It's important to note that the "/proc/self/status" pseudo-file is synthesized - // by the Virtual File System (VFS), meaning it is not read from a slow or - // non-volatile storage medium so buffering might not be as beneficial because - // all data is read from memory, though this approach does incur a syscall for - // each byte read. - // - // We cannot make assumptions about the file size or the position of the - // target prefix ("TracerPid:"), so the function does not use - // `[std::fs::read_to_string]` thus not employing UTF-8 to ASCII checking, - // conversion, or parsing as we're looking for an ASCII prefix. - // - // These condiderations make the function deviate from the familiar concise pattern - // of searching for a string in a text file. - - fn read_byte(file: &mut File) -> Option { - let mut buffer = [0]; - file.read_exact(&mut buffer).ok()?; - Some(buffer[0]) - } - - // The ASCII prefix of the datum we're interested in. - const TRACER_PID: &[u8] = b"TracerPid:\t"; - - let mut file = File::open("/proc/self/status").ok()?; - let mut matched = 0; - - // Look for the `TRACER_PID` prefix. - while let Some(byte) = read_byte(&mut file) { - if byte == TRACER_PID[matched] { - matched += 1; - if matched == TRACER_PID.len() { - break; - } - } else { - matched = 0; - } - } - - // Was the prefix found? - if matched != TRACER_PID.len() { - return None; - } - - // It was; get the ASCII representation of the first digit - // of the PID. That is enough to see if there is a debugger - // attached as the kernel does not pad the PID on the left - // with the leading zeroes. - let byte = read_byte(&mut file)?; - if byte.is_ascii_digit() && byte != b'0' { - Some(DebuggerPresence::Detected) - } else { - Some(DebuggerPresence::NotDetected) - } - } -} - -#[cfg(not(any( - target_os = "windows", - target_vendor = "apple", - target_os = "freebsd", - target_os = "linux" -)))] +#[cfg(not(any(target_os = "windows", target_vendor = "apple", target_os = "freebsd")))] mod os { pub(super) fn is_debugger_present() -> Option { None diff --git a/library/std/src/sys/mod.rs b/library/std/src/sys/mod.rs index 96d6f2c87c4c..df25b84fbbe5 100644 --- a/library/std/src/sys/mod.rs +++ b/library/std/src/sys/mod.rs @@ -15,6 +15,7 @@ pub mod dbg; pub mod exit_guard; pub mod os_str; pub mod path; +pub mod random; pub mod sync; pub mod thread_local; diff --git a/library/std/src/sys/os_str/bytes.rs b/library/std/src/sys/os_str/bytes.rs index 992767211d08..8e0609fe48c5 100644 --- a/library/std/src/sys/os_str/bytes.rs +++ b/library/std/src/sys/os_str/bytes.rs @@ -2,7 +2,6 @@ //! systems: just a `Vec`/`[u8]`. use core::clone::CloneToUninit; -use core::ptr::addr_of_mut; use crate::borrow::Cow; use crate::collections::TryReserveError; @@ -355,6 +354,6 @@ unsafe impl CloneToUninit for Slice { #[cfg_attr(debug_assertions, track_caller)] unsafe fn clone_to_uninit(&self, dst: *mut Self) { // SAFETY: we're just a wrapper around [u8] - unsafe { self.inner.clone_to_uninit(addr_of_mut!((*dst).inner)) } + unsafe { self.inner.clone_to_uninit(&raw mut (*dst).inner) } } } diff --git a/library/std/src/sys/os_str/wtf8.rs b/library/std/src/sys/os_str/wtf8.rs index 6fbbec7a9458..b3834388df68 100644 --- a/library/std/src/sys/os_str/wtf8.rs +++ b/library/std/src/sys/os_str/wtf8.rs @@ -1,7 +1,6 @@ //! The underlying OsString/OsStr implementation on Windows is a //! wrapper around the "WTF-8" encoding; see the `wtf8` module for more. use core::clone::CloneToUninit; -use core::ptr::addr_of_mut; use crate::borrow::Cow; use crate::collections::TryReserveError; @@ -278,6 +277,6 @@ unsafe impl CloneToUninit for Slice { #[cfg_attr(debug_assertions, track_caller)] unsafe fn clone_to_uninit(&self, dst: *mut Self) { // SAFETY: we're just a wrapper around Wtf8 - unsafe { self.inner.clone_to_uninit(addr_of_mut!((*dst).inner)) } + unsafe { self.inner.clone_to_uninit(&raw mut (*dst).inner) } } } diff --git a/library/std/src/sys/pal/hermit/mod.rs b/library/std/src/sys/pal/hermit/mod.rs index 1f2e5d9469f5..f49ef9471749 100644 --- a/library/std/src/sys/pal/hermit/mod.rs +++ b/library/std/src/sys/pal/hermit/mod.rs @@ -52,20 +52,6 @@ pub fn abort_internal() -> ! { unsafe { hermit_abi::abort() } } -pub fn hashmap_random_keys() -> (u64, u64) { - let mut buf = [0; 16]; - let mut slice = &mut buf[..]; - while !slice.is_empty() { - let res = cvt(unsafe { hermit_abi::read_entropy(slice.as_mut_ptr(), slice.len(), 0) }) - .expect("failed to generate random hashmap keys"); - slice = &mut slice[res as usize..]; - } - - let key1 = buf[..8].try_into().unwrap(); - let key2 = buf[8..].try_into().unwrap(); - (u64::from_ne_bytes(key1), u64::from_ne_bytes(key2)) -} - // This function is needed by the panic runtime. The symbol is named in // pre-link args for the target specification, so keep that in sync. #[cfg(not(test))] diff --git a/library/std/src/sys/pal/hermit/net.rs b/library/std/src/sys/pal/hermit/net.rs index 416469c00373..d9baa091a232 100644 --- a/library/std/src/sys/pal/hermit/net.rs +++ b/library/std/src/sys/pal/hermit/net.rs @@ -192,7 +192,7 @@ impl Socket { buf.as_mut_ptr(), buf.len(), flags, - core::ptr::addr_of_mut!(storage) as *mut _, + (&raw mut storage) as *mut _, &mut addrlen, ) })?; @@ -298,7 +298,7 @@ impl Socket { netc::ioctl( self.as_raw_fd(), netc::FIONBIO, - core::ptr::addr_of_mut!(nonblocking) as *mut core::ffi::c_void, + (&raw mut nonblocking) as *mut core::ffi::c_void, ) }) .map(drop) diff --git a/library/std/src/sys/pal/hermit/time.rs b/library/std/src/sys/pal/hermit/time.rs index 99166b15602a..e0b6eb76b03a 100644 --- a/library/std/src/sys/pal/hermit/time.rs +++ b/library/std/src/sys/pal/hermit/time.rs @@ -107,8 +107,7 @@ pub struct Instant(Timespec); impl Instant { pub fn now() -> Instant { let mut time: Timespec = Timespec::zero(); - let _ = - unsafe { hermit_abi::clock_gettime(CLOCK_MONOTONIC, core::ptr::addr_of_mut!(time.t)) }; + let _ = unsafe { hermit_abi::clock_gettime(CLOCK_MONOTONIC, &raw mut time.t) }; Instant(time) } @@ -209,8 +208,7 @@ impl SystemTime { pub fn now() -> SystemTime { let mut time: Timespec = Timespec::zero(); - let _ = - unsafe { hermit_abi::clock_gettime(CLOCK_REALTIME, core::ptr::addr_of_mut!(time.t)) }; + let _ = unsafe { hermit_abi::clock_gettime(CLOCK_REALTIME, &raw mut time.t) }; SystemTime(time) } diff --git a/library/std/src/sys/pal/sgx/abi/usercalls/mod.rs b/library/std/src/sys/pal/sgx/abi/usercalls/mod.rs index def1ccdf81ac..90b9b59471a5 100644 --- a/library/std/src/sys/pal/sgx/abi/usercalls/mod.rs +++ b/library/std/src/sys/pal/sgx/abi/usercalls/mod.rs @@ -1,6 +1,6 @@ use crate::cmp; use crate::io::{Error as IoError, ErrorKind, IoSlice, IoSliceMut, Result as IoResult}; -use crate::sys::rand::rdrand64; +use crate::random::{DefaultRandomSource, Random}; use crate::time::{Duration, Instant}; pub(crate) mod alloc; @@ -164,7 +164,7 @@ pub fn wait(event_mask: u64, mut timeout: u64) -> IoResult { // trusted to ensure accurate timeouts. if let Ok(timeout_signed) = i64::try_from(timeout) { let tenth = timeout_signed / 10; - let deviation = (rdrand64() as i64).checked_rem(tenth).unwrap_or(0); + let deviation = i64::random(&mut DefaultRandomSource).checked_rem(tenth).unwrap_or(0); timeout = timeout_signed.saturating_add(deviation) as _; } } diff --git a/library/std/src/sys/pal/sgx/mod.rs b/library/std/src/sys/pal/sgx/mod.rs index 8d29b2ec6193..586ccd18c2f5 100644 --- a/library/std/src/sys/pal/sgx/mod.rs +++ b/library/std/src/sys/pal/sgx/mod.rs @@ -132,24 +132,6 @@ pub extern "C" fn __rust_abort() { abort_internal(); } -pub mod rand { - pub fn rdrand64() -> u64 { - unsafe { - let mut ret: u64 = 0; - for _ in 0..10 { - if crate::arch::x86_64::_rdrand64_step(&mut ret) == 1 { - return ret; - } - } - rtabort!("Failed to obtain random data"); - } - } -} - -pub fn hashmap_random_keys() -> (u64, u64) { - (self::rand::rdrand64(), self::rand::rdrand64()) -} - pub use crate::sys_common::{AsInner, FromInner, IntoInner}; pub trait TryIntoInner: Sized { diff --git a/library/std/src/sys/pal/solid/mod.rs b/library/std/src/sys/pal/solid/mod.rs index 6ebcf5b7c48c..d41042be5184 100644 --- a/library/std/src/sys/pal/solid/mod.rs +++ b/library/std/src/sys/pal/solid/mod.rs @@ -62,13 +62,3 @@ pub fn decode_error_kind(code: i32) -> crate::io::ErrorKind { pub fn abort_internal() -> ! { unsafe { libc::abort() } } - -pub fn hashmap_random_keys() -> (u64, u64) { - unsafe { - let mut out = crate::mem::MaybeUninit::<[u64; 2]>::uninit(); - let result = abi::SOLID_RNG_SampleRandomBytes(out.as_mut_ptr() as *mut u8, 16); - assert_eq!(result, 0, "SOLID_RNG_SampleRandomBytes failed: {result}"); - let [x1, x2] = out.assume_init(); - (x1, x2) - } -} diff --git a/library/std/src/sys/pal/teeos/mod.rs b/library/std/src/sys/pal/teeos/mod.rs index 00e386042400..60a227afb84e 100644 --- a/library/std/src/sys/pal/teeos/mod.rs +++ b/library/std/src/sys/pal/teeos/mod.rs @@ -6,8 +6,6 @@ #![allow(unused_variables)] #![allow(dead_code)] -pub use self::rand::hashmap_random_keys; - #[path = "../unsupported/args.rs"] pub mod args; #[path = "../unsupported/env.rs"] @@ -23,7 +21,6 @@ pub mod os; pub mod pipe; #[path = "../unsupported/process.rs"] pub mod process; -mod rand; pub mod stdio; pub mod thread; #[allow(non_upper_case_globals)] diff --git a/library/std/src/sys/pal/teeos/rand.rs b/library/std/src/sys/pal/teeos/rand.rs deleted file mode 100644 index b45c3bb40e78..000000000000 --- a/library/std/src/sys/pal/teeos/rand.rs +++ /dev/null @@ -1,21 +0,0 @@ -pub fn hashmap_random_keys() -> (u64, u64) { - const KEY_LEN: usize = core::mem::size_of::(); - - let mut v = [0u8; KEY_LEN * 2]; - imp::fill_bytes(&mut v); - - let key1 = v[0..KEY_LEN].try_into().unwrap(); - let key2 = v[KEY_LEN..].try_into().unwrap(); - - (u64::from_ne_bytes(key1), u64::from_ne_bytes(key2)) -} - -mod imp { - extern "C" { - fn TEE_GenerateRandom(randomBuffer: *mut core::ffi::c_void, randomBufferLen: libc::size_t); - } - - pub fn fill_bytes(v: &mut [u8]) { - unsafe { TEE_GenerateRandom(v.as_mut_ptr() as _, v.len() * crate::mem::size_of::()) } - } -} diff --git a/library/std/src/sys/pal/uefi/mod.rs b/library/std/src/sys/pal/uefi/mod.rs index ac22f4ded885..c0ab52f650aa 100644 --- a/library/std/src/sys/pal/uefi/mod.rs +++ b/library/std/src/sys/pal/uefi/mod.rs @@ -179,39 +179,6 @@ pub extern "C" fn __rust_abort() { abort_internal(); } -#[inline] -pub fn hashmap_random_keys() -> (u64, u64) { - get_random().unwrap() -} - -fn get_random() -> Option<(u64, u64)> { - use r_efi::protocols::rng; - - let mut buf = [0u8; 16]; - let handles = helpers::locate_handles(rng::PROTOCOL_GUID).ok()?; - for handle in handles { - if let Ok(protocol) = helpers::open_protocol::(handle, rng::PROTOCOL_GUID) { - let r = unsafe { - ((*protocol.as_ptr()).get_rng)( - protocol.as_ptr(), - crate::ptr::null_mut(), - buf.len(), - buf.as_mut_ptr(), - ) - }; - if r.is_error() { - continue; - } else { - return Some(( - u64::from_le_bytes(buf[..8].try_into().ok()?), - u64::from_le_bytes(buf[8..].try_into().ok()?), - )); - } - } - } - None -} - /// Disable access to BootServices if `EVT_SIGNAL_EXIT_BOOT_SERVICES` is signaled extern "efiapi" fn exit_boot_service_handler(_e: r_efi::efi::Event, _ctx: *mut crate::ffi::c_void) { uefi::env::disable_boot_services(); diff --git a/library/std/src/sys/pal/unix/args.rs b/library/std/src/sys/pal/unix/args.rs index a943e3a581a8..8438a61e90fe 100644 --- a/library/std/src/sys/pal/unix/args.rs +++ b/library/std/src/sys/pal/unix/args.rs @@ -113,6 +113,7 @@ impl DoubleEndedIterator for Args { target_os = "nto", target_os = "hurd", target_os = "rtems", + target_os = "nuttx", ))] mod imp { use crate::ffi::c_char; diff --git a/library/std/src/sys/pal/unix/env.rs b/library/std/src/sys/pal/unix/env.rs index b2d399b8791b..2aee0b5d4605 100644 --- a/library/std/src/sys/pal/unix/env.rs +++ b/library/std/src/sys/pal/unix/env.rs @@ -283,3 +283,14 @@ pub mod os { pub const EXE_SUFFIX: &str = ""; pub const EXE_EXTENSION: &str = ""; } + +#[cfg(target_os = "nuttx")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "nuttx"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} diff --git a/library/std/src/sys/pal/unix/fd.rs b/library/std/src/sys/pal/unix/fd.rs index 135042779ad8..6a28799ca55e 100644 --- a/library/std/src/sys/pal/unix/fd.rs +++ b/library/std/src/sys/pal/unix/fd.rs @@ -98,7 +98,12 @@ impl FileDesc { Ok(ret as usize) } - #[cfg(not(any(target_os = "espidf", target_os = "horizon", target_os = "vita")))] + #[cfg(not(any( + target_os = "espidf", + target_os = "horizon", + target_os = "vita", + target_os = "nuttx" + )))] pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { let ret = cvt(unsafe { libc::readv( @@ -110,14 +115,24 @@ impl FileDesc { Ok(ret as usize) } - #[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "vita"))] + #[cfg(any( + target_os = "espidf", + target_os = "horizon", + target_os = "vita", + target_os = "nuttx" + ))] pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { io::default_read_vectored(|b| self.read(b), bufs) } #[inline] pub fn is_read_vectored(&self) -> bool { - cfg!(not(any(target_os = "espidf", target_os = "horizon", target_os = "vita"))) + cfg!(not(any( + target_os = "espidf", + target_os = "horizon", + target_os = "vita", + target_os = "nuttx" + ))) } pub fn read_to_end(&self, buf: &mut Vec) -> io::Result { @@ -297,7 +312,12 @@ impl FileDesc { Ok(ret as usize) } - #[cfg(not(any(target_os = "espidf", target_os = "horizon", target_os = "vita")))] + #[cfg(not(any( + target_os = "espidf", + target_os = "horizon", + target_os = "vita", + target_os = "nuttx" + )))] pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { let ret = cvt(unsafe { libc::writev( @@ -309,14 +329,24 @@ impl FileDesc { Ok(ret as usize) } - #[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "vita"))] + #[cfg(any( + target_os = "espidf", + target_os = "horizon", + target_os = "vita", + target_os = "nuttx" + ))] pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { io::default_write_vectored(|b| self.write(b), bufs) } #[inline] pub fn is_write_vectored(&self) -> bool { - cfg!(not(any(target_os = "espidf", target_os = "horizon", target_os = "vita"))) + cfg!(not(any( + target_os = "espidf", + target_os = "horizon", + target_os = "vita", + target_os = "nuttx" + ))) } #[cfg_attr(target_os = "vxworks", allow(unused_unsafe))] diff --git a/library/std/src/sys/pal/unix/fs.rs b/library/std/src/sys/pal/unix/fs.rs index dade6d6bbaee..39aabf0b2d67 100644 --- a/library/std/src/sys/pal/unix/fs.rs +++ b/library/std/src/sys/pal/unix/fs.rs @@ -479,6 +479,7 @@ impl FileAttr { target_os = "vita", target_os = "hurd", target_os = "rtems", + target_os = "nuttx", )))] pub fn modified(&self) -> io::Result { #[cfg(target_pointer_width = "32")] @@ -501,7 +502,7 @@ impl FileAttr { SystemTime::new(self.stat.st_mtime as i64, 0) } - #[cfg(any(target_os = "horizon", target_os = "hurd"))] + #[cfg(any(target_os = "horizon", target_os = "hurd", target_os = "nuttx"))] pub fn modified(&self) -> io::Result { SystemTime::new(self.stat.st_mtim.tv_sec as i64, self.stat.st_mtim.tv_nsec as i64) } @@ -513,6 +514,7 @@ impl FileAttr { target_os = "vita", target_os = "hurd", target_os = "rtems", + target_os = "nuttx", )))] pub fn accessed(&self) -> io::Result { #[cfg(target_pointer_width = "32")] @@ -535,7 +537,7 @@ impl FileAttr { SystemTime::new(self.stat.st_atime as i64, 0) } - #[cfg(any(target_os = "horizon", target_os = "hurd"))] + #[cfg(any(target_os = "horizon", target_os = "hurd", target_os = "nuttx"))] pub fn accessed(&self) -> io::Result { SystemTime::new(self.stat.st_atim.tv_sec as i64, self.stat.st_atim.tv_nsec as i64) } @@ -738,7 +740,7 @@ impl Iterator for ReadDir { // // Like for uninitialized contents, converting entry_ptr to `&dirent64` // would not be legal. However, unique to dirent64 is that we don't even - // get to use `addr_of!((*entry_ptr).d_name)` because that operation + // get to use `&raw const (*entry_ptr).d_name` because that operation // requires the full extent of *entry_ptr to be in bounds of the same // allocation, which is not necessarily the case here. // @@ -752,7 +754,7 @@ impl Iterator for ReadDir { } else { #[allow(deref_nullptr)] { - ptr::addr_of!((*ptr::null::()).$field) + &raw const (*ptr::null::()).$field } } }}; @@ -866,6 +868,7 @@ impl Drop for Dir { target_os = "horizon", target_os = "vxworks", target_os = "rtems", + target_os = "nuttx", )))] { let fd = unsafe { libc::dirfd(self.0) }; @@ -1000,6 +1003,13 @@ impl DirEntry { self.entry.d_fileno as u64 } + #[cfg(target_os = "nuttx")] + pub fn ino(&self) -> u64 { + // Leave this 0 for now, as NuttX does not provide an inode number + // in its directory entries. + 0 + } + #[cfg(any( target_os = "netbsd", target_os = "openbsd", @@ -1327,7 +1337,8 @@ impl File { target_os = "redox", target_os = "espidf", target_os = "horizon", - target_os = "vxworks" + target_os = "vxworks", + target_os = "nuttx", )))] let to_timespec = |time: Option| match time { Some(time) if let Some(ts) = time.t.to_timespec() => Ok(ts), @@ -1342,7 +1353,7 @@ impl File { None => Ok(libc::timespec { tv_sec: 0, tv_nsec: libc::UTIME_OMIT as _ }), }; cfg_if::cfg_if! { - if #[cfg(any(target_os = "redox", target_os = "espidf", target_os = "horizon", target_os = "vxworks"))] { + if #[cfg(any(target_os = "redox", target_os = "espidf", target_os = "horizon", target_os = "vxworks", target_os = "nuttx"))] { // Redox doesn't appear to support `UTIME_OMIT`. // ESP-IDF and HorizonOS do not support `futimens` at all and the behavior for those OS is therefore // the same as for Redox. @@ -1374,7 +1385,7 @@ impl File { } cvt(unsafe { libc::fsetattrlist( self.as_raw_fd(), - core::ptr::addr_of!(attrlist).cast::().cast_mut(), + (&raw const attrlist).cast::().cast_mut(), buf.as_ptr().cast::().cast_mut(), num_times * mem::size_of::(), 0 @@ -1933,7 +1944,7 @@ pub fn copy(from: &Path, to: &Path) -> io::Result { libc::copyfile_state_get( state.0, libc::COPYFILE_STATE_COPIED as u32, - core::ptr::addr_of_mut!(bytes_copied) as *mut libc::c_void, + (&raw mut bytes_copied) as *mut libc::c_void, ) })?; Ok(bytes_copied as u64) diff --git a/library/std/src/sys/pal/unix/mod.rs b/library/std/src/sys/pal/unix/mod.rs index e8428eccb169..4fe18daa2040 100644 --- a/library/std/src/sys/pal/unix/mod.rs +++ b/library/std/src/sys/pal/unix/mod.rs @@ -1,6 +1,5 @@ #![allow(missing_docs, nonstandard_style)] -pub use self::rand::hashmap_random_keys; use crate::io::ErrorKind; #[cfg(not(target_os = "espidf"))] @@ -26,7 +25,6 @@ pub use self::l4re::net; pub mod os; pub mod pipe; pub mod process; -pub mod rand; pub mod stack_overflow; pub mod stdio; pub mod thread; @@ -224,6 +222,7 @@ static ON_BROKEN_PIPE_FLAG_USED: crate::sync::atomic::AtomicBool = target_os = "horizon", target_os = "vxworks", target_os = "vita", + target_os = "nuttx", )))] pub(crate) fn on_broken_pipe_flag_used() -> bool { ON_BROKEN_PIPE_FLAG_USED.load(crate::sync::atomic::Ordering::Relaxed) @@ -281,6 +280,7 @@ pub fn decode_error_kind(errno: i32) -> ErrorKind { libc::ETIMEDOUT => TimedOut, libc::ETXTBSY => ExecutableFileBusy, libc::EXDEV => CrossesDevices, + libc::EINPROGRESS => InProgress, libc::EACCES | libc::EPERM => PermissionDenied, @@ -426,7 +426,7 @@ cfg_if::cfg_if! { } } -#[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "vita"))] +#[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "vita", target_os = "nuttx"))] mod unsupported { use crate::io; diff --git a/library/std/src/sys/pal/unix/net.rs b/library/std/src/sys/pal/unix/net.rs index e98232ba89ac..6a67bb0a101e 100644 --- a/library/std/src/sys/pal/unix/net.rs +++ b/library/std/src/sys/pal/unix/net.rs @@ -38,19 +38,19 @@ pub fn cvt_gai(err: c_int) -> io::Result<()> { // We may need to trigger a glibc workaround. See on_resolver_failure() for details. on_resolver_failure(); - #[cfg(not(target_os = "espidf"))] + #[cfg(not(any(target_os = "espidf", target_os = "nuttx")))] if err == libc::EAI_SYSTEM { return Err(io::Error::last_os_error()); } - #[cfg(not(target_os = "espidf"))] + #[cfg(not(any(target_os = "espidf", target_os = "nuttx")))] let detail = unsafe { // We can't always expect a UTF-8 environment. When we don't get that luxury, // it's better to give a low-quality error message than none at all. CStr::from_ptr(libc::gai_strerror(err)).to_string_lossy() }; - #[cfg(target_os = "espidf")] + #[cfg(any(target_os = "espidf", target_os = "nuttx"))] let detail = ""; Err(io::Error::new( @@ -329,7 +329,7 @@ impl Socket { buf.as_mut_ptr() as *mut c_void, buf.len(), flags, - core::ptr::addr_of_mut!(storage) as *mut _, + (&raw mut storage) as *mut _, &mut addrlen, ) })?; diff --git a/library/std/src/sys/pal/unix/os.rs b/library/std/src/sys/pal/unix/os.rs index 503f8915256e..f983d174ed61 100644 --- a/library/std/src/sys/pal/unix/os.rs +++ b/library/std/src/sys/pal/unix/os.rs @@ -48,6 +48,7 @@ extern "C" { target_os = "openbsd", target_os = "android", target_os = "redox", + target_os = "nuttx", target_env = "newlib" ), link_name = "__errno" @@ -399,6 +400,7 @@ pub fn current_exe() -> io::Result { target_os = "linux", target_os = "hurd", target_os = "android", + target_os = "nuttx", target_os = "emscripten" ))] pub fn current_exe() -> io::Result { @@ -610,7 +612,7 @@ pub unsafe fn environ() -> *mut *const *const c_char { extern "C" { static mut environ: *const *const c_char; } - ptr::addr_of_mut!(environ) + &raw mut environ } static ENV_LOCK: RwLock<()> = RwLock::new(()); @@ -717,6 +719,7 @@ pub fn home_dir() -> Option { target_os = "espidf", target_os = "horizon", target_os = "vita", + target_os = "nuttx", all(target_vendor = "apple", not(target_os = "macos")), ))] unsafe fn fallback() -> Option { @@ -730,6 +733,7 @@ pub fn home_dir() -> Option { target_os = "espidf", target_os = "horizon", target_os = "vita", + target_os = "nuttx", all(target_vendor = "apple", not(target_os = "macos")), )))] unsafe fn fallback() -> Option { diff --git a/library/std/src/sys/pal/unix/process/mod.rs b/library/std/src/sys/pal/unix/process/mod.rs index 074f0a105e32..2751d51c44d2 100644 --- a/library/std/src/sys/pal/unix/process/mod.rs +++ b/library/std/src/sys/pal/unix/process/mod.rs @@ -2,10 +2,10 @@ pub use self::process_common::{Command, CommandArgs, ExitCode, Stdio, StdioPipes pub use self::process_inner::{ExitStatus, ExitStatusError, Process}; pub use crate::ffi::OsString as EnvKey; -#[cfg_attr(any(target_os = "espidf", target_os = "horizon"), allow(unused))] +#[cfg_attr(any(target_os = "espidf", target_os = "horizon", target_os = "nuttx"), allow(unused))] mod process_common; -#[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "vita"))] +#[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "vita", target_os = "nuttx"))] mod process_unsupported; cfg_if::cfg_if! { @@ -16,7 +16,7 @@ cfg_if::cfg_if! { } else if #[cfg(target_os = "vxworks")] { #[path = "process_vxworks.rs"] mod process_inner; - } else if #[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "vita"))] { + } else if #[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "vita", target_os = "nuttx"))] { mod process_inner { pub use super::process_unsupported::*; } diff --git a/library/std/src/sys/pal/unix/process/process_fuchsia.rs b/library/std/src/sys/pal/unix/process/process_fuchsia.rs index 34ff464aa37f..5d0110cf55dc 100644 --- a/library/std/src/sys/pal/unix/process/process_fuchsia.rs +++ b/library/std/src/sys/pal/unix/process/process_fuchsia.rs @@ -178,7 +178,7 @@ impl Process { zx_cvt(zx_object_get_info( self.handle.raw(), ZX_INFO_PROCESS, - core::ptr::addr_of_mut!(proc_info) as *mut libc::c_void, + (&raw mut proc_info) as *mut libc::c_void, mem::size_of::(), &mut actual, &mut avail, @@ -215,7 +215,7 @@ impl Process { zx_cvt(zx_object_get_info( self.handle.raw(), ZX_INFO_PROCESS, - core::ptr::addr_of_mut!(proc_info) as *mut libc::c_void, + (&raw mut proc_info) as *mut libc::c_void, mem::size_of::(), &mut actual, &mut avail, diff --git a/library/std/src/sys/pal/unix/process/process_unix.rs b/library/std/src/sys/pal/unix/process/process_unix.rs index d812aa0e02cf..5d30f388da18 100644 --- a/library/std/src/sys/pal/unix/process/process_unix.rs +++ b/library/std/src/sys/pal/unix/process/process_unix.rs @@ -788,15 +788,15 @@ impl Command { let mut iov = [IoSlice::new(b"")]; let mut msg: libc::msghdr = mem::zeroed(); - msg.msg_iov = core::ptr::addr_of_mut!(iov) as *mut _; + msg.msg_iov = (&raw mut iov) as *mut _; msg.msg_iovlen = 1; // only attach cmsg if we successfully acquired the pidfd if pidfd >= 0 { msg.msg_controllen = mem::size_of_val(&cmsg.buf) as _; - msg.msg_control = core::ptr::addr_of_mut!(cmsg.buf) as *mut _; + msg.msg_control = (&raw mut cmsg.buf) as *mut _; - let hdr = CMSG_FIRSTHDR(core::ptr::addr_of_mut!(msg) as *mut _); + let hdr = CMSG_FIRSTHDR((&raw mut msg) as *mut _); (*hdr).cmsg_level = SOL_SOCKET; (*hdr).cmsg_type = SCM_RIGHTS; (*hdr).cmsg_len = CMSG_LEN(SCM_MSG_LEN as _) as _; @@ -838,17 +838,17 @@ impl Command { let mut msg: libc::msghdr = mem::zeroed(); - msg.msg_iov = core::ptr::addr_of_mut!(iov) as *mut _; + msg.msg_iov = (&raw mut iov) as *mut _; msg.msg_iovlen = 1; msg.msg_controllen = mem::size_of::() as _; - msg.msg_control = core::ptr::addr_of_mut!(cmsg) as *mut _; + msg.msg_control = (&raw mut cmsg) as *mut _; match cvt_r(|| libc::recvmsg(sock.as_raw(), &mut msg, libc::MSG_CMSG_CLOEXEC)) { Err(_) => return -1, Ok(_) => {} } - let hdr = CMSG_FIRSTHDR(core::ptr::addr_of_mut!(msg) as *mut _); + let hdr = CMSG_FIRSTHDR((&raw mut msg) as *mut _); if hdr.is_null() || (*hdr).cmsg_level != SOL_SOCKET || (*hdr).cmsg_type != SCM_RIGHTS diff --git a/library/std/src/sys/pal/unix/rand.rs b/library/std/src/sys/pal/unix/rand.rs deleted file mode 100644 index cc0852aab439..000000000000 --- a/library/std/src/sys/pal/unix/rand.rs +++ /dev/null @@ -1,302 +0,0 @@ -pub fn hashmap_random_keys() -> (u64, u64) { - const KEY_LEN: usize = core::mem::size_of::(); - - let mut v = [0u8; KEY_LEN * 2]; - if let Err(err) = read(&mut v) { - panic!("failed to retrieve random hash map seed: {err}"); - } - - let key1 = v[0..KEY_LEN].try_into().unwrap(); - let key2 = v[KEY_LEN..].try_into().unwrap(); - - (u64::from_ne_bytes(key1), u64::from_ne_bytes(key2)) -} - -cfg_if::cfg_if! { - if #[cfg(any( - target_vendor = "apple", - target_os = "openbsd", - target_os = "emscripten", - target_os = "vita", - all(target_os = "netbsd", not(netbsd10)), - target_os = "fuchsia", - target_os = "vxworks", - ))] { - // Some systems have a syscall that directly retrieves random data. - // If that is guaranteed to be available, use it. - use imp::syscall as read; - } else { - // Otherwise, try the syscall to see if it exists only on some systems - // and fall back to reading from the random device otherwise. - fn read(bytes: &mut [u8]) -> crate::io::Result<()> { - use crate::fs::File; - use crate::io::Read; - use crate::sync::OnceLock; - - #[cfg(any( - target_os = "linux", - target_os = "android", - target_os = "espidf", - target_os = "horizon", - target_os = "freebsd", - target_os = "dragonfly", - target_os = "solaris", - target_os = "illumos", - netbsd10, - ))] - if let Some(res) = imp::syscall(bytes) { - return res; - } - - const PATH: &'static str = if cfg!(target_os = "redox") { - "/scheme/rand" - } else { - "/dev/urandom" - }; - - static FILE: OnceLock = OnceLock::new(); - - FILE.get_or_try_init(|| File::open(PATH))?.read_exact(bytes) - } - } -} - -// All these systems a `getrandom` syscall. -// -// It is not guaranteed to be available, so return None to fallback to the file -// implementation. -#[cfg(any( - target_os = "linux", - target_os = "android", - target_os = "espidf", - target_os = "horizon", - target_os = "freebsd", - target_os = "dragonfly", - target_os = "solaris", - target_os = "illumos", - netbsd10, -))] -mod imp { - use crate::io::{Error, Result}; - use crate::sync::atomic::{AtomicBool, Ordering}; - use crate::sys::os::errno; - - #[cfg(any(target_os = "linux", target_os = "android"))] - fn getrandom(buf: &mut [u8]) -> libc::ssize_t { - use crate::sys::weak::syscall; - - // A weak symbol allows interposition, e.g. for perf measurements that want to - // disable randomness for consistency. Otherwise, we'll try a raw syscall. - // (`getrandom` was added in glibc 2.25, musl 1.1.20, android API level 28) - syscall! { - fn getrandom( - buffer: *mut libc::c_void, - length: libc::size_t, - flags: libc::c_uint - ) -> libc::ssize_t - } - - // This provides the best quality random numbers available at the given moment - // without ever blocking, and is preferable to falling back to /dev/urandom. - static GRND_INSECURE_AVAILABLE: AtomicBool = AtomicBool::new(true); - if GRND_INSECURE_AVAILABLE.load(Ordering::Relaxed) { - let ret = unsafe { getrandom(buf.as_mut_ptr().cast(), buf.len(), libc::GRND_INSECURE) }; - if ret == -1 && errno() as libc::c_int == libc::EINVAL { - GRND_INSECURE_AVAILABLE.store(false, Ordering::Relaxed); - } else { - return ret; - } - } - - unsafe { getrandom(buf.as_mut_ptr().cast(), buf.len(), libc::GRND_NONBLOCK) } - } - - #[cfg(any( - target_os = "dragonfly", - target_os = "espidf", - target_os = "horizon", - target_os = "freebsd", - netbsd10, - target_os = "illumos", - target_os = "solaris" - ))] - fn getrandom(buf: &mut [u8]) -> libc::ssize_t { - unsafe { libc::getrandom(buf.as_mut_ptr().cast(), buf.len(), 0) } - } - - pub fn syscall(v: &mut [u8]) -> Option> { - static GETRANDOM_UNAVAILABLE: AtomicBool = AtomicBool::new(false); - - if GETRANDOM_UNAVAILABLE.load(Ordering::Relaxed) { - return None; - } - - let mut read = 0; - while read < v.len() { - let result = getrandom(&mut v[read..]); - if result == -1 { - let err = errno() as libc::c_int; - if err == libc::EINTR { - continue; - } else if err == libc::ENOSYS || err == libc::EPERM { - // `getrandom` is not supported on the current system. - // - // Also fall back in case it is disabled by something like - // seccomp or inside of docker. - // - // If the `getrandom` syscall is not implemented in the current kernel version it should return an - // `ENOSYS` error. Docker also blocks the whole syscall inside unprivileged containers, and - // returns `EPERM` (instead of `ENOSYS`) when a program tries to invoke the syscall. Because of - // that we need to check for *both* `ENOSYS` and `EPERM`. - // - // Note that Docker's behavior is breaking other projects (notably glibc), so they're planning - // to update their filtering to return `ENOSYS` in a future release: - // - // https://github.com/moby/moby/issues/42680 - // - GETRANDOM_UNAVAILABLE.store(true, Ordering::Relaxed); - return None; - } else if err == libc::EAGAIN { - // getrandom has failed because it would have blocked as the - // non-blocking pool (urandom) has not been initialized in - // the kernel yet due to a lack of entropy. Fallback to - // reading from `/dev/urandom` which will return potentially - // insecure random data to avoid blocking applications which - // could depend on this call without ever knowing they do and - // don't have a work around. - return None; - } else { - return Some(Err(Error::from_raw_os_error(err))); - } - } else { - read += result as usize; - } - } - - Some(Ok(())) - } -} - -#[cfg(any( - target_os = "macos", // Supported since macOS 10.12+. - target_os = "openbsd", - target_os = "emscripten", - target_os = "vita", -))] -mod imp { - use crate::io::{Error, Result}; - - pub fn syscall(v: &mut [u8]) -> Result<()> { - // getentropy(2) permits a maximum buffer size of 256 bytes - for s in v.chunks_mut(256) { - let ret = unsafe { libc::getentropy(s.as_mut_ptr().cast(), s.len()) }; - if ret == -1 { - return Err(Error::last_os_error()); - } - } - - Ok(()) - } -} - -// On Apple platforms, `CCRandomGenerateBytes` and `SecRandomCopyBytes` simply -// call into `CCRandomCopyBytes` with `kCCRandomDefault`. `CCRandomCopyBytes` -// manages a CSPRNG which is seeded from the kernel's CSPRNG and which runs on -// its own thread accessed via GCD. This seems needlessly heavyweight for our purposes -// so we only use it when `getentropy` is blocked, which appears to be the case -// on all platforms except macOS (see #102643). -// -// `CCRandomGenerateBytes` is used instead of `SecRandomCopyBytes` because the former is accessible -// via `libSystem` (libc) while the other needs to link to `Security.framework`. -#[cfg(all(target_vendor = "apple", not(target_os = "macos")))] -mod imp { - use libc::size_t; - - use crate::ffi::{c_int, c_void}; - use crate::io::{Error, Result}; - - pub fn syscall(v: &mut [u8]) -> Result<()> { - extern "C" { - fn CCRandomGenerateBytes(bytes: *mut c_void, count: size_t) -> c_int; - } - - let ret = unsafe { CCRandomGenerateBytes(v.as_mut_ptr().cast(), v.len()) }; - if ret != -1 { Ok(()) } else { Err(Error::last_os_error()) } - } -} - -// FIXME: once the 10.x release becomes the minimum, this can be dropped for simplification. -#[cfg(all(target_os = "netbsd", not(netbsd10)))] -mod imp { - use crate::io::{Error, Result}; - use crate::ptr; - - pub fn syscall(v: &mut [u8]) -> Result<()> { - let mib = [libc::CTL_KERN, libc::KERN_ARND]; - // kern.arandom permits a maximum buffer size of 256 bytes - for s in v.chunks_mut(256) { - let mut s_len = s.len(); - let ret = unsafe { - libc::sysctl( - mib.as_ptr(), - mib.len() as libc::c_uint, - s.as_mut_ptr() as *mut _, - &mut s_len, - ptr::null(), - 0, - ) - }; - if ret == -1 { - return Err(Error::last_os_error()); - } else if s_len != s.len() { - // FIXME(joboet): this can't actually happen, can it? - panic!("read less bytes than requested from kern.arandom"); - } - } - - Ok(()) - } -} - -#[cfg(target_os = "fuchsia")] -mod imp { - use crate::io::Result; - - #[link(name = "zircon")] - extern "C" { - fn zx_cprng_draw(buffer: *mut u8, len: usize); - } - - pub fn syscall(v: &mut [u8]) -> Result<()> { - unsafe { zx_cprng_draw(v.as_mut_ptr(), v.len()) }; - Ok(()) - } -} - -#[cfg(target_os = "vxworks")] -mod imp { - use core::sync::atomic::AtomicBool; - use core::sync::atomic::Ordering::Relaxed; - - use crate::io::{Error, Result}; - - pub fn syscall(v: &mut [u8]) -> Result<()> { - static RNG_INIT: AtomicBool = AtomicBool::new(false); - while !RNG_INIT.load(Relaxed) { - let ret = unsafe { libc::randSecure() }; - if ret < 0 { - return Err(Error::last_os_error()); - } else if ret > 0 { - RNG_INIT.store(true, Relaxed); - break; - } - - unsafe { libc::usleep(10) }; - } - - let ret = unsafe { - libc::randABytes(v.as_mut_ptr() as *mut libc::c_uchar, v.len() as libc::c_int) - }; - if ret >= 0 { Ok(()) } else { Err(Error::last_os_error()) } - } -} diff --git a/library/std/src/sys/pal/unix/stack_overflow.rs b/library/std/src/sys/pal/unix/stack_overflow.rs index e0a0d0973c65..ac0858e1de87 100644 --- a/library/std/src/sys/pal/unix/stack_overflow.rs +++ b/library/std/src/sys/pal/unix/stack_overflow.rs @@ -426,8 +426,8 @@ mod imp { match sysctlbyname.get() { Some(fcn) if unsafe { fcn(oid.as_ptr(), - ptr::addr_of_mut!(guard).cast(), - ptr::addr_of_mut!(size), + (&raw mut guard).cast(), + &raw mut size, ptr::null_mut(), 0) == 0 } => guard, diff --git a/library/std/src/sys/pal/unix/thread.rs b/library/std/src/sys/pal/unix/thread.rs index 7fe9b6c3e52f..2f2d6e6add39 100644 --- a/library/std/src/sys/pal/unix/thread.rs +++ b/library/std/src/sys/pal/unix/thread.rs @@ -140,7 +140,12 @@ impl Thread { } } - #[cfg(any(target_os = "freebsd", target_os = "dragonfly", target_os = "openbsd"))] + #[cfg(any( + target_os = "freebsd", + target_os = "dragonfly", + target_os = "openbsd", + target_os = "nuttx" + ))] pub fn set_name(name: &CStr) { unsafe { libc::pthread_set_name_np(libc::pthread_self(), name.as_ptr()); @@ -253,7 +258,7 @@ impl Thread { tv_nsec: nsecs, }; secs -= ts.tv_sec as u64; - let ts_ptr = core::ptr::addr_of_mut!(ts); + let ts_ptr = &raw mut ts; if libc::nanosleep(ts_ptr, ts_ptr) == -1 { assert_eq!(os::errno(), libc::EINTR); secs += ts.tv_sec as u64; @@ -442,8 +447,8 @@ pub fn available_parallelism() -> io::Result> { libc::sysctl( mib.as_mut_ptr(), 2, - core::ptr::addr_of_mut!(cpus) as *mut _, - core::ptr::addr_of_mut!(cpus_size) as *mut _, + (&raw mut cpus) as *mut _, + (&raw mut cpus_size) as *mut _, ptr::null_mut(), 0, ) @@ -517,7 +522,7 @@ mod cgroups { use crate::borrow::Cow; use crate::ffi::OsString; use crate::fs::{File, exists}; - use crate::io::{BufRead, BufReader, Read}; + use crate::io::{BufRead, Read}; use crate::os::unix::ffi::OsStringExt; use crate::path::{Path, PathBuf}; use crate::str::from_utf8; @@ -690,7 +695,7 @@ mod cgroups { /// If the cgroupfs is a bind mount then `group_path` is adjusted to skip /// over the already-included prefix fn find_mountpoint(group_path: &Path) -> Option<(Cow<'static, str>, &Path)> { - let mut reader = BufReader::new(File::open("/proc/self/mountinfo").ok()?); + let mut reader = File::open_buffered("/proc/self/mountinfo").ok()?; let mut line = String::with_capacity(256); loop { line.clear(); @@ -747,12 +752,15 @@ unsafe fn min_stack_size(attr: *const libc::pthread_attr_t) -> usize { } // No point in looking up __pthread_get_minstack() on non-glibc platforms. -#[cfg(all(not(all(target_os = "linux", target_env = "gnu")), not(target_os = "netbsd")))] +#[cfg(all( + not(all(target_os = "linux", target_env = "gnu")), + not(any(target_os = "netbsd", target_os = "nuttx")) +))] unsafe fn min_stack_size(_: *const libc::pthread_attr_t) -> usize { libc::PTHREAD_STACK_MIN } -#[cfg(target_os = "netbsd")] +#[cfg(any(target_os = "netbsd", target_os = "nuttx"))] unsafe fn min_stack_size(_: *const libc::pthread_attr_t) -> usize { static STACK: crate::sync::OnceLock = crate::sync::OnceLock::new(); diff --git a/library/std/src/sys/pal/unsupported/common.rs b/library/std/src/sys/pal/unsupported/common.rs index 76f80291f0ea..34a766683830 100644 --- a/library/std/src/sys/pal/unsupported/common.rs +++ b/library/std/src/sys/pal/unsupported/common.rs @@ -27,7 +27,3 @@ pub fn decode_error_kind(_code: i32) -> crate::io::ErrorKind { pub fn abort_internal() -> ! { core::intrinsics::abort(); } - -pub fn hashmap_random_keys() -> (u64, u64) { - (1, 2) -} diff --git a/library/std/src/sys/pal/wasi/helpers.rs b/library/std/src/sys/pal/wasi/helpers.rs index 37ef17858cb9..404747f0dc75 100644 --- a/library/std/src/sys/pal/wasi/helpers.rs +++ b/library/std/src/sys/pal/wasi/helpers.rs @@ -1,6 +1,6 @@ #![forbid(unsafe_op_in_unsafe_fn)] -use crate::{io as std_io, mem}; +use crate::io as std_io; #[inline] pub fn is_interrupted(errno: i32) -> bool { @@ -108,16 +108,6 @@ pub fn abort_internal() -> ! { unsafe { libc::abort() } } -pub fn hashmap_random_keys() -> (u64, u64) { - let mut ret = (0u64, 0u64); - unsafe { - let base = &mut ret as *mut (u64, u64) as *mut u8; - let len = mem::size_of_val(&ret); - wasi::random_get(base, len).expect("random_get failure"); - } - ret -} - #[inline] pub(crate) fn err2io(err: wasi::Errno) -> std_io::Error { std_io::Error::from_raw_os_error(err.raw().into()) diff --git a/library/std/src/sys/pal/wasi/mod.rs b/library/std/src/sys/pal/wasi/mod.rs index 8051021a5889..5d54c7903065 100644 --- a/library/std/src/sys/pal/wasi/mod.rs +++ b/library/std/src/sys/pal/wasi/mod.rs @@ -47,4 +47,4 @@ mod helpers; // then the compiler complains about conflicts. use helpers::err2io; -pub use helpers::{abort_internal, decode_error_kind, hashmap_random_keys, is_interrupted}; +pub use helpers::{abort_internal, decode_error_kind, is_interrupted}; diff --git a/library/std/src/sys/pal/wasip2/mod.rs b/library/std/src/sys/pal/wasip2/mod.rs index 546fadbe5011..17b26543bd75 100644 --- a/library/std/src/sys/pal/wasip2/mod.rs +++ b/library/std/src/sys/pal/wasip2/mod.rs @@ -50,6 +50,6 @@ mod helpers; // then the compiler complains about conflicts. use helpers::err2io; -pub use helpers::{abort_internal, decode_error_kind, hashmap_random_keys, is_interrupted}; +pub use helpers::{abort_internal, decode_error_kind, is_interrupted}; mod cabi_realloc; diff --git a/library/std/src/sys/pal/windows/api.rs b/library/std/src/sys/pal/windows/api.rs index 9e336ff2d473..ebe207fde935 100644 --- a/library/std/src/sys/pal/windows/api.rs +++ b/library/std/src/sys/pal/windows/api.rs @@ -30,7 +30,6 @@ //! should go in sys/pal/windows/mod.rs rather than here. See `IoResult` as an example. use core::ffi::c_void; -use core::ptr::addr_of; use super::c; @@ -186,7 +185,7 @@ unsafe trait SizedSetFileInformation: Sized { unsafe impl SetFileInformation for T { const CLASS: i32 = T::CLASS; fn as_ptr(&self) -> *const c_void { - addr_of!(*self).cast::() + (&raw const *self).cast::() } fn size(&self) -> u32 { win32_size_of::() diff --git a/library/std/src/sys/pal/windows/fs.rs b/library/std/src/sys/pal/windows/fs.rs index be26356bb406..aab471e28eae 100644 --- a/library/std/src/sys/pal/windows/fs.rs +++ b/library/std/src/sys/pal/windows/fs.rs @@ -1,5 +1,3 @@ -use core::ptr::addr_of; - use super::api::{self, WinError}; use super::{IoResult, to_u16s}; use crate::borrow::Cow; @@ -325,7 +323,7 @@ impl File { let result = c::SetFileInformationByHandle( handle.as_raw_handle(), c::FileEndOfFileInfo, - ptr::addr_of!(eof).cast::(), + (&raw const eof).cast::(), mem::size_of::() as u32, ); if result == 0 { @@ -364,7 +362,7 @@ impl File { cvt(c::GetFileInformationByHandleEx( self.handle.as_raw_handle(), c::FileAttributeTagInfo, - ptr::addr_of_mut!(attr_tag).cast(), + (&raw mut attr_tag).cast(), mem::size_of::().try_into().unwrap(), ))?; if attr_tag.FileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 { @@ -396,7 +394,7 @@ impl File { cvt(c::GetFileInformationByHandleEx( self.handle.as_raw_handle(), c::FileBasicInfo, - core::ptr::addr_of_mut!(info) as *mut c_void, + (&raw mut info) as *mut c_void, size as u32, ))?; let mut attr = FileAttr { @@ -428,7 +426,7 @@ impl File { cvt(c::GetFileInformationByHandleEx( self.handle.as_raw_handle(), c::FileStandardInfo, - core::ptr::addr_of_mut!(info) as *mut c_void, + (&raw mut info) as *mut c_void, size as u32, ))?; attr.file_size = info.AllocationSize as u64; @@ -438,7 +436,7 @@ impl File { cvt(c::GetFileInformationByHandleEx( self.handle.as_raw_handle(), c::FileAttributeTagInfo, - ptr::addr_of_mut!(attr_tag).cast(), + (&raw mut attr_tag).cast(), mem::size_of::().try_into().unwrap(), ))?; if attr_tag.FileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 { @@ -545,22 +543,20 @@ impl File { unsafe { let (path_buffer, subst_off, subst_len, relative) = match (*buf).ReparseTag { c::IO_REPARSE_TAG_SYMLINK => { - let info: *mut c::SYMBOLIC_LINK_REPARSE_BUFFER = - ptr::addr_of_mut!((*buf).rest).cast(); + let info: *mut c::SYMBOLIC_LINK_REPARSE_BUFFER = (&raw mut (*buf).rest).cast(); assert!(info.is_aligned()); ( - ptr::addr_of_mut!((*info).PathBuffer).cast::(), + (&raw mut (*info).PathBuffer).cast::(), (*info).SubstituteNameOffset / 2, (*info).SubstituteNameLength / 2, (*info).Flags & c::SYMLINK_FLAG_RELATIVE != 0, ) } c::IO_REPARSE_TAG_MOUNT_POINT => { - let info: *mut c::MOUNT_POINT_REPARSE_BUFFER = - ptr::addr_of_mut!((*buf).rest).cast(); + let info: *mut c::MOUNT_POINT_REPARSE_BUFFER = (&raw mut (*buf).rest).cast(); assert!(info.is_aligned()); ( - ptr::addr_of_mut!((*info).PathBuffer).cast::(), + (&raw mut (*info).PathBuffer).cast::(), (*info).SubstituteNameOffset / 2, (*info).SubstituteNameLength / 2, false, @@ -643,7 +639,7 @@ impl File { cvt(c::GetFileInformationByHandleEx( self.handle.as_raw_handle(), c::FileBasicInfo, - core::ptr::addr_of_mut!(info) as *mut c_void, + (&raw mut info) as *mut c_void, size as u32, ))?; Ok(info) @@ -790,11 +786,11 @@ impl<'a> Iterator for DirBuffIter<'a> { // it does not seem that reality is so kind, and assuming this // caused crashes in some cases (https://github.com/rust-lang/rust/issues/104530) // presumably, this can be blamed on buggy filesystem drivers, but who knows. - let next_entry = ptr::addr_of!((*info).NextEntryOffset).read_unaligned() as usize; - let length = ptr::addr_of!((*info).FileNameLength).read_unaligned() as usize; - let attrs = ptr::addr_of!((*info).FileAttributes).read_unaligned(); + let next_entry = (&raw const (*info).NextEntryOffset).read_unaligned() as usize; + let length = (&raw const (*info).FileNameLength).read_unaligned() as usize; + let attrs = (&raw const (*info).FileAttributes).read_unaligned(); let name = from_maybe_unaligned( - ptr::addr_of!((*info).FileName).cast::(), + (&raw const (*info).FileName).cast::(), length / size_of::(), ); let is_directory = (attrs & c::FILE_ATTRIBUTE_DIRECTORY) != 0; @@ -1326,7 +1322,7 @@ pub fn copy(from: &Path, to: &Path) -> io::Result { pfrom.as_ptr(), pto.as_ptr(), Some(callback), - core::ptr::addr_of_mut!(size) as *mut _, + (&raw mut size) as *mut _, ptr::null_mut(), 0, ) @@ -1405,7 +1401,7 @@ pub fn junction_point(original: &Path, link: &Path) -> io::Result<()> { cvt(c::DeviceIoControl( d.as_raw_handle(), c::FSCTL_SET_REPARSE_POINT, - addr_of!(header).cast::(), + (&raw const header).cast::(), data_len as u32 + 8, ptr::null_mut(), 0, diff --git a/library/std/src/sys/pal/windows/futex.rs b/library/std/src/sys/pal/windows/futex.rs index f16a9f534a3e..4d6c4df9a5a9 100644 --- a/library/std/src/sys/pal/windows/futex.rs +++ b/library/std/src/sys/pal/windows/futex.rs @@ -57,7 +57,7 @@ pub fn wait_on_address( unsafe { let addr = ptr::from_ref(address).cast::(); let size = mem::size_of::(); - let compare_addr = ptr::addr_of!(compare).cast::(); + let compare_addr = (&raw const compare).cast::(); let timeout = timeout.map(dur2timeout).unwrap_or(c::INFINITE); c::WaitOnAddress(addr, compare_addr, size, timeout) == c::TRUE } diff --git a/library/std/src/sys/pal/windows/io.rs b/library/std/src/sys/pal/windows/io.rs index 785a3f6768b7..1e7d02908f63 100644 --- a/library/std/src/sys/pal/windows/io.rs +++ b/library/std/src/sys/pal/windows/io.rs @@ -122,7 +122,7 @@ fn msys_tty_on(handle: BorrowedHandle<'_>) -> bool { c::GetFileInformationByHandleEx( handle.as_raw_handle(), c::FileNameInfo, - core::ptr::addr_of_mut!(name_info) as *mut c_void, + (&raw mut name_info) as *mut c_void, size_of::() as u32, ) }; diff --git a/library/std/src/sys/pal/windows/mod.rs b/library/std/src/sys/pal/windows/mod.rs index f5ed3e4628e1..1ea253e5e526 100644 --- a/library/std/src/sys/pal/windows/mod.rs +++ b/library/std/src/sys/pal/windows/mod.rs @@ -1,7 +1,6 @@ #![allow(missing_docs, nonstandard_style)] #![forbid(unsafe_op_in_unsafe_fn)] -pub use self::rand::hashmap_random_keys; use crate::ffi::{OsStr, OsString}; use crate::io::ErrorKind; use crate::mem::MaybeUninit; @@ -27,7 +26,6 @@ pub mod net; pub mod os; pub mod pipe; pub mod process; -pub mod rand; pub mod stdio; pub mod thread; pub mod time; diff --git a/library/std/src/sys/pal/windows/net.rs b/library/std/src/sys/pal/windows/net.rs index 61a4504cf654..fd62d1f407c2 100644 --- a/library/std/src/sys/pal/windows/net.rs +++ b/library/std/src/sys/pal/windows/net.rs @@ -390,7 +390,7 @@ impl Socket { buf.as_mut_ptr() as *mut _, length, flags, - core::ptr::addr_of_mut!(storage) as *mut _, + (&raw mut storage) as *mut _, &mut addrlen, ) }; diff --git a/library/std/src/sys/pal/windows/pipe.rs b/library/std/src/sys/pal/windows/pipe.rs index 7d1b5aca1d5f..a8f6617c9dc8 100644 --- a/library/std/src/sys/pal/windows/pipe.rs +++ b/library/std/src/sys/pal/windows/pipe.rs @@ -2,12 +2,13 @@ use crate::ffi::OsStr; use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; use crate::os::windows::prelude::*; use crate::path::Path; +use crate::random::{DefaultRandomSource, Random}; use crate::sync::atomic::AtomicUsize; use crate::sync::atomic::Ordering::Relaxed; +use crate::sys::c; use crate::sys::fs::{File, OpenOptions}; use crate::sys::handle::Handle; use crate::sys::pal::windows::api::{self, WinError}; -use crate::sys::{c, hashmap_random_keys}; use crate::sys_common::{FromInner, IntoInner}; use crate::{mem, ptr}; @@ -79,7 +80,7 @@ pub fn anon_pipe(ours_readable: bool, their_handle_inheritable: bool) -> io::Res name = format!( r"\\.\pipe\__rust_anonymous_pipe1__.{}.{}", c::GetCurrentProcessId(), - random_number() + random_number(), ); let wide_name = OsStr::new(&name).encode_wide().chain(Some(0)).collect::>(); let mut flags = c::FILE_FLAG_FIRST_PIPE_INSTANCE | c::FILE_FLAG_OVERLAPPED; @@ -214,7 +215,7 @@ fn random_number() -> usize { return N.fetch_add(1, Relaxed); } - N.store(hashmap_random_keys().0 as usize, Relaxed); + N.store(usize::random(&mut DefaultRandomSource), Relaxed); } } @@ -374,7 +375,7 @@ impl AnonPipe { let mut overlapped: c::OVERLAPPED = unsafe { crate::mem::zeroed() }; // `hEvent` is unused by `ReadFileEx` and `WriteFileEx`. // Therefore the documentation suggests using it to smuggle a pointer to the callback. - overlapped.hEvent = core::ptr::addr_of_mut!(async_result) as *mut _; + overlapped.hEvent = (&raw mut async_result) as *mut _; // Asynchronous read of the pipe. // If successful, `callback` will be called once it completes. diff --git a/library/std/src/sys/pal/windows/process.rs b/library/std/src/sys/pal/windows/process.rs index 01d34d84fe92..584d5714419b 100644 --- a/library/std/src/sys/pal/windows/process.rs +++ b/library/std/src/sys/pal/windows/process.rs @@ -395,10 +395,10 @@ impl Command { StartupInfo: si, lpAttributeList: proc_thread_attribute_list.0.as_mut_ptr() as _, }; - si_ptr = core::ptr::addr_of_mut!(si_ex) as _; + si_ptr = (&raw mut si_ex) as _; } else { si.cb = mem::size_of::() as u32; - si_ptr = core::ptr::addr_of_mut!(si) as _; + si_ptr = (&raw mut si) as _; } unsafe { @@ -991,7 +991,7 @@ fn make_proc_thread_attribute_list( // Therefore, we ensure that we don't add more attributes than the buffer was initialized for. for (&attribute, value) in attributes.iter().take(attribute_count as usize) { match value { - ProcThreadAttributeValue::Data(value) => { + let value_ptr = (&raw const *value.data) as _; let value_ptr = core::ptr::addr_of!(*value.data) as _; cvt(unsafe { c::UpdateProcThreadAttribute( diff --git a/library/std/src/sys/pal/windows/rand.rs b/library/std/src/sys/pal/windows/rand.rs deleted file mode 100644 index e366bb995626..000000000000 --- a/library/std/src/sys/pal/windows/rand.rs +++ /dev/null @@ -1,27 +0,0 @@ -use core::{mem, ptr}; - -use crate::sys::c; - -#[cfg(not(target_vendor = "win7"))] -#[inline] -pub fn hashmap_random_keys() -> (u64, u64) { - let mut v = (0, 0); - let ret = unsafe { c::ProcessPrng(ptr::addr_of_mut!(v).cast::(), mem::size_of_val(&v)) }; - // ProcessPrng is documented as always returning `TRUE`. - // https://learn.microsoft.com/en-us/windows/win32/seccng/processprng#return-value - debug_assert_eq!(ret, c::TRUE); - v -} - -#[cfg(target_vendor = "win7")] -pub fn hashmap_random_keys() -> (u64, u64) { - use crate::ffi::c_void; - use crate::io; - - let mut v = (0, 0); - let ret = unsafe { - c::RtlGenRandom(ptr::addr_of_mut!(v).cast::(), mem::size_of_val(&v) as u32) - }; - - if ret != 0 { v } else { panic!("RNG broken: {}", io::Error::last_os_error()) } -} diff --git a/library/std/src/sys/pal/zkvm/mod.rs b/library/std/src/sys/pal/zkvm/mod.rs index 20fdb7468a40..6ea057720296 100644 --- a/library/std/src/sys/pal/zkvm/mod.rs +++ b/library/std/src/sys/pal/zkvm/mod.rs @@ -60,11 +60,3 @@ pub fn decode_error_kind(_code: i32) -> crate::io::ErrorKind { pub fn abort_internal() -> ! { core::intrinsics::abort(); } - -pub fn hashmap_random_keys() -> (u64, u64) { - let mut buf = [0u32; 4]; - unsafe { - abi::sys_rand(buf.as_mut_ptr(), 4); - }; - ((buf[0] as u64) << 32 + buf[1] as u64, (buf[2] as u64) << 32 + buf[3] as u64) -} diff --git a/library/std/src/sys/personality/mod.rs b/library/std/src/sys/personality/mod.rs index 68085d026c40..9754e840d151 100644 --- a/library/std/src/sys/personality/mod.rs +++ b/library/std/src/sys/personality/mod.rs @@ -31,7 +31,7 @@ cfg_if::cfg_if! { target_os = "psp", target_os = "xous", target_os = "solid_asp3", - all(target_family = "unix", not(target_os = "espidf"), not(target_os = "l4re"), not(target_os = "rtems")), + all(target_family = "unix", not(target_os = "espidf"), not(target_os = "l4re"), not(target_os = "rtems"), not(target_os = "nuttx")), all(target_vendor = "fortanix", target_env = "sgx"), ))] { mod gcc; diff --git a/library/std/src/sys/random/apple.rs b/library/std/src/sys/random/apple.rs new file mode 100644 index 000000000000..417198c9d850 --- /dev/null +++ b/library/std/src/sys/random/apple.rs @@ -0,0 +1,15 @@ +//! Random data on Apple platforms. +//! +//! `CCRandomGenerateBytes` calls into `CCRandomCopyBytes` with `kCCRandomDefault`. +//! `CCRandomCopyBytes` manages a CSPRNG which is seeded from the kernel's CSPRNG. +//! We use `CCRandomGenerateBytes` instead of `SecCopyBytes` because it is accessible via +//! `libSystem` (libc) while the other needs to link to `Security.framework`. +//! +//! Note that technically, `arc4random_buf` is available as well, but that calls +//! into the same system service anyway, and `CCRandomGenerateBytes` has been +//! proven to be App Store-compatible. + +pub fn fill_bytes(bytes: &mut [u8]) { + let ret = unsafe { libc::CCRandomGenerateBytes(bytes.as_mut_ptr().cast(), bytes.len()) }; + assert_eq!(ret, libc::kCCSuccess, "failed to generate random data"); +} diff --git a/library/std/src/sys/random/arc4random.rs b/library/std/src/sys/random/arc4random.rs new file mode 100644 index 000000000000..32467e9ebaa6 --- /dev/null +++ b/library/std/src/sys/random/arc4random.rs @@ -0,0 +1,34 @@ +//! Random data generation with `arc4random_buf`. +//! +//! Contrary to its name, `arc4random` doesn't actually use the horribly-broken +//! RC4 cypher anymore, at least not on modern systems, but rather something +//! like ChaCha20 with continual reseeding from the OS. That makes it an ideal +//! source of large quantities of cryptographically secure data, which is exactly +//! what we need for `DefaultRandomSource`. Unfortunately, it's not available +//! on all UNIX systems, most notably Linux (until recently, but it's just a +//! wrapper for `getrandom`. Since we need to hook into `getrandom` directly +//! for `HashMap` keys anyway, we just keep our version). + +#[cfg(not(any( + target_os = "haiku", + target_os = "illumos", + target_os = "solaris", + target_os = "vita", +)))] +use libc::arc4random_buf; + +// FIXME: move these to libc (except Haiku, that one needs to link to libbsd.so). +#[cfg(any( + target_os = "haiku", // See https://git.haiku-os.org/haiku/tree/headers/compatibility/bsd/stdlib.h + target_os = "illumos", // See https://www.illumos.org/man/3C/arc4random + target_os = "solaris", // See https://docs.oracle.com/cd/E88353_01/html/E37843/arc4random-3c.html + target_os = "vita", // See https://github.com/vitasdk/newlib/blob/b89e5bc183b516945f9ee07eef483ecb916e45ff/newlib/libc/include/stdlib.h#L74 +))] +#[cfg_attr(target_os = "haiku", link(name = "bsd"))] +extern "C" { + fn arc4random_buf(buf: *mut core::ffi::c_void, nbytes: libc::size_t); +} + +pub fn fill_bytes(bytes: &mut [u8]) { + unsafe { arc4random_buf(bytes.as_mut_ptr().cast(), bytes.len()) } +} diff --git a/library/std/src/sys/random/espidf.rs b/library/std/src/sys/random/espidf.rs new file mode 100644 index 000000000000..fd52cb5559ce --- /dev/null +++ b/library/std/src/sys/random/espidf.rs @@ -0,0 +1,9 @@ +use crate::ffi::c_void; + +extern "C" { + fn esp_fill_random(buf: *mut c_void, len: usize); +} + +pub fn fill_bytes(bytes: &mut [u8]) { + unsafe { esp_fill_random(bytes.as_mut_ptr().cast(), bytes.len()) } +} diff --git a/library/std/src/sys/random/fuchsia.rs b/library/std/src/sys/random/fuchsia.rs new file mode 100644 index 000000000000..77d72b3c5b78 --- /dev/null +++ b/library/std/src/sys/random/fuchsia.rs @@ -0,0 +1,13 @@ +//! Random data generation using the Zircon kernel. +//! +//! Fuchsia, as always, is quite nice and provides exactly the API we need: +//! . + +#[link(name = "zircon")] +extern "C" { + fn zx_cprng_draw(buffer: *mut u8, len: usize); +} + +pub fn fill_bytes(bytes: &mut [u8]) { + unsafe { zx_cprng_draw(bytes.as_mut_ptr(), bytes.len()) } +} diff --git a/library/std/src/sys/random/getentropy.rs b/library/std/src/sys/random/getentropy.rs new file mode 100644 index 000000000000..110ac134c1f4 --- /dev/null +++ b/library/std/src/sys/random/getentropy.rs @@ -0,0 +1,17 @@ +//! Random data generation through `getentropy`. +//! +//! Since issue 8 (2024), the POSIX specification mandates the existence of the +//! `getentropy` function, which fills a slice of up to `GETENTROPY_MAX` bytes +//! (256 on all known platforms) with random data. Unfortunately, it's only +//! meant to be used to seed other CPRNGs, which we don't have, so we only use +//! it where `arc4random_buf` and friends aren't available or secure (currently +//! that's only the case on Emscripten). + +pub fn fill_bytes(bytes: &mut [u8]) { + // GETENTROPY_MAX isn't defined yet on most platforms, but it's mandated + // to be at least 256, so just use that as limit. + for chunk in bytes.chunks_mut(256) { + let r = unsafe { libc::getentropy(chunk.as_mut_ptr().cast(), chunk.len()) }; + assert_ne!(r, -1, "failed to generate random data"); + } +} diff --git a/library/std/src/sys/random/hermit.rs b/library/std/src/sys/random/hermit.rs new file mode 100644 index 000000000000..92c0550d2d58 --- /dev/null +++ b/library/std/src/sys/random/hermit.rs @@ -0,0 +1,7 @@ +pub fn fill_bytes(mut bytes: &mut [u8]) { + while !bytes.is_empty() { + let res = unsafe { hermit_abi::read_entropy(bytes.as_mut_ptr(), bytes.len(), 0) }; + assert_ne!(res, -1, "failed to generate random data"); + bytes = &mut bytes[res as usize..]; + } +} diff --git a/library/std/src/sys/random/horizon.rs b/library/std/src/sys/random/horizon.rs new file mode 100644 index 000000000000..0be2eae20a72 --- /dev/null +++ b/library/std/src/sys/random/horizon.rs @@ -0,0 +1,7 @@ +pub fn fill_bytes(mut bytes: &mut [u8]) { + while !bytes.is_empty() { + let r = unsafe { libc::getrandom(bytes.as_mut_ptr().cast(), bytes.len(), 0) }; + assert_ne!(r, -1, "failed to generate random data"); + bytes = &mut bytes[r as usize..]; + } +} diff --git a/library/std/src/sys/random/linux.rs b/library/std/src/sys/random/linux.rs new file mode 100644 index 000000000000..073fdc45e611 --- /dev/null +++ b/library/std/src/sys/random/linux.rs @@ -0,0 +1,170 @@ +//! Random data generation with the Linux kernel. +//! +//! The first interface random data interface to be introduced on Linux were +//! the `/dev/random` and `/dev/urandom` special files. As paths can become +//! unreachable when inside a chroot and when the file descriptors are exhausted, +//! this was not enough to provide userspace with a reliable source of randomness, +//! so when the OpenBSD 5.6 introduced the `getentropy` syscall, Linux 3.17 got +//! its very own `getrandom` syscall to match.[^1] Unfortunately, even if our +//! minimum supported version were high enough, we still couldn't rely on the +//! syscall being available, as it is blocked in `seccomp` by default. +//! +//! The question is therefore which of the random sources to use. Historically, +//! the kernel contained two pools: the blocking and non-blocking pool. The +//! blocking pool used entropy estimation to limit the amount of available +//! bytes, while the non-blocking pool, once initialized using the blocking +//! pool, uses a CPRNG to return an unlimited number of random bytes. With a +//! strong enough CPRNG however, the entropy estimation didn't contribute that +//! much towards security while being an excellent vector for DoS attacs. Thus, +//! the blocking pool was removed in kernel version 5.6.[^2] That patch did not +//! magically increase the quality of the non-blocking pool, however, so we can +//! safely consider it strong enough even in older kernel versions and use it +//! unconditionally. +//! +//! One additional consideration to make is that the non-blocking pool is not +//! always initialized during early boot. We want the best quality of randomness +//! for the output of `DefaultRandomSource` so we simply wait until it is +//! initialized. When `HashMap` keys however, this represents a potential source +//! of deadlocks, as the additional entropy may only be generated once the +//! program makes forward progress. In that case, we just use the best random +//! data the system has available at the time. +//! +//! So in conclusion, we always want the output of the non-blocking pool, but +//! may need to wait until it is initalized. The default behaviour of `getrandom` +//! is to wait until the non-blocking pool is initialized and then draw from there, +//! so if `getrandom` is available, we use its default to generate the bytes. For +//! `HashMap`, however, we need to specify the `GRND_INSECURE` flags, but that +//! is only available starting with kernel version 5.6. Thus, if we detect that +//! the flag is unsupported, we try `GRND_NONBLOCK` instead, which will only +//! succeed if the pool is initialized. If it isn't, we fall back to the file +//! access method. +//! +//! The behaviour of `/dev/urandom` is inverse to that of `getrandom`: it always +//! yields data, even when the pool is not initialized. For generating `HashMap` +//! keys, this is not important, so we can use it directly. For secure data +//! however, we need to wait until initialization, which we can do by `poll`ing +//! `/dev/random`. +//! +//! TLDR: our fallback strategies are: +//! +//! Secure data | `HashMap` keys +//! --------------------------------------------|------------------ +//! getrandom(0) | getrandom(GRND_INSECURE) +//! poll("/dev/random") && read("/dev/urandom") | getrandom(GRND_NONBLOCK) +//! | read("/dev/urandom") +//! +//! [^1]: +//! [^2]: +//! +// FIXME(in 2040 or so): once the minimum kernel version is 5.6, remove the +// `GRND_NONBLOCK` fallback and use `/dev/random` instead of `/dev/urandom` +// when secure data is required. + +use crate::fs::File; +use crate::io::Read; +use crate::os::fd::AsRawFd; +use crate::sync::OnceLock; +use crate::sync::atomic::AtomicBool; +use crate::sync::atomic::Ordering::{Acquire, Relaxed, Release}; +use crate::sys::pal::os::errno; +use crate::sys::pal::weak::syscall; + +fn getrandom(mut bytes: &mut [u8], insecure: bool) { + // A weak symbol allows interposition, e.g. for perf measurements that want to + // disable randomness for consistency. Otherwise, we'll try a raw syscall. + // (`getrandom` was added in glibc 2.25, musl 1.1.20, android API level 28) + syscall! { + fn getrandom( + buffer: *mut libc::c_void, + length: libc::size_t, + flags: libc::c_uint + ) -> libc::ssize_t + } + + static GETRANDOM_AVAILABLE: AtomicBool = AtomicBool::new(true); + static GRND_INSECURE_AVAILABLE: AtomicBool = AtomicBool::new(true); + static URANDOM_READY: AtomicBool = AtomicBool::new(false); + static DEVICE: OnceLock = OnceLock::new(); + + if GETRANDOM_AVAILABLE.load(Relaxed) { + loop { + if bytes.is_empty() { + return; + } + + let flags = if insecure { + if GRND_INSECURE_AVAILABLE.load(Relaxed) { + libc::GRND_INSECURE + } else { + libc::GRND_NONBLOCK + } + } else { + 0 + }; + + let ret = unsafe { getrandom(bytes.as_mut_ptr().cast(), bytes.len(), flags) }; + if ret != -1 { + bytes = &mut bytes[ret as usize..]; + } else { + match errno() { + libc::EINTR => continue, + // `GRND_INSECURE` is not available, try + // `GRND_NONBLOCK`. + libc::EINVAL if flags == libc::GRND_INSECURE => { + GRND_INSECURE_AVAILABLE.store(false, Relaxed); + continue; + } + // The pool is not initialized yet, fall back to + // /dev/urandom for now. + libc::EAGAIN if flags == libc::GRND_NONBLOCK => break, + // `getrandom` is unavailable or blocked by seccomp. + // Don't try it again and fall back to /dev/urandom. + libc::ENOSYS | libc::EPERM => { + GETRANDOM_AVAILABLE.store(false, Relaxed); + break; + } + _ => panic!("failed to generate random data"), + } + } + } + } + + // When we want cryptographic strength, we need to wait for the CPRNG-pool + // to become initialized. Do this by polling `/dev/random` until it is ready. + if !insecure { + if !URANDOM_READY.load(Acquire) { + let random = File::open("/dev/random").expect("failed to open /dev/random"); + let mut fd = libc::pollfd { fd: random.as_raw_fd(), events: libc::POLLIN, revents: 0 }; + + while !URANDOM_READY.load(Acquire) { + let ret = unsafe { libc::poll(&mut fd, 1, -1) }; + match ret { + 1 => { + assert_eq!(fd.revents, libc::POLLIN); + URANDOM_READY.store(true, Release); + break; + } + -1 if errno() == libc::EINTR => continue, + _ => panic!("poll(\"/dev/random\") failed"), + } + } + } + } + + DEVICE + .get_or_try_init(|| File::open("/dev/urandom")) + .and_then(|mut dev| dev.read_exact(bytes)) + .expect("failed to generate random data"); +} + +pub fn fill_bytes(bytes: &mut [u8]) { + getrandom(bytes, false); +} + +pub fn hashmap_random_keys() -> (u64, u64) { + let mut bytes = [0; 16]; + getrandom(&mut bytes, true); + let k1 = u64::from_ne_bytes(bytes[..8].try_into().unwrap()); + let k2 = u64::from_ne_bytes(bytes[8..].try_into().unwrap()); + (k1, k2) +} diff --git a/library/std/src/sys/random/mod.rs b/library/std/src/sys/random/mod.rs new file mode 100644 index 000000000000..d625814d15b3 --- /dev/null +++ b/library/std/src/sys/random/mod.rs @@ -0,0 +1,96 @@ +cfg_if::cfg_if! { + // Tier 1 + if #[cfg(any(target_os = "linux", target_os = "android"))] { + mod linux; + pub use linux::{fill_bytes, hashmap_random_keys}; + } else if #[cfg(target_os = "windows")] { + mod windows; + pub use windows::fill_bytes; + } else if #[cfg(target_vendor = "apple")] { + mod apple; + pub use apple::fill_bytes; + // Others, in alphabetical ordering. + } else if #[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "haiku", + target_os = "illumos", + target_os = "netbsd", + target_os = "openbsd", + target_os = "solaris", + target_os = "vita", + ))] { + mod arc4random; + pub use arc4random::fill_bytes; + } else if #[cfg(target_os = "emscripten")] { + mod getentropy; + pub use getentropy::fill_bytes; + } else if #[cfg(target_os = "espidf")] { + mod espidf; + pub use espidf::fill_bytes; + } else if #[cfg(target_os = "fuchsia")] { + mod fuchsia; + pub use fuchsia::fill_bytes; + } else if #[cfg(target_os = "hermit")] { + mod hermit; + pub use hermit::fill_bytes; + } else if #[cfg(target_os = "horizon")] { + // FIXME: add arc4random_buf to shim-3ds + mod horizon; + pub use horizon::fill_bytes; + } else if #[cfg(any( + target_os = "hurd", + target_os = "l4re", + target_os = "nto", + target_os = "nuttx", + ))] { + mod unix_legacy; + pub use unix_legacy::fill_bytes; + } else if #[cfg(target_os = "redox")] { + mod redox; + pub use redox::fill_bytes; + } else if #[cfg(all(target_vendor = "fortanix", target_env = "sgx"))] { + mod sgx; + pub use sgx::fill_bytes; + } else if #[cfg(target_os = "solid_asp3")] { + mod solid; + pub use solid::fill_bytes; + } else if #[cfg(target_os = "teeos")] { + mod teeos; + pub use teeos::fill_bytes; + } else if #[cfg(target_os = "uefi")] { + mod uefi; + pub use uefi::fill_bytes; + } else if #[cfg(target_os = "vxworks")] { + mod vxworks; + pub use vxworks::fill_bytes; + } else if #[cfg(target_os = "wasi")] { + mod wasi; + pub use wasi::fill_bytes; + } else if #[cfg(target_os = "zkvm")] { + mod zkvm; + pub use zkvm::fill_bytes; + } else if #[cfg(any( + all(target_family = "wasm", target_os = "unknown"), + target_os = "xous", + ))] { + // FIXME: finally remove std support for wasm32-unknown-unknown + // FIXME: add random data generation to xous + mod unsupported; + pub use unsupported::{fill_bytes, hashmap_random_keys}; + } +} + +#[cfg(not(any( + target_os = "linux", + target_os = "android", + all(target_family = "wasm", target_os = "unknown"), + target_os = "xous", +)))] +pub fn hashmap_random_keys() -> (u64, u64) { + let mut buf = [0; 16]; + fill_bytes(&mut buf); + let k1 = u64::from_ne_bytes(buf[..8].try_into().unwrap()); + let k2 = u64::from_ne_bytes(buf[8..].try_into().unwrap()); + (k1, k2) +} diff --git a/library/std/src/sys/random/redox.rs b/library/std/src/sys/random/redox.rs new file mode 100644 index 000000000000..b004335a3517 --- /dev/null +++ b/library/std/src/sys/random/redox.rs @@ -0,0 +1,12 @@ +use crate::fs::File; +use crate::io::Read; +use crate::sync::OnceLock; + +static SCHEME: OnceLock = OnceLock::new(); + +pub fn fill_bytes(bytes: &mut [u8]) { + SCHEME + .get_or_try_init(|| File::open("/scheme/rand")) + .and_then(|mut scheme| scheme.read_exact(bytes)) + .expect("failed to generate random data"); +} diff --git a/library/std/src/sys/random/sgx.rs b/library/std/src/sys/random/sgx.rs new file mode 100644 index 000000000000..c3647a8df220 --- /dev/null +++ b/library/std/src/sys/random/sgx.rs @@ -0,0 +1,67 @@ +use crate::arch::x86_64::{_rdrand16_step, _rdrand32_step, _rdrand64_step}; + +const RETRIES: u32 = 10; + +fn fail() -> ! { + panic!("failed to generate random data"); +} + +fn rdrand64() -> u64 { + unsafe { + let mut ret: u64 = 0; + for _ in 0..RETRIES { + if _rdrand64_step(&mut ret) == 1 { + return ret; + } + } + + fail(); + } +} + +fn rdrand32() -> u32 { + unsafe { + let mut ret: u32 = 0; + for _ in 0..RETRIES { + if _rdrand32_step(&mut ret) == 1 { + return ret; + } + } + + fail(); + } +} + +fn rdrand16() -> u16 { + unsafe { + let mut ret: u16 = 0; + for _ in 0..RETRIES { + if _rdrand16_step(&mut ret) == 1 { + return ret; + } + } + + fail(); + } +} + +pub fn fill_bytes(bytes: &mut [u8]) { + let mut chunks = bytes.array_chunks_mut(); + for chunk in &mut chunks { + *chunk = rdrand64().to_ne_bytes(); + } + + let mut chunks = chunks.into_remainder().array_chunks_mut(); + for chunk in &mut chunks { + *chunk = rdrand32().to_ne_bytes(); + } + + let mut chunks = chunks.into_remainder().array_chunks_mut(); + for chunk in &mut chunks { + *chunk = rdrand16().to_ne_bytes(); + } + + if let [byte] = chunks.into_remainder() { + *byte = rdrand16() as u8; + } +} diff --git a/library/std/src/sys/random/solid.rs b/library/std/src/sys/random/solid.rs new file mode 100644 index 000000000000..545771150e28 --- /dev/null +++ b/library/std/src/sys/random/solid.rs @@ -0,0 +1,8 @@ +use crate::sys::pal::abi; + +pub fn fill_bytes(bytes: &mut [u8]) { + unsafe { + let result = abi::SOLID_RNG_SampleRandomBytes(bytes.as_mut_ptr(), bytes.len()); + assert_eq!(result, 0, "failed to generate random data"); + } +} diff --git a/library/std/src/sys/random/teeos.rs b/library/std/src/sys/random/teeos.rs new file mode 100644 index 000000000000..fd6b24e19e98 --- /dev/null +++ b/library/std/src/sys/random/teeos.rs @@ -0,0 +1,7 @@ +extern "C" { + fn TEE_GenerateRandom(randomBuffer: *mut core::ffi::c_void, randomBufferLen: libc::size_t); +} + +pub fn fill_bytes(bytes: &mut [u8]) { + unsafe { TEE_GenerateRandom(bytes.as_mut_ptr().cast(), bytes.len()) } +} diff --git a/library/std/src/sys/random/uefi.rs b/library/std/src/sys/random/uefi.rs new file mode 100644 index 000000000000..a4d29e66f387 --- /dev/null +++ b/library/std/src/sys/random/uefi.rs @@ -0,0 +1,27 @@ +use r_efi::protocols::rng; + +use crate::sys::pal::helpers; + +pub fn fill_bytes(bytes: &mut [u8]) { + let handles = + helpers::locate_handles(rng::PROTOCOL_GUID).expect("failed to generate random data"); + for handle in handles { + if let Ok(protocol) = helpers::open_protocol::(handle, rng::PROTOCOL_GUID) { + let r = unsafe { + ((*protocol.as_ptr()).get_rng)( + protocol.as_ptr(), + crate::ptr::null_mut(), + bytes.len(), + bytes.as_mut_ptr(), + ) + }; + if r.is_error() { + continue; + } else { + return; + } + } + } + + panic!("failed to generate random data"); +} diff --git a/library/std/src/sys/random/unix_legacy.rs b/library/std/src/sys/random/unix_legacy.rs new file mode 100644 index 000000000000..587068b0d664 --- /dev/null +++ b/library/std/src/sys/random/unix_legacy.rs @@ -0,0 +1,20 @@ +//! Random data from `/dev/urandom` +//! +//! Before `getentropy` was standardized in 2024, UNIX didn't have a standardized +//! way of getting random data, so systems just followed the precedent set by +//! Linux and exposed random devices at `/dev/random` and `/dev/urandom`. Thus, +//! for the few systems that support neither `arc4random_buf` nor `getentropy` +//! yet, we just read from the file. + +use crate::fs::File; +use crate::io::Read; +use crate::sync::OnceLock; + +static DEVICE: OnceLock = OnceLock::new(); + +pub fn fill_bytes(bytes: &mut [u8]) { + DEVICE + .get_or_try_init(|| File::open("/dev/urandom")) + .and_then(|mut dev| dev.read_exact(bytes)) + .expect("failed to generate random data"); +} diff --git a/library/std/src/sys/random/unsupported.rs b/library/std/src/sys/random/unsupported.rs new file mode 100644 index 000000000000..d68ce4a9e870 --- /dev/null +++ b/library/std/src/sys/random/unsupported.rs @@ -0,0 +1,15 @@ +use crate::ptr; + +pub fn fill_bytes(_: &mut [u8]) { + panic!("this target does not support random data generation"); +} + +pub fn hashmap_random_keys() -> (u64, u64) { + // Use allocation addresses for a bit of randomness. This isn't + // particularily secure, but there isn't really an alternative. + let stack = 0u8; + let heap = Box::new(0u8); + let k1 = ptr::from_ref(&stack).addr() as u64; + let k2 = ptr::from_ref(&*heap).addr() as u64; + (k1, k2) +} diff --git a/library/std/src/sys/random/vxworks.rs b/library/std/src/sys/random/vxworks.rs new file mode 100644 index 000000000000..d549ccebdb2c --- /dev/null +++ b/library/std/src/sys/random/vxworks.rs @@ -0,0 +1,25 @@ +use crate::sync::atomic::AtomicBool; +use crate::sync::atomic::Ordering::Relaxed; + +static RNG_INIT: AtomicBool = AtomicBool::new(false); + +pub fn fill_bytes(mut bytes: &mut [u8]) { + while !RNG_INIT.load(Relaxed) { + let ret = unsafe { libc::randSecure() }; + if ret < 0 { + panic!("failed to generate random data"); + } else if ret > 0 { + RNG_INIT.store(true, Relaxed); + break; + } + + unsafe { libc::usleep(10) }; + } + + while !bytes.is_empty() { + let len = bytes.len().try_into().unwrap_or(libc::c_int::MAX); + let ret = unsafe { libc::randABytes(bytes.as_mut_ptr(), len) }; + assert!(ret >= 0, "failed to generate random data"); + bytes = &mut bytes[len as usize..]; + } +} diff --git a/library/std/src/sys/random/wasi.rs b/library/std/src/sys/random/wasi.rs new file mode 100644 index 000000000000..d41da3751fc0 --- /dev/null +++ b/library/std/src/sys/random/wasi.rs @@ -0,0 +1,5 @@ +pub fn fill_bytes(bytes: &mut [u8]) { + unsafe { + wasi::random_get(bytes.as_mut_ptr(), bytes.len()).expect("failed to generate random data") + } +} diff --git a/library/std/src/sys/random/windows.rs b/library/std/src/sys/random/windows.rs new file mode 100644 index 000000000000..7566000f9e6f --- /dev/null +++ b/library/std/src/sys/random/windows.rs @@ -0,0 +1,20 @@ +use crate::sys::c; + +#[cfg(not(target_vendor = "win7"))] +#[inline] +pub fn fill_bytes(bytes: &mut [u8]) { + let ret = unsafe { c::ProcessPrng(bytes.as_mut_ptr(), bytes.len()) }; + // ProcessPrng is documented as always returning `TRUE`. + // https://learn.microsoft.com/en-us/windows/win32/seccng/processprng#return-value + debug_assert_eq!(ret, c::TRUE); +} + +#[cfg(target_vendor = "win7")] +pub fn fill_bytes(mut bytes: &mut [u8]) { + while !bytes.is_empty() { + let len = bytes.len().try_into().unwrap_or(u32::MAX); + let ret = unsafe { c::RtlGenRandom(bytes.as_mut_ptr().cast(), len) }; + assert_ne!(ret, 0, "failed to generate random data"); + bytes = &mut bytes[len as usize..]; + } +} diff --git a/library/std/src/sys/random/zkvm.rs b/library/std/src/sys/random/zkvm.rs new file mode 100644 index 000000000000..3011942f6b26 --- /dev/null +++ b/library/std/src/sys/random/zkvm.rs @@ -0,0 +1,21 @@ +use crate::sys::pal::abi; + +pub fn fill_bytes(bytes: &mut [u8]) { + let (pre, words, post) = unsafe { bytes.align_to_mut::() }; + if !words.is_empty() { + unsafe { + abi::sys_rand(words.as_mut_ptr(), words.len()); + } + } + + let mut buf = [0u32; 2]; + let len = (pre.len() + post.len() + size_of::() - 1) / size_of::(); + if len != 0 { + unsafe { abi::sys_rand(buf.as_mut_ptr(), len) }; + } + + let buf = buf.map(u32::to_ne_bytes); + let buf = buf.as_flattened(); + pre.copy_from_slice(&buf[..pre.len()]); + post.copy_from_slice(&buf[pre.len()..pre.len() + post.len()]); +} diff --git a/library/std/src/sys/sync/thread_parking/pthread.rs b/library/std/src/sys/sync/thread_parking/pthread.rs index c64600e9e29c..5f195d0bb0cf 100644 --- a/library/std/src/sys/sync/thread_parking/pthread.rs +++ b/library/std/src/sys/sync/thread_parking/pthread.rs @@ -3,7 +3,6 @@ use crate::cell::UnsafeCell; use crate::marker::PhantomPinned; use crate::pin::Pin; -use crate::ptr::addr_of_mut; use crate::sync::atomic::AtomicUsize; use crate::sync::atomic::Ordering::{Acquire, Relaxed, Release}; #[cfg(not(target_os = "nto"))] @@ -101,8 +100,8 @@ impl Parker { // This could lead to undefined behaviour when deadlocking. This is avoided // by not deadlocking. Note in particular the unlocking operation before any // panic, as code after the panic could try to park again. - addr_of_mut!((*parker).state).write(AtomicUsize::new(EMPTY)); - addr_of_mut!((*parker).lock).write(UnsafeCell::new(libc::PTHREAD_MUTEX_INITIALIZER)); + (&raw mut (*parker).state).write(AtomicUsize::new(EMPTY)); + (&raw mut (*parker).lock).write(UnsafeCell::new(libc::PTHREAD_MUTEX_INITIALIZER)); cfg_if::cfg_if! { if #[cfg(any( @@ -112,9 +111,9 @@ impl Parker { target_os = "vita", target_vendor = "apple", ))] { - addr_of_mut!((*parker).cvar).write(UnsafeCell::new(libc::PTHREAD_COND_INITIALIZER)); + (&raw mut (*parker).cvar).write(UnsafeCell::new(libc::PTHREAD_COND_INITIALIZER)); } else if #[cfg(any(target_os = "espidf", target_os = "horizon"))] { - let r = libc::pthread_cond_init(addr_of_mut!((*parker).cvar).cast(), crate::ptr::null()); + let r = libc::pthread_cond_init((&raw mut (*parker).cvar).cast(), crate::ptr::null()); assert_eq!(r, 0); } else { use crate::mem::MaybeUninit; @@ -123,7 +122,7 @@ impl Parker { assert_eq!(r, 0); let r = libc::pthread_condattr_setclock(attr.as_mut_ptr(), libc::CLOCK_MONOTONIC); assert_eq!(r, 0); - let r = libc::pthread_cond_init(addr_of_mut!((*parker).cvar).cast(), attr.as_ptr()); + let r = libc::pthread_cond_init((&raw mut (*parker).cvar).cast(), attr.as_ptr()); assert_eq!(r, 0); let r = libc::pthread_condattr_destroy(attr.as_mut_ptr()); assert_eq!(r, 0); diff --git a/library/std/src/sys/sync/thread_parking/windows7.rs b/library/std/src/sys/sync/thread_parking/windows7.rs index cdd59757fe2c..8f7e66c46ef7 100644 --- a/library/std/src/sys/sync/thread_parking/windows7.rs +++ b/library/std/src/sys/sync/thread_parking/windows7.rs @@ -178,7 +178,7 @@ impl Parker { } fn ptr(&self) -> *const c_void { - core::ptr::addr_of!(self.state).cast::() + (&raw const self.state).cast::() } } diff --git a/library/std/src/sys/thread_local/destructors/linux_like.rs b/library/std/src/sys/thread_local/destructors/linux_like.rs index c381be0bf8c7..f473dc4d79df 100644 --- a/library/std/src/sys/thread_local/destructors/linux_like.rs +++ b/library/std/src/sys/thread_local/destructors/linux_like.rs @@ -47,7 +47,7 @@ pub unsafe fn register(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { dtor, ), t.cast(), - core::ptr::addr_of!(__dso_handle) as *mut _, + (&raw const __dso_handle) as *mut _, ); } } else { diff --git a/library/std/src/sys_common/net.rs b/library/std/src/sys_common/net.rs index 85a82b088cd4..5a0ad9075810 100644 --- a/library/std/src/sys_common/net.rs +++ b/library/std/src/sys_common/net.rs @@ -21,6 +21,7 @@ cfg_if::cfg_if! { target_os = "haiku", target_os = "l4re", target_os = "nto", + target_os = "nuttx", target_vendor = "apple", ))] { use crate::sys::net::netc::IPV6_JOIN_GROUP as IPV6_ADD_MEMBERSHIP; @@ -73,7 +74,7 @@ pub fn setsockopt( sock.as_raw(), level, option_name, - core::ptr::addr_of!(option_value) as *const _, + (&raw const option_value) as *const _, mem::size_of::() as c::socklen_t, ))?; Ok(()) @@ -88,7 +89,7 @@ pub fn getsockopt(sock: &Socket, level: c_int, option_name: c_int) -> i sock.as_raw(), level, option_name, - core::ptr::addr_of_mut!(option_value) as *mut _, + (&raw mut option_value) as *mut _, &mut option_len, ))?; Ok(option_value) @@ -102,7 +103,7 @@ where unsafe { let mut storage: c::sockaddr_storage = mem::zeroed(); let mut len = mem::size_of_val(&storage) as c::socklen_t; - cvt(f(core::ptr::addr_of_mut!(storage) as *mut _, &mut len))?; + cvt(f((&raw mut storage) as *mut _, &mut len))?; sockaddr_to_addr(&storage, len as usize) } } @@ -451,7 +452,7 @@ impl TcpListener { pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> { let mut storage: c::sockaddr_storage = unsafe { mem::zeroed() }; let mut len = mem::size_of_val(&storage) as c::socklen_t; - let sock = self.inner.accept(core::ptr::addr_of_mut!(storage) as *mut _, &mut len)?; + let sock = self.inner.accept((&raw mut storage) as *mut _, &mut len)?; let addr = sockaddr_to_addr(&storage, len as usize)?; Ok((TcpStream { inner: sock }, addr)) } diff --git a/library/std/src/sys_common/wtf8.rs b/library/std/src/sys_common/wtf8.rs index 554e07c1e59b..19d4c94f4509 100644 --- a/library/std/src/sys_common/wtf8.rs +++ b/library/std/src/sys_common/wtf8.rs @@ -26,7 +26,6 @@ use crate::borrow::Cow; use crate::collections::TryReserveError; use crate::hash::{Hash, Hasher}; use crate::iter::FusedIterator; -use crate::ptr::addr_of_mut; use crate::rc::Rc; use crate::sync::Arc; use crate::sys_common::AsInner; @@ -1055,6 +1054,6 @@ unsafe impl CloneToUninit for Wtf8 { #[cfg_attr(debug_assertions, track_caller)] unsafe fn clone_to_uninit(&self, dst: *mut Self) { // SAFETY: we're just a wrapper around [u8] - unsafe { self.bytes.clone_to_uninit(addr_of_mut!((*dst).bytes)) } + unsafe { self.bytes.clone_to_uninit(&raw mut (*dst).bytes) } } } diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index a53e3565dfe3..41f02af93668 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -165,7 +165,6 @@ use crate::marker::PhantomData; use crate::mem::{self, ManuallyDrop, forget}; use crate::num::NonZero; use crate::pin::Pin; -use crate::ptr::addr_of_mut; use crate::sync::Arc; use crate::sync::atomic::{AtomicUsize, Ordering}; use crate::sys::sync::Parker; @@ -1386,9 +1385,9 @@ impl Thread { let inner = unsafe { let mut arc = Arc::::new_uninit(); let ptr = Arc::get_mut_unchecked(&mut arc).as_mut_ptr(); - addr_of_mut!((*ptr).name).write(name); - addr_of_mut!((*ptr).id).write(ThreadId::new()); - Parker::new_in_place(addr_of_mut!((*ptr).parker)); + (&raw mut (*ptr).name).write(name); + (&raw mut (*ptr).id).write(ThreadId::new()); + Parker::new_in_place(&raw mut (*ptr).parker); Pin::new_unchecked(arc.assume_init()) }; diff --git a/library/test/src/lib.rs b/library/test/src/lib.rs index ebbe50cc6515..4b2c65cfdf54 100644 --- a/library/test/src/lib.rs +++ b/library/test/src/lib.rs @@ -18,6 +18,7 @@ #![doc(test(attr(deny(warnings))))] #![doc(rust_logo)] #![feature(rustdoc_internals)] +#![feature(file_buffered)] #![feature(internal_output_capture)] #![feature(staged_api)] #![feature(process_exitcode_internals)] diff --git a/library/test/src/term/terminfo/mod.rs b/library/test/src/term/terminfo/mod.rs index ac10ec2b850e..974b8afd598d 100644 --- a/library/test/src/term/terminfo/mod.rs +++ b/library/test/src/term/terminfo/mod.rs @@ -3,9 +3,8 @@ use std::collections::HashMap; use std::fs::File; use std::io::prelude::*; -use std::io::{self, BufReader}; use std::path::Path; -use std::{env, error, fmt}; +use std::{env, error, fmt, io}; use parm::{Param, Variables, expand}; use parser::compiled::{msys_terminfo, parse}; @@ -102,8 +101,7 @@ impl TermInfo { } // Keep the metadata small fn _from_path(path: &Path) -> Result { - let file = File::open(path).map_err(Error::IoError)?; - let mut reader = BufReader::new(file); + let mut reader = File::open_buffered(path).map_err(Error::IoError)?; parse(&mut reader, false).map_err(Error::MalformedTerminfo) } } diff --git a/library/unwind/src/lib.rs b/library/unwind/src/lib.rs index 26ed00bfbd53..46026324d2f8 100644 --- a/library/unwind/src/lib.rs +++ b/library/unwind/src/lib.rs @@ -23,6 +23,7 @@ cfg_if::cfg_if! { target_os = "none", target_os = "espidf", target_os = "rtems", + target_os = "nuttx", ))] { // These "unix" family members do not have unwinder. } else if #[cfg(any( diff --git a/library/unwind/src/libunwind.rs b/library/unwind/src/libunwind.rs index 1d856ce1879a..715f8b57876a 100644 --- a/library/unwind/src/libunwind.rs +++ b/library/unwind/src/libunwind.rs @@ -222,14 +222,14 @@ if #[cfg(any(target_vendor = "apple", target_os = "netbsd", not(target_arch = "a pub unsafe fn _Unwind_GetGR(ctx: *mut _Unwind_Context, reg_index: c_int) -> _Unwind_Word { let mut val: _Unwind_Word = core::ptr::null(); _Unwind_VRS_Get(ctx, _UVRSC_CORE, reg_index as _Unwind_Word, _UVRSD_UINT32, - core::ptr::addr_of_mut!(val) as *mut c_void); + (&raw mut val) as *mut c_void); val } pub unsafe fn _Unwind_SetGR(ctx: *mut _Unwind_Context, reg_index: c_int, value: _Unwind_Word) { let mut value = value; _Unwind_VRS_Set(ctx, _UVRSC_CORE, reg_index as _Unwind_Word, _UVRSD_UINT32, - core::ptr::addr_of_mut!(value) as *mut c_void); + (&raw mut value) as *mut c_void); } pub unsafe fn _Unwind_GetIP(ctx: *mut _Unwind_Context) diff --git a/src/bootstrap/mk/Makefile.in b/src/bootstrap/mk/Makefile.in index 3fa2b3c22921..92c8f5dc452f 100644 --- a/src/bootstrap/mk/Makefile.in +++ b/src/bootstrap/mk/Makefile.in @@ -95,7 +95,7 @@ install: tidy: $(Q)$(BOOTSTRAP) test --stage 2 src/tools/tidy $(BOOTSTRAP_ARGS) prepare: - $(Q)$(BOOTSTRAP) build --stage 2 nonexistent/path/to/trigger/cargo/metadata + $(Q)$(BOOTSTRAP) build --stage 2 --dry-run ## MSVC native builders diff --git a/src/bootstrap/src/core/build_steps/doc.rs b/src/bootstrap/src/core/build_steps/doc.rs index 919a42180d75..3d504c3771fb 100644 --- a/src/bootstrap/src/core/build_steps/doc.rs +++ b/src/bootstrap/src/core/build_steps/doc.rs @@ -63,7 +63,7 @@ macro_rules! book { src: builder.src.join($path), parent: Some(self), languages: $lang.into(), - rustdoc: None, + rustdoc_compiler: None, }) } } @@ -113,7 +113,7 @@ impl Step for UnstableBook { src: builder.md_doc_out(self.target).join("unstable-book"), parent: Some(self), languages: vec![], - rustdoc: None, + rustdoc_compiler: None, }) } } @@ -125,7 +125,7 @@ struct RustbookSrc { src: PathBuf, parent: Option

, languages: Vec<&'static str>, - rustdoc: Option, + rustdoc_compiler: Option, } impl Step for RustbookSrc

{ @@ -157,7 +157,9 @@ impl Step for RustbookSrc

{ let _ = fs::remove_dir_all(&out); let mut rustbook_cmd = builder.tool_cmd(Tool::Rustbook); - if let Some(mut rustdoc) = self.rustdoc { + + if let Some(compiler) = self.rustdoc_compiler { + let mut rustdoc = builder.rustdoc(compiler); rustdoc.pop(); let old_path = env::var_os("PATH").unwrap_or_default(); let new_path = @@ -165,6 +167,7 @@ impl Step for RustbookSrc

{ .expect("could not add rustdoc to PATH"); rustbook_cmd.env("PATH", new_path); + builder.add_rustc_lib_path(compiler, &mut rustbook_cmd); } rustbook_cmd.arg("build").arg(&src).arg("-d").arg(&out).run(builder); @@ -240,7 +243,7 @@ impl Step for TheBook { src: absolute_path.clone(), parent: Some(self), languages: vec![], - rustdoc: None, + rustdoc_compiler: None, }); // building older edition redirects @@ -253,7 +256,7 @@ impl Step for TheBook { // treat the other editions as not having a parent. parent: Option::::None, languages: vec![], - rustdoc: None, + rustdoc_compiler: None, }); } @@ -1236,7 +1239,7 @@ impl Step for RustcBook { src: out_base, parent: Some(self), languages: vec![], - rustdoc: None, + rustdoc_compiler: None, }); } } @@ -1270,16 +1273,15 @@ impl Step for Reference { // This is needed for generating links to the standard library using // the mdbook-spec plugin. builder.ensure(compile::Std::new(self.compiler, builder.config.build)); - let rustdoc = builder.rustdoc(self.compiler); // Run rustbook/mdbook to generate the HTML pages. builder.ensure(RustbookSrc { target: self.target, name: "reference".to_owned(), src: builder.src.join("src/doc/reference"), + rustdoc_compiler: Some(self.compiler), parent: Some(self), languages: vec![], - rustdoc: Some(rustdoc), }); } } diff --git a/src/bootstrap/src/core/build_steps/setup.rs b/src/bootstrap/src/core/build_steps/setup.rs index f4c5fe5ff941..0ee2cb451f39 100644 --- a/src/bootstrap/src/core/build_steps/setup.rs +++ b/src/bootstrap/src/core/build_steps/setup.rs @@ -46,6 +46,7 @@ static SETTINGS_HASHES: &[&str] = &[ "47d227f424bf889b0d899b9cc992d5695e1b78c406e183cd78eafefbe5488923", "b526bd58d0262dd4dda2bff5bc5515b705fb668a46235ace3e057f807963a11a", "828666b021d837a33e78d870b56d34c88a5e2c85de58b693607ec574f0c27000", + "811fb3b063c739d261fd8590dd30242e117908f5a095d594fa04585daa18ec4d", ]; static RUST_ANALYZER_SETTINGS: &str = include_str!("../../../../etc/rust_analyzer_settings.json"); diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 8f076e5554ee..870fe6a9f165 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -1730,8 +1730,23 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the let is_rustdoc = suite.ends_with("rustdoc-ui") || suite.ends_with("rustdoc-js"); if mode == "run-make" { - let cargo = builder.ensure(tool::Cargo { compiler, target: compiler.host }); - cmd.arg("--cargo-path").arg(cargo); + let cargo_path = if builder.top_stage == 0 { + // If we're using `--stage 0`, we should provide the bootstrap cargo. + builder.initial_cargo.clone() + } else { + // We need to properly build cargo using the suitable stage compiler. + + // HACK: currently tool stages are off-by-one compared to compiler stages, i.e. if + // you give `tool::Cargo` a stage 1 rustc, it will cause stage 2 rustc to be built + // and produce a cargo built with stage 2 rustc. To fix this, we need to chop off + // the compiler stage by 1 to align with expected `./x test run-make --stage N` + // behavior, i.e. we need to pass `N - 1` compiler stage to cargo. See also Miri + // which does a similar hack. + let compiler = builder.compiler(builder.top_stage - 1, compiler.host); + builder.ensure(tool::Cargo { compiler, target: compiler.host }) + }; + + cmd.arg("--cargo-path").arg(cargo_path); } // Avoid depending on rustdoc when we don't need it. @@ -2088,8 +2103,6 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the cmd.arg("--rustfix-coverage"); } - cmd.env("BOOTSTRAP_CARGO", &builder.initial_cargo); - cmd.arg("--channel").arg(&builder.config.channel); if !builder.config.omit_git_hash { diff --git a/src/bootstrap/src/core/sanity.rs b/src/bootstrap/src/core/sanity.rs index 5425739c56fc..888ba8e2a3f7 100644 --- a/src/bootstrap/src/core/sanity.rs +++ b/src/bootstrap/src/core/sanity.rs @@ -294,19 +294,19 @@ than building it. } } - for host in &build.hosts { - if !build.config.dry_run() { + if !build.config.dry_run() { + for host in &build.hosts { cmd_finder.must_have(build.cxx(*host).unwrap()); - } - if build.config.llvm_enabled(*host) { - // Externally configured LLVM requires FileCheck to exist - let filecheck = build.llvm_filecheck(build.build); - if !filecheck.starts_with(&build.out) - && !filecheck.exists() - && build.config.codegen_tests - { - panic!("FileCheck executable {filecheck:?} does not exist"); + if build.config.llvm_enabled(*host) { + // Externally configured LLVM requires FileCheck to exist + let filecheck = build.llvm_filecheck(build.build); + if !filecheck.starts_with(&build.out) + && !filecheck.exists() + && build.config.codegen_tests + { + panic!("FileCheck executable {filecheck:?} does not exist"); + } } } } @@ -355,7 +355,8 @@ than building it. // There are three builds of cmake on windows: MSVC, MinGW, and // Cygwin. The Cygwin build does not have generators for Visual // Studio, so detect that here and error. - let out = command("cmake").arg("--help").run_capture_stdout(build).stdout(); + let out = + command("cmake").arg("--help").run_always().run_capture_stdout(build).stdout(); if !out.contains("Visual Studio") { panic!( " diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index 88122237858d..7bf5b4e23d29 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -596,15 +596,6 @@ impl Build { _ => (), } - { - let builder = builder::Builder::new(self); - if let Some(path) = builder.paths.first() { - if path == Path::new("nonexistent/path/to/trigger/cargo/metadata") { - return; - } - } - } - if !self.config.dry_run() { { // We first do a dry-run. This is a sanity-check to ensure that diff --git a/src/doc/book b/src/doc/book index e7d217be2a75..99cf75a5414f 160000 --- a/src/doc/book +++ b/src/doc/book @@ -1 +1 @@ -Subproject commit e7d217be2a75ef1753f0988d6ccaba4d7e376259 +Subproject commit 99cf75a5414fa8adbe3974bd0836661ca901708f diff --git a/src/doc/edition-guide b/src/doc/edition-guide index b3ca7ade0f87..c7ebae25cb48 160000 --- a/src/doc/edition-guide +++ b/src/doc/edition-guide @@ -1 +1 @@ -Subproject commit b3ca7ade0f87d7e3fb538776defc5b2cc4188172 +Subproject commit c7ebae25cb4801a31b6f05353f6d85bfa6feedd1 diff --git a/src/doc/reference b/src/doc/reference index 687faf9958c5..24fb2687cdbc 160000 --- a/src/doc/reference +++ b/src/doc/reference @@ -1 +1 @@ -Subproject commit 687faf9958c52116d003b41dfd29cc1cf44f5311 +Subproject commit 24fb2687cdbc54fa18ae4acf5d879cfceca77b2c diff --git a/src/doc/rustc-dev-guide b/src/doc/rustc-dev-guide index 0ed9229f5b6f..555f3de2fa0d 160000 --- a/src/doc/rustc-dev-guide +++ b/src/doc/rustc-dev-guide @@ -1 +1 @@ -Subproject commit 0ed9229f5b6f7824b333beabd7e3d5ba4b9bd971 +Subproject commit 555f3de2fa0d61c4294b74d245f1cbad6fcbf589 diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index 827a7065f3e0..207eb5d6d4f7 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -138,7 +138,6 @@ target | std | notes [`aarch64-apple-ios`](platform-support/apple-ios.md) | ✓ | ARM64 iOS [`aarch64-apple-ios-macabi`](platform-support/apple-ios-macabi.md) | ✓ | Mac Catalyst on ARM64 [`aarch64-apple-ios-sim`](platform-support/apple-ios.md) | ✓ | Apple iOS Simulator on ARM64 -`aarch64-fuchsia` | ✓ | Alias for `aarch64-unknown-fuchsia` [`aarch64-unknown-fuchsia`](platform-support/fuchsia.md) | ✓ | ARM64 Fuchsia [`aarch64-linux-android`](platform-support/android.md) | ✓ | ARM64 Android [`aarch64-pc-windows-gnullvm`](platform-support/pc-windows-gnullvm.md) | ✓ | ARM64 MinGW (Windows 10+), LLVM ABI @@ -199,7 +198,6 @@ target | std | notes [`x86_64-apple-ios`](platform-support/apple-ios.md) | ✓ | 64-bit x86 iOS [`x86_64-apple-ios-macabi`](platform-support/apple-ios-macabi.md) | ✓ | Mac Catalyst on x86_64 [`x86_64-fortanix-unknown-sgx`](platform-support/x86_64-fortanix-unknown-sgx.md) | ✓ | [Fortanix ABI] for 64-bit Intel SGX -`x86_64-fuchsia` | ✓ | Alias for `x86_64-unknown-fuchsia` [`x86_64-unknown-fuchsia`](platform-support/fuchsia.md) | ✓ | 64-bit x86 Fuchsia [`x86_64-linux-android`](platform-support/android.md) | ✓ | 64-bit x86 Android `x86_64-pc-solaris` | ✓ | 64-bit Solaris 11, illumos @@ -315,6 +313,7 @@ target | std | host | notes `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`](platform-support/vxworks.md) | ✓ | | [^x86_32-floats-return-ABI] +[`loongarch64-unknown-linux-ohos`](platform-support/openharmony.md) | ✓ | | LoongArch64 OpenHarmony [`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 @@ -359,12 +358,14 @@ target | std | host | notes [`riscv32imc-esp-espidf`](platform-support/esp-idf.md) | ✓ | | RISC-V ESP-IDF [`riscv32imac-esp-espidf`](platform-support/esp-idf.md) | ✓ | | RISC-V ESP-IDF [`riscv32imafc-esp-espidf`](platform-support/esp-idf.md) | ✓ | | RISC-V ESP-IDF +[`riscv32-wrs-vxworks`](platform-support/vxworks.md) | ✓ | | [`riscv64gc-unknown-hermit`](platform-support/hermit.md) | ✓ | | RISC-V Hermit `riscv64gc-unknown-freebsd` | | | RISC-V FreeBSD `riscv64gc-unknown-fuchsia` | | | RISC-V Fuchsia [`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 +[`riscv64-wrs-vxworks`](platform-support/vxworks.md) | ✓ | | `s390x-unknown-linux-musl` | | | S390x Linux (kernel 3.2, musl 1.2.3) `sparc-unknown-linux-gnu` | ✓ | | 32-bit SPARC Linux [`sparc-unknown-none-elf`](./platform-support/sparc-unknown-none-elf.md) | * | | Bare 32-bit SPARC V7+ diff --git a/src/doc/rustc/src/platform-support/openharmony.md b/src/doc/rustc/src/platform-support/openharmony.md index b2ddbfdfa291..1632f44aeecc 100644 --- a/src/doc/rustc/src/platform-support/openharmony.md +++ b/src/doc/rustc/src/platform-support/openharmony.md @@ -2,6 +2,14 @@ **Tier: 2** +* aarch64-unknown-linux-ohos +* armv7-unknown-linux-ohos +* x86_64-unknown-linux-ohos + +**Tier: 3** + +* loongarch64-unknown-linux-ohos + Targets for the [OpenHarmony](https://gitee.com/openharmony/docs/) operating system. diff --git a/src/doc/rustc/src/platform-support/vxworks.md b/src/doc/rustc/src/platform-support/vxworks.md index f1860346c8f2..6aa3d8b73618 100644 --- a/src/doc/rustc/src/platform-support/vxworks.md +++ b/src/doc/rustc/src/platform-support/vxworks.md @@ -14,6 +14,8 @@ Target triplets available: - `powerpc-wrs-vxworks` - `powerpc64-wrs-vxworks` - `powerpc-wrs-vxworks-spe` +- `riscv32-wrs-vxworks` +- `riscv64-wrs-vxworks` ## Target maintainers diff --git a/src/etc/rust_analyzer_settings.json b/src/etc/rust_analyzer_settings.json index d329fe997cd7..a20105f0ef37 100644 --- a/src/etc/rust_analyzer_settings.json +++ b/src/etc/rust_analyzer_settings.json @@ -10,6 +10,7 @@ ], "rust-analyzer.linkedProjects": [ "Cargo.toml", + "library/Cargo.toml", "src/tools/x/Cargo.toml", "src/bootstrap/Cargo.toml", "src/tools/rust-analyzer/Cargo.toml", diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 701bf6180967..b7d6b3dda72b 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -373,7 +373,7 @@ pub(crate) fn build_impls( let tcx = cx.tcx; // for each implementation of an item represented by `did`, build the clean::Item for that impl - for &did in tcx.inherent_impls(did).into_iter().flatten() { + for &did in tcx.inherent_impls(did).into_iter() { cx.with_param_env(did, |cx| { build_impl(cx, did, attrs, ret); }); @@ -388,7 +388,7 @@ pub(crate) fn build_impls( if tcx.has_attr(did, sym::rustc_has_incoherent_inherent_impls) { let type_ = if tcx.is_trait(did) { SimplifiedType::Trait(did) } else { SimplifiedType::Adt(did) }; - for &did in tcx.incoherent_impls(type_).into_iter().flatten() { + for &did in tcx.incoherent_impls(type_).into_iter() { cx.with_param_env(did, |cx| { build_impl(cx, did, attrs, ret); }); diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 31710bc014af..bc6b5a7d3e31 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -384,7 +384,45 @@ fn is_field_vis_inherited(tcx: TyCtxt<'_>, def_id: DefId) -> bool { impl Item { pub(crate) fn stability(&self, tcx: TyCtxt<'_>) -> Option { - self.def_id().and_then(|did| tcx.lookup_stability(did)) + let (mut def_id, mut stability) = if let Some(inlined) = self.inline_stmt_id { + let inlined_def_id = inlined.to_def_id(); + if let Some(stability) = tcx.lookup_stability(inlined_def_id) { + (inlined_def_id, stability) + } else { + // For re-exports into crates without `staged_api`, reuse the original stability. + // This is necessary, because we always want to mark unstable items. + let def_id = self.def_id()?; + return tcx.lookup_stability(def_id); + } + } else { + let def_id = self.def_id()?; + let stability = tcx.lookup_stability(def_id)?; + (def_id, stability) + }; + + let StabilityLevel::Stable { mut since, allowed_through_unstable_modules: false } = + stability.level + else { + return Some(stability); + }; + + // If any of the item's ancestors was stabilized later or is still unstable, + // then report the ancestor's stability instead. + while let Some(parent_def_id) = tcx.opt_parent(def_id) { + if let Some(parent_stability) = tcx.lookup_stability(parent_def_id) { + match parent_stability.level { + StabilityLevel::Unstable { .. } => return Some(parent_stability), + StabilityLevel::Stable { since: parent_since, .. } => { + if parent_since > since { + stability = parent_stability; + since = parent_since; + } + } + } + } + def_id = parent_def_id; + } + Some(stability) } pub(crate) fn const_stability(&self, tcx: TyCtxt<'_>) -> Option { @@ -1863,7 +1901,7 @@ impl PrimitiveType { .get(self) .into_iter() .flatten() - .flat_map(move |&simp| tcx.incoherent_impls(simp).into_iter().flatten()) + .flat_map(move |&simp| tcx.incoherent_impls(simp).into_iter()) .copied() } @@ -1871,7 +1909,7 @@ impl PrimitiveType { Self::simplified_types() .values() .flatten() - .flat_map(move |&simp| tcx.incoherent_impls(simp).into_iter().flatten()) + .flat_map(move |&simp| tcx.incoherent_impls(simp).into_iter()) .copied() } diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs index 162537992157..12246b0d416a 100644 --- a/src/librustdoc/html/render/write_shared.rs +++ b/src/librustdoc/html/render/write_shared.rs @@ -16,7 +16,7 @@ use std::cell::RefCell; use std::ffi::OsString; use std::fs::File; -use std::io::{self, BufWriter, Write as _}; +use std::io::{self, Write as _}; use std::iter::once; use std::marker::PhantomData; use std::path::{Component, Path, PathBuf}; @@ -1020,8 +1020,7 @@ where for part in parts { template.append(part); } - let file = try_err!(File::create(&path), &path); - let mut file = BufWriter::new(file); + let mut file = try_err!(File::create_buffered(&path), &path); try_err!(write!(file, "{template}"), &path); try_err!(file.flush(), &path); } diff --git a/src/librustdoc/html/sources.rs b/src/librustdoc/html/sources.rs index c8c45b8fa322..9ba0fb7b83c9 100644 --- a/src/librustdoc/html/sources.rs +++ b/src/librustdoc/html/sources.rs @@ -26,8 +26,11 @@ pub(crate) fn render(cx: &mut Context<'_>, krate: &clean::Crate) -> Result<(), E let dst = cx.dst.join("src").join(krate.name(cx.tcx()).as_str()); cx.shared.ensure_dir(&dst)?; + let crate_name = krate.name(cx.tcx()); + let crate_name = crate_name.as_str(); - let mut collector = SourceCollector { dst, cx, emitted_local_sources: FxHashSet::default() }; + let mut collector = + SourceCollector { dst, cx, emitted_local_sources: FxHashSet::default(), crate_name }; collector.visit_crate(krate); Ok(()) } @@ -115,6 +118,8 @@ struct SourceCollector<'a, 'tcx> { /// Root destination to place all HTML output into dst: PathBuf, emitted_local_sources: FxHashSet, + + crate_name: &'a str, } impl DocVisitor for SourceCollector<'_, '_> { @@ -210,6 +215,9 @@ impl SourceCollector<'_, '_> { }, ); + let src_fname = p.file_name().expect("source has no filename").to_os_string(); + let mut fname = src_fname.clone(); + let root_path = PathBuf::from("../../").join(root_path.into_inner()); let mut root_path = root_path.to_string_lossy(); if let Some(c) = root_path.as_bytes().last() @@ -217,12 +225,12 @@ impl SourceCollector<'_, '_> { { root_path += "/"; } + let mut file_path = Path::new(&self.crate_name).join(&*cur.borrow()); + file_path.push(&fname); + fname.push(".html"); let mut cur = self.dst.join(cur.into_inner()); shared.ensure_dir(&cur)?; - let src_fname = p.file_name().expect("source has no filename").to_os_string(); - let mut fname = src_fname.clone(); - fname.push(".html"); cur.push(&fname); let title = format!("{} - source", src_fname.to_string_lossy()); @@ -250,7 +258,7 @@ impl SourceCollector<'_, '_> { cx, &root_path, highlight::DecorationInfo::default(), - SourceContext::Standalone, + SourceContext::Standalone { file_path }, ) }, &shared.style_files, @@ -312,10 +320,11 @@ struct ScrapedSource<'a, Code: std::fmt::Display> { struct Source { lines: RangeInclusive, code_html: Code, + file_path: Option<(String, String)>, } pub(crate) enum SourceContext<'a> { - Standalone, + Standalone { file_path: PathBuf }, Embedded(ScrapedInfo<'a>), } @@ -344,9 +353,19 @@ pub(crate) fn print_src( }); let lines = s.lines().count(); match source_context { - SourceContext::Standalone => { - Source { lines: (1..=lines), code_html: code }.render_into(&mut writer).unwrap() + SourceContext::Standalone { file_path } => Source { + lines: (1..=lines), + code_html: code, + file_path: if let Some(file_name) = file_path.file_name() + && let Some(file_path) = file_path.parent() + { + Some((file_path.display().to_string(), file_name.display().to_string())) + } else { + None + }, } + .render_into(&mut writer) + .unwrap(), SourceContext::Embedded(info) => { let lines = (1 + info.offset)..=(lines + info.offset); ScrapedSource { info, lines, code_html: code }.render_into(&mut writer).unwrap(); diff --git a/src/librustdoc/html/static/css/noscript.css b/src/librustdoc/html/static/css/noscript.css index e62b16267f10..477a79d63e94 100644 --- a/src/librustdoc/html/static/css/noscript.css +++ b/src/librustdoc/html/static/css/noscript.css @@ -61,6 +61,8 @@ nav.sub { --copy-path-img-hover-filter: invert(35%); --code-example-button-color: #7f7f7f; --code-example-button-hover-color: #595959; + --settings-menu-filter: invert(50%); + --settings-menu-hover-filter: invert(35%); --codeblock-error-hover-color: rgb(255, 0, 0); --codeblock-error-color: rgba(255, 0, 0, .5); --codeblock-ignore-hover-color: rgb(255, 142, 0); @@ -87,7 +89,6 @@ nav.sub { --search-tab-button-not-selected-background: #e6e6e6; --search-tab-button-selected-border-top-color: #0089ff; --search-tab-button-selected-background: #fff; - --settings-menu-filter: none; --stab-background-color: #fff5d6; --stab-code-color: #000; --code-highlight-kw-color: #8959a8; @@ -192,6 +193,8 @@ nav.sub { --search-tab-button-not-selected-background: #252525; --search-tab-button-selected-border-top-color: #0089ff; --search-tab-button-selected-background: #353535; + --settings-menu-filter: invert(50%); + --settings-menu-hover-filter: invert(65%); --stab-background-color: #314559; --stab-code-color: #e6e1cf; --code-highlight-kw-color: #ab8ac1; diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index 04b0eba74509..40391b1b4dfe 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -34,6 +34,8 @@ xmlns="http://www.w3.org/2000/svg" fill="black" height="18px">\ '); --button-left-margin: 4px; --button-border-radius: 2px; + --toolbar-button-border-radius: 6px; + --code-block-border-radius: 6px; } /* See FiraSans-LICENSE.txt for the Fira Sans license. */ @@ -165,7 +167,7 @@ h1, h2, h3, h4 { .main-heading h1 { margin: 0; padding: 0; - flex-grow: 1; + grid-area: main-heading-h1; /* We use overflow-wrap: break-word for Safari, which doesn't recognize `anywhere`: https://developer.mozilla.org/en-US/docs/Web/CSS/overflow-wrap */ overflow-wrap: break-word; @@ -174,10 +176,28 @@ h1, h2, h3, h4 { overflow-wrap: anywhere; } .main-heading { - display: flex; - flex-wrap: wrap; + position: relative; + display: grid; + grid-template-areas: + "main-heading-breadcrumbs main-heading-breadcrumbs" + "main-heading-h1 main-heading-toolbar" + "main-heading-sub-heading main-heading-toolbar"; + grid-template-columns: 1fr max-content; + grid-template-rows: 25px min-content min-content; padding-bottom: 6px; - margin-bottom: 15px; + margin-bottom: 11px; +} +.rustdoc-breadcrumbs { + grid-area: main-heading-breadcrumbs; + height: 25px; + line-height: 1.25; + display: flex; + align-items: end; +} +.rustdoc-breadcrumbs a { + padding: 4px 0; + margin: -4px 0; + z-index: 1; } /* The only headings that get underlines are: Markdown-generated headings within the top-doc @@ -218,11 +238,13 @@ h1, h2, h3, h4, h5, h6, .search-results .result-name, .item-name > a, .out-of-band, +.sub-heading, span.since, a.src, -#help-button > a, +rustdoc-toolbar, summary.hideme, .scraped-example-list, +.rustdoc-breadcrumbs, /* This selector is for the items listed in the "all items" page. */ ul.all-items { font-family: "Fira Sans", Arial, NanumBarunGothic, sans-serif; @@ -891,14 +913,27 @@ both the code example and the line numbers, so we need to remove the radius in t overflow-x: auto; } -.out-of-band { +.sub-heading { + font-size: 1rem; flex-grow: 0; - font-size: 1.125rem; + grid-area: main-heading-sub-heading; + line-height: 1.25; + padding-bottom: 4px; +} + +.main-heading rustdoc-toolbar, .main-heading .out-of-band { + grid-area: main-heading-toolbar; +} +rustdoc-toolbar { + display: flex; + flex-direction: row; + flex-wrap: nowrap; } .docblock code, .docblock-short code, pre, .rustdoc.src .example-wrap, .example-wrap .src-line-numbers { background-color: var(--code-block-background-color); + border-radius: var(--code-block-border-radius); } #main-content { @@ -945,7 +980,7 @@ div.where { nav.sub { flex-grow: 1; flex-flow: row nowrap; - margin: 4px 0 25px 0; + margin: 4px 0 0 0; display: flex; align-items: center; } @@ -956,7 +991,7 @@ nav.sub { flex-grow: 1; } .src nav.sub { - margin: 0 0 15px 0; + margin: 0 0 -10px 0; } .section-header { @@ -1066,6 +1101,11 @@ table, with boxes (i.e. from the flex layout) */ align-items: baseline; } +.search-results-title + .sub-heading { + color: var(--main-color); + display: flex; + align-items: center; +} #crate-search-div { /* ensures that 100% in properties of #crate-search-div:after are relative to the size of this div */ @@ -1289,10 +1329,16 @@ so that we can apply CSS-filters to change the arrow color in themes */ border-color: var(--settings-input-color) !important; } +#settings.popover { + --popover-arrow-offset: 202px; + top: calc(100% - 16px); +} + /* use larger max-width for help popover, but not for help.html */ #help.popover { max-width: 600px; - --popover-arrow-offset: 48px; + --popover-arrow-offset: 118px; + top: calc(100% - 16px); } #help dt { @@ -1300,10 +1346,15 @@ so that we can apply CSS-filters to change the arrow color in themes */ clear: left; margin-right: 0.5rem; } +#help dd { + margin-bottom: 0.5rem; +} #help span.top, #help span.bottom { text-align: center; display: block; font-size: 1.125rem; + padding: 0 0.5rem; + text-wrap-style: balance; } #help span.top { margin: 10px 0; @@ -1315,10 +1366,13 @@ so that we can apply CSS-filters to change the arrow color in themes */ clear: both; border-top: 1px solid var(--border-color); } +.side-by-side { + display: flex; + margin-bottom: 20px; +} .side-by-side > div { width: 50%; - float: left; - padding: 0 20px 20px 17px; + padding: 0 20px 0 17px; } .item-info .stab { @@ -1381,7 +1435,9 @@ so that we can apply CSS-filters to change the arrow color in themes */ } .rightside:not(a), -.out-of-band { +.out-of-band, +.sub-heading, +rustdoc-toolbar { color: var(--right-side-color); } @@ -1595,8 +1651,8 @@ instead, we check that it's not a "finger" cursor. display: block; } -.out-of-band > span.since { - font-size: 1.25rem; +.main-heading span.since::before { + content: "Since "; } .sub-variant h4 { @@ -1631,7 +1687,6 @@ instead, we check that it's not a "finger" cursor. padding-right: 3px; background-color: var(--target-background-color); border-right: 3px solid var(--target-border-color); - animation: 0.65s cubic-bezier(0, 0, 0.1, 1.0) 0.1s targetfadein; } .code-header a.tooltip { @@ -1656,12 +1711,18 @@ a.tooltip:hover::after { content: "\00a0"; } -/* This animation is layered onto the mistake-proofing delay for dismissing - a hovered tooltip, to ensure it feels responsive even with the delay. - */ -.fade-out { - opacity: 0; - transition: opacity 0.45s cubic-bezier(0, 0, 0.1, 1.0); +@media not (prefers-reduced-motion) { + :target { + animation: 0.65s cubic-bezier(0, 0, 0.1, 1.0) 0.1s targetfadein; + } + + /* This animation is layered onto the mistake-proofing delay for dismissing + a hovered tooltip, to ensure it feels responsive even with the delay. + */ + .fade-out { + opacity: 0; + transition: opacity 0.45s cubic-bezier(0, 0, 0.1, 1.0); + } } .popover.tooltip .content { @@ -1698,6 +1759,7 @@ a.tooltip:hover::after { } #search-tabs { + margin-top: 0.25rem; display: flex; flex-direction: row; gap: 1px; @@ -1759,9 +1821,10 @@ a.tooltip:hover::after { border-bottom: 1px solid var(--border-color); } -#settings-menu, #help-button { +#settings-menu, #help-button, button#toggle-all-docs { margin-left: var(--button-left-margin); display: flex; + line-height: 1.25; } #sidebar-button { display: none; @@ -1785,33 +1848,36 @@ a.tooltip:hover::after { .hide-sidebar .src #sidebar-button { position: static; } -#settings-menu > a, #help-button > a, #sidebar-button > a { +#settings-menu > a, #help-button > a, #sidebar-button > a, button#toggle-all-docs { display: flex; align-items: center; justify-content: center; - background-color: var(--button-background-color); - border: 1px solid var(--border-color); + flex-direction: column; + border: 1px solid transparent; border-radius: var(--button-border-radius); - color: var(--settings-button-color); - /* Rare exception to specifying font sizes in rem. Since this is acting - as an icon, it's okay to specify their sizes in pixels. */ - font-size: 20px; + color: var(--main-color); +} +#settings-menu > a, #help-button > a, button#toggle-all-docs { + width: 80px; + border-radius: var(--toolbar-button-border-radius); +} +#sidebar-button > a { + background-color: var(--button-background-color); + border-color: var(--border-color); width: 33px; } -#settings-menu > a:hover, #settings-menu > a:focus, -#help-button > a:hover, #help-button > a:focus, -#sidebar-button > a:hover, #sidebar-button > a:focus { +#settings-menu > a:hover, #settings-menu > a:focus-visible, +#help-button > a:hover, #help-button > a:focus-visible, +#sidebar-button > a:hover, #sidebar-button > a:focus-visible, +button#toggle-all-docs:hover, button#toggle-all-docs:focus-visible { border-color: var(--settings-button-border-focus); + text-decoration: none; } -#settings-menu > a { - line-height: 0; - font-size: 0; -} #settings-menu > a:before { /* Wheel */ - content: url('data:image/svg+xml,\ '); - width: 22px; - height: 22px; + width: 18px; + height: 18px; filter: var(--settings-menu-filter); } +button#toggle-all-docs:before { + /* Custom arrow icon */ + content: url('data:image/svg+xml,\ + '); + width: 18px; + height: 18px; + filter: var(--settings-menu-filter); +} + +#help-button > a:before { + /* Question mark with circle */ + content: url('data:image/svg+xml,\ + \ + ?'); + width: 18px; + height: 18px; + filter: var(--settings-menu-filter); +} + +button#toggle-all-docs:before, +#help-button > a:before, +#settings-menu > a:before { + filter: var(--settings-menu-filter); + margin: 8px; +} + +@media not (pointer: coarse) { + button#toggle-all-docs:hover:before, + #help-button > a:hover:before, + #settings-menu > a:hover:before { + filter: var(--settings-menu-hover-filter); + } +} + +button[disabled]#toggle-all-docs { + opacity: 0.25; + border: solid 1px var(--main-background-color); + background-size: cover; +} + +button[disabled]#toggle-all-docs:hover { + border: solid 1px var(--main-background-color); + cursor: not-allowed; +} + +rustdoc-toolbar span.label { + font-size: 1rem; + flex-grow: 1; + padding-bottom: 4px; +} + #sidebar-button > a:before { /* sidebar resizer image */ content: url('data:image/svg+xml,\ - \ + \ \ '); width: 22px; @@ -1948,10 +2068,10 @@ details.toggle > summary.hideme > span { } details.toggle > summary::before { - /* toggle plus */ - background: url('data:image/svg+xml,') no-repeat top left; + /* arrow pointing left */ + background: url('data:image/svg+xml,\ + '); content: ""; cursor: pointer; width: 16px; @@ -2034,10 +2154,10 @@ details.toggle[open] > summary.hideme > span { } details.toggle[open] > summary::before { - /* toggle minus */ - background: url('data:image/svg+xml,') no-repeat top left; + /* arrow pointing down */ + background: url('data:image/svg+xml,\ + '); } details.toggle[open] > summary::after { @@ -2090,6 +2210,12 @@ However, it's not needed with smaller screen width because the doc/code block is #search-tabs .count { display: block; } + .side-by-side { + flex-direction: column-reverse; + } + .side-by-side > div { + width: auto; + } } /* @@ -2106,6 +2232,25 @@ in src-script.js and main.js scroll-margin-top: 45px; } + /* We don't display this button on mobile devices. */ + #copy-path { + display: none; + } + + /* Text label takes up too much space at this size. */ + rustdoc-toolbar span.label { + display: none; + } + #settings-menu > a, #help-button > a, button#toggle-all-docs { + width: 33px; + } + #settings.popover { + --popover-arrow-offset: 86px; + } + #help.popover { + --popover-arrow-offset: 48px; + } + .rustdoc { /* Sidebar should overlay main content, rather than pushing main content to the right. Turn off `display: flex` on the body element. */ @@ -2117,20 +2262,6 @@ in src-script.js and main.js padding-top: 0px; } - .main-heading { - flex-direction: column; - } - - .out-of-band { - text-align: left; - margin-left: initial; - padding: initial; - } - - .out-of-band .since::before { - content: "Since "; - } - /* Hide the logo and item name from the sidebar. Those are displayed in the mobile-topbar instead. */ .sidebar .logo-container, @@ -2163,6 +2294,9 @@ in src-script.js and main.js .src .search-form { margin-left: 40px; } + .src .main-heading { + margin-left: 8px; + } .hide-sidebar .search-form { margin-left: 32px; } @@ -2234,16 +2368,11 @@ in src-script.js and main.js left: -11px; } - /* We don't display these buttons on mobile devices. */ - #copy-path, #help-button { - display: none; - } - /* sidebar button becomes topbar button */ #sidebar-button > a:before { content: url('data:image/svg+xml,\ - \ + \ \ '); width: 22px; @@ -2305,7 +2434,7 @@ in src-script.js and main.js } .src nav.sub { - margin: 0; + margin: 0 0 -25px 0; padding: var(--nav-sub-mobile-padding); } } @@ -2543,6 +2672,8 @@ by default. --copy-path-img-hover-filter: invert(35%); --code-example-button-color: #7f7f7f; --code-example-button-hover-color: #595959; + --settings-menu-filter: invert(50%); + --settings-menu-hover-filter: invert(35%); --codeblock-error-hover-color: rgb(255, 0, 0); --codeblock-error-color: rgba(255, 0, 0, .5); --codeblock-ignore-hover-color: rgb(255, 142, 0); @@ -2569,7 +2700,6 @@ by default. --search-tab-button-not-selected-background: #e6e6e6; --search-tab-button-selected-border-top-color: #0089ff; --search-tab-button-selected-background: #fff; - --settings-menu-filter: none; --stab-background-color: #fff5d6; --stab-code-color: #000; --code-highlight-kw-color: #8959a8; @@ -2673,7 +2803,8 @@ by default. --search-tab-button-not-selected-background: #252525; --search-tab-button-selected-border-top-color: #0089ff; --search-tab-button-selected-background: #353535; - --settings-menu-filter: none; + --settings-menu-filter: invert(50%); + --settings-menu-hover-filter: invert(65%); --stab-background-color: #314559; --stab-code-color: #e6e1cf; --code-highlight-kw-color: #ab8ac1; @@ -2784,7 +2915,8 @@ Original by Dempfi (https://github.com/dempfi/ayu) --search-tab-button-not-selected-background: transparent !important; --search-tab-button-selected-border-top-color: none; --search-tab-button-selected-background: #141920 !important; - --settings-menu-filter: invert(100%); + --settings-menu-filter: invert(70%); + --settings-menu-hover-filter: invert(100%); --stab-background-color: #314559; --stab-code-color: #e6e1cf; --code-highlight-kw-color: #ff7733; diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js index 0eba80133df1..0d556e0a6827 100644 --- a/src/librustdoc/html/static/js/main.js +++ b/src/librustdoc/html/static/js/main.js @@ -1,6 +1,6 @@ // Local js definitions: /* global addClass, getSettingValue, hasClass, searchState, updateLocalStorage */ -/* global onEach, onEachLazy, removeClass, getVar */ +/* global onEachLazy, removeClass, getVar */ "use strict"; @@ -19,17 +19,25 @@ function resourcePath(basename, extension) { function hideMain() { addClass(document.getElementById(MAIN_ID), "hidden"); + const toggle = document.getElementById("toggle-all-docs"); + if (toggle) { + toggle.setAttribute("disabled", "disabled"); + } } function showMain() { - removeClass(document.getElementById(MAIN_ID), "hidden"); -} - -function blurHandler(event, parentElem, hideCallback) { - if (!parentElem.contains(document.activeElement) && - !parentElem.contains(event.relatedTarget) - ) { - hideCallback(); + const main = document.getElementById(MAIN_ID); + removeClass(main, "hidden"); + const mainHeading = main.querySelector(".main-heading"); + if (mainHeading && searchState.rustdocToolbar) { + if (searchState.rustdocToolbar.parentElement) { + searchState.rustdocToolbar.parentElement.removeChild(searchState.rustdocToolbar); + } + mainHeading.appendChild(searchState.rustdocToolbar); + } + const toggle = document.getElementById("toggle-all-docs"); + if (toggle) { + toggle.removeAttribute("disabled"); } } @@ -167,6 +175,14 @@ function switchDisplayedElement(elemToDisplay) { el.appendChild(elemToDisplay); hideMain(); removeClass(el, "hidden"); + + const mainHeading = elemToDisplay.querySelector(".main-heading"); + if (mainHeading && searchState.rustdocToolbar) { + if (searchState.rustdocToolbar.parentElement) { + searchState.rustdocToolbar.parentElement.removeChild(searchState.rustdocToolbar); + } + mainHeading.appendChild(searchState.rustdocToolbar); + } } function browserSupportsHistoryApi() { @@ -194,33 +210,36 @@ function preLoadCss(cssUrl) { document.head.append(script); } - getSettingsButton().onclick = event => { - if (event.ctrlKey || event.altKey || event.metaKey) { - return; - } - window.hideAllModals(false); - addClass(getSettingsButton(), "rotate"); - event.preventDefault(); - // Sending request for the CSS and the JS files at the same time so it will - // hopefully be loaded when the JS will generate the settings content. - loadScript(getVar("static-root-path") + getVar("settings-js")); - // Pre-load all theme CSS files, so that switching feels seamless. - // - // When loading settings.html as a standalone page, the equivalent HTML is - // generated in context.rs. - setTimeout(() => { - const themes = getVar("themes").split(","); - for (const theme of themes) { - // if there are no themes, do nothing - // "".split(",") == [""] - if (theme !== "") { - preLoadCss(getVar("root-path") + theme + ".css"); - } + if (getSettingsButton()) { + getSettingsButton().onclick = event => { + if (event.ctrlKey || event.altKey || event.metaKey) { + return; } - }, 0); - }; + window.hideAllModals(false); + addClass(getSettingsButton(), "rotate"); + event.preventDefault(); + // Sending request for the CSS and the JS files at the same time so it will + // hopefully be loaded when the JS will generate the settings content. + loadScript(getVar("static-root-path") + getVar("settings-js")); + // Pre-load all theme CSS files, so that switching feels seamless. + // + // When loading settings.html as a standalone page, the equivalent HTML is + // generated in context.rs. + setTimeout(() => { + const themes = getVar("themes").split(","); + for (const theme of themes) { + // if there are no themes, do nothing + // "".split(",") == [""] + if (theme !== "") { + preLoadCss(getVar("root-path") + theme + ".css"); + } + } + }, 0); + }; + } window.searchState = { + rustdocToolbar: document.querySelector("rustdoc-toolbar"), loadingText: "Loading search results...", input: document.getElementsByClassName("search-input")[0], outputElement: () => { @@ -919,8 +938,7 @@ function preLoadCss(cssUrl) { e.open = true; } }); - innerToggle.title = "collapse all docs"; - innerToggle.children[0].innerText = "\u2212"; // "\u2212" is "−" minus sign + innerToggle.children[0].innerText = "Summary"; } function collapseAllDocs() { @@ -934,8 +952,7 @@ function preLoadCss(cssUrl) { e.open = false; } }); - innerToggle.title = "expand all docs"; - innerToggle.children[0].innerText = "+"; + innerToggle.children[0].innerText = "Show all"; } function toggleAllDocs() { @@ -1330,7 +1347,13 @@ function preLoadCss(cssUrl) { } function helpBlurHandler(event) { - blurHandler(event, getHelpButton(), window.hidePopoverMenus); + if (!getHelpButton().contains(document.activeElement) && + !getHelpButton().contains(event.relatedTarget) && + !getSettingsButton().contains(document.activeElement) && + !getSettingsButton().contains(event.relatedTarget) + ) { + window.hidePopoverMenus(); + } } function buildHelpMenu() { @@ -1433,9 +1456,13 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm * Hide all the popover menus. */ window.hidePopoverMenus = () => { - onEachLazy(document.querySelectorAll(".search-form .popover"), elem => { + onEachLazy(document.querySelectorAll("rustdoc-toolbar .popover"), elem => { elem.style.display = "none"; }); + const button = getHelpButton(); + if (button) { + removeClass(button, "help-open"); + } }; /** @@ -1460,7 +1487,9 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm function showHelp() { // Prevent `blur` events from being dispatched as a result of closing // other modals. - getHelpButton().querySelector("a").focus(); + const button = getHelpButton(); + addClass(button, "help-open"); + button.querySelector("a").focus(); const menu = getHelpMenu(true); if (menu.style.display === "none") { window.hideAllModals(); @@ -1468,28 +1497,15 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm } } + const helpLink = document.querySelector(`#${HELP_BUTTON_ID} > a`); if (isHelpPage) { - showHelp(); - document.querySelector(`#${HELP_BUTTON_ID} > a`).addEventListener("click", event => { - // Already on the help page, make help button a no-op. - const target = event.target; - if (target.tagName !== "A" || - target.parentElement.id !== HELP_BUTTON_ID || - event.ctrlKey || - event.altKey || - event.metaKey) { - return; - } - event.preventDefault(); - }); - } else { - document.querySelector(`#${HELP_BUTTON_ID} > a`).addEventListener("click", event => { + buildHelpMenu(); + } else if (helpLink) { + helpLink.addEventListener("click", event => { // By default, have help button open docs in a popover. // If user clicks with a moderator, though, use default browser behavior, // probably opening in a new window or tab. - const target = event.target; - if (target.tagName !== "A" || - target.parentElement.id !== HELP_BUTTON_ID || + if (!helpLink.contains(helpLink) || event.ctrlKey || event.altKey || event.metaKey) { @@ -1812,14 +1828,11 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm return; } but.onclick = () => { - const parent = but.parentElement; const path = []; - - onEach(parent.childNodes, child => { - if (child.tagName === "A") { - path.push(child.textContent); - } + onEachLazy(document.querySelectorAll(".rustdoc-breadcrumbs a"), a => { + path.push(a.textContent); }); + path.push(document.querySelector("title").textContent.split(" ")[0]); copyContentToClipboard(path.join("::")); copyButtonAnimation(but); diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index 4da0bbc787d9..eed64d024c04 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -3597,15 +3597,16 @@ async function showResults(results, go_to_first, filterCrates) { let crates = ""; if (rawSearchIndex.size > 1) { - crates = " in 

"; for (const c of rawSearchIndex.keys()) { crates += ``; } - crates += "
"; + crates += ""; } - let output = `

Results${crates}

`; + let output = `
\ +

Results

${crates}
`; if (results.query.error !== null) { const error = results.query.error; error.forEach((value, index) => { @@ -3662,6 +3663,9 @@ async function showResults(results, go_to_first, filterCrates) { resultsElem.appendChild(ret_returned[0]); search.innerHTML = output; + if (searchState.rustdocToolbar) { + search.querySelector(".main-heading").appendChild(searchState.rustdocToolbar); + } const crateSearch = document.getElementById("crate-search"); if (crateSearch) { crateSearch.addEventListener("input", updateCrate); diff --git a/src/librustdoc/html/static/js/settings.js b/src/librustdoc/html/static/js/settings.js index c52a19ef9873..183663b94fc2 100644 --- a/src/librustdoc/html/static/js/settings.js +++ b/src/librustdoc/html/static/js/settings.js @@ -1,7 +1,7 @@ // Local js definitions: /* global getSettingValue, updateLocalStorage, updateTheme */ -/* global addClass, removeClass, onEach, onEachLazy, blurHandler */ -/* global MAIN_ID, getVar, getSettingsButton */ +/* global addClass, removeClass, onEach, onEachLazy */ +/* global MAIN_ID, getVar, getSettingsButton, getHelpButton */ "use strict"; @@ -267,15 +267,16 @@ } function settingsBlurHandler(event) { - blurHandler(event, getSettingsButton(), window.hidePopoverMenus); + if (!getHelpButton().contains(document.activeElement) && + !getHelpButton().contains(event.relatedTarget) && + !getSettingsButton().contains(document.activeElement) && + !getSettingsButton().contains(event.relatedTarget) + ) { + window.hidePopoverMenus(); + } } - if (isSettingsPage) { - // We replace the existing "onclick" callback to do nothing if clicked. - getSettingsButton().onclick = event => { - event.preventDefault(); - }; - } else { + if (!isSettingsPage) { // We replace the existing "onclick" callback. const settingsButton = getSettingsButton(); const settingsMenu = document.getElementById("settings"); diff --git a/src/librustdoc/html/static/js/storage.js b/src/librustdoc/html/static/js/storage.js index d75fb7a7fb5a..d77804d045e3 100644 --- a/src/librustdoc/html/static/js/storage.js +++ b/src/librustdoc/html/static/js/storage.js @@ -274,16 +274,29 @@ class RustdocSearchElement extends HTMLElement { spellcheck="false" placeholder="Type ‘S’ or ‘/’ to search, ‘?’ for more options…" type="search"> -
- `; } } window.customElements.define("rustdoc-search", RustdocSearchElement); +class RustdocToolbarElement extends HTMLElement { + constructor() { + super(); + } + connectedCallback() { + // Avoid replacing the children after they're already here. + if (this.firstElementChild) { + return; + } + const rootPath = getVar("root-path"); + this.innerHTML = ` +
+ Settings +
+
+ Help +
+ `; + } +} +window.customElements.define("rustdoc-toolbar", RustdocToolbarElement); diff --git a/src/librustdoc/html/templates/print_item.html b/src/librustdoc/html/templates/print_item.html index 76e770453b6e..32ded1fbe424 100644 --- a/src/librustdoc/html/templates/print_item.html +++ b/src/librustdoc/html/templates/print_item.html @@ -1,26 +1,33 @@
{# #} + {% if !path_components.is_empty() %} + + {% for (i, component) in path_components.iter().enumerate() %} + {% if i != 0 %} + :: + {% endif %} + {{component.name}} + {% endfor %} + + {% endif %}

{{typ}} - {# The breadcrumbs of the item path, like std::string #} - {% for component in path_components %} - {{component.name}}:: - {% endfor %} - {{name}} {# #} + + {{name}} + {# #} {# #}

{# #} - + {# #} + {% if !stability_since_raw.is_empty() %} - {{ stability_since_raw|safe +}} · {#+ #} + {{ stability_since_raw|safe +}} {% endif %} {% match src_href %} {% when Some with (href) %} - source · {#+ #} + {% if !stability_since_raw.is_empty() +%} · {%+ endif %} + source {#+ #} {% else %} {% endmatch %} - {# #} {# #}
{# #} diff --git a/src/librustdoc/html/templates/source.html b/src/librustdoc/html/templates/source.html index 60a47f1b5de7..9daa0cf8ff68 100644 --- a/src/librustdoc/html/templates/source.html +++ b/src/librustdoc/html/templates/source.html @@ -1,4 +1,15 @@ -
+{% match file_path %} +{% when Some with ((path, name)) %} +
{# #} +

{# #} +
{{path}}/
+ {{name}} +

{# #} + {# #} +
+{% else %} +{% endmatch %} +
{# #} {# https://developers.google.com/search/docs/crawling-indexing/robots-meta-tag#data-nosnippet-attr Do not show "1 2 3 4 5 ..." in web search results. #}
diff --git a/src/librustdoc/json/mod.rs b/src/librustdoc/json/mod.rs
index 7f82fb5c6865..b7a683eed1c2 100644
--- a/src/librustdoc/json/mod.rs
+++ b/src/librustdoc/json/mod.rs
@@ -286,7 +286,7 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> {
 
             self.serialize_and_write(
                 output_crate,
-                BufWriter::new(try_err!(File::create(&p), p)),
+                try_err!(File::create_buffered(&p), p),
                 &p.display().to_string(),
             )
         } else {
diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs
index f210873e6962..2be415e2e0ef 100644
--- a/src/librustdoc/lib.rs
+++ b/src/librustdoc/lib.rs
@@ -5,11 +5,13 @@
 #![feature(rustc_private)]
 #![feature(assert_matches)]
 #![feature(box_patterns)]
+#![feature(file_buffered)]
 #![feature(if_let_guard)]
 #![feature(impl_trait_in_assoc_type)]
 #![feature(iter_intersperse)]
 #![feature(let_chains)]
 #![feature(never_type)]
+#![feature(os_str_display)]
 #![feature(round_char_boundary)]
 #![feature(test)]
 #![feature(type_alias_impl_trait)]
diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs
index ddc80ea7cb12..29342dcac592 100644
--- a/src/librustdoc/passes/collect_intra_doc_links.rs
+++ b/src/librustdoc/passes/collect_intra_doc_links.rs
@@ -608,7 +608,6 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
                 let mut assoc_items: Vec<_> = tcx
                     .inherent_impls(did)
                     .into_iter()
-                    .flatten()
                     .flat_map(|&imp| {
                         filter_assoc_items_by_name_and_namespace(
                             tcx,
diff --git a/src/tools/clippy/.github/deploy.sh b/src/tools/clippy/.github/deploy.sh
index d937661c0f82..5b4b4be4e36b 100644
--- a/src/tools/clippy/.github/deploy.sh
+++ b/src/tools/clippy/.github/deploy.sh
@@ -25,16 +25,15 @@ if [[ $BETA = "true" ]]; then
 fi
 
 # Generate version index that is shown as root index page
-cp util/gh-pages/versions.html out/index.html
-
-echo "Making the versions.json file"
-python3 ./util/versions.py out
+python3 ./util/versions.py ./util/gh-pages/versions.html out
 
 # Now let's go have some fun with the cloned repo
 cd out
 git config user.name "GHA CI"
 git config user.email "gha@ci.invalid"
 
+git status
+
 if [[ -n $TAG_NAME ]]; then
   # track files, so that the following check works
   git add --intent-to-add "$TAG_NAME"
@@ -46,8 +45,6 @@ if [[ -n $TAG_NAME ]]; then
   git add "$TAG_NAME"
   # Update the symlink
   git add stable
-  # Update versions file
-  git add versions.json
   git commit -m "Add documentation for ${TAG_NAME} release: ${SHA}"
 elif [[ $BETA = "true" ]]; then
   if git diff --exit-code --quiet -- beta/; then
diff --git a/src/tools/clippy/.github/workflows/clippy_bors.yml b/src/tools/clippy/.github/workflows/clippy_bors.yml
index 2aa13313fa51..026771e6fcf4 100644
--- a/src/tools/clippy/.github/workflows/clippy_bors.yml
+++ b/src/tools/clippy/.github/workflows/clippy_bors.yml
@@ -162,7 +162,7 @@ jobs:
         find $DIR ! -executable -o -type d ! -path $DIR | xargs rm -rf
 
     - name: Upload Binaries
-      uses: actions/upload-artifact@v3
+      uses: actions/upload-artifact@v4
       with:
         name: binaries
         path: target/debug
@@ -202,7 +202,7 @@ jobs:
 
     # Download
     - name: Download target dir
-      uses: actions/download-artifact@v3
+      uses: actions/download-artifact@v4
       with:
         name: binaries
         path: target/debug
diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md
index 9bc4ad9698db..41a86e8ce510 100644
--- a/src/tools/clippy/CHANGELOG.md
+++ b/src/tools/clippy/CHANGELOG.md
@@ -6,7 +6,61 @@ document.
 
 ## Unreleased / Beta / In Rust Nightly
 
-[c9139bd5...master](https://github.com/rust-lang/rust-clippy/compare/c9139bd5...master)
+[b794b8e0...master](https://github.com/rust-lang/rust-clippy/compare/b794b8e0...master)
+
+## Rust 1.81
+
+Current stable, released 2024-09-05
+
+### New Lints
+
+* Added [`cfg_not_test`] to `restriction`
+  [#11293](https://github.com/rust-lang/rust-clippy/pull/11293)
+* Added [`byte_char_slices`] to `style`
+  [#10155](https://github.com/rust-lang/rust-clippy/pull/10155)
+* Added [`set_contains_or_insert`] to `nursery`
+  [#12873](https://github.com/rust-lang/rust-clippy/pull/12873)
+* Added [`manual_rotate`] to `style`
+  [#12983](https://github.com/rust-lang/rust-clippy/pull/12983)
+* Added [`unnecessary_min_or_max`] to `complexity`
+  [#12368](https://github.com/rust-lang/rust-clippy/pull/12368)
+* Added [`manual_inspect`] to `complexity`
+  [#12287](https://github.com/rust-lang/rust-clippy/pull/12287)
+* Added [`field_scoped_visibility_modifiers`] to `restriction`
+  [#12893](https://github.com/rust-lang/rust-clippy/pull/12893)
+* Added [`manual_pattern_char_comparison`] to `style`
+  [#12849](https://github.com/rust-lang/rust-clippy/pull/12849)
+* Added [`needless_maybe_sized`] to `suspicious`
+  [#10632](https://github.com/rust-lang/rust-clippy/pull/10632)
+* Added [`needless_character_iteration`] to `suspicious`
+  [#12815](https://github.com/rust-lang/rust-clippy/pull/12815)
+
+### Moves and Deprecations
+
+* [`allow_attributes`], [`allow_attributes_without_reason`]: Now work on stable
+  [rust#120924](https://github.com/rust-lang/rust/pull/120924)
+* Renamed `overflow_check_conditional` to [`panicking_overflow_checks`]
+  [#12944](https://github.com/rust-lang/rust-clippy/pull/12944)
+* Moved [`panicking_overflow_checks`] to `correctness` (From `complexity` now deny-by-default)
+  [#12944](https://github.com/rust-lang/rust-clippy/pull/12944)
+* Renamed `thread_local_initializer_can_be_made_const` to [`missing_const_for_thread_local`]
+  [#12974](https://github.com/rust-lang/rust-clippy/pull/12974)
+* Deprecated [`maybe_misused_cfg`] and [`mismatched_target_os`] as they are now caught by cargo
+  and rustc
+  [#12875](https://github.com/rust-lang/rust-clippy/pull/12875)
+
+### Enhancements
+
+* [`significant_drop_in_scrutinee`]: Now also checks scrutinies of `while let` and `for let`
+  expressions
+  [#12870](https://github.com/rust-lang/rust-clippy/pull/12870)
+* [`std_instead_of_core`]: Now respects the `msrv` configuration
+  [#13168](https://github.com/rust-lang/rust-clippy/pull/13168)
+
+### ICE Fixes
+
+* [`suboptimal_flops`]: No longer crashes on custom `.log()` functions
+  [#12884](https://github.com/rust-lang/rust-clippy/pull/12884)
 
 ## Rust 1.80
 
@@ -5500,6 +5554,7 @@ Released 2018-09-13
 [`invalid_regex`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_regex
 [`invalid_upcast_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_upcast_comparisons
 [`invalid_utf8_in_unchecked`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_utf8_in_unchecked
+[`inverted_saturating_sub`]: https://rust-lang.github.io/rust-clippy/master/index.html#inverted_saturating_sub
 [`invisible_characters`]: https://rust-lang.github.io/rust-clippy/master/index.html#invisible_characters
 [`is_digit_ascii_radix`]: https://rust-lang.github.io/rust-clippy/master/index.html#is_digit_ascii_radix
 [`items_after_statements`]: https://rust-lang.github.io/rust-clippy/master/index.html#items_after_statements
@@ -5559,6 +5614,7 @@ Released 2018-09-13
 [`manual_bits`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_bits
 [`manual_c_str_literals`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_c_str_literals
 [`manual_clamp`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_clamp
+[`manual_div_ceil`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_div_ceil
 [`manual_filter`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_filter
 [`manual_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_filter_map
 [`manual_find`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find
@@ -5570,6 +5626,7 @@ Released 2018-09-13
 [`manual_is_ascii_check`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_ascii_check
 [`manual_is_finite`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_finite
 [`manual_is_infinite`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_infinite
+[`manual_is_power_of_two`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_power_of_two
 [`manual_is_variant_and`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_variant_and
 [`manual_let_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_let_else
 [`manual_main_separator_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_main_separator_str
@@ -5716,6 +5773,7 @@ Released 2018-09-13
 [`non_minimal_cfg`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_minimal_cfg
 [`non_octal_unix_permissions`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_octal_unix_permissions
 [`non_send_fields_in_send_ty`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_send_fields_in_send_ty
+[`non_zero_suggestions`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_zero_suggestions
 [`nonminimal_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonminimal_bool
 [`nonsensical_open_options`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonsensical_open_options
 [`nonstandard_macro_braces`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonstandard_macro_braces
@@ -5757,6 +5815,7 @@ Released 2018-09-13
 [`pathbuf_init_then_push`]: https://rust-lang.github.io/rust-clippy/master/index.html#pathbuf_init_then_push
 [`pattern_type_mismatch`]: https://rust-lang.github.io/rust-clippy/master/index.html#pattern_type_mismatch
 [`permissions_set_readonly_false`]: https://rust-lang.github.io/rust-clippy/master/index.html#permissions_set_readonly_false
+[`pointers_in_nomem_asm_block`]: https://rust-lang.github.io/rust-clippy/master/index.html#pointers_in_nomem_asm_block
 [`positional_named_format_parameters`]: https://rust-lang.github.io/rust-clippy/master/index.html#positional_named_format_parameters
 [`possible_missing_comma`]: https://rust-lang.github.io/rust-clippy/master/index.html#possible_missing_comma
 [`precedence`]: https://rust-lang.github.io/rust-clippy/master/index.html#precedence
@@ -5962,6 +6021,7 @@ Released 2018-09-13
 [`unnecessary_fallible_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_fallible_conversions
 [`unnecessary_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_filter_map
 [`unnecessary_find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_find_map
+[`unnecessary_first_then_check`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_first_then_check
 [`unnecessary_fold`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_fold
 [`unnecessary_get_then_check`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_get_then_check
 [`unnecessary_join`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_join
@@ -6003,6 +6063,7 @@ Released 2018-09-13
 [`unused_result_ok`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_result_ok
 [`unused_rounding`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_rounding
 [`unused_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_self
+[`unused_trait_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_trait_names
 [`unused_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_unit
 [`unusual_byte_groupings`]: https://rust-lang.github.io/rust-clippy/master/index.html#unusual_byte_groupings
 [`unwrap_in_result`]: https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_in_result
@@ -6013,6 +6074,7 @@ Released 2018-09-13
 [`use_debug`]: https://rust-lang.github.io/rust-clippy/master/index.html#use_debug
 [`use_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#use_self
 [`used_underscore_binding`]: https://rust-lang.github.io/rust-clippy/master/index.html#used_underscore_binding
+[`used_underscore_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#used_underscore_items
 [`useless_asref`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_asref
 [`useless_attribute`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_attribute
 [`useless_conversion`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_conversion
@@ -6047,6 +6109,7 @@ Released 2018-09-13
 [`zero_repeat_side_effects`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_repeat_side_effects
 [`zero_sized_map_values`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_sized_map_values
 [`zero_width_space`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_width_space
+[`zombie_processes`]: https://rust-lang.github.io/rust-clippy/master/index.html#zombie_processes
 [`zst_offset`]: https://rust-lang.github.io/rust-clippy/master/index.html#zst_offset
 
 
diff --git a/src/tools/clippy/Cargo.toml b/src/tools/clippy/Cargo.toml
index b48b881097f4..cf810798d8cc 100644
--- a/src/tools/clippy/Cargo.toml
+++ b/src/tools/clippy/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "clippy"
-version = "0.1.82"
+version = "0.1.83"
 description = "A bunch of helpful lints to avoid common pitfalls in Rust"
 repository = "https://github.com/rust-lang/rust-clippy"
 readme = "README.md"
@@ -31,7 +31,7 @@ anstream = "0.6.0"
 
 [dev-dependencies]
 cargo_metadata = "0.18.1"
-ui_test = "0.25"
+ui_test = "0.26.4"
 regex = "1.5.5"
 serde = { version = "1.0.145", features = ["derive"] }
 serde_json = "1.0.122"
diff --git a/src/tools/clippy/book/src/lint_configuration.md b/src/tools/clippy/book/src/lint_configuration.md
index 78348797588a..91159bc79c51 100644
--- a/src/tools/clippy/book/src/lint_configuration.md
+++ b/src/tools/clippy/book/src/lint_configuration.md
@@ -727,6 +727,7 @@ The minimum rust version that the project supports. Defaults to the `rust-versio
 * [`uninlined_format_args`](https://rust-lang.github.io/rust-clippy/master/index.html#uninlined_format_args)
 * [`unnecessary_lazy_evaluations`](https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations)
 * [`unnested_or_patterns`](https://rust-lang.github.io/rust-clippy/master/index.html#unnested_or_patterns)
+* [`unused_trait_names`](https://rust-lang.github.io/rust-clippy/master/index.html#unused_trait_names)
 * [`use_self`](https://rust-lang.github.io/rust-clippy/master/index.html#use_self)
 
 
diff --git a/src/tools/clippy/clippy_config/Cargo.toml b/src/tools/clippy/clippy_config/Cargo.toml
index 5c4e0761dbca..9da7112345de 100644
--- a/src/tools/clippy/clippy_config/Cargo.toml
+++ b/src/tools/clippy/clippy_config/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "clippy_config"
-version = "0.1.82"
+version = "0.1.83"
 edition = "2021"
 
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
diff --git a/src/tools/clippy/clippy_config/src/conf.rs b/src/tools/clippy/clippy_config/src/conf.rs
index a6f1b958bfb1..757620341ccc 100644
--- a/src/tools/clippy/clippy_config/src/conf.rs
+++ b/src/tools/clippy/clippy_config/src/conf.rs
@@ -1,6 +1,6 @@
+use crate::ClippyConfiguration;
 use crate::msrvs::Msrv;
 use crate::types::{DisallowedPath, MacroMatcher, MatchLintBehaviour, PubUnderscoreFieldsBehaviour, Rename};
-use crate::ClippyConfiguration;
 use rustc_errors::Applicability;
 use rustc_session::Session;
 use rustc_span::edit_distance::edit_distance;
@@ -563,6 +563,7 @@ define_Conf! {
         uninlined_format_args,
         unnecessary_lazy_evaluations,
         unnested_or_patterns,
+        unused_trait_names,
         use_self,
     )]
     msrv: Msrv = Msrv::empty(),
@@ -864,7 +865,7 @@ fn calculate_dimensions(fields: &[&str]) -> (usize, Vec) {
             cmp::max(1, terminal_width / (SEPARATOR_WIDTH + max_field_width))
         });
 
-    let rows = (fields.len() + (columns - 1)) / columns;
+    let rows = fields.len().div_ceil(columns);
 
     let column_widths = (0..columns)
         .map(|column| {
diff --git a/src/tools/clippy/clippy_config/src/lib.rs b/src/tools/clippy/clippy_config/src/lib.rs
index ac838cc81d2f..c63d98a0a13f 100644
--- a/src/tools/clippy/clippy_config/src/lib.rs
+++ b/src/tools/clippy/clippy_config/src/lib.rs
@@ -26,5 +26,5 @@ mod metadata;
 pub mod msrvs;
 pub mod types;
 
-pub use conf::{get_configuration_metadata, lookup_conf_file, Conf};
+pub use conf::{Conf, get_configuration_metadata, lookup_conf_file};
 pub use metadata::ClippyConfiguration;
diff --git a/src/tools/clippy/clippy_config/src/msrvs.rs b/src/tools/clippy/clippy_config/src/msrvs.rs
index a2b4b6e256fa..e30df3d32341 100644
--- a/src/tools/clippy/clippy_config/src/msrvs.rs
+++ b/src/tools/clippy/clippy_config/src/msrvs.rs
@@ -1,7 +1,7 @@
 use rustc_ast::Attribute;
 use rustc_attr::parse_version;
 use rustc_session::{RustcVersion, Session};
-use rustc_span::{sym, Symbol};
+use rustc_span::{Symbol, sym};
 use serde::Deserialize;
 use std::fmt;
 
@@ -23,6 +23,7 @@ msrv_aliases! {
     1,80,0 { BOX_INTO_ITER}
     1,77,0 { C_STR_LITERALS }
     1,76,0 { PTR_FROM_REF, OPTION_RESULT_INSPECT }
+    1,73,0 { MANUAL_DIV_CEIL }
     1,71,0 { TUPLE_ARRAY_CONVERSIONS, BUILD_HASHER_HASH_ONE }
     1,70,0 { OPTION_RESULT_IS_VARIANT_AND, BINARY_HEAP_RETAIN }
     1,68,0 { PATH_MAIN_SEPARATOR_STR }
@@ -38,7 +39,7 @@ msrv_aliases! {
     1,52,0 { STR_SPLIT_ONCE, REM_EUCLID_CONST }
     1,51,0 { BORROW_AS_PTR, SEEK_FROM_CURRENT, UNSIGNED_ABS }
     1,50,0 { BOOL_THEN, CLAMP }
-    1,47,0 { TAU, IS_ASCII_DIGIT_CONST, ARRAY_IMPL_ANY_LEN }
+    1,47,0 { TAU, IS_ASCII_DIGIT_CONST, ARRAY_IMPL_ANY_LEN, SATURATING_SUB_CONST }
     1,46,0 { CONST_IF_MATCH }
     1,45,0 { STR_STRIP_PREFIX }
     1,43,0 { LOG2_10, LOG10_2, NUMERIC_ASSOCIATED_CONSTANTS }
@@ -50,6 +51,7 @@ msrv_aliases! {
     1,36,0 { ITERATOR_COPIED }
     1,35,0 { OPTION_COPIED, RANGE_CONTAINS }
     1,34,0 { TRY_FROM }
+    1,33,0 { UNDERSCORE_IMPORTS }
     1,30,0 { ITERATOR_FIND_MAP, TOOL_ATTRIBUTES }
     1,29,0 { ITER_FLATTEN }
     1,28,0 { FROM_BOOL }
diff --git a/src/tools/clippy/clippy_config/src/types.rs b/src/tools/clippy/clippy_config/src/types.rs
index d47e34bb5bce..bab63911182d 100644
--- a/src/tools/clippy/clippy_config/src/types.rs
+++ b/src/tools/clippy/clippy_config/src/types.rs
@@ -1,5 +1,5 @@
 use serde::de::{self, Deserializer, Visitor};
-use serde::{ser, Deserialize, Serialize};
+use serde::{Deserialize, Serialize, ser};
 use std::fmt;
 
 #[derive(Debug, Deserialize)]
diff --git a/src/tools/clippy/clippy_dev/src/fmt.rs b/src/tools/clippy/clippy_dev/src/fmt.rs
index 5fc4365c6e78..8c61c35533cf 100644
--- a/src/tools/clippy/clippy_dev/src/fmt.rs
+++ b/src/tools/clippy/clippy_dev/src/fmt.rs
@@ -1,6 +1,6 @@
 use crate::clippy_project_root;
 use itertools::Itertools;
-use rustc_lexer::{tokenize, TokenKind};
+use rustc_lexer::{TokenKind, tokenize};
 use shell_escape::escape;
 use std::ffi::{OsStr, OsString};
 use std::ops::ControlFlow;
diff --git a/src/tools/clippy/clippy_dev/src/new_lint.rs b/src/tools/clippy/clippy_dev/src/new_lint.rs
index 87117832fb95..5fd65017cfb9 100644
--- a/src/tools/clippy/clippy_dev/src/new_lint.rs
+++ b/src/tools/clippy/clippy_dev/src/new_lint.rs
@@ -441,7 +441,7 @@ fn create_lint_for_ty(lint: &LintData<'_>, enable_msrv: bool, ty: &str) -> io::R
 
 #[allow(clippy::too_many_lines)]
 fn setup_mod_file(path: &Path, lint: &LintData<'_>) -> io::Result<&'static str> {
-    use super::update_lints::{match_tokens, LintDeclSearchResult};
+    use super::update_lints::{LintDeclSearchResult, match_tokens};
     use rustc_lexer::TokenKind;
 
     let lint_name_upper = lint.name.to_uppercase();
diff --git a/src/tools/clippy/clippy_dev/src/serve.rs b/src/tools/clippy/clippy_dev/src/serve.rs
index 19560b31fd3e..cc14cd8dae69 100644
--- a/src/tools/clippy/clippy_dev/src/serve.rs
+++ b/src/tools/clippy/clippy_dev/src/serve.rs
@@ -29,7 +29,7 @@ pub fn run(port: u16, lint: Option) -> ! {
         }
         if let Some(url) = url.take() {
             thread::spawn(move || {
-                Command::new(PYTHON)
+                let mut child = Command::new(PYTHON)
                     .arg("-m")
                     .arg("http.server")
                     .arg(port.to_string())
@@ -40,6 +40,7 @@ pub fn run(port: u16, lint: Option) -> ! {
                 thread::sleep(Duration::from_millis(500));
                 // Launch browser after first export.py has completed and http.server is up
                 let _result = opener::open(url);
+                child.wait().unwrap();
             });
         }
         thread::sleep(Duration::from_millis(1000));
diff --git a/src/tools/clippy/clippy_dev/src/update_lints.rs b/src/tools/clippy/clippy_dev/src/update_lints.rs
index 8dbda1c634c2..d6ed36d52f4e 100644
--- a/src/tools/clippy/clippy_dev/src/update_lints.rs
+++ b/src/tools/clippy/clippy_dev/src/update_lints.rs
@@ -1,7 +1,7 @@
 use crate::clippy_project_root;
 use aho_corasick::AhoCorasickBuilder;
 use itertools::Itertools;
-use rustc_lexer::{tokenize, unescape, LiteralKind, TokenKind};
+use rustc_lexer::{LiteralKind, TokenKind, tokenize, unescape};
 use std::collections::{HashMap, HashSet};
 use std::ffi::OsStr;
 use std::fmt::{self, Write};
@@ -1048,23 +1048,17 @@ mod tests {
             Lint::new("incorrect_match", "group1", "\"abc\"", "module_name", Range::default()),
         ];
         let mut expected: HashMap> = HashMap::new();
-        expected.insert(
-            "group1".to_string(),
-            vec![
-                Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name", Range::default()),
-                Lint::new("incorrect_match", "group1", "\"abc\"", "module_name", Range::default()),
-            ],
-        );
-        expected.insert(
-            "group2".to_string(),
-            vec![Lint::new(
-                "should_assert_eq2",
-                "group2",
-                "\"abc\"",
-                "module_name",
-                Range::default(),
-            )],
-        );
+        expected.insert("group1".to_string(), vec![
+            Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name", Range::default()),
+            Lint::new("incorrect_match", "group1", "\"abc\"", "module_name", Range::default()),
+        ]);
+        expected.insert("group2".to_string(), vec![Lint::new(
+            "should_assert_eq2",
+            "group2",
+            "\"abc\"",
+            "module_name",
+            Range::default(),
+        )]);
         assert_eq!(expected, Lint::by_lint_group(lints.into_iter()));
     }
 }
diff --git a/src/tools/clippy/clippy_lints/Cargo.toml b/src/tools/clippy/clippy_lints/Cargo.toml
index fbd4566da58c..d1188940b46a 100644
--- a/src/tools/clippy/clippy_lints/Cargo.toml
+++ b/src/tools/clippy/clippy_lints/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "clippy_lints"
-version = "0.1.82"
+version = "0.1.83"
 description = "A bunch of helpful lints to avoid common pitfalls in Rust"
 repository = "https://github.com/rust-lang/rust-clippy"
 readme = "README.md"
diff --git a/src/tools/clippy/clippy_lints/src/absolute_paths.rs b/src/tools/clippy/clippy_lints/src/absolute_paths.rs
index c72b3f1318c8..1af6d448a93c 100644
--- a/src/tools/clippy/clippy_lints/src/absolute_paths.rs
+++ b/src/tools/clippy/clippy_lints/src/absolute_paths.rs
@@ -3,12 +3,12 @@ use clippy_utils::diagnostics::span_lint;
 use clippy_utils::is_from_proc_macro;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_hir::def::{DefKind, Res};
-use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX};
+use rustc_hir::def_id::{CRATE_DEF_INDEX, DefId};
 use rustc_hir::{HirId, ItemKind, Node, Path};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::impl_lint_pass;
-use rustc_span::symbol::kw;
 use rustc_span::Symbol;
+use rustc_span::symbol::kw;
 
 declare_clippy_lint! {
     /// ### What it does
diff --git a/src/tools/clippy/clippy_lints/src/almost_complete_range.rs b/src/tools/clippy/clippy_lints/src/almost_complete_range.rs
index 451bae959874..370f0c482fd5 100644
--- a/src/tools/clippy/clippy_lints/src/almost_complete_range.rs
+++ b/src/tools/clippy/clippy_lints/src/almost_complete_range.rs
@@ -1,5 +1,5 @@
-use clippy_config::msrvs::{self, Msrv};
 use clippy_config::Conf;
+use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::source::{trim_span, walk_span_to_context};
 use rustc_ast::ast::{Expr, ExprKind, LitKind, Pat, PatKind, RangeEnd, RangeLimits};
diff --git a/src/tools/clippy/clippy_lints/src/approx_const.rs b/src/tools/clippy/clippy_lints/src/approx_const.rs
index 56f5e903dc3e..4f8f091a0956 100644
--- a/src/tools/clippy/clippy_lints/src/approx_const.rs
+++ b/src/tools/clippy/clippy_lints/src/approx_const.rs
@@ -1,10 +1,10 @@
-use clippy_config::msrvs::{self, Msrv};
 use clippy_config::Conf;
+use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::span_lint_and_help;
 use rustc_ast::ast::{FloatTy, LitFloatType, LitKind};
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::{impl_lint_pass, RustcVersion};
+use rustc_session::{RustcVersion, impl_lint_pass};
 use rustc_span::symbol;
 use std::f64::consts as f64;
 
diff --git a/src/tools/clippy/clippy_lints/src/arc_with_non_send_sync.rs b/src/tools/clippy/clippy_lints/src/arc_with_non_send_sync.rs
index 4eafa330fafa..2643f850879c 100644
--- a/src/tools/clippy/clippy_lints/src/arc_with_non_send_sync.rs
+++ b/src/tools/clippy/clippy_lints/src/arc_with_non_send_sync.rs
@@ -4,8 +4,8 @@ use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
 use rustc_hir::{Expr, ExprKind, QPath};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty;
-use rustc_middle::ty::print::with_forced_trimmed_paths;
 use rustc_middle::ty::GenericArgKind;
+use rustc_middle::ty::print::with_forced_trimmed_paths;
 use rustc_session::declare_lint_pass;
 use rustc_span::symbol::sym;
 
diff --git a/src/tools/clippy/clippy_lints/src/assertions_on_constants.rs b/src/tools/clippy/clippy_lints/src/assertions_on_constants.rs
index 7eaac80f969f..b6684825835a 100644
--- a/src/tools/clippy/clippy_lints/src/assertions_on_constants.rs
+++ b/src/tools/clippy/clippy_lints/src/assertions_on_constants.rs
@@ -1,7 +1,7 @@
 use clippy_utils::consts::{ConstEvalCtxt, Constant};
 use clippy_utils::diagnostics::span_lint_and_help;
 use clippy_utils::is_inside_always_const_context;
-use clippy_utils::macros::{find_assert_args, root_macro_call_first_node, PanicExpn};
+use clippy_utils::macros::{PanicExpn, find_assert_args, root_macro_call_first_node};
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::declare_lint_pass;
diff --git a/src/tools/clippy/clippy_lints/src/assertions_on_result_states.rs b/src/tools/clippy/clippy_lints/src/assertions_on_result_states.rs
index f1cb4a05af86..c073dee855e2 100644
--- a/src/tools/clippy/clippy_lints/src/assertions_on_result_states.rs
+++ b/src/tools/clippy/clippy_lints/src/assertions_on_result_states.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::macros::{find_assert_args, root_macro_call_first_node, PanicExpn};
+use clippy_utils::macros::{PanicExpn, find_assert_args, root_macro_call_first_node};
 use clippy_utils::source::snippet_with_context;
 use clippy_utils::ty::{has_debug_impl, is_copy, is_type_diagnostic_item};
 use clippy_utils::usage::local_used_after_expr;
diff --git a/src/tools/clippy/clippy_lints/src/assigning_clones.rs b/src/tools/clippy/clippy_lints/src/assigning_clones.rs
index 55645d04eef3..0b82c0cd04c1 100644
--- a/src/tools/clippy/clippy_lints/src/assigning_clones.rs
+++ b/src/tools/clippy/clippy_lints/src/assigning_clones.rs
@@ -1,7 +1,7 @@
-use clippy_config::msrvs::{self, Msrv};
 use clippy_config::Conf;
+use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::mir::{enclosing_mir, PossibleBorrowerMap};
+use clippy_utils::mir::{PossibleBorrowerMap, enclosing_mir};
 use clippy_utils::sugg::Sugg;
 use clippy_utils::{is_diag_trait_item, is_in_test, last_path_segment, local_is_initialized, path_to_local};
 use rustc_errors::Applicability;
diff --git a/src/tools/clippy/clippy_lints/src/attrs/allow_attributes_without_reason.rs b/src/tools/clippy/clippy_lints/src/attrs/allow_attributes_without_reason.rs
index 4ab97118df1d..40959eccd3a9 100644
--- a/src/tools/clippy/clippy_lints/src/attrs/allow_attributes_without_reason.rs
+++ b/src/tools/clippy/clippy_lints/src/attrs/allow_attributes_without_reason.rs
@@ -1,4 +1,4 @@
-use super::{Attribute, ALLOW_ATTRIBUTES_WITHOUT_REASON};
+use super::{ALLOW_ATTRIBUTES_WITHOUT_REASON, Attribute};
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::is_from_proc_macro;
 use rustc_ast::{MetaItemKind, NestedMetaItem};
@@ -26,7 +26,7 @@ pub(super) fn check<'cx>(cx: &LateContext<'cx>, name: Symbol, items: &[NestedMet
         cx,
         ALLOW_ATTRIBUTES_WITHOUT_REASON,
         attr.span,
-        format!("`{}` attribute without specifying a reason", name.as_str()),
+        format!("`{name}` attribute without specifying a reason"),
         |diag| {
             diag.help("try adding a reason at the end with `, reason = \"..\"`");
         },
diff --git a/src/tools/clippy/clippy_lints/src/attrs/blanket_clippy_restriction_lints.rs b/src/tools/clippy/clippy_lints/src/attrs/blanket_clippy_restriction_lints.rs
index 9b08fd6d654a..508963a20ea7 100644
--- a/src/tools/clippy/clippy_lints/src/attrs/blanket_clippy_restriction_lints.rs
+++ b/src/tools/clippy/clippy_lints/src/attrs/blanket_clippy_restriction_lints.rs
@@ -1,10 +1,10 @@
-use super::utils::extract_clippy_lint;
 use super::BLANKET_CLIPPY_RESTRICTION_LINTS;
+use super::utils::extract_clippy_lint;
 use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then};
 use rustc_ast::NestedMetaItem;
 use rustc_lint::{LateContext, Level, LintContext};
 use rustc_span::symbol::Symbol;
-use rustc_span::{sym, DUMMY_SP};
+use rustc_span::{DUMMY_SP, sym};
 
 pub(super) fn check(cx: &LateContext<'_>, name: Symbol, items: &[NestedMetaItem]) {
     for lint in items {
diff --git a/src/tools/clippy/clippy_lints/src/attrs/deprecated_cfg_attr.rs b/src/tools/clippy/clippy_lints/src/attrs/deprecated_cfg_attr.rs
index e872ab6b4b5f..abf924f7542e 100644
--- a/src/tools/clippy/clippy_lints/src/attrs/deprecated_cfg_attr.rs
+++ b/src/tools/clippy/clippy_lints/src/attrs/deprecated_cfg_attr.rs
@@ -1,4 +1,4 @@
-use super::{unnecessary_clippy_cfg, Attribute, DEPRECATED_CFG_ATTR, DEPRECATED_CLIPPY_CFG_ATTR};
+use super::{Attribute, DEPRECATED_CFG_ATTR, DEPRECATED_CLIPPY_CFG_ATTR, unnecessary_clippy_cfg};
 use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use rustc_ast::AttrStyle;
diff --git a/src/tools/clippy/clippy_lints/src/attrs/duplicated_attributes.rs b/src/tools/clippy/clippy_lints/src/attrs/duplicated_attributes.rs
index 40a1c4e28842..55f8e1072db7 100644
--- a/src/tools/clippy/clippy_lints/src/attrs/duplicated_attributes.rs
+++ b/src/tools/clippy/clippy_lints/src/attrs/duplicated_attributes.rs
@@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_then;
 use rustc_ast::{Attribute, MetaItem};
 use rustc_data_structures::fx::FxHashMap;
 use rustc_lint::LateContext;
-use rustc_span::{sym, Span};
+use rustc_span::{Span, sym};
 use std::collections::hash_map::Entry;
 
 fn emit_if_duplicated(
@@ -36,7 +36,7 @@ fn check_duplicated_attr(
     }
     let Some(ident) = attr.ident() else { return };
     let name = ident.name;
-    if name == sym::doc || name == sym::cfg_attr || name == sym::rustc_on_unimplemented {
+    if name == sym::doc || name == sym::cfg_attr || name == sym::rustc_on_unimplemented || name == sym::reason {
         // FIXME: Would be nice to handle `cfg_attr` as well. Only problem is to check that cfg
         // conditions are the same.
         // `#[rustc_on_unimplemented]` contains duplicated subattributes, that's expected.
diff --git a/src/tools/clippy/clippy_lints/src/attrs/empty_line_after.rs b/src/tools/clippy/clippy_lints/src/attrs/empty_line_after.rs
deleted file mode 100644
index 7ff644b4c445..000000000000
--- a/src/tools/clippy/clippy_lints/src/attrs/empty_line_after.rs
+++ /dev/null
@@ -1,52 +0,0 @@
-use super::{EMPTY_LINE_AFTER_DOC_COMMENTS, EMPTY_LINE_AFTER_OUTER_ATTR};
-use clippy_utils::diagnostics::span_lint;
-use clippy_utils::source::{is_present_in_source, without_block_comments, SpanRangeExt};
-use rustc_ast::{AttrKind, AttrStyle};
-use rustc_lint::EarlyContext;
-use rustc_span::Span;
-
-/// Check for empty lines after outer attributes.
-///
-/// Attributes and documentation comments are both considered outer attributes
-/// by the AST. However, the average user likely considers them to be different.
-/// Checking for empty lines after each of these attributes is split into two different
-/// lints but can share the same logic.
-pub(super) fn check(cx: &EarlyContext<'_>, item: &rustc_ast::Item) {
-    let mut iter = item.attrs.iter().peekable();
-    while let Some(attr) = iter.next() {
-        if (matches!(attr.kind, AttrKind::Normal(..)) || matches!(attr.kind, AttrKind::DocComment(..)))
-            && attr.style == AttrStyle::Outer
-            && is_present_in_source(cx, attr.span)
-        {
-            let begin_of_attr_to_item = Span::new(attr.span.lo(), item.span.lo(), item.span.ctxt(), item.span.parent());
-            let end_of_attr_to_next_attr_or_item = Span::new(
-                attr.span.hi(),
-                iter.peek().map_or(item.span.lo(), |next_attr| next_attr.span.lo()),
-                item.span.ctxt(),
-                item.span.parent(),
-            );
-
-            if let Some(snippet) = end_of_attr_to_next_attr_or_item.get_source_text(cx) {
-                let lines = snippet.split('\n').collect::>();
-                let lines = without_block_comments(lines);
-
-                if lines.iter().filter(|l| l.trim().is_empty()).count() > 2 {
-                    let (lint_msg, lint_type) = match attr.kind {
-                        AttrKind::DocComment(..) => (
-                            "found an empty line after a doc comment. \
-                            Perhaps you need to use `//!` to make a comment on a module, remove the empty line, or make a regular comment with `//`?",
-                            EMPTY_LINE_AFTER_DOC_COMMENTS,
-                        ),
-                        AttrKind::Normal(..) => (
-                            "found an empty line after an outer attribute. \
-                            Perhaps you forgot to add a `!` to make it an inner attribute?",
-                            EMPTY_LINE_AFTER_OUTER_ATTR,
-                        ),
-                    };
-
-                    span_lint(cx, lint_type, begin_of_attr_to_item, lint_msg);
-                }
-            }
-        }
-    }
-}
diff --git a/src/tools/clippy/clippy_lints/src/attrs/inline_always.rs b/src/tools/clippy/clippy_lints/src/attrs/inline_always.rs
index 3b5b80ffefaf..d41bb580c6cd 100644
--- a/src/tools/clippy/clippy_lints/src/attrs/inline_always.rs
+++ b/src/tools/clippy/clippy_lints/src/attrs/inline_always.rs
@@ -1,10 +1,10 @@
-use super::utils::is_word;
 use super::INLINE_ALWAYS;
+use super::utils::is_word;
 use clippy_utils::diagnostics::span_lint;
 use rustc_ast::Attribute;
 use rustc_lint::LateContext;
 use rustc_span::symbol::Symbol;
-use rustc_span::{sym, Span};
+use rustc_span::{Span, sym};
 
 pub(super) fn check(cx: &LateContext<'_>, span: Span, name: Symbol, attrs: &[Attribute]) {
     if span.from_expansion() {
diff --git a/src/tools/clippy/clippy_lints/src/attrs/mod.rs b/src/tools/clippy/clippy_lints/src/attrs/mod.rs
index 3b14e9aee7fc..888f28fa2258 100644
--- a/src/tools/clippy/clippy_lints/src/attrs/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/attrs/mod.rs
@@ -4,7 +4,6 @@ mod blanket_clippy_restriction_lints;
 mod deprecated_cfg_attr;
 mod deprecated_semver;
 mod duplicated_attributes;
-mod empty_line_after;
 mod inline_always;
 mod mixed_attributes_style;
 mod non_minimal_cfg;
@@ -13,8 +12,8 @@ mod unnecessary_clippy_cfg;
 mod useless_attribute;
 mod utils;
 
-use clippy_config::msrvs::{self, Msrv};
 use clippy_config::Conf;
+use clippy_config::msrvs::{self, Msrv};
 use rustc_ast::{Attribute, MetaItemKind, NestedMetaItem};
 use rustc_hir::{ImplItem, Item, ItemKind, TraitItem};
 use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass};
@@ -126,94 +125,6 @@ declare_clippy_lint! {
     "use of `#[deprecated(since = \"x\")]` where x is not semver"
 }
 
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for empty lines after outer attributes
-    ///
-    /// ### Why is this bad?
-    /// Most likely the attribute was meant to be an inner attribute using a '!'.
-    /// If it was meant to be an outer attribute, then the following item
-    /// should not be separated by empty lines.
-    ///
-    /// ### Known problems
-    /// Can cause false positives.
-    ///
-    /// From the clippy side it's difficult to detect empty lines between an attributes and the
-    /// following item because empty lines and comments are not part of the AST. The parsing
-    /// currently works for basic cases but is not perfect.
-    ///
-    /// ### Example
-    /// ```no_run
-    /// #[allow(dead_code)]
-    ///
-    /// fn not_quite_good_code() { }
-    /// ```
-    ///
-    /// Use instead:
-    /// ```no_run
-    /// // Good (as inner attribute)
-    /// #![allow(dead_code)]
-    ///
-    /// fn this_is_fine() { }
-    ///
-    /// // or
-    ///
-    /// // Good (as outer attribute)
-    /// #[allow(dead_code)]
-    /// fn this_is_fine_too() { }
-    /// ```
-    #[clippy::version = "pre 1.29.0"]
-    pub EMPTY_LINE_AFTER_OUTER_ATTR,
-    nursery,
-    "empty line after outer attribute"
-}
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for empty lines after documentation comments.
-    ///
-    /// ### Why is this bad?
-    /// The documentation comment was most likely meant to be an inner attribute or regular comment.
-    /// If it was intended to be a documentation comment, then the empty line should be removed to
-    /// be more idiomatic.
-    ///
-    /// ### Known problems
-    /// Only detects empty lines immediately following the documentation. If the doc comment is followed
-    /// by an attribute and then an empty line, this lint will not trigger. Use `empty_line_after_outer_attr`
-    /// in combination with this lint to detect both cases.
-    ///
-    /// Does not detect empty lines after doc attributes (e.g. `#[doc = ""]`).
-    ///
-    /// ### Example
-    /// ```no_run
-    /// /// Some doc comment with a blank line after it.
-    ///
-    /// fn not_quite_good_code() { }
-    /// ```
-    ///
-    /// Use instead:
-    /// ```no_run
-    /// /// Good (no blank line)
-    /// fn this_is_fine() { }
-    /// ```
-    ///
-    /// ```no_run
-    /// // Good (convert to a regular comment)
-    ///
-    /// fn this_is_fine_too() { }
-    /// ```
-    ///
-    /// ```no_run
-    /// //! Good (convert to a comment on an inner attribute)
-    ///
-    /// fn this_is_fine_as_well() { }
-    /// ```
-    #[clippy::version = "1.70.0"]
-    pub EMPTY_LINE_AFTER_DOC_COMMENTS,
-    nursery,
-    "empty line after documentation comments"
-}
-
 declare_clippy_lint! {
     /// ### What it does
     /// Checks for `warn`/`deny`/`forbid` attributes targeting the whole clippy::restriction category.
@@ -601,18 +512,12 @@ impl EarlyAttributes {
 
 impl_lint_pass!(EarlyAttributes => [
     DEPRECATED_CFG_ATTR,
-    EMPTY_LINE_AFTER_OUTER_ATTR,
-    EMPTY_LINE_AFTER_DOC_COMMENTS,
     NON_MINIMAL_CFG,
     DEPRECATED_CLIPPY_CFG_ATTR,
     UNNECESSARY_CLIPPY_CFG,
 ]);
 
 impl EarlyLintPass for EarlyAttributes {
-    fn check_item(&mut self, cx: &EarlyContext<'_>, item: &rustc_ast::Item) {
-        empty_line_after::check(cx, item);
-    }
-
     fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) {
         deprecated_cfg_attr::check(cx, attr, &self.msrv);
         deprecated_cfg_attr::check_clippy(cx, attr);
diff --git a/src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs b/src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs
index 67ba605a59fa..12668c616c13 100644
--- a/src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs
+++ b/src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs
@@ -1,7 +1,7 @@
 use super::utils::{extract_clippy_lint, is_lint_level, is_word};
 use super::{Attribute, USELESS_ATTRIBUTE};
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::source::{first_line_of_span, SpanRangeExt};
+use clippy_utils::source::{SpanRangeExt, first_line_of_span};
 use rustc_ast::NestedMetaItem;
 use rustc_errors::Applicability;
 use rustc_hir::{Item, ItemKind};
@@ -51,6 +51,7 @@ pub(super) fn check(cx: &LateContext<'_>, item: &Item<'_>, attrs: &[Attribute])
                                         | "module_name_repetitions"
                                         | "single_component_path_imports"
                                         | "disallowed_types"
+                                        | "unused_trait_names"
                                 )
                             }) {
                                 return;
diff --git a/src/tools/clippy/clippy_lints/src/await_holding_invalid.rs b/src/tools/clippy/clippy_lints/src/await_holding_invalid.rs
index d5f017b26509..9952d0af99e5 100644
--- a/src/tools/clippy/clippy_lints/src/await_holding_invalid.rs
+++ b/src/tools/clippy/clippy_lints/src/await_holding_invalid.rs
@@ -7,7 +7,7 @@ use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::mir::CoroutineLayout;
 use rustc_middle::ty::TyCtxt;
 use rustc_session::impl_lint_pass;
-use rustc_span::{sym, Span};
+use rustc_span::{Span, sym};
 
 declare_clippy_lint! {
     /// ### What it does
diff --git a/src/tools/clippy/clippy_lints/src/booleans.rs b/src/tools/clippy/clippy_lints/src/booleans.rs
index e933fdf1d6e6..3c2af72624f6 100644
--- a/src/tools/clippy/clippy_lints/src/booleans.rs
+++ b/src/tools/clippy/clippy_lints/src/booleans.rs
@@ -4,12 +4,12 @@ use clippy_utils::source::SpanRangeExt;
 use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
 use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
-use rustc_hir::intravisit::{walk_expr, FnKind, Visitor};
+use rustc_hir::intravisit::{FnKind, Visitor, walk_expr};
 use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, UnOp};
 use rustc_lint::{LateContext, LateLintPass, Level};
 use rustc_session::declare_lint_pass;
 use rustc_span::def_id::LocalDefId;
-use rustc_span::{sym, Span};
+use rustc_span::{Span, sym};
 
 declare_clippy_lint! {
     /// ### What it does
diff --git a/src/tools/clippy/clippy_lints/src/box_default.rs b/src/tools/clippy/clippy_lints/src/box_default.rs
index 8459f051d3d9..8261c65354fd 100644
--- a/src/tools/clippy/clippy_lints/src/box_default.rs
+++ b/src/tools/clippy/clippy_lints/src/box_default.rs
@@ -4,7 +4,7 @@ use clippy_utils::ty::expr_sig;
 use clippy_utils::{is_default_equivalent, path_def_id};
 use rustc_errors::Applicability;
 use rustc_hir::def::Res;
-use rustc_hir::intravisit::{walk_ty, Visitor};
+use rustc_hir::intravisit::{Visitor, walk_ty};
 use rustc_hir::{Block, Expr, ExprKind, LetStmt, Node, QPath, Ty, TyKind};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
diff --git a/src/tools/clippy/clippy_lints/src/byte_char_slices.rs b/src/tools/clippy/clippy_lints/src/byte_char_slices.rs
index a9fe190f1777..dd2620b0b9df 100644
--- a/src/tools/clippy/clippy_lints/src/byte_char_slices.rs
+++ b/src/tools/clippy/clippy_lints/src/byte_char_slices.rs
@@ -22,7 +22,7 @@ declare_clippy_lint! {
     /// ```ignore
     /// b"Hello"
     /// ```
-    #[clippy::version = "1.68.0"]
+    #[clippy::version = "1.81.0"]
     pub BYTE_CHAR_SLICES,
     style,
     "hard to read byte char slice"
diff --git a/src/tools/clippy/clippy_lints/src/cargo/lint_groups_priority.rs b/src/tools/clippy/clippy_lints/src/cargo/lint_groups_priority.rs
index e924542fea2a..ffd6c520c9ae 100644
--- a/src/tools/clippy/clippy_lints/src/cargo/lint_groups_priority.rs
+++ b/src/tools/clippy/clippy_lints/src/cargo/lint_groups_priority.rs
@@ -2,7 +2,7 @@ use super::LINT_GROUPS_PRIORITY;
 use clippy_utils::diagnostics::span_lint_and_then;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::Applicability;
-use rustc_lint::{unerased_lint_store, LateContext};
+use rustc_lint::{LateContext, unerased_lint_store};
 use rustc_span::{BytePos, Pos, SourceFile, Span, SyntaxContext};
 use serde::{Deserialize, Serialize};
 use std::collections::BTreeMap;
diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs b/src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs
index 346aed7e9f11..84a44b14dde5 100644
--- a/src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs
@@ -10,7 +10,7 @@ use rustc_lint::LateContext;
 use rustc_middle::ty::{self, FloatTy, Ty};
 use rustc_span::hygiene;
 
-use super::{utils, CAST_LOSSLESS};
+use super::{CAST_LOSSLESS, utils};
 
 pub(super) fn check(
     cx: &LateContext<'_>,
diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs b/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs
index 5708aae3f3ec..40a1a9d1ce8f 100644
--- a/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs
@@ -12,7 +12,7 @@ use rustc_middle::ty::{self, FloatTy, Ty};
 use rustc_span::Span;
 use rustc_target::abi::IntegerType;
 
-use super::{utils, CAST_ENUM_TRUNCATION, CAST_POSSIBLE_TRUNCATION};
+use super::{CAST_ENUM_TRUNCATION, CAST_POSSIBLE_TRUNCATION, utils};
 
 fn constant_int(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option {
     if let Some(Constant::Int(c)) = ConstEvalCtxt::new(cx).eval(expr) {
diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_possible_wrap.rs b/src/tools/clippy/clippy_lints/src/casts/cast_possible_wrap.rs
index 11274383595a..3cf4a43b0d4c 100644
--- a/src/tools/clippy/clippy_lints/src/casts/cast_possible_wrap.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/cast_possible_wrap.rs
@@ -3,7 +3,7 @@ use rustc_hir::Expr;
 use rustc_lint::LateContext;
 use rustc_middle::ty::Ty;
 
-use super::{utils, CAST_POSSIBLE_WRAP};
+use super::{CAST_POSSIBLE_WRAP, utils};
 
 // this should be kept in sync with the allowed bit widths of `usize` and `isize`
 const ALLOWED_POINTER_SIZES: [u64; 3] = [16, 32, 64];
diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_precision_loss.rs b/src/tools/clippy/clippy_lints/src/casts/cast_precision_loss.rs
index 035666e4d4c9..1eb115ce6bdc 100644
--- a/src/tools/clippy/clippy_lints/src/casts/cast_precision_loss.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/cast_precision_loss.rs
@@ -4,7 +4,7 @@ use rustc_hir::Expr;
 use rustc_lint::LateContext;
 use rustc_middle::ty::{self, FloatTy, Ty};
 
-use super::{utils, CAST_PRECISION_LOSS};
+use super::{CAST_PRECISION_LOSS, utils};
 
 pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) {
     if !cast_from.is_integral() || cast_to.is_integral() {
diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_sign_loss.rs b/src/tools/clippy/clippy_lints/src/casts/cast_sign_loss.rs
index 9daf237344a4..4be53ace6871 100644
--- a/src/tools/clippy/clippy_lints/src/casts/cast_sign_loss.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/cast_sign_loss.rs
@@ -3,7 +3,7 @@ use std::ops::ControlFlow;
 
 use clippy_utils::consts::{ConstEvalCtxt, Constant};
 use clippy_utils::diagnostics::span_lint;
-use clippy_utils::visitors::{for_each_expr_without_closures, Descend};
+use clippy_utils::visitors::{Descend, for_each_expr_without_closures};
 use clippy_utils::{method_chain_args, sext};
 use rustc_hir::{BinOpKind, Expr, ExprKind};
 use rustc_lint::LateContext;
diff --git a/src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast.rs b/src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast.rs
index dbe03e4ae809..ac1a355c8d96 100644
--- a/src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast.rs
@@ -5,7 +5,7 @@ use rustc_hir::Expr;
 use rustc_lint::LateContext;
 use rustc_middle::ty::{self, Ty, UintTy};
 
-use super::{utils, FN_TO_NUMERIC_CAST};
+use super::{FN_TO_NUMERIC_CAST, utils};
 
 pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) {
     // We only want to check casts to `ty::Uint` or `ty::Int`
diff --git a/src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast_with_truncation.rs b/src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast_with_truncation.rs
index dfbae1618ac6..18e7798452ea 100644
--- a/src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast_with_truncation.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast_with_truncation.rs
@@ -5,7 +5,7 @@ use rustc_hir::Expr;
 use rustc_lint::LateContext;
 use rustc_middle::ty::{self, Ty};
 
-use super::{utils, FN_TO_NUMERIC_CAST_WITH_TRUNCATION};
+use super::{FN_TO_NUMERIC_CAST_WITH_TRUNCATION, utils};
 
 pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) {
     // We only want to check casts to `ty::Uint` or `ty::Int`
diff --git a/src/tools/clippy/clippy_lints/src/casts/mod.rs b/src/tools/clippy/clippy_lints/src/casts/mod.rs
index c31716fbcee1..3acd4eca420e 100644
--- a/src/tools/clippy/clippy_lints/src/casts/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/mod.rs
@@ -23,8 +23,8 @@ mod unnecessary_cast;
 mod utils;
 mod zero_ptr;
 
-use clippy_config::msrvs::{self, Msrv};
 use clippy_config::Conf;
+use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::is_hir_ty_cfg_dependant;
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
@@ -410,19 +410,27 @@ declare_clippy_lint! {
     /// ### Why is this bad?
     /// Though `as` casts between raw pointers are not terrible, `pointer::cast_mut` and
     /// `pointer::cast_const` are safer because they cannot accidentally cast the pointer to another
-    /// type.
+    /// type. Or, when null pointers are involved, `null()` and `null_mut()` can be used directly.
     ///
     /// ### Example
     /// ```no_run
     /// let ptr: *const u32 = &42_u32;
     /// let mut_ptr = ptr as *mut u32;
     /// let ptr = mut_ptr as *const u32;
+    /// let ptr1 = std::ptr::null::() as *mut u32;
+    /// let ptr2 = std::ptr::null_mut::() as *const u32;
+    /// let ptr3 = std::ptr::null::().cast_mut();
+    /// let ptr4 = std::ptr::null_mut::().cast_const();
     /// ```
     /// Use instead:
     /// ```no_run
     /// let ptr: *const u32 = &42_u32;
     /// let mut_ptr = ptr.cast_mut();
     /// let ptr = mut_ptr.cast_const();
+    /// let ptr1 = std::ptr::null_mut::();
+    /// let ptr2 = std::ptr::null::();
+    /// let ptr3 = std::ptr::null_mut::();
+    /// let ptr4 = std::ptr::null::();
     /// ```
     #[clippy::version = "1.72.0"]
     pub PTR_CAST_CONSTNESS,
@@ -809,6 +817,7 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
         char_lit_as_u8::check(cx, expr);
         ptr_as_ptr::check(cx, expr, &self.msrv);
         cast_slice_different_sizes::check(cx, expr, &self.msrv);
+        ptr_cast_constness::check_null_ptr_cast_method(cx, expr);
     }
 
     extract_msrv_attr!(LateContext);
diff --git a/src/tools/clippy/clippy_lints/src/casts/ptr_cast_constness.rs b/src/tools/clippy/clippy_lints/src/casts/ptr_cast_constness.rs
index 7513e18d408b..7518dd2435ae 100644
--- a/src/tools/clippy/clippy_lints/src/casts/ptr_cast_constness.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/ptr_cast_constness.rs
@@ -1,10 +1,12 @@
 use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::std_or_core;
 use clippy_utils::sugg::Sugg;
 use rustc_errors::Applicability;
-use rustc_hir::{Expr, Mutability};
+use rustc_hir::{Expr, ExprKind, Mutability, QPath};
 use rustc_lint::LateContext;
 use rustc_middle::ty::{self, Ty, TypeVisitableExt};
+use rustc_span::sym;
 
 use super::PTR_CAST_CONSTNESS;
 
@@ -16,8 +18,7 @@ pub(super) fn check<'tcx>(
     cast_to: Ty<'tcx>,
     msrv: &Msrv,
 ) {
-    if msrv.meets(msrvs::POINTER_CAST_CONSTNESS)
-        && let ty::RawPtr(from_ty, from_mutbl) = cast_from.kind()
+    if let ty::RawPtr(from_ty, from_mutbl) = cast_from.kind()
         && let ty::RawPtr(to_ty, to_mutbl) = cast_to.kind()
         && matches!(
             (from_mutbl, to_mutbl),
@@ -26,20 +27,74 @@ pub(super) fn check<'tcx>(
         && from_ty == to_ty
         && !from_ty.has_erased_regions()
     {
-        let sugg = Sugg::hir(cx, cast_expr, "_");
-        let constness = match *to_mutbl {
-            Mutability::Not => "const",
-            Mutability::Mut => "mut",
-        };
+        if let ExprKind::Call(func, []) = cast_expr.kind
+            && let ExprKind::Path(QPath::Resolved(None, path)) = func.kind
+            && let Some(defid) = path.res.opt_def_id()
+            && let Some(prefix) = std_or_core(cx)
+            && let mut app = Applicability::MachineApplicable
+            && let sugg = format!("{}", Sugg::hir_with_applicability(cx, cast_expr, "_", &mut app))
+            && let Some((_, after_lt)) = sugg.split_once("::<")
+            && let Some((source, target, target_func)) = match cx.tcx.get_diagnostic_name(defid) {
+                Some(sym::ptr_null) => Some(("const", "mutable", "null_mut")),
+                Some(sym::ptr_null_mut) => Some(("mutable", "const", "null")),
+                _ => None,
+            }
+        {
+            span_lint_and_sugg(
+                cx,
+                PTR_CAST_CONSTNESS,
+                expr.span,
+                format!("`as` casting to make a {source} null pointer into a {target} null pointer"),
+                format!("use `{target_func}()` directly instead"),
+                format!("{prefix}::ptr::{target_func}::<{after_lt}"),
+                app,
+            );
+            return;
+        }
 
+        if msrv.meets(msrvs::POINTER_CAST_CONSTNESS) {
+            let sugg = Sugg::hir(cx, cast_expr, "_");
+            let constness = match *to_mutbl {
+                Mutability::Not => "const",
+                Mutability::Mut => "mut",
+            };
+
+            span_lint_and_sugg(
+                cx,
+                PTR_CAST_CONSTNESS,
+                expr.span,
+                "`as` casting between raw pointers while changing only its constness",
+                format!("try `pointer::cast_{constness}`, a safer alternative"),
+                format!("{}.cast_{constness}()", sugg.maybe_par()),
+                Applicability::MachineApplicable,
+            );
+        }
+    }
+}
+
+pub(super) fn check_null_ptr_cast_method(cx: &LateContext<'_>, expr: &Expr<'_>) {
+    if let ExprKind::MethodCall(method, cast_expr, [], _) = expr.kind
+        && let ExprKind::Call(func, []) = cast_expr.kind
+        && let ExprKind::Path(QPath::Resolved(None, path)) = func.kind
+        && let Some(defid) = path.res.opt_def_id()
+        && let method = match (cx.tcx.get_diagnostic_name(defid), method.ident.as_str()) {
+            (Some(sym::ptr_null), "cast_mut") => "null_mut",
+            (Some(sym::ptr_null_mut), "cast_const") => "null",
+            _ => return,
+        }
+        && let Some(prefix) = std_or_core(cx)
+        && let mut app = Applicability::MachineApplicable
+        && let sugg = format!("{}", Sugg::hir_with_applicability(cx, cast_expr, "_", &mut app))
+        && let Some((_, after_lt)) = sugg.split_once("::<")
+    {
         span_lint_and_sugg(
             cx,
             PTR_CAST_CONSTNESS,
             expr.span,
-            "`as` casting between raw pointers while changing only its constness",
-            format!("try `pointer::cast_{constness}`, a safer alternative"),
-            format!("{}.cast_{constness}()", sugg.maybe_par()),
-            Applicability::MachineApplicable,
+            "changing constness of a null pointer",
+            format!("use `{method}()` directly instead"),
+            format!("{prefix}::ptr::{method}::<{after_lt}"),
+            app,
         );
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/casts/ref_as_ptr.rs b/src/tools/clippy/clippy_lints/src/casts/ref_as_ptr.rs
index 5f48b8bd2063..dfa240ccec62 100644
--- a/src/tools/clippy/clippy_lints/src/casts/ref_as_ptr.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/ref_as_ptr.rs
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::sugg::Sugg;
-use clippy_utils::{expr_use_ctxt, is_no_std_crate, ExprUseNode};
+use clippy_utils::{ExprUseNode, expr_use_ctxt, is_no_std_crate};
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, Mutability, Ty, TyKind};
 use rustc_lint::LateContext;
diff --git a/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs b/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs
index 566adc83d690..811d33c8bde2 100644
--- a/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::numeric_literal::NumericLiteral;
-use clippy_utils::source::{snippet_opt, SpanRangeExt};
-use clippy_utils::visitors::{for_each_expr_without_closures, Visitable};
+use clippy_utils::source::{SpanRangeExt, snippet_opt};
+use clippy_utils::visitors::{Visitable, for_each_expr_without_closures};
 use clippy_utils::{get_parent_expr, is_hir_ty_cfg_dependant, is_ty_alias, path_to_local};
 use rustc_ast::{LitFloatType, LitIntType, LitKind};
 use rustc_errors::Applicability;
diff --git a/src/tools/clippy/clippy_lints/src/casts/utils.rs b/src/tools/clippy/clippy_lints/src/casts/utils.rs
index 5a4f20f09906..5ccba92a0afe 100644
--- a/src/tools/clippy/clippy_lints/src/casts/utils.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/utils.rs
@@ -1,4 +1,4 @@
-use clippy_utils::ty::{read_explicit_enum_value, EnumValue};
+use clippy_utils::ty::{EnumValue, read_explicit_enum_value};
 use rustc_middle::ty::{self, AdtDef, IntTy, Ty, TyCtxt, UintTy, VariantDiscr};
 
 /// Returns the size in bits of an integral type.
diff --git a/src/tools/clippy/clippy_lints/src/cfg_not_test.rs b/src/tools/clippy/clippy_lints/src/cfg_not_test.rs
index d820c1e0720b..884d15cabffc 100644
--- a/src/tools/clippy/clippy_lints/src/cfg_not_test.rs
+++ b/src/tools/clippy/clippy_lints/src/cfg_not_test.rs
@@ -22,7 +22,7 @@ declare_clippy_lint! {
     /// # fn important_check() {}
     /// important_check();
     /// ```
-    #[clippy::version = "1.73.0"]
+    #[clippy::version = "1.81.0"]
     pub CFG_NOT_TEST,
     restriction,
     "enforce against excluding code from test builds"
diff --git a/src/tools/clippy/clippy_lints/src/checked_conversions.rs b/src/tools/clippy/clippy_lints/src/checked_conversions.rs
index dd7c34d1e468..d3aa2fd1ea12 100644
--- a/src/tools/clippy/clippy_lints/src/checked_conversions.rs
+++ b/src/tools/clippy/clippy_lints/src/checked_conversions.rs
@@ -1,8 +1,8 @@
-use clippy_config::msrvs::{self, Msrv};
 use clippy_config::Conf;
+use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::{is_in_const_context, is_integer_literal, SpanlessEq};
+use clippy_utils::{SpanlessEq, is_in_const_context, is_integer_literal};
 use rustc_errors::Applicability;
 use rustc_hir::{BinOpKind, Expr, ExprKind, QPath, TyKind};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
diff --git a/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs b/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs
index 0099eefbc8dc..495d8ce3fa70 100644
--- a/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs
+++ b/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs
@@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_help;
 use clippy_utils::source::{IntoSpan, SpanRangeExt};
 use clippy_utils::ty::is_type_diagnostic_item;
 use clippy_utils::visitors::for_each_expr_without_closures;
-use clippy_utils::{get_async_fn_body, is_async_fn, LimitStack};
+use clippy_utils::{LimitStack, get_async_fn_body, is_async_fn};
 use core::ops::ControlFlow;
 use rustc_ast::ast::Attribute;
 use rustc_hir::intravisit::FnKind;
@@ -11,7 +11,7 @@ use rustc_hir::{Body, Expr, ExprKind, FnDecl};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_session::impl_lint_pass;
 use rustc_span::def_id::LocalDefId;
-use rustc_span::{sym, Span};
+use rustc_span::{Span, sym};
 
 declare_clippy_lint! {
     /// ### What it does
diff --git a/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs b/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs
index c6847411c75a..8276e53648c0 100644
--- a/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs
+++ b/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint;
 use clippy_utils::ty::{get_type_diagnostic_name, is_type_lang_item};
-use clippy_utils::visitors::{for_each_expr, Visitable};
+use clippy_utils::visitors::{Visitable, for_each_expr};
 use clippy_utils::{get_enclosing_block, path_to_local_id};
 use core::ops::ControlFlow;
 use rustc_hir::{Body, ExprKind, HirId, LangItem, LetStmt, Node, PatKind};
diff --git a/src/tools/clippy/clippy_lints/src/comparison_chain.rs b/src/tools/clippy/clippy_lints/src/comparison_chain.rs
index 5d78744e9b5e..b9baf9af248e 100644
--- a/src/tools/clippy/clippy_lints/src/comparison_chain.rs
+++ b/src/tools/clippy/clippy_lints/src/comparison_chain.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_help;
 use clippy_utils::ty::implements_trait;
-use clippy_utils::{if_sequence, is_else_clause, is_in_const_context, SpanlessEq};
+use clippy_utils::{SpanlessEq, if_sequence, is_else_clause, is_in_const_context};
 use rustc_hir::{BinOpKind, Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::declare_lint_pass;
diff --git a/src/tools/clippy/clippy_lints/src/copies.rs b/src/tools/clippy/clippy_lints/src/copies.rs
index 86e0368c4e42..d90a22bb62a6 100644
--- a/src/tools/clippy/clippy_lints/src/copies.rs
+++ b/src/tools/clippy/clippy_lints/src/copies.rs
@@ -1,16 +1,17 @@
 use clippy_config::Conf;
 use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_then};
-use clippy_utils::source::{first_line_of_span, indent_of, reindent_multiline, snippet, IntoSpan, SpanRangeExt};
-use clippy_utils::ty::{needs_ordered_drop, InteriorMut};
+use clippy_utils::source::{IntoSpan, SpanRangeExt, first_line_of_span, indent_of, reindent_multiline, snippet};
+use clippy_utils::ty::{InteriorMut, needs_ordered_drop};
 use clippy_utils::visitors::for_each_expr_without_closures;
 use clippy_utils::{
-    capture_local_usage, eq_expr_value, find_binding_init, get_enclosing_block, hash_expr, hash_stmt, if_sequence,
-    is_else_clause, is_lint_allowed, path_to_local, search_same, ContainsName, HirEqInterExpr, SpanlessEq,
+    ContainsName, HirEqInterExpr, SpanlessEq, capture_local_usage, eq_expr_value, find_binding_init,
+    get_enclosing_block, hash_expr, hash_stmt, if_sequence, is_else_clause, is_lint_allowed, path_to_local,
+    search_same,
 };
 use core::iter;
 use core::ops::ControlFlow;
 use rustc_errors::Applicability;
-use rustc_hir::{intravisit, BinOpKind, Block, Expr, ExprKind, HirId, HirIdSet, Stmt, StmtKind};
+use rustc_hir::{BinOpKind, Block, Expr, ExprKind, HirId, HirIdSet, Stmt, StmtKind, intravisit};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::TyCtxt;
 use rustc_session::impl_lint_pass;
diff --git a/src/tools/clippy/clippy_lints/src/crate_in_macro_def.rs b/src/tools/clippy/clippy_lints/src/crate_in_macro_def.rs
index 678bdbc0ffb8..c8f814137289 100644
--- a/src/tools/clippy/clippy_lints/src/crate_in_macro_def.rs
+++ b/src/tools/clippy/clippy_lints/src/crate_in_macro_def.rs
@@ -5,8 +5,8 @@ use rustc_ast::tokenstream::{TokenStream, TokenTree};
 use rustc_errors::Applicability;
 use rustc_lint::{EarlyContext, EarlyLintPass};
 use rustc_session::declare_lint_pass;
-use rustc_span::symbol::sym;
 use rustc_span::Span;
+use rustc_span::symbol::sym;
 
 declare_clippy_lint! {
     /// ### What it does
diff --git a/src/tools/clippy/clippy_lints/src/dbg_macro.rs b/src/tools/clippy/clippy_lints/src/dbg_macro.rs
index 93c8fff05e9e..a96c86f07657 100644
--- a/src/tools/clippy/clippy_lints/src/dbg_macro.rs
+++ b/src/tools/clippy/clippy_lints/src/dbg_macro.rs
@@ -1,7 +1,7 @@
 use clippy_config::Conf;
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::is_in_test;
-use clippy_utils::macros::{macro_backtrace, MacroCall};
+use clippy_utils::macros::{MacroCall, macro_backtrace};
 use clippy_utils::source::snippet_with_applicability;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::Applicability;
@@ -9,7 +9,7 @@ use rustc_hir::{Expr, ExprKind, Node};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
 use rustc_session::impl_lint_pass;
-use rustc_span::{sym, Span, SyntaxContext};
+use rustc_span::{Span, SyntaxContext, sym};
 
 declare_clippy_lint! {
     /// ### What it does
diff --git a/src/tools/clippy/clippy_lints/src/declared_lints.rs b/src/tools/clippy/clippy_lints/src/declared_lints.rs
index 60e517131737..2b229d2fe6ac 100644
--- a/src/tools/clippy/clippy_lints/src/declared_lints.rs
+++ b/src/tools/clippy/clippy_lints/src/declared_lints.rs
@@ -49,8 +49,6 @@ pub static LINTS: &[&crate::LintInfo] = &[
     crate::attrs::DEPRECATED_CLIPPY_CFG_ATTR_INFO,
     crate::attrs::DEPRECATED_SEMVER_INFO,
     crate::attrs::DUPLICATED_ATTRIBUTES_INFO,
-    crate::attrs::EMPTY_LINE_AFTER_DOC_COMMENTS_INFO,
-    crate::attrs::EMPTY_LINE_AFTER_OUTER_ATTR_INFO,
     crate::attrs::INLINE_ALWAYS_INFO,
     crate::attrs::MIXED_ATTRIBUTES_STYLE_INFO,
     crate::attrs::NON_MINIMAL_CFG_INFO,
@@ -138,6 +136,8 @@ pub static LINTS: &[&crate::LintInfo] = &[
     crate::doc::DOC_LINK_WITH_QUOTES_INFO,
     crate::doc::DOC_MARKDOWN_INFO,
     crate::doc::EMPTY_DOCS_INFO,
+    crate::doc::EMPTY_LINE_AFTER_DOC_COMMENTS_INFO,
+    crate::doc::EMPTY_LINE_AFTER_OUTER_ATTR_INFO,
     crate::doc::MISSING_ERRORS_DOC_INFO,
     crate::doc::MISSING_PANICS_DOC_INFO,
     crate::doc::MISSING_SAFETY_DOC_INFO,
@@ -217,6 +217,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
     crate::implicit_return::IMPLICIT_RETURN_INFO,
     crate::implicit_saturating_add::IMPLICIT_SATURATING_ADD_INFO,
     crate::implicit_saturating_sub::IMPLICIT_SATURATING_SUB_INFO,
+    crate::implicit_saturating_sub::INVERTED_SATURATING_SUB_INFO,
     crate::implied_bounds_in_impls::IMPLIED_BOUNDS_IN_IMPLS_INFO,
     crate::incompatible_msrv::INCOMPATIBLE_MSRV_INFO,
     crate::inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR_INFO,
@@ -300,10 +301,12 @@ pub static LINTS: &[&crate::LintInfo] = &[
     crate::manual_async_fn::MANUAL_ASYNC_FN_INFO,
     crate::manual_bits::MANUAL_BITS_INFO,
     crate::manual_clamp::MANUAL_CLAMP_INFO,
+    crate::manual_div_ceil::MANUAL_DIV_CEIL_INFO,
     crate::manual_float_methods::MANUAL_IS_FINITE_INFO,
     crate::manual_float_methods::MANUAL_IS_INFINITE_INFO,
     crate::manual_hash_one::MANUAL_HASH_ONE_INFO,
     crate::manual_is_ascii_check::MANUAL_IS_ASCII_CHECK_INFO,
+    crate::manual_is_power_of_two::MANUAL_IS_POWER_OF_TWO_INFO,
     crate::manual_let_else::MANUAL_LET_ELSE_INFO,
     crate::manual_main_separator_str::MANUAL_MAIN_SEPARATOR_STR_INFO,
     crate::manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE_INFO,
@@ -464,6 +467,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
     crate::methods::UNNECESSARY_FALLIBLE_CONVERSIONS_INFO,
     crate::methods::UNNECESSARY_FILTER_MAP_INFO,
     crate::methods::UNNECESSARY_FIND_MAP_INFO,
+    crate::methods::UNNECESSARY_FIRST_THEN_CHECK_INFO,
     crate::methods::UNNECESSARY_FOLD_INFO,
     crate::methods::UNNECESSARY_GET_THEN_CHECK_INFO,
     crate::methods::UNNECESSARY_JOIN_INFO,
@@ -486,6 +490,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
     crate::misc::SHORT_CIRCUIT_STATEMENT_INFO,
     crate::misc::TOPLEVEL_REF_ARG_INFO,
     crate::misc::USED_UNDERSCORE_BINDING_INFO,
+    crate::misc::USED_UNDERSCORE_ITEMS_INFO,
     crate::misc_early::BUILTIN_TYPE_SHADOW_INFO,
     crate::misc_early::DOUBLE_NEG_INFO,
     crate::misc_early::DUPLICATE_UNDERSCORE_ARGUMENT_INFO,
@@ -553,6 +558,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
     crate::non_expressive_names::SIMILAR_NAMES_INFO,
     crate::non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS_INFO,
     crate::non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY_INFO,
+    crate::non_zero_suggestions::NON_ZERO_SUGGESTIONS_INFO,
     crate::nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES_INFO,
     crate::octal_escapes::OCTAL_ESCAPES_INFO,
     crate::only_used_in_recursion::ONLY_USED_IN_RECURSION_INFO,
@@ -598,6 +604,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
     crate::pathbuf_init_then_push::PATHBUF_INIT_THEN_PUSH_INFO,
     crate::pattern_type_mismatch::PATTERN_TYPE_MISMATCH_INFO,
     crate::permissions_set_readonly_false::PERMISSIONS_SET_READONLY_FALSE_INFO,
+    crate::pointers_in_nomem_asm_block::POINTERS_IN_NOMEM_ASM_BLOCK_INFO,
     crate::precedence::PRECEDENCE_INFO,
     crate::ptr::CMP_NULL_INFO,
     crate::ptr::INVALID_NULL_PTR_USAGE_INFO,
@@ -741,6 +748,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
     crate::unused_result_ok::UNUSED_RESULT_OK_INFO,
     crate::unused_rounding::UNUSED_ROUNDING_INFO,
     crate::unused_self::UNUSED_SELF_INFO,
+    crate::unused_trait_names::UNUSED_TRAIT_NAMES_INFO,
     crate::unused_unit::UNUSED_UNIT_INFO,
     crate::unwrap::PANICKING_UNWRAP_INFO,
     crate::unwrap::UNNECESSARY_UNWRAP_INFO,
@@ -767,4 +775,5 @@ pub static LINTS: &[&crate::LintInfo] = &[
     crate::zero_div_zero::ZERO_DIVIDED_BY_ZERO_INFO,
     crate::zero_repeat_side_effects::ZERO_REPEAT_SIDE_EFFECTS_INFO,
     crate::zero_sized_map_values::ZERO_SIZED_MAP_VALUES_INFO,
+    crate::zombie_processes::ZOMBIE_PROCESSES_INFO,
 ];
diff --git a/src/tools/clippy/clippy_lints/src/default.rs b/src/tools/clippy/clippy_lints/src/default.rs
index 0b7279f2b360..dc10b64698b4 100644
--- a/src/tools/clippy/clippy_lints/src/default.rs
+++ b/src/tools/clippy/clippy_lints/src/default.rs
@@ -11,7 +11,7 @@ use rustc_middle::ty;
 use rustc_middle::ty::print::with_forced_trimmed_paths;
 use rustc_session::impl_lint_pass;
 use rustc_span::symbol::{Ident, Symbol};
-use rustc_span::{sym, Span};
+use rustc_span::{Span, sym};
 
 declare_clippy_lint! {
     /// ### What it does
diff --git a/src/tools/clippy/clippy_lints/src/default_constructed_unit_structs.rs b/src/tools/clippy/clippy_lints/src/default_constructed_unit_structs.rs
index 137781754966..33a97222b8f8 100644
--- a/src/tools/clippy/clippy_lints/src/default_constructed_unit_structs.rs
+++ b/src/tools/clippy/clippy_lints/src/default_constructed_unit_structs.rs
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::is_ty_alias;
-use hir::def::Res;
 use hir::ExprKind;
+use hir::def::Res;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_lint::{LateContext, LateLintPass};
diff --git a/src/tools/clippy/clippy_lints/src/default_instead_of_iter_empty.rs b/src/tools/clippy/clippy_lints/src/default_instead_of_iter_empty.rs
index ac49e6f1a482..056e39c02af9 100644
--- a/src/tools/clippy/clippy_lints/src/default_instead_of_iter_empty.rs
+++ b/src/tools/clippy/clippy_lints/src/default_instead_of_iter_empty.rs
@@ -2,10 +2,10 @@ use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet_with_context;
 use clippy_utils::{last_path_segment, std_or_core};
 use rustc_errors::Applicability;
-use rustc_hir::{def, Expr, ExprKind, GenericArg, QPath, TyKind};
+use rustc_hir::{Expr, ExprKind, GenericArg, QPath, TyKind, def};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::declare_lint_pass;
-use rustc_span::{sym, SyntaxContext};
+use rustc_span::{SyntaxContext, sym};
 
 declare_clippy_lint! {
     /// ### What it does
diff --git a/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs b/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs
index 05c3cd3c8140..a065dc2cf7e5 100644
--- a/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs
+++ b/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs
@@ -3,7 +3,7 @@ use clippy_utils::numeric_literal;
 use clippy_utils::source::snippet_opt;
 use rustc_ast::ast::{LitFloatType, LitIntType, LitKind};
 use rustc_errors::Applicability;
-use rustc_hir::intravisit::{walk_expr, walk_stmt, Visitor};
+use rustc_hir::intravisit::{Visitor, walk_expr, walk_stmt};
 use rustc_hir::{Block, Body, ConstContext, Expr, ExprKind, FnRetTy, HirId, Lit, Stmt, StmtKind};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
diff --git a/src/tools/clippy/clippy_lints/src/dereference.rs b/src/tools/clippy/clippy_lints/src/dereference.rs
index 0e55d3db469a..f34f5e056064 100644
--- a/src/tools/clippy/clippy_lints/src/dereference.rs
+++ b/src/tools/clippy/clippy_lints/src/dereference.rs
@@ -3,14 +3,14 @@ use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
 use clippy_utils::sugg::has_enclosing_paren;
 use clippy_utils::ty::{implements_trait, is_manually_drop};
 use clippy_utils::{
-    expr_use_ctxt, get_parent_expr, is_block_like, is_lint_allowed, path_to_local, peel_middle_ty_refs, DefinedTy,
-    ExprUseNode,
+    DefinedTy, ExprUseNode, expr_use_ctxt, get_parent_expr, is_block_like, is_lint_allowed, path_to_local,
+    peel_middle_ty_refs,
 };
 use core::mem;
 use rustc_ast::util::parser::{PREC_PREFIX, PREC_UNAMBIGUOUS};
 use rustc_data_structures::fx::FxIndexMap;
 use rustc_errors::Applicability;
-use rustc_hir::intravisit::{walk_ty, Visitor};
+use rustc_hir::intravisit::{Visitor, walk_ty};
 use rustc_hir::{
     self as hir, BindingMode, Body, BodyId, BorrowKind, Expr, ExprKind, HirId, MatchSource, Mutability, Node, Pat,
     PatKind, Path, QPath, TyKind, UnOp,
@@ -290,13 +290,10 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
                             && let Some(ty) = use_node.defined_ty(cx)
                             && TyCoercionStability::for_defined_ty(cx, ty, use_node.is_return()).is_deref_stable()
                         {
-                            self.state = Some((
-                                State::ExplicitDeref { mutability: None },
-                                StateData {
-                                    first_expr: expr,
-                                    adjusted_ty,
-                                },
-                            ));
+                            self.state = Some((State::ExplicitDeref { mutability: None }, StateData {
+                                first_expr: expr,
+                                adjusted_ty,
+                            }));
                         }
                     },
                     RefOp::Method { mutbl, is_ufcs }
@@ -458,13 +455,10 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
                             && next_adjust.map_or(true, |a| matches!(a.kind, Adjust::Deref(_) | Adjust::Borrow(_)))
                             && iter.all(|a| matches!(a.kind, Adjust::Deref(_) | Adjust::Borrow(_)))
                         {
-                            self.state = Some((
-                                State::Borrow { mutability },
-                                StateData {
-                                    first_expr: expr,
-                                    adjusted_ty,
-                                },
-                            ));
+                            self.state = Some((State::Borrow { mutability }, StateData {
+                                first_expr: expr,
+                                adjusted_ty,
+                            }));
                         }
                     },
                     _ => {},
@@ -508,13 +502,10 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
                 let stability = state.stability;
                 report(cx, expr, State::DerefedBorrow(state), data, typeck);
                 if stability.is_deref_stable() {
-                    self.state = Some((
-                        State::Borrow { mutability },
-                        StateData {
-                            first_expr: expr,
-                            adjusted_ty,
-                        },
-                    ));
+                    self.state = Some((State::Borrow { mutability }, StateData {
+                        first_expr: expr,
+                        adjusted_ty,
+                    }));
                 }
             },
             (Some((State::DerefedBorrow(state), data)), RefOp::Deref) => {
@@ -539,13 +530,10 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
                 } else if stability.is_deref_stable()
                     && let Some(parent) = get_parent_expr(cx, expr)
                 {
-                    self.state = Some((
-                        State::ExplicitDeref { mutability: None },
-                        StateData {
-                            first_expr: parent,
-                            adjusted_ty,
-                        },
-                    ));
+                    self.state = Some((State::ExplicitDeref { mutability: None }, StateData {
+                        first_expr: parent,
+                        adjusted_ty,
+                    }));
                 }
             },
 
@@ -1138,20 +1126,17 @@ impl<'tcx> Dereferencing<'tcx> {
         if let Some(outer_pat) = self.ref_locals.get_mut(&local) {
             if let Some(pat) = outer_pat {
                 // Check for auto-deref
-                if !matches!(
-                    cx.typeck_results().expr_adjustments(e),
-                    [
-                        Adjustment {
-                            kind: Adjust::Deref(_),
-                            ..
-                        },
-                        Adjustment {
-                            kind: Adjust::Deref(_),
-                            ..
-                        },
+                if !matches!(cx.typeck_results().expr_adjustments(e), [
+                    Adjustment {
+                        kind: Adjust::Deref(_),
+                        ..
+                    },
+                    Adjustment {
+                        kind: Adjust::Deref(_),
                         ..
-                    ]
-                ) {
+                    },
+                    ..
+                ]) {
                     match get_parent_expr(cx, e) {
                         // Field accesses are the same no matter the number of references.
                         Some(Expr {
diff --git a/src/tools/clippy/clippy_lints/src/derivable_impls.rs b/src/tools/clippy/clippy_lints/src/derivable_impls.rs
index f27f68e2cbc5..2920bbb4c81e 100644
--- a/src/tools/clippy/clippy_lints/src/derivable_impls.rs
+++ b/src/tools/clippy/clippy_lints/src/derivable_impls.rs
@@ -1,5 +1,5 @@
-use clippy_config::msrvs::{self, Msrv};
 use clippy_config::Conf;
+use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::source::indent_of;
 use clippy_utils::{is_default_equivalent, peel_blocks};
diff --git a/src/tools/clippy/clippy_lints/src/derive.rs b/src/tools/clippy/clippy_lints/src/derive.rs
index 636698e96f6d..35a97adfb452 100644
--- a/src/tools/clippy/clippy_lints/src/derive.rs
+++ b/src/tools/clippy/clippy_lints/src/derive.rs
@@ -3,7 +3,7 @@ use clippy_utils::ty::{implements_trait, implements_trait_with_env, is_copy};
 use clippy_utils::{has_non_exhaustive_attr, is_lint_allowed, match_def_path, paths};
 use rustc_errors::Applicability;
 use rustc_hir::def_id::DefId;
-use rustc_hir::intravisit::{walk_expr, walk_fn, walk_item, FnKind, Visitor};
+use rustc_hir::intravisit::{FnKind, Visitor, walk_expr, walk_fn, walk_item};
 use rustc_hir::{
     self as hir, BlockCheckMode, BodyId, Expr, ExprKind, FnDecl, Impl, Item, ItemKind, Safety, UnsafeSource,
 };
@@ -15,7 +15,7 @@ use rustc_middle::ty::{
 };
 use rustc_session::declare_lint_pass;
 use rustc_span::def_id::LocalDefId;
-use rustc_span::{sym, Span};
+use rustc_span::{Span, sym};
 
 declare_clippy_lint! {
     /// ### What it does
@@ -386,7 +386,6 @@ fn check_unsafe_derive_deserialize<'tcx>(
             .tcx
             .inherent_impls(def.did())
             .into_iter()
-            .flatten()
             .map(|imp_did| cx.tcx.hir().expect_item(imp_did.expect_local()))
             .any(|imp| has_unsafe(cx, imp))
     {
diff --git a/src/tools/clippy/clippy_lints/src/doc/empty_line_after.rs b/src/tools/clippy/clippy_lints/src/doc/empty_line_after.rs
new file mode 100644
index 000000000000..de7a2c2433f4
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/doc/empty_line_after.rs
@@ -0,0 +1,342 @@
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::source::{SpanRangeExt, snippet_indent};
+use clippy_utils::tokenize_with_text;
+use itertools::Itertools;
+use rustc_ast::token::CommentKind;
+use rustc_ast::{AttrKind, AttrStyle, Attribute};
+use rustc_errors::{Applicability, Diag, SuggestionStyle};
+use rustc_hir::{ItemKind, Node};
+use rustc_lexer::TokenKind;
+use rustc_lint::LateContext;
+use rustc_span::{BytePos, ExpnKind, InnerSpan, Span, SpanData};
+
+use super::{EMPTY_LINE_AFTER_DOC_COMMENTS, EMPTY_LINE_AFTER_OUTER_ATTR};
+
+#[derive(Debug, PartialEq, Clone, Copy)]
+enum StopKind {
+    Attr,
+    Doc(CommentKind),
+}
+
+impl StopKind {
+    fn is_doc(self) -> bool {
+        matches!(self, StopKind::Doc(_))
+    }
+}
+
+#[derive(Debug)]
+struct Stop {
+    span: Span,
+    kind: StopKind,
+    first: usize,
+    last: usize,
+}
+
+impl Stop {
+    fn convert_to_inner(&self) -> (Span, String) {
+        let inner = match self.kind {
+            // #|[...]
+            StopKind::Attr => InnerSpan::new(1, 1),
+            // /// or /**
+            //   ^      ^
+            StopKind::Doc(_) => InnerSpan::new(2, 3),
+        };
+        (self.span.from_inner(inner), "!".into())
+    }
+
+    fn comment_out(&self, cx: &LateContext<'_>, suggestions: &mut Vec<(Span, String)>) {
+        match self.kind {
+            StopKind::Attr => {
+                if cx.tcx.sess.source_map().is_multiline(self.span) {
+                    suggestions.extend([
+                        (self.span.shrink_to_lo(), "/* ".into()),
+                        (self.span.shrink_to_hi(), " */".into()),
+                    ]);
+                } else {
+                    suggestions.push((self.span.shrink_to_lo(), "// ".into()));
+                }
+            },
+            StopKind::Doc(CommentKind::Line) => suggestions.push((self.span.shrink_to_lo(), "// ".into())),
+            StopKind::Doc(CommentKind::Block) => {
+                // /** outer */  /*! inner */
+                //  ^             ^
+                let asterisk = self.span.from_inner(InnerSpan::new(1, 2));
+                suggestions.push((asterisk, String::new()));
+            },
+        }
+    }
+
+    fn from_attr(cx: &LateContext<'_>, attr: &Attribute) -> Option {
+        let SpanData { lo, hi, .. } = attr.span.data();
+        let file = cx.tcx.sess.source_map().lookup_source_file(lo);
+
+        Some(Self {
+            span: attr.span,
+            kind: match attr.kind {
+                AttrKind::Normal(_) => StopKind::Attr,
+                AttrKind::DocComment(comment_kind, _) => StopKind::Doc(comment_kind),
+            },
+            first: file.lookup_line(file.relative_position(lo))?,
+            last: file.lookup_line(file.relative_position(hi))?,
+        })
+    }
+}
+
+/// Represents a set of attrs/doc comments separated by 1 or more empty lines
+///
+/// ```ignore
+/// /// chunk 1 docs
+/// // not an empty line so also part of chunk 1
+/// #[chunk_1_attrs] // <-- prev_stop
+///
+/// /* gap */
+///
+/// /// chunk 2 docs // <-- next_stop
+/// #[chunk_2_attrs]
+/// ```
+struct Gap<'a> {
+    /// The span of individual empty lines including the newline at the end of the line
+    empty_lines: Vec,
+    has_comment: bool,
+    next_stop: &'a Stop,
+    prev_stop: &'a Stop,
+    /// The chunk that includes [`prev_stop`](Self::prev_stop)
+    prev_chunk: &'a [Stop],
+}
+
+impl<'a> Gap<'a> {
+    fn new(cx: &LateContext<'_>, prev_chunk: &'a [Stop], next_chunk: &'a [Stop]) -> Option {
+        let prev_stop = prev_chunk.last()?;
+        let next_stop = next_chunk.first()?;
+        let gap_span = prev_stop.span.between(next_stop.span);
+        let gap_snippet = gap_span.get_source_text(cx)?;
+
+        let mut has_comment = false;
+        let mut empty_lines = Vec::new();
+
+        for (token, source, inner_span) in tokenize_with_text(&gap_snippet) {
+            match token {
+                TokenKind::BlockComment {
+                    doc_style: None,
+                    terminated: true,
+                }
+                | TokenKind::LineComment { doc_style: None } => has_comment = true,
+                TokenKind::Whitespace => {
+                    let newlines = source.bytes().positions(|b| b == b'\n');
+                    empty_lines.extend(
+                        newlines
+                            .tuple_windows()
+                            .map(|(a, b)| InnerSpan::new(inner_span.start + a + 1, inner_span.start + b))
+                            .map(|inner_span| gap_span.from_inner(inner_span)),
+                    );
+                },
+                // Ignore cfg_attr'd out attributes as they may contain empty lines, could also be from macro
+                // shenanigans
+                _ => return None,
+            }
+        }
+
+        (!empty_lines.is_empty()).then_some(Self {
+            empty_lines,
+            has_comment,
+            next_stop,
+            prev_stop,
+            prev_chunk,
+        })
+    }
+
+    fn contiguous_empty_lines(&self) -> impl Iterator + '_ {
+        self.empty_lines
+            // The `+ BytePos(1)` means "next line", because each empty line span is "N:1-N:1".
+            .chunk_by(|a, b| a.hi() + BytePos(1) == b.lo())
+            .map(|chunk| {
+                let first = chunk.first().expect("at least one empty line");
+                let last = chunk.last().expect("at least one empty line");
+                // The BytePos subtraction here is safe, as before an empty line, there must be at least one
+                // attribute/comment. The span needs to start at the end of the previous line.
+                first.with_lo(first.lo() - BytePos(1)).with_hi(last.hi())
+            })
+    }
+}
+
+/// If the node the attributes/docs apply to is the first in the module/crate suggest converting
+/// them to inner attributes/docs
+fn suggest_inner(cx: &LateContext<'_>, diag: &mut Diag<'_, ()>, kind: StopKind, gaps: &[Gap<'_>]) {
+    let Some(owner) = cx.last_node_with_lint_attrs.as_owner() else {
+        return;
+    };
+    let parent_desc = match cx.tcx.parent_hir_node(owner.into()) {
+        Node::Item(item)
+            if let ItemKind::Mod(parent_mod) = item.kind
+                && let [first, ..] = parent_mod.item_ids
+                && first.owner_id == owner =>
+        {
+            "parent module"
+        },
+        Node::Crate(crate_mod)
+            if let Some(first) = crate_mod
+                .item_ids
+                .iter()
+                .map(|&id| cx.tcx.hir().item(id))
+                // skip prelude imports
+                .find(|item| !matches!(item.span.ctxt().outer_expn_data().kind, ExpnKind::AstPass(_)))
+                && first.owner_id == owner =>
+        {
+            "crate"
+        },
+        _ => return,
+    };
+
+    diag.multipart_suggestion_verbose(
+        match kind {
+            StopKind::Attr => format!("if the attribute should apply to the {parent_desc} use an inner attribute"),
+            StopKind::Doc(_) => format!("if the comment should document the {parent_desc} use an inner doc comment"),
+        },
+        gaps.iter()
+            .flat_map(|gap| gap.prev_chunk)
+            .map(Stop::convert_to_inner)
+            .collect(),
+        Applicability::MaybeIncorrect,
+    );
+}
+
+fn check_gaps(cx: &LateContext<'_>, gaps: &[Gap<'_>]) -> bool {
+    let Some(first_gap) = gaps.first() else {
+        return false;
+    };
+    let empty_lines = || gaps.iter().flat_map(|gap| gap.empty_lines.iter().copied());
+    let contiguous_empty_lines = || gaps.iter().flat_map(Gap::contiguous_empty_lines);
+    let mut has_comment = false;
+    let mut has_attr = false;
+    for gap in gaps {
+        has_comment |= gap.has_comment;
+        if !has_attr {
+            has_attr = gap.prev_chunk.iter().any(|stop| stop.kind == StopKind::Attr);
+        }
+    }
+    let kind = first_gap.prev_stop.kind;
+    let (lint, kind_desc) = match kind {
+        StopKind::Attr => (EMPTY_LINE_AFTER_OUTER_ATTR, "outer attribute"),
+        StopKind::Doc(_) => (EMPTY_LINE_AFTER_DOC_COMMENTS, "doc comment"),
+    };
+    let (lines, are, them) = if empty_lines().nth(1).is_some() {
+        ("lines", "are", "them")
+    } else {
+        ("line", "is", "it")
+    };
+    span_lint_and_then(
+        cx,
+        lint,
+        first_gap.prev_stop.span.to(empty_lines().last().unwrap()),
+        format!("empty {lines} after {kind_desc}"),
+        |diag| {
+            if let Some(owner) = cx.last_node_with_lint_attrs.as_owner() {
+                let def_id = owner.to_def_id();
+                let def_descr = cx.tcx.def_descr(def_id);
+                diag.span_label(cx.tcx.def_span(def_id), match kind {
+                    StopKind::Attr => format!("the attribute applies to this {def_descr}"),
+                    StopKind::Doc(_) => format!("the comment documents this {def_descr}"),
+                });
+            }
+
+            diag.multipart_suggestion_with_style(
+                format!("if the empty {lines} {are} unintentional remove {them}"),
+                contiguous_empty_lines()
+                    .map(|empty_lines| (empty_lines, String::new()))
+                    .collect(),
+                Applicability::MaybeIncorrect,
+                SuggestionStyle::HideCodeAlways,
+            );
+
+            if has_comment && kind.is_doc() {
+                // Likely doc comments that applied to some now commented out code
+                //
+                // /// Old docs for Foo
+                // // struct Foo;
+
+                let mut suggestions = Vec::new();
+                for stop in gaps.iter().flat_map(|gap| gap.prev_chunk) {
+                    stop.comment_out(cx, &mut suggestions);
+                }
+                let name = match cx.tcx.hir().opt_name(cx.last_node_with_lint_attrs) {
+                    Some(name) => format!("`{name}`"),
+                    None => "this".into(),
+                };
+                diag.multipart_suggestion_verbose(
+                    format!("if the doc comment should not document {name} comment it out"),
+                    suggestions,
+                    Applicability::MaybeIncorrect,
+                );
+            } else {
+                suggest_inner(cx, diag, kind, gaps);
+            }
+
+            if kind == StopKind::Doc(CommentKind::Line)
+                && gaps
+                    .iter()
+                    .all(|gap| !gap.has_comment && gap.next_stop.kind == StopKind::Doc(CommentKind::Line))
+            {
+                // Commentless empty gaps between line doc comments, possibly intended to be part of the markdown
+
+                let indent = snippet_indent(cx, first_gap.prev_stop.span).unwrap_or_default();
+                diag.multipart_suggestion_verbose(
+                    format!("if the documentation should include the empty {lines} include {them} in the comment"),
+                    empty_lines()
+                        .map(|empty_line| (empty_line, format!("{indent}///")))
+                        .collect(),
+                    Applicability::MaybeIncorrect,
+                );
+            }
+        },
+    );
+    kind.is_doc()
+}
+
+/// Returns `true` if [`EMPTY_LINE_AFTER_DOC_COMMENTS`] triggered, used to skip other doc comment
+/// lints where they would be confusing
+///
+/// [`EMPTY_LINE_AFTER_OUTER_ATTR`] is also here to share an implementation but does not return
+/// `true` if it triggers
+pub(super) fn check(cx: &LateContext<'_>, attrs: &[Attribute]) -> bool {
+    let mut outer = attrs
+        .iter()
+        .filter(|attr| attr.style == AttrStyle::Outer && !attr.span.from_expansion())
+        .map(|attr| Stop::from_attr(cx, attr))
+        .collect::>>()
+        .unwrap_or_default();
+
+    if outer.is_empty() {
+        return false;
+    }
+
+    // Push a fake attribute Stop for the item itself so we check for gaps between the last outer
+    // attr/doc comment and the item they apply to
+    let span = cx.tcx.hir().span(cx.last_node_with_lint_attrs);
+    if !span.from_expansion()
+        && let Ok(line) = cx.tcx.sess.source_map().lookup_line(span.lo())
+    {
+        outer.push(Stop {
+            span,
+            kind: StopKind::Attr,
+            first: line.line,
+            // last doesn't need to be accurate here, we don't compare it with anything
+            last: line.line,
+        });
+    }
+
+    let mut gaps = Vec::new();
+    let mut last = 0;
+    for pos in outer
+        .array_windows()
+        .positions(|[a, b]| b.first.saturating_sub(a.last) > 1)
+    {
+        // we want to be after the first stop in the window
+        let pos = pos + 1;
+        if let Some(gap) = Gap::new(cx, &outer[last..pos], &outer[pos..]) {
+            last = pos;
+            gaps.push(gap);
+        }
+    }
+
+    check_gaps(cx, &gaps)
+}
diff --git a/src/tools/clippy/clippy_lints/src/doc/lazy_continuation.rs b/src/tools/clippy/clippy_lints/src/doc/lazy_continuation.rs
index 771bcac24418..f9e4a43c0e7a 100644
--- a/src/tools/clippy/clippy_lints/src/doc/lazy_continuation.rs
+++ b/src/tools/clippy/clippy_lints/src/doc/lazy_continuation.rs
@@ -22,7 +22,6 @@ pub(super) fn check(
     range: Range,
     mut span: Span,
     containers: &[super::Container],
-    line_break_span: Span,
 ) {
     if doc[range.clone()].contains('\t') {
         // We don't do tab stops correctly.
@@ -52,29 +51,6 @@ pub(super) fn check(
             "doc list item without indentation"
         };
         span_lint_and_then(cx, DOC_LAZY_CONTINUATION, span, msg, |diag| {
-            let snippet = clippy_utils::source::snippet(cx, line_break_span, "");
-            if snippet.chars().filter(|&c| c == '\n').count() > 1
-                && let Some(doc_comment_start) = snippet.rfind('\n')
-                && let doc_comment = snippet[doc_comment_start..].trim()
-                && (doc_comment == "///" || doc_comment == "//!")
-            {
-                // suggest filling in a blank line
-                diag.span_suggestion_verbose(
-                    line_break_span.shrink_to_lo(),
-                    "if this should be its own paragraph, add a blank doc comment line",
-                    format!("\n{doc_comment}"),
-                    Applicability::MaybeIncorrect,
-                );
-                if ccount > 0 || blockquote_level > 0 {
-                    diag.help("if this not intended to be a quote at all, escape it with `\\>`");
-                } else {
-                    let indent = list_indentation - lcount;
-                    diag.help(format!(
-                        "if this is intended to be part of the list, indent {indent} spaces"
-                    ));
-                }
-                return;
-            }
             if ccount == 0 && blockquote_level == 0 {
                 // simpler suggestion style for indentation
                 let indent = list_indentation - lcount;
diff --git a/src/tools/clippy/clippy_lints/src/doc/link_with_quotes.rs b/src/tools/clippy/clippy_lints/src/doc/link_with_quotes.rs
index 01191e811b00..1d4345e4541d 100644
--- a/src/tools/clippy/clippy_lints/src/doc/link_with_quotes.rs
+++ b/src/tools/clippy/clippy_lints/src/doc/link_with_quotes.rs
@@ -3,7 +3,7 @@ use std::ops::Range;
 use clippy_utils::diagnostics::span_lint;
 use rustc_lint::LateContext;
 
-use super::{Fragments, DOC_LINK_WITH_QUOTES};
+use super::{DOC_LINK_WITH_QUOTES, Fragments};
 
 pub fn check(cx: &LateContext<'_>, trimmed_text: &str, range: Range, fragments: Fragments<'_>) {
     if ((trimmed_text.starts_with('\'') && trimmed_text.ends_with('\''))
diff --git a/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs b/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs
index e3d748407261..c9173030a55e 100644
--- a/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs
+++ b/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs
@@ -5,7 +5,7 @@ use clippy_utils::{is_doc_hidden, return_ty};
 use rustc_hir::{BodyId, FnSig, OwnerId, Safety};
 use rustc_lint::LateContext;
 use rustc_middle::ty;
-use rustc_span::{sym, Span};
+use rustc_span::{Span, sym};
 
 pub fn check(
     cx: &LateContext<'_>,
diff --git a/src/tools/clippy/clippy_lints/src/doc/mod.rs b/src/tools/clippy/clippy_lints/src/doc/mod.rs
index 790579b21c90..0ae1fad56921 100644
--- a/src/tools/clippy/clippy_lints/src/doc/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/doc/mod.rs
@@ -23,15 +23,16 @@ use rustc_middle::hir::nested_filter;
 use rustc_middle::lint::in_external_macro;
 use rustc_middle::ty;
 use rustc_resolve::rustdoc::{
-    add_doc_fragment, attrs_to_doc_fragments, main_body_opts, source_span_for_markdown_range, span_of_fragments,
-    DocFragment,
+    DocFragment, add_doc_fragment, attrs_to_doc_fragments, main_body_opts, source_span_for_markdown_range,
+    span_of_fragments,
 };
 use rustc_session::impl_lint_pass;
 use rustc_span::edition::Edition;
-use rustc_span::{sym, Span};
+use rustc_span::{Span, sym};
 use std::ops::Range;
 use url::Url;
 
+mod empty_line_after;
 mod link_with_quotes;
 mod markdown;
 mod missing_headers;
@@ -449,13 +450,88 @@ declare_clippy_lint! {
     /// /// and probably spanning a many rows.
     /// struct Foo {}
     /// ```
-    #[clippy::version = "1.81.0"]
+    #[clippy::version = "1.82.0"]
     pub TOO_LONG_FIRST_DOC_PARAGRAPH,
     style,
     "ensure that the first line of a documentation paragraph isn't too long"
 }
 
-#[derive(Clone)]
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for empty lines after outer attributes
+    ///
+    /// ### Why is this bad?
+    /// The attribute may have meant to be an inner attribute (`#![attr]`). If
+    /// it was meant to be an outer attribute (`#[attr]`) then the empty line
+    /// should be removed
+    ///
+    /// ### Example
+    /// ```no_run
+    /// #[allow(dead_code)]
+    ///
+    /// fn not_quite_good_code() {}
+    /// ```
+    ///
+    /// Use instead:
+    /// ```no_run
+    /// // Good (as inner attribute)
+    /// #![allow(dead_code)]
+    ///
+    /// fn this_is_fine() {}
+    ///
+    /// // or
+    ///
+    /// // Good (as outer attribute)
+    /// #[allow(dead_code)]
+    /// fn this_is_fine_too() {}
+    /// ```
+    #[clippy::version = "pre 1.29.0"]
+    pub EMPTY_LINE_AFTER_OUTER_ATTR,
+    suspicious,
+    "empty line after outer attribute"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for empty lines after doc comments.
+    ///
+    /// ### Why is this bad?
+    /// The doc comment may have meant to be an inner doc comment, regular
+    /// comment or applied to some old code that is now commented out. If it was
+    /// intended to be a doc comment, then the empty line should be removed.
+    ///
+    /// ### Example
+    /// ```no_run
+    /// /// Some doc comment with a blank line after it.
+    ///
+    /// fn f() {}
+    ///
+    /// /// Docs for `old_code`
+    /// // fn old_code() {}
+    ///
+    /// fn new_code() {}
+    /// ```
+    ///
+    /// Use instead:
+    /// ```no_run
+    /// //! Convert it to an inner doc comment
+    ///
+    /// // Or a regular comment
+    ///
+    /// /// Or remove the empty line
+    /// fn f() {}
+    ///
+    /// // /// Docs for `old_code`
+    /// // fn old_code() {}
+    ///
+    /// fn new_code() {}
+    /// ```
+    #[clippy::version = "1.70.0"]
+    pub EMPTY_LINE_AFTER_DOC_COMMENTS,
+    suspicious,
+    "empty line after doc comments"
+}
+
 pub struct Documentation {
     valid_idents: FxHashSet,
     check_private_items: bool,
@@ -482,6 +558,8 @@ impl_lint_pass!(Documentation => [
     SUSPICIOUS_DOC_COMMENTS,
     EMPTY_DOCS,
     DOC_LAZY_CONTINUATION,
+    EMPTY_LINE_AFTER_OUTER_ATTR,
+    EMPTY_LINE_AFTER_DOC_COMMENTS,
     TOO_LONG_FIRST_DOC_PARAGRAPH,
 ]);
 
@@ -612,12 +690,10 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet, attrs: &[
         Some(("fake".into(), "fake".into()))
     }
 
-    if is_doc_hidden(attrs) {
+    if suspicious_doc_comments::check(cx, attrs) || empty_line_after::check(cx, attrs) || is_doc_hidden(attrs) {
         return None;
     }
 
-    suspicious_doc_comments::check(cx, attrs);
-
     let (fragments, _) = attrs_to_doc_fragments(
         attrs.iter().filter_map(|attr| {
             if in_external_macro(cx.sess(), attr.span) {
@@ -816,7 +892,6 @@ fn check_doc<'a, Events: Iterator, Range, attrs: &[Attribute]) {
+pub fn check(cx: &LateContext<'_>, attrs: &[Attribute]) -> bool {
     let replacements: Vec<_> = collect_doc_replacements(attrs);
 
     if let Some((&(lo_span, _), &(hi_span, _))) = replacements.first().zip(replacements.last()) {
@@ -24,6 +24,10 @@ pub fn check(cx: &LateContext<'_>, attrs: &[Attribute]) {
                 );
             },
         );
+
+        true
+    } else {
+        false
     }
 }
 
diff --git a/src/tools/clippy/clippy_lints/src/doc/too_long_first_doc_paragraph.rs b/src/tools/clippy/clippy_lints/src/doc/too_long_first_doc_paragraph.rs
index 7bb3bb12f2ca..0165d24c7df2 100644
--- a/src/tools/clippy/clippy_lints/src/doc/too_long_first_doc_paragraph.rs
+++ b/src/tools/clippy/clippy_lints/src/doc/too_long_first_doc_paragraph.rs
@@ -79,10 +79,17 @@ pub(super) fn check(
                 && let new_span = first_span.with_hi(second_span.lo()).with_lo(first_span.hi())
                 && let Some(snippet) = snippet_opt(cx, new_span)
             {
+                let Some(first) = snippet_opt(cx, first_span) else {
+                    return;
+                };
+                let Some(comment_form) = first.get(..3) else {
+                    return;
+                };
+
                 diag.span_suggestion(
                     new_span,
                     "add an empty line",
-                    format!("{snippet}///\n"),
+                    format!("{snippet}{comment_form}{snippet}"),
                     Applicability::MachineApplicable,
                 );
             }
diff --git a/src/tools/clippy/clippy_lints/src/entry.rs b/src/tools/clippy/clippy_lints/src/entry.rs
index 9c5100a8c1af..70524e458c78 100644
--- a/src/tools/clippy/clippy_lints/src/entry.rs
+++ b/src/tools/clippy/clippy_lints/src/entry.rs
@@ -1,17 +1,17 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::{reindent_multiline, snippet_indent, snippet_with_applicability, snippet_with_context};
 use clippy_utils::{
-    can_move_expr_to_closure_no_visit, higher, is_expr_final_block_expr, is_expr_used_or_unified,
-    peel_hir_expr_while, SpanlessEq,
+    SpanlessEq, can_move_expr_to_closure_no_visit, higher, is_expr_final_block_expr, is_expr_used_or_unified,
+    peel_hir_expr_while,
 };
 use core::fmt::{self, Write};
 use rustc_errors::Applicability;
 use rustc_hir::hir_id::HirIdSet;
-use rustc_hir::intravisit::{walk_expr, Visitor};
+use rustc_hir::intravisit::{Visitor, walk_expr};
 use rustc_hir::{Block, Expr, ExprKind, HirId, Pat, Stmt, StmtKind, UnOp};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::declare_lint_pass;
-use rustc_span::{sym, Span, SyntaxContext, DUMMY_SP};
+use rustc_span::{DUMMY_SP, Span, SyntaxContext, sym};
 
 declare_clippy_lint! {
     /// ### What it does
diff --git a/src/tools/clippy/clippy_lints/src/enum_clike.rs b/src/tools/clippy/clippy_lints/src/enum_clike.rs
index 4755cefe784c..e9e9d00907ea 100644
--- a/src/tools/clippy/clippy_lints/src/enum_clike.rs
+++ b/src/tools/clippy/clippy_lints/src/enum_clike.rs
@@ -1,4 +1,4 @@
-use clippy_utils::consts::{mir_to_const, Constant};
+use clippy_utils::consts::{Constant, mir_to_const};
 use clippy_utils::diagnostics::span_lint;
 use rustc_hir::{Item, ItemKind};
 use rustc_lint::{LateContext, LateLintPass};
diff --git a/src/tools/clippy/clippy_lints/src/escape.rs b/src/tools/clippy/clippy_lints/src/escape.rs
index 803606979410..5588124e791e 100644
--- a/src/tools/clippy/clippy_lints/src/escape.rs
+++ b/src/tools/clippy/clippy_lints/src/escape.rs
@@ -1,15 +1,15 @@
 use clippy_config::Conf;
 use clippy_utils::diagnostics::span_lint_hir;
-use rustc_hir::{intravisit, AssocItemKind, Body, FnDecl, HirId, HirIdSet, Impl, ItemKind, Node, Pat, PatKind};
+use rustc_hir::{AssocItemKind, Body, FnDecl, HirId, HirIdSet, Impl, ItemKind, Node, Pat, PatKind, intravisit};
 use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::mir::FakeReadCause;
 use rustc_middle::ty::layout::LayoutOf;
 use rustc_middle::ty::{self, TraitRef, Ty, TyCtxt};
 use rustc_session::impl_lint_pass;
+use rustc_span::Span;
 use rustc_span::def_id::LocalDefId;
 use rustc_span::symbol::kw;
-use rustc_span::Span;
 use rustc_target::spec::abi::Abi;
 
 pub struct BoxedLocal {
diff --git a/src/tools/clippy/clippy_lints/src/excessive_bools.rs b/src/tools/clippy/clippy_lints/src/excessive_bools.rs
index 8f469efb1b50..c88fb50b5afc 100644
--- a/src/tools/clippy/clippy_lints/src/excessive_bools.rs
+++ b/src/tools/clippy/clippy_lints/src/excessive_bools.rs
@@ -5,8 +5,8 @@ use rustc_hir::intravisit::FnKind;
 use rustc_hir::{Body, FnDecl, Item, ItemKind, TraitFn, TraitItem, TraitItemKind, Ty};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::impl_lint_pass;
-use rustc_span::def_id::LocalDefId;
 use rustc_span::Span;
+use rustc_span::def_id::LocalDefId;
 use rustc_target::spec::abi::Abi;
 
 declare_clippy_lint! {
diff --git a/src/tools/clippy/clippy_lints/src/excessive_nesting.rs b/src/tools/clippy/clippy_lints/src/excessive_nesting.rs
index 5154edd4399b..ce0e0faa0148 100644
--- a/src/tools/clippy/clippy_lints/src/excessive_nesting.rs
+++ b/src/tools/clippy/clippy_lints/src/excessive_nesting.rs
@@ -2,7 +2,7 @@ use clippy_config::Conf;
 use clippy_utils::diagnostics::span_lint_and_help;
 use clippy_utils::source::snippet;
 use rustc_ast::node_id::NodeSet;
-use rustc_ast::visit::{walk_block, walk_item, Visitor};
+use rustc_ast::visit::{Visitor, walk_block, walk_item};
 use rustc_ast::{Block, Crate, Inline, Item, ItemKind, ModKind, NodeId};
 use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
diff --git a/src/tools/clippy/clippy_lints/src/explicit_write.rs b/src/tools/clippy/clippy_lints/src/explicit_write.rs
index 3c4a043a732b..5b423a96918f 100644
--- a/src/tools/clippy/clippy_lints/src/explicit_write.rs
+++ b/src/tools/clippy/clippy_lints/src/explicit_write.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::macros::{format_args_inputs_span, FormatArgsStorage};
+use clippy_utils::macros::{FormatArgsStorage, format_args_inputs_span};
 use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::{is_expn_of, path_def_id};
 use rustc_errors::Applicability;
@@ -7,7 +7,7 @@ use rustc_hir::def::Res;
 use rustc_hir::{BindingMode, Block, BlockCheckMode, Expr, ExprKind, Node, PatKind, QPath, Stmt, StmtKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::impl_lint_pass;
-use rustc_span::{sym, ExpnId};
+use rustc_span::{ExpnId, sym};
 
 declare_clippy_lint! {
     /// ### What it does
diff --git a/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs b/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs
index bfe4e253ae47..bf9388b4a70f 100644
--- a/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs
+++ b/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs
@@ -3,7 +3,7 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then};
 use clippy_utils::{is_from_proc_macro, trait_ref_of_method};
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_errors::Applicability;
-use rustc_hir::intravisit::{walk_impl_item, walk_item, walk_param_bound, walk_ty, Visitor};
+use rustc_hir::intravisit::{Visitor, walk_impl_item, walk_item, walk_param_bound, walk_ty};
 use rustc_hir::{
     BodyId, ExprKind, GenericBound, GenericParam, GenericParamKind, Generics, ImplItem, ImplItemKind, Item, ItemKind,
     PredicateOrigin, Ty, TyKind, WherePredicate,
@@ -12,8 +12,8 @@ use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::hir::nested_filter;
 use rustc_middle::lint::in_external_macro;
 use rustc_session::impl_lint_pass;
-use rustc_span::def_id::{DefId, LocalDefId};
 use rustc_span::Span;
+use rustc_span::def_id::{DefId, LocalDefId};
 
 declare_clippy_lint! {
     /// ### What it does
diff --git a/src/tools/clippy/clippy_lints/src/fallible_impl_from.rs b/src/tools/clippy/clippy_lints/src/fallible_impl_from.rs
index 0446943321aa..747ea9a43446 100644
--- a/src/tools/clippy/clippy_lints/src/fallible_impl_from.rs
+++ b/src/tools/clippy/clippy_lints/src/fallible_impl_from.rs
@@ -6,7 +6,7 @@ use rustc_hir as hir;
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty;
 use rustc_session::declare_lint_pass;
-use rustc_span::{sym, Span};
+use rustc_span::{Span, sym};
 
 declare_clippy_lint! {
     /// ### What it does
diff --git a/src/tools/clippy/clippy_lints/src/field_scoped_visibility_modifiers.rs b/src/tools/clippy/clippy_lints/src/field_scoped_visibility_modifiers.rs
index 95b8e882da79..ba2b37fbf11a 100644
--- a/src/tools/clippy/clippy_lints/src/field_scoped_visibility_modifiers.rs
+++ b/src/tools/clippy/clippy_lints/src/field_scoped_visibility_modifiers.rs
@@ -41,7 +41,7 @@ declare_clippy_lint! {
     ///     }
     /// }
     /// ```
-    #[clippy::version = "1.78.0"]
+    #[clippy::version = "1.81.0"]
     pub FIELD_SCOPED_VISIBILITY_MODIFIERS,
     restriction,
     "checks for usage of a scoped visibility modifier, like `pub(crate)`, on fields"
diff --git a/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs b/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs
index bf4bcabfe891..8da6623f34de 100644
--- a/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs
+++ b/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs
@@ -1,4 +1,4 @@
-use clippy_utils::consts::Constant::{Int, F32, F64};
+use clippy_utils::consts::Constant::{F32, F64, Int};
 use clippy_utils::consts::{ConstEvalCtxt, Constant};
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::{
diff --git a/src/tools/clippy/clippy_lints/src/format.rs b/src/tools/clippy/clippy_lints/src/format.rs
index 4dedff69b9ab..5e3f6b6a1370 100644
--- a/src/tools/clippy/clippy_lints/src/format.rs
+++ b/src/tools/clippy/clippy_lints/src/format.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::macros::{find_format_arg_expr, root_macro_call_first_node, FormatArgsStorage};
-use clippy_utils::source::{snippet_with_context, SpanRangeExt};
+use clippy_utils::macros::{FormatArgsStorage, find_format_arg_expr, root_macro_call_first_node};
+use clippy_utils::source::{SpanRangeExt, snippet_with_context};
 use clippy_utils::sugg::Sugg;
 use rustc_ast::{FormatArgsPiece, FormatOptions, FormatTrait};
 use rustc_errors::Applicability;
@@ -8,7 +8,7 @@ use rustc_hir::{Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty;
 use rustc_session::impl_lint_pass;
-use rustc_span::{sym, Span};
+use rustc_span::{Span, sym};
 
 declare_clippy_lint! {
     /// ### What it does
diff --git a/src/tools/clippy/clippy_lints/src/format_args.rs b/src/tools/clippy/clippy_lints/src/format_args.rs
index 020c0c5440d9..83ab9f6557bd 100644
--- a/src/tools/clippy/clippy_lints/src/format_args.rs
+++ b/src/tools/clippy/clippy_lints/src/format_args.rs
@@ -1,11 +1,12 @@
 use arrayvec::ArrayVec;
-use clippy_config::msrvs::{self, Msrv};
 use clippy_config::Conf;
+use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
 use clippy_utils::is_diag_trait_item;
 use clippy_utils::macros::{
-    find_format_arg_expr, format_arg_removal_span, format_placeholder_format_span, is_assert_macro, is_format_macro,
-    is_panic, matching_root_macro_call, root_macro_call_first_node, FormatArgsStorage, FormatParamUsage, MacroCall,
+    FormatArgsStorage, FormatParamUsage, MacroCall, find_format_arg_expr, format_arg_removal_span,
+    format_placeholder_format_span, is_assert_macro, is_format_macro, is_panic, matching_root_macro_call,
+    root_macro_call_first_node,
 };
 use clippy_utils::source::SpanRangeExt;
 use clippy_utils::ty::{implements_trait, is_type_lang_item};
@@ -18,11 +19,11 @@ use rustc_errors::Applicability;
 use rustc_errors::SuggestionStyle::{CompletelyHidden, ShowCode};
 use rustc_hir::{Expr, ExprKind, LangItem};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
-use rustc_middle::ty::adjustment::{Adjust, Adjustment};
 use rustc_middle::ty::Ty;
+use rustc_middle::ty::adjustment::{Adjust, Adjustment};
 use rustc_session::impl_lint_pass;
 use rustc_span::edition::Edition::Edition2021;
-use rustc_span::{sym, Span, Symbol};
+use rustc_span::{Span, Symbol, sym};
 
 declare_clippy_lint! {
     /// ### What it does
diff --git a/src/tools/clippy/clippy_lints/src/format_impl.rs b/src/tools/clippy/clippy_lints/src/format_impl.rs
index 7cd12ac7db85..c196f404ce67 100644
--- a/src/tools/clippy/clippy_lints/src/format_impl.rs
+++ b/src/tools/clippy/clippy_lints/src/format_impl.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
-use clippy_utils::macros::{find_format_arg_expr, is_format_macro, root_macro_call_first_node, FormatArgsStorage};
+use clippy_utils::macros::{FormatArgsStorage, find_format_arg_expr, is_format_macro, root_macro_call_first_node};
 use clippy_utils::{get_parent_as_impl, is_diag_trait_item, path_to_local, peel_ref_operators};
 use rustc_ast::{FormatArgsPiece, FormatTrait};
 use rustc_errors::Applicability;
@@ -7,7 +7,7 @@ use rustc_hir::{Expr, ExprKind, Impl, ImplItem, ImplItemKind, QPath};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::impl_lint_pass;
 use rustc_span::symbol::kw;
-use rustc_span::{sym, Symbol};
+use rustc_span::{Symbol, sym};
 
 declare_clippy_lint! {
     /// ### What it does
diff --git a/src/tools/clippy/clippy_lints/src/format_push_string.rs b/src/tools/clippy/clippy_lints/src/format_push_string.rs
index c6f03c3a7cf9..8b1f86cbb913 100644
--- a/src/tools/clippy/clippy_lints/src/format_push_string.rs
+++ b/src/tools/clippy/clippy_lints/src/format_push_string.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::ty::is_type_lang_item;
 use clippy_utils::higher;
+use clippy_utils::ty::is_type_lang_item;
 use rustc_hir::{BinOpKind, Expr, ExprKind, LangItem, MatchSource};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::declare_lint_pass;
diff --git a/src/tools/clippy/clippy_lints/src/from_over_into.rs b/src/tools/clippy/clippy_lints/src/from_over_into.rs
index b84bf7c821ce..8832cd42dd98 100644
--- a/src/tools/clippy/clippy_lints/src/from_over_into.rs
+++ b/src/tools/clippy/clippy_lints/src/from_over_into.rs
@@ -1,11 +1,11 @@
-use clippy_config::msrvs::{self, Msrv};
 use clippy_config::Conf;
+use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::macros::span_is_local;
 use clippy_utils::path_def_id;
 use clippy_utils::source::SpanRangeExt;
 use rustc_errors::Applicability;
-use rustc_hir::intravisit::{walk_path, Visitor};
+use rustc_hir::intravisit::{Visitor, walk_path};
 use rustc_hir::{
     FnRetTy, GenericArg, GenericArgs, HirId, Impl, ImplItemKind, ImplItemRef, Item, ItemKind, PatKind, Path,
     PathSegment, Ty, TyKind,
diff --git a/src/tools/clippy/clippy_lints/src/from_str_radix_10.rs b/src/tools/clippy/clippy_lints/src/from_str_radix_10.rs
index e5fa67d69642..7361546153c2 100644
--- a/src/tools/clippy/clippy_lints/src/from_str_radix_10.rs
+++ b/src/tools/clippy/clippy_lints/src/from_str_radix_10.rs
@@ -3,7 +3,7 @@ use clippy_utils::sugg::Sugg;
 use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item};
 use clippy_utils::{is_in_const_context, is_integer_literal};
 use rustc_errors::Applicability;
-use rustc_hir::{def, Expr, ExprKind, LangItem, PrimTy, QPath, TyKind};
+use rustc_hir::{Expr, ExprKind, LangItem, PrimTy, QPath, TyKind, def};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::Ty;
 use rustc_session::declare_lint_pass;
diff --git a/src/tools/clippy/clippy_lints/src/functions/mod.rs b/src/tools/clippy/clippy_lints/src/functions/mod.rs
index 0f48941783b2..ba8a459c917e 100644
--- a/src/tools/clippy/clippy_lints/src/functions/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/functions/mod.rs
@@ -14,8 +14,8 @@ use rustc_hir::intravisit;
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::TyCtxt;
 use rustc_session::impl_lint_pass;
-use rustc_span::def_id::{DefIdSet, LocalDefId};
 use rustc_span::Span;
+use rustc_span::def_id::{DefIdSet, LocalDefId};
 
 declare_clippy_lint! {
     /// ### What it does
diff --git a/src/tools/clippy/clippy_lints/src/functions/must_use.rs b/src/tools/clippy/clippy_lints/src/functions/must_use.rs
index 938281210470..cfd11e9339fb 100644
--- a/src/tools/clippy/clippy_lints/src/functions/must_use.rs
+++ b/src/tools/clippy/clippy_lints/src/functions/must_use.rs
@@ -8,7 +8,7 @@ use rustc_infer::infer::TyCtxtInferExt;
 use rustc_lint::{LateContext, LintContext};
 use rustc_middle::lint::in_external_macro;
 use rustc_middle::ty::{self, Ty};
-use rustc_span::{sym, Span};
+use rustc_span::{Span, sym};
 
 use clippy_utils::attrs::is_proc_macro;
 use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then};
diff --git a/src/tools/clippy/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs b/src/tools/clippy/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs
index 1aeefe73cf68..c2b40ae01d4c 100644
--- a/src/tools/clippy/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs
+++ b/src/tools/clippy/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs
@@ -1,4 +1,4 @@
-use rustc_hir::{self as hir, intravisit, HirId, HirIdSet};
+use rustc_hir::{self as hir, HirId, HirIdSet, intravisit};
 use rustc_lint::LateContext;
 use rustc_middle::ty;
 use rustc_span::def_id::LocalDefId;
diff --git a/src/tools/clippy/clippy_lints/src/functions/renamed_function_params.rs b/src/tools/clippy/clippy_lints/src/functions/renamed_function_params.rs
index c7de0385c021..ac2e866e4ff7 100644
--- a/src/tools/clippy/clippy_lints/src/functions/renamed_function_params.rs
+++ b/src/tools/clippy/clippy_lints/src/functions/renamed_function_params.rs
@@ -4,8 +4,8 @@ use rustc_hir::def_id::{DefId, DefIdSet};
 use rustc_hir::hir_id::OwnerId;
 use rustc_hir::{Impl, ImplItem, ImplItemKind, ImplItemRef, ItemKind, Node, TraitRef};
 use rustc_lint::LateContext;
-use rustc_span::symbol::{kw, Ident, Symbol};
 use rustc_span::Span;
+use rustc_span::symbol::{Ident, Symbol, kw};
 
 use super::RENAMED_FUNCTION_PARAMS;
 
diff --git a/src/tools/clippy/clippy_lints/src/functions/result.rs b/src/tools/clippy/clippy_lints/src/functions/result.rs
index c3a0b40a677a..d4eaa1663208 100644
--- a/src/tools/clippy/clippy_lints/src/functions/result.rs
+++ b/src/tools/clippy/clippy_lints/src/functions/result.rs
@@ -3,11 +3,11 @@ use rustc_hir as hir;
 use rustc_lint::{LateContext, LintContext};
 use rustc_middle::lint::in_external_macro;
 use rustc_middle::ty::{self, Ty};
-use rustc_span::{sym, Span};
+use rustc_span::{Span, sym};
 
 use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then};
 use clippy_utils::trait_ref_of_method;
-use clippy_utils::ty::{approx_ty_size, is_type_diagnostic_item, AdtVariantInfo};
+use clippy_utils::ty::{AdtVariantInfo, approx_ty_size, is_type_diagnostic_item};
 
 use super::{RESULT_LARGE_ERR, RESULT_UNIT_ERR};
 
diff --git a/src/tools/clippy/clippy_lints/src/future_not_send.rs b/src/tools/clippy/clippy_lints/src/future_not_send.rs
index 9488ba756865..a4dbe134f365 100644
--- a/src/tools/clippy/clippy_lints/src/future_not_send.rs
+++ b/src/tools/clippy/clippy_lints/src/future_not_send.rs
@@ -8,7 +8,7 @@ use rustc_middle::ty::print::PrintTraitRefExt;
 use rustc_middle::ty::{self, AliasTy, ClauseKind, PredicateKind};
 use rustc_session::declare_lint_pass;
 use rustc_span::def_id::LocalDefId;
-use rustc_span::{sym, Span};
+use rustc_span::{Span, sym};
 use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
 use rustc_trait_selection::traits::{self, FulfillmentError, ObligationCtxt};
 
diff --git a/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs b/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs
index 0bca53c1536d..d63c18c0edaa 100644
--- a/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs
+++ b/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs
@@ -1,5 +1,5 @@
-use clippy_config::msrvs::{self, Msrv};
 use clippy_config::Conf;
+use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::eager_or_lazy::switch_to_eager_eval;
 use clippy_utils::source::snippet_with_context;
@@ -105,6 +105,8 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone {
                             snippet_with_context(cx, first_stmt.span.until(then_arg.span), ctxt, "..", &mut app);
                         let closure = if method_name == "then" { "|| " } else { "" };
                         format!("{closure} {{ {block_snippet}; {arg_snip} }}")
+                    } else if method_name == "then" {
+                        (std::borrow::Cow::Borrowed("|| ") + arg_snip).into_owned()
                     } else {
                         arg_snip.into_owned()
                     };
diff --git a/src/tools/clippy/clippy_lints/src/implicit_hasher.rs b/src/tools/clippy/clippy_lints/src/implicit_hasher.rs
index e56f33f8dcfe..f683925145a2 100644
--- a/src/tools/clippy/clippy_lints/src/implicit_hasher.rs
+++ b/src/tools/clippy/clippy_lints/src/implicit_hasher.rs
@@ -3,18 +3,18 @@ use std::collections::BTreeMap;
 
 use rustc_errors::{Applicability, Diag};
 use rustc_hir as hir;
-use rustc_hir::intravisit::{walk_body, walk_expr, walk_inf, walk_ty, Visitor};
+use rustc_hir::intravisit::{Visitor, walk_body, walk_expr, walk_inf, walk_ty};
 use rustc_hir::{Body, Expr, ExprKind, GenericArg, Item, ItemKind, QPath, TyKind};
 use rustc_hir_analysis::lower_ty;
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::hir::nested_filter;
 use rustc_middle::ty::{Ty, TypeckResults};
 use rustc_session::declare_lint_pass;
-use rustc_span::symbol::sym;
 use rustc_span::Span;
+use rustc_span::symbol::sym;
 
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::source::{snippet, IntoSpan, SpanRangeExt};
+use clippy_utils::source::{IntoSpan, SpanRangeExt, snippet};
 use clippy_utils::ty::is_type_diagnostic_item;
 
 declare_clippy_lint! {
diff --git a/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs b/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs
index 127c73ed637b..f4a64f5c20b4 100644
--- a/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs
+++ b/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs
@@ -1,11 +1,17 @@
-use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::{higher, is_integer_literal, peel_blocks_with_stmt, SpanlessEq};
+use clippy_config::Conf;
+use clippy_config::msrvs::{self, Msrv};
+use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
+use clippy_utils::source::snippet_opt;
+use clippy_utils::{
+    SpanlessEq, higher, is_in_const_context, is_integer_literal, path_to_local, peel_blocks, peel_blocks_with_stmt,
+};
 use rustc_ast::ast::LitKind;
 use rustc_data_structures::packed::Pu128;
 use rustc_errors::Applicability;
-use rustc_hir::{BinOpKind, Expr, ExprKind, QPath};
+use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind, HirId, QPath};
 use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::declare_lint_pass;
+use rustc_session::impl_lint_pass;
+use rustc_span::Span;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -39,7 +45,49 @@ declare_clippy_lint! {
     "Perform saturating subtraction instead of implicitly checking lower bound of data type"
 }
 
-declare_lint_pass!(ImplicitSaturatingSub => [IMPLICIT_SATURATING_SUB]);
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for comparisons between integers, followed by subtracting the greater value from the
+    /// lower one.
+    ///
+    /// ### Why is this bad?
+    /// This could result in an underflow and is most likely not what the user wants. If this was
+    /// intended to be a saturated subtraction, consider using the `saturating_sub` method directly.
+    ///
+    /// ### Example
+    /// ```no_run
+    /// let a = 12u32;
+    /// let b = 13u32;
+    ///
+    /// let result = if a > b { b - a } else { 0 };
+    /// ```
+    ///
+    /// Use instead:
+    /// ```no_run
+    /// let a = 12u32;
+    /// let b = 13u32;
+    ///
+    /// let result = a.saturating_sub(b);
+    /// ```
+    #[clippy::version = "1.44.0"]
+    pub INVERTED_SATURATING_SUB,
+    correctness,
+    "Check if a variable is smaller than another one and still subtract from it even if smaller"
+}
+
+pub struct ImplicitSaturatingSub {
+    msrv: Msrv,
+}
+
+impl_lint_pass!(ImplicitSaturatingSub => [IMPLICIT_SATURATING_SUB, INVERTED_SATURATING_SUB]);
+
+impl ImplicitSaturatingSub {
+    pub fn new(conf: &'static Conf) -> Self {
+        Self {
+            msrv: conf.msrv.clone(),
+        }
+    }
+}
 
 impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
@@ -50,73 +98,260 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub {
 
             // Check if the conditional expression is a binary operation
             && let ExprKind::Binary(ref cond_op, cond_left, cond_right) = cond.kind
+        {
+            check_with_condition(cx, expr, cond_op.node, cond_left, cond_right, then);
+        } else if let Some(higher::If {
+            cond,
+            then: if_block,
+            r#else: Some(else_block),
+        }) = higher::If::hir(expr)
+            && let ExprKind::Binary(ref cond_op, cond_left, cond_right) = cond.kind
+        {
+            check_manual_check(
+                cx, expr, cond_op, cond_left, cond_right, if_block, else_block, &self.msrv,
+            );
+        }
+    }
+
+    extract_msrv_attr!(LateContext);
+}
 
-            // Ensure that the binary operator is >, !=, or <
-            && (BinOpKind::Ne == cond_op.node || BinOpKind::Gt == cond_op.node || BinOpKind::Lt == cond_op.node)
+#[allow(clippy::too_many_arguments)]
+fn check_manual_check<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &Expr<'tcx>,
+    condition: &BinOp,
+    left_hand: &Expr<'tcx>,
+    right_hand: &Expr<'tcx>,
+    if_block: &Expr<'tcx>,
+    else_block: &Expr<'tcx>,
+    msrv: &Msrv,
+) {
+    let ty = cx.typeck_results().expr_ty(left_hand);
+    if ty.is_numeric() && !ty.is_signed() {
+        match condition.node {
+            BinOpKind::Gt | BinOpKind::Ge => check_gt(
+                cx,
+                condition.span,
+                expr.span,
+                left_hand,
+                right_hand,
+                if_block,
+                else_block,
+                msrv,
+            ),
+            BinOpKind::Lt | BinOpKind::Le => check_gt(
+                cx,
+                condition.span,
+                expr.span,
+                right_hand,
+                left_hand,
+                if_block,
+                else_block,
+                msrv,
+            ),
+            _ => {},
+        }
+    }
+}
 
-            // Check if assign operation is done
-            && let Some(target) = subtracts_one(cx, then)
+#[allow(clippy::too_many_arguments)]
+fn check_gt(
+    cx: &LateContext<'_>,
+    condition_span: Span,
+    expr_span: Span,
+    big_var: &Expr<'_>,
+    little_var: &Expr<'_>,
+    if_block: &Expr<'_>,
+    else_block: &Expr<'_>,
+    msrv: &Msrv,
+) {
+    if let Some(big_var) = Var::new(big_var)
+        && let Some(little_var) = Var::new(little_var)
+    {
+        check_subtraction(
+            cx,
+            condition_span,
+            expr_span,
+            big_var,
+            little_var,
+            if_block,
+            else_block,
+            msrv,
+        );
+    }
+}
 
-            // Extracting out the variable name
-            && let ExprKind::Path(QPath::Resolved(_, ares_path)) = target.kind
+struct Var {
+    span: Span,
+    hir_id: HirId,
+}
+
+impl Var {
+    fn new(expr: &Expr<'_>) -> Option {
+        path_to_local(expr).map(|hir_id| Self {
+            span: expr.span,
+            hir_id,
+        })
+    }
+}
+
+#[allow(clippy::too_many_arguments)]
+fn check_subtraction(
+    cx: &LateContext<'_>,
+    condition_span: Span,
+    expr_span: Span,
+    big_var: Var,
+    little_var: Var,
+    if_block: &Expr<'_>,
+    else_block: &Expr<'_>,
+    msrv: &Msrv,
+) {
+    let if_block = peel_blocks(if_block);
+    let else_block = peel_blocks(else_block);
+    if is_integer_literal(if_block, 0) {
+        // We need to check this case as well to prevent infinite recursion.
+        if is_integer_literal(else_block, 0) {
+            // Well, seems weird but who knows?
+            return;
+        }
+        // If the subtraction is done in the `else` block, then we need to also revert the two
+        // variables as it means that the check was reverted too.
+        check_subtraction(
+            cx,
+            condition_span,
+            expr_span,
+            little_var,
+            big_var,
+            else_block,
+            if_block,
+            msrv,
+        );
+        return;
+    }
+    if is_integer_literal(else_block, 0)
+        && let ExprKind::Binary(op, left, right) = if_block.kind
+        && let BinOpKind::Sub = op.node
+    {
+        let local_left = path_to_local(left);
+        let local_right = path_to_local(right);
+        if Some(big_var.hir_id) == local_left && Some(little_var.hir_id) == local_right {
+            // This part of the condition is voluntarily split from the one before to ensure that
+            // if `snippet_opt` fails, it won't try the next conditions.
+            if let Some(big_var_snippet) = snippet_opt(cx, big_var.span)
+                && let Some(little_var_snippet) = snippet_opt(cx, little_var.span)
+                && (!is_in_const_context(cx) || msrv.meets(msrvs::SATURATING_SUB_CONST))
+            {
+                span_lint_and_sugg(
+                    cx,
+                    IMPLICIT_SATURATING_SUB,
+                    expr_span,
+                    "manual arithmetic check found",
+                    "replace it with",
+                    format!("{big_var_snippet}.saturating_sub({little_var_snippet})"),
+                    Applicability::MachineApplicable,
+                );
+            }
+        } else if Some(little_var.hir_id) == local_left
+            && Some(big_var.hir_id) == local_right
+            && let Some(big_var_snippet) = snippet_opt(cx, big_var.span)
+            && let Some(little_var_snippet) = snippet_opt(cx, little_var.span)
         {
-            // Handle symmetric conditions in the if statement
-            let (cond_var, cond_num_val) = if SpanlessEq::new(cx).eq_expr(cond_left, target) {
-                if BinOpKind::Gt == cond_op.node || BinOpKind::Ne == cond_op.node {
-                    (cond_left, cond_right)
-                } else {
-                    return;
-                }
-            } else if SpanlessEq::new(cx).eq_expr(cond_right, target) {
-                if BinOpKind::Lt == cond_op.node || BinOpKind::Ne == cond_op.node {
-                    (cond_right, cond_left)
-                } else {
-                    return;
-                }
+            span_lint_and_then(
+                cx,
+                INVERTED_SATURATING_SUB,
+                condition_span,
+                "inverted arithmetic check before subtraction",
+                |diag| {
+                    diag.span_note(
+                        if_block.span,
+                        format!("this subtraction underflows when `{little_var_snippet} < {big_var_snippet}`"),
+                    );
+                    diag.span_suggestion(
+                        if_block.span,
+                        "try replacing it with",
+                        format!("{big_var_snippet} - {little_var_snippet}"),
+                        Applicability::MaybeIncorrect,
+                    );
+                },
+            );
+        }
+    }
+}
+
+fn check_with_condition<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &Expr<'tcx>,
+    cond_op: BinOpKind,
+    cond_left: &Expr<'tcx>,
+    cond_right: &Expr<'tcx>,
+    then: &Expr<'tcx>,
+) {
+    // Ensure that the binary operator is >, !=, or <
+    if (BinOpKind::Ne == cond_op || BinOpKind::Gt == cond_op || BinOpKind::Lt == cond_op)
+
+        // Check if assign operation is done
+        && let Some(target) = subtracts_one(cx, then)
+
+        // Extracting out the variable name
+        && let ExprKind::Path(QPath::Resolved(_, ares_path)) = target.kind
+    {
+        // Handle symmetric conditions in the if statement
+        let (cond_var, cond_num_val) = if SpanlessEq::new(cx).eq_expr(cond_left, target) {
+            if BinOpKind::Gt == cond_op || BinOpKind::Ne == cond_op {
+                (cond_left, cond_right)
             } else {
                 return;
-            };
-
-            // Check if the variable in the condition statement is an integer
-            if !cx.typeck_results().expr_ty(cond_var).is_integral() {
+            }
+        } else if SpanlessEq::new(cx).eq_expr(cond_right, target) {
+            if BinOpKind::Lt == cond_op || BinOpKind::Ne == cond_op {
+                (cond_right, cond_left)
+            } else {
                 return;
             }
+        } else {
+            return;
+        };
 
-            // Get the variable name
-            let var_name = ares_path.segments[0].ident.name.as_str();
-            match cond_num_val.kind {
-                ExprKind::Lit(cond_lit) => {
-                    // Check if the constant is zero
-                    if let LitKind::Int(Pu128(0), _) = cond_lit.node {
-                        if cx.typeck_results().expr_ty(cond_left).is_signed() {
-                        } else {
-                            print_lint_and_sugg(cx, var_name, expr);
-                        };
-                    }
-                },
-                ExprKind::Path(QPath::TypeRelative(_, name)) => {
-                    if name.ident.as_str() == "MIN"
-                        && let Some(const_id) = cx.typeck_results().type_dependent_def_id(cond_num_val.hir_id)
-                        && let Some(impl_id) = cx.tcx.impl_of_method(const_id)
-                        && let None = cx.tcx.impl_trait_ref(impl_id) // An inherent impl
-                        && cx.tcx.type_of(impl_id).instantiate_identity().is_integral()
-                    {
-                        print_lint_and_sugg(cx, var_name, expr);
-                    }
-                },
-                ExprKind::Call(func, []) => {
-                    if let ExprKind::Path(QPath::TypeRelative(_, name)) = func.kind
-                        && name.ident.as_str() == "min_value"
-                        && let Some(func_id) = cx.typeck_results().type_dependent_def_id(func.hir_id)
-                        && let Some(impl_id) = cx.tcx.impl_of_method(func_id)
-                        && let None = cx.tcx.impl_trait_ref(impl_id) // An inherent impl
-                        && cx.tcx.type_of(impl_id).instantiate_identity().is_integral()
-                    {
+        // Check if the variable in the condition statement is an integer
+        if !cx.typeck_results().expr_ty(cond_var).is_integral() {
+            return;
+        }
+
+        // Get the variable name
+        let var_name = ares_path.segments[0].ident.name.as_str();
+        match cond_num_val.kind {
+            ExprKind::Lit(cond_lit) => {
+                // Check if the constant is zero
+                if let LitKind::Int(Pu128(0), _) = cond_lit.node {
+                    if cx.typeck_results().expr_ty(cond_left).is_signed() {
+                    } else {
                         print_lint_and_sugg(cx, var_name, expr);
-                    }
-                },
-                _ => (),
-            }
+                    };
+                }
+            },
+            ExprKind::Path(QPath::TypeRelative(_, name)) => {
+                if name.ident.as_str() == "MIN"
+                    && let Some(const_id) = cx.typeck_results().type_dependent_def_id(cond_num_val.hir_id)
+                    && let Some(impl_id) = cx.tcx.impl_of_method(const_id)
+                    && let None = cx.tcx.impl_trait_ref(impl_id) // An inherent impl
+                    && cx.tcx.type_of(impl_id).instantiate_identity().is_integral()
+                {
+                    print_lint_and_sugg(cx, var_name, expr);
+                }
+            },
+            ExprKind::Call(func, []) => {
+                if let ExprKind::Path(QPath::TypeRelative(_, name)) = func.kind
+                    && name.ident.as_str() == "min_value"
+                    && let Some(func_id) = cx.typeck_results().type_dependent_def_id(func.hir_id)
+                    && let Some(impl_id) = cx.tcx.impl_of_method(func_id)
+                    && let None = cx.tcx.impl_trait_ref(impl_id) // An inherent impl
+                    && cx.tcx.type_of(impl_id).instantiate_identity().is_integral()
+                {
+                    print_lint_and_sugg(cx, var_name, expr);
+                }
+            },
+            _ => (),
         }
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs b/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs
index 2ad045e12686..0b3a6ee1beae 100644
--- a/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs
+++ b/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs
@@ -1,5 +1,5 @@
-use clippy_config::msrvs::Msrv;
 use clippy_config::Conf;
+use clippy_config::msrvs::Msrv;
 use clippy_utils::diagnostics::span_lint;
 use clippy_utils::is_in_test;
 use rustc_attr::{StabilityLevel, StableSince};
@@ -7,7 +7,7 @@ use rustc_data_structures::fx::FxHashMap;
 use rustc_hir::{Expr, ExprKind, HirId};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::TyCtxt;
-use rustc_session::{impl_lint_pass, RustcVersion};
+use rustc_session::{RustcVersion, impl_lint_pass};
 use rustc_span::def_id::DefId;
 use rustc_span::{ExpnKind, Span};
 
diff --git a/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs b/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs
index 2f9661c9ea38..39afb6810b8e 100644
--- a/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs
+++ b/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs
@@ -1,5 +1,5 @@
-use clippy_config::msrvs::{self, Msrv};
 use clippy_config::Conf;
+use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::consts::{ConstEvalCtxt, Constant};
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::higher::IfLet;
@@ -8,14 +8,14 @@ use clippy_utils::{is_expn_of, is_lint_allowed, path_to_local};
 use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
 use rustc_errors::Applicability;
 use rustc_hir as hir;
-use rustc_hir::intravisit::{self, Visitor};
 use rustc_hir::HirId;
+use rustc_hir::intravisit::{self, Visitor};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::hir::nested_filter;
 use rustc_middle::ty;
 use rustc_session::impl_lint_pass;
-use rustc_span::symbol::Ident;
 use rustc_span::Span;
+use rustc_span::symbol::Ident;
 
 declare_clippy_lint! {
     /// ### What it does
diff --git a/src/tools/clippy/clippy_lints/src/ineffective_open_options.rs b/src/tools/clippy/clippy_lints/src/ineffective_open_options.rs
index af5b1f957399..3a28553b55e2 100644
--- a/src/tools/clippy/clippy_lints/src/ineffective_open_options.rs
+++ b/src/tools/clippy/clippy_lints/src/ineffective_open_options.rs
@@ -7,7 +7,7 @@ use rustc_hir::{Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty;
 use rustc_session::declare_lint_pass;
-use rustc_span::{sym, BytePos, Span};
+use rustc_span::{BytePos, Span, sym};
 
 declare_clippy_lint! {
     /// ### What it does
diff --git a/src/tools/clippy/clippy_lints/src/inherent_impl.rs b/src/tools/clippy/clippy_lints/src/inherent_impl.rs
index d39f910f9936..309d2dfb28b8 100644
--- a/src/tools/clippy/clippy_lints/src/inherent_impl.rs
+++ b/src/tools/clippy/clippy_lints/src/inherent_impl.rs
@@ -51,9 +51,7 @@ impl<'tcx> LateLintPass<'tcx> for MultipleInherentImpl {
         // List of spans to lint. (lint_span, first_span)
         let mut lint_spans = Vec::new();
 
-        let Ok(impls) = cx.tcx.crate_inherent_impls(()) else {
-            return;
-        };
+        let (impls, _) = cx.tcx.crate_inherent_impls(());
 
         for (&id, impl_ids) in &impls.inherent_impls {
             if impl_ids.len() < 2
diff --git a/src/tools/clippy/clippy_lints/src/instant_subtraction.rs b/src/tools/clippy/clippy_lints/src/instant_subtraction.rs
index 6d6820311b67..66931a7f98c3 100644
--- a/src/tools/clippy/clippy_lints/src/instant_subtraction.rs
+++ b/src/tools/clippy/clippy_lints/src/instant_subtraction.rs
@@ -1,5 +1,5 @@
-use clippy_config::msrvs::{self, Msrv};
 use clippy_config::Conf;
+use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet_with_context;
 use clippy_utils::sugg::Sugg;
diff --git a/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs b/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs
index 74bf82f58bdc..66a8a3167a4f 100644
--- a/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs
+++ b/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs
@@ -8,8 +8,8 @@ use rustc_data_structures::fx::FxHashSet;
 use rustc_hir::{EnumDef, FieldDef, Item, ItemKind, OwnerId, Variant, VariantData};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::impl_lint_pass;
-use rustc_span::symbol::Symbol;
 use rustc_span::Span;
+use rustc_span::symbol::Symbol;
 
 declare_clippy_lint! {
     /// ### What it does
diff --git a/src/tools/clippy/clippy_lints/src/items_after_test_module.rs b/src/tools/clippy/clippy_lints/src/items_after_test_module.rs
index 8caa484bb993..1ac549b74ac6 100644
--- a/src/tools/clippy/clippy_lints/src/items_after_test_module.rs
+++ b/src/tools/clippy/clippy_lints/src/items_after_test_module.rs
@@ -6,7 +6,7 @@ use rustc_hir::{HirId, Item, ItemKind, Mod};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::declare_lint_pass;
 use rustc_span::hygiene::AstPass;
-use rustc_span::{sym, ExpnKind};
+use rustc_span::{ExpnKind, sym};
 
 declare_clippy_lint! {
     /// ### What it does
diff --git a/src/tools/clippy/clippy_lints/src/iter_not_returning_iterator.rs b/src/tools/clippy/clippy_lints/src/iter_not_returning_iterator.rs
index ba0cd5d6eb35..b19b348c7430 100644
--- a/src/tools/clippy/clippy_lints/src/iter_not_returning_iterator.rs
+++ b/src/tools/clippy/clippy_lints/src/iter_not_returning_iterator.rs
@@ -4,7 +4,7 @@ use rustc_hir::def_id::LocalDefId;
 use rustc_hir::{FnSig, ImplItem, ImplItemKind, Item, ItemKind, Node, TraitItem, TraitItemKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::declare_lint_pass;
-use rustc_span::{sym, Symbol};
+use rustc_span::{Symbol, sym};
 
 declare_clippy_lint! {
     /// ### What it does
diff --git a/src/tools/clippy/clippy_lints/src/iter_over_hash_type.rs b/src/tools/clippy/clippy_lints/src/iter_over_hash_type.rs
index f162948bb445..5131f5b7269b 100644
--- a/src/tools/clippy/clippy_lints/src/iter_over_hash_type.rs
+++ b/src/tools/clippy/clippy_lints/src/iter_over_hash_type.rs
@@ -55,7 +55,9 @@ impl LateLintPass<'_> for IterOverHashType {
         if let Some(for_loop) = ForLoop::hir(expr)
             && !for_loop.body.span.from_expansion()
             && let ty = cx.typeck_results().expr_ty(for_loop.arg).peel_refs()
-            && hash_iter_tys.into_iter().any(|sym| is_type_diagnostic_item(cx, ty, sym))
+            && hash_iter_tys
+                .into_iter()
+                .any(|sym| is_type_diagnostic_item(cx, ty, sym))
         {
             span_lint(
                 cx,
diff --git a/src/tools/clippy/clippy_lints/src/large_enum_variant.rs b/src/tools/clippy/clippy_lints/src/large_enum_variant.rs
index 2f027c117072..923089c72232 100644
--- a/src/tools/clippy/clippy_lints/src/large_enum_variant.rs
+++ b/src/tools/clippy/clippy_lints/src/large_enum_variant.rs
@@ -1,7 +1,7 @@
 use clippy_config::Conf;
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::ty::{approx_ty_size, is_copy, AdtVariantInfo};
+use clippy_utils::ty::{AdtVariantInfo, approx_ty_size, is_copy};
 use rustc_errors::Applicability;
 use rustc_hir::{Item, ItemKind};
 use rustc_lint::{LateContext, LateLintPass};
diff --git a/src/tools/clippy/clippy_lints/src/large_stack_arrays.rs b/src/tools/clippy/clippy_lints/src/large_stack_arrays.rs
index 15a75b060896..0f061d6de504 100644
--- a/src/tools/clippy/clippy_lints/src/large_stack_arrays.rs
+++ b/src/tools/clippy/clippy_lints/src/large_stack_arrays.rs
@@ -8,7 +8,7 @@ use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::layout::LayoutOf;
 use rustc_middle::ty::{self, ConstKind};
 use rustc_session::impl_lint_pass;
-use rustc_span::{sym, Span};
+use rustc_span::{Span, sym};
 
 declare_clippy_lint! {
     /// ### What it does
diff --git a/src/tools/clippy/clippy_lints/src/legacy_numeric_constants.rs b/src/tools/clippy/clippy_lints/src/legacy_numeric_constants.rs
index ccab1e27d3b0..e17d6213679a 100644
--- a/src/tools/clippy/clippy_lints/src/legacy_numeric_constants.rs
+++ b/src/tools/clippy/clippy_lints/src/legacy_numeric_constants.rs
@@ -1,5 +1,5 @@
-use clippy_config::msrvs::{Msrv, NUMERIC_ASSOCIATED_CONSTANTS};
 use clippy_config::Conf;
+use clippy_config::msrvs::{Msrv, NUMERIC_ASSOCIATED_CONSTANTS};
 use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then};
 use clippy_utils::{get_parent_expr, is_from_proc_macro};
 use hir::def_id::DefId;
@@ -10,7 +10,7 @@ use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
 use rustc_session::impl_lint_pass;
 use rustc_span::symbol::kw;
-use rustc_span::{sym, Symbol};
+use rustc_span::{Symbol, sym};
 
 declare_clippy_lint! {
     /// ### What it does
diff --git a/src/tools/clippy/clippy_lints/src/len_zero.rs b/src/tools/clippy/clippy_lints/src/len_zero.rs
index 0bc7fc2dd9d1..8bc2a56af993 100644
--- a/src/tools/clippy/clippy_lints/src/len_zero.rs
+++ b/src/tools/clippy/clippy_lints/src/len_zero.rs
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
-use clippy_utils::source::{snippet_with_context, SpanRangeExt};
-use clippy_utils::sugg::{has_enclosing_paren, Sugg};
-use clippy_utils::{get_item_name, get_parent_as_impl, is_lint_allowed, peel_ref_operators};
+use clippy_utils::source::{SpanRangeExt, snippet_with_context};
+use clippy_utils::sugg::{Sugg, has_enclosing_paren};
+use clippy_utils::{get_item_name, get_parent_as_impl, is_lint_allowed, is_trait_method, peel_ref_operators};
 use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
 use rustc_hir::def::Res;
@@ -185,6 +185,19 @@ impl<'tcx> LateLintPass<'tcx> for LenZero {
             );
         }
 
+        if let ExprKind::MethodCall(method, lhs_expr, [rhs_expr], _) = expr.kind
+            && is_trait_method(cx, expr, sym::PartialEq)
+            && !expr.span.from_expansion()
+        {
+            check_empty_expr(
+                cx,
+                expr.span,
+                lhs_expr,
+                peel_ref_operators(cx, rhs_expr),
+                (method.ident.name == sym::ne).then_some("!").unwrap_or_default(),
+            );
+        }
+
         if let ExprKind::Binary(Spanned { node: cmp, .. }, left, right) = expr.kind
             && !expr.span.from_expansion()
         {
@@ -448,7 +461,6 @@ fn check_for_is_empty(
         .tcx
         .inherent_impls(impl_ty)
         .into_iter()
-        .flatten()
         .flat_map(|&id| cx.tcx.associated_items(id).filter_by_name_unhygienic(is_empty))
         .find(|item| item.kind == AssocKind::Fn);
 
@@ -616,7 +628,7 @@ fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
     /// Checks the inherent impl's items for an `is_empty(self)` method.
     fn has_is_empty_impl(cx: &LateContext<'_>, id: DefId) -> bool {
         let is_empty = sym!(is_empty);
-        cx.tcx.inherent_impls(id).into_iter().flatten().any(|imp| {
+        cx.tcx.inherent_impls(id).into_iter().any(|imp| {
             cx.tcx
                 .associated_items(*imp)
                 .filter_by_name_unhygienic(is_empty)
diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs
index 2ac06b360bea..1d41f568f378 100644
--- a/src/tools/clippy/clippy_lints/src/lib.rs
+++ b/src/tools/clippy/clippy_lints/src/lib.rs
@@ -203,9 +203,11 @@ mod manual_assert;
 mod manual_async_fn;
 mod manual_bits;
 mod manual_clamp;
+mod manual_div_ceil;
 mod manual_float_methods;
 mod manual_hash_one;
 mod manual_is_ascii_check;
+mod manual_is_power_of_two;
 mod manual_let_else;
 mod manual_main_separator_str;
 mod manual_non_exhaustive;
@@ -271,6 +273,7 @@ mod non_copy_const;
 mod non_expressive_names;
 mod non_octal_unix_permissions;
 mod non_send_fields_in_send_ty;
+mod non_zero_suggestions;
 mod nonstandard_macro_braces;
 mod octal_escapes;
 mod only_used_in_recursion;
@@ -287,6 +290,7 @@ mod pass_by_ref_or_value;
 mod pathbuf_init_then_push;
 mod pattern_type_mismatch;
 mod permissions_set_readonly_false;
+mod pointers_in_nomem_asm_block;
 mod precedence;
 mod ptr;
 mod ptr_offset_with_cast;
@@ -372,6 +376,7 @@ mod unused_peekable;
 mod unused_result_ok;
 mod unused_rounding;
 mod unused_self;
+mod unused_trait_names;
 mod unused_unit;
 mod unwrap;
 mod unwrap_in_result;
@@ -386,9 +391,10 @@ mod write;
 mod zero_div_zero;
 mod zero_repeat_side_effects;
 mod zero_sized_map_values;
+mod zombie_processes;
 // end lints modules, do not remove this comment, it’s used in `update_lints`
 
-use clippy_config::{get_configuration_metadata, Conf};
+use clippy_config::{Conf, get_configuration_metadata};
 use clippy_utils::macros::FormatArgsStorage;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_lint::{Lint, LintId};
@@ -623,7 +629,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
     store.register_late_pass(|_| Box::new(unit_return_expecting_ord::UnitReturnExpectingOrd));
     store.register_late_pass(|_| Box::new(strings::StringAdd));
     store.register_late_pass(|_| Box::new(implicit_return::ImplicitReturn));
-    store.register_late_pass(|_| Box::new(implicit_saturating_sub::ImplicitSaturatingSub));
+    store.register_late_pass(move |_| Box::new(implicit_saturating_sub::ImplicitSaturatingSub::new(conf)));
     store.register_late_pass(|_| Box::new(default_numeric_fallback::DefaultNumericFallback));
     store.register_late_pass(|_| Box::new(inconsistent_struct_constructor::InconsistentStructConstructor));
     store.register_late_pass(|_| Box::new(non_octal_unix_permissions::NonOctalUnixPermissions));
@@ -632,8 +638,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
     let format_args = format_args_storage.clone();
     store.register_late_pass(move |_| Box::new(methods::Methods::new(conf, format_args.clone())));
     store.register_late_pass(move |_| Box::new(matches::Matches::new(conf)));
-    store.register_early_pass(move || Box::new(manual_non_exhaustive::ManualNonExhaustiveStruct::new(conf)));
-    store.register_late_pass(move |_| Box::new(manual_non_exhaustive::ManualNonExhaustiveEnum::new(conf)));
+    store.register_late_pass(move |_| Box::new(manual_non_exhaustive::ManualNonExhaustive::new(conf)));
     store.register_late_pass(move |_| Box::new(manual_strip::ManualStrip::new(conf)));
     store.register_early_pass(move || Box::new(redundant_static_lifetimes::RedundantStaticLifetimes::new(conf)));
     store.register_early_pass(move || Box::new(redundant_field_names::RedundantFieldNames::new(conf)));
@@ -933,5 +938,11 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
     store.register_late_pass(|_| Box::new(set_contains_or_insert::SetContainsOrInsert));
     store.register_early_pass(|| Box::new(byte_char_slices::ByteCharSlice));
     store.register_early_pass(|| Box::new(cfg_not_test::CfgNotTest));
+    store.register_late_pass(|_| Box::new(zombie_processes::ZombieProcesses));
+    store.register_late_pass(|_| Box::new(pointers_in_nomem_asm_block::PointersInNomemAsmBlock));
+    store.register_late_pass(move |_| Box::new(manual_div_ceil::ManualDivCeil::new(conf)));
+    store.register_late_pass(|_| Box::new(manual_is_power_of_two::ManualIsPowerOfTwo));
+    store.register_late_pass(|_| Box::new(non_zero_suggestions::NonZeroSuggestions));
+    store.register_late_pass(move |_| Box::new(unused_trait_names::UnusedTraitNames::new(conf)));
     // add lints here, do not remove this comment, it's used in `new_lint`
 }
diff --git a/src/tools/clippy/clippy_lints/src/lifetimes.rs b/src/tools/clippy/clippy_lints/src/lifetimes.rs
index ba34af9c100e..755afe959b37 100644
--- a/src/tools/clippy/clippy_lints/src/lifetimes.rs
+++ b/src/tools/clippy/clippy_lints/src/lifetimes.rs
@@ -3,25 +3,25 @@ use clippy_utils::trait_ref_of_method;
 use itertools::Itertools;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_errors::Applicability;
+use rustc_hir::FnRetTy::Return;
 use rustc_hir::intravisit::nested_filter::{self as hir_nested_filter, NestedFilter};
 use rustc_hir::intravisit::{
-    walk_fn_decl, walk_generic_param, walk_generics, walk_impl_item_ref, walk_item, walk_param_bound,
-    walk_poly_trait_ref, walk_trait_ref, walk_ty, Visitor,
+    Visitor, walk_fn_decl, walk_generic_param, walk_generics, walk_impl_item_ref, walk_item, walk_param_bound,
+    walk_poly_trait_ref, walk_trait_ref, walk_ty,
 };
-use rustc_hir::FnRetTy::Return;
 use rustc_hir::{
-    lang_items, BareFnTy, BodyId, FnDecl, FnSig, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics,
-    Impl, ImplItem, ImplItemKind, Item, ItemKind, Lifetime, LifetimeName, LifetimeParamKind, Node, PolyTraitRef,
-    PredicateOrigin, TraitFn, TraitItem, TraitItemKind, Ty, TyKind, WherePredicate,
+    BareFnTy, BodyId, FnDecl, FnSig, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics, Impl,
+    ImplItem, ImplItemKind, Item, ItemKind, Lifetime, LifetimeName, LifetimeParamKind, Node, PolyTraitRef,
+    PredicateOrigin, TraitFn, TraitItem, TraitItemKind, Ty, TyKind, WherePredicate, lang_items,
 };
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::hir::map::Map;
 use rustc_middle::hir::nested_filter as middle_nested_filter;
 use rustc_middle::lint::in_external_macro;
 use rustc_session::declare_lint_pass;
-use rustc_span::def_id::LocalDefId;
-use rustc_span::symbol::{kw, Ident, Symbol};
 use rustc_span::Span;
+use rustc_span::def_id::LocalDefId;
+use rustc_span::symbol::{Ident, Symbol, kw};
 use std::ops::ControlFlow;
 
 declare_clippy_lint! {
diff --git a/src/tools/clippy/clippy_lints/src/loops/explicit_counter_loop.rs b/src/tools/clippy/clippy_lints/src/loops/explicit_counter_loop.rs
index 73a23615c2dc..9aa4d2f0adc2 100644
--- a/src/tools/clippy/clippy_lints/src/loops/explicit_counter_loop.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/explicit_counter_loop.rs
@@ -1,4 +1,4 @@
-use super::{make_iterator_snippet, IncrementVisitor, InitializeVisitor, EXPLICIT_COUNTER_LOOP};
+use super::{EXPLICIT_COUNTER_LOOP, IncrementVisitor, InitializeVisitor, make_iterator_snippet};
 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
 use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::{get_enclosing_block, is_integer_const};
diff --git a/src/tools/clippy/clippy_lints/src/loops/infinite_loop.rs b/src/tools/clippy/clippy_lints/src/loops/infinite_loop.rs
index 5b5bb88c1790..858e3be5093e 100644
--- a/src/tools/clippy/clippy_lints/src/loops/infinite_loop.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/infinite_loop.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::{fn_def_id, is_from_proc_macro, is_lint_allowed};
-use hir::intravisit::{walk_expr, Visitor};
+use hir::intravisit::{Visitor, walk_expr};
 use hir::{Expr, ExprKind, FnRetTy, FnSig, Node};
 use rustc_ast::Label;
 use rustc_errors::Applicability;
diff --git a/src/tools/clippy/clippy_lints/src/loops/manual_find.rs b/src/tools/clippy/clippy_lints/src/loops/manual_find.rs
index b27528c11d48..bfe2e68b5d1f 100644
--- a/src/tools/clippy/clippy_lints/src/loops/manual_find.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/manual_find.rs
@@ -1,5 +1,5 @@
-use super::utils::make_iterator_snippet;
 use super::MANUAL_FIND;
+use super::utils::make_iterator_snippet;
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::ty::implements_trait;
diff --git a/src/tools/clippy/clippy_lints/src/loops/manual_flatten.rs b/src/tools/clippy/clippy_lints/src/loops/manual_flatten.rs
index dbc094a6d73f..366c310592f5 100644
--- a/src/tools/clippy/clippy_lints/src/loops/manual_flatten.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/manual_flatten.rs
@@ -1,5 +1,5 @@
-use super::utils::make_iterator_snippet;
 use super::MANUAL_FLATTEN;
+use super::utils::make_iterator_snippet;
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::visitors::is_local_used;
 use clippy_utils::{higher, path_to_local_id, peel_blocks_with_stmt};
diff --git a/src/tools/clippy/clippy_lints/src/loops/manual_while_let_some.rs b/src/tools/clippy/clippy_lints/src/loops/manual_while_let_some.rs
index 55d1b9ee6767..7476a87267f3 100644
--- a/src/tools/clippy/clippy_lints/src/loops/manual_while_let_some.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/manual_while_let_some.rs
@@ -1,10 +1,10 @@
+use clippy_utils::SpanlessEq;
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::source::snippet;
-use clippy_utils::SpanlessEq;
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind, Pat, Stmt, StmtKind, UnOp};
 use rustc_lint::LateContext;
-use rustc_span::{sym, Symbol, Span};
+use rustc_span::{Span, Symbol, sym};
 use std::borrow::Cow;
 
 use super::MANUAL_WHILE_LET_SOME;
diff --git a/src/tools/clippy/clippy_lints/src/loops/mod.rs b/src/tools/clippy/clippy_lints/src/loops/mod.rs
index 92ccc0cc0a15..17215621d2aa 100644
--- a/src/tools/clippy/clippy_lints/src/loops/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/mod.rs
@@ -22,15 +22,15 @@ mod while_immutable_condition;
 mod while_let_loop;
 mod while_let_on_iterator;
 
-use clippy_config::msrvs::Msrv;
 use clippy_config::Conf;
+use clippy_config::msrvs::Msrv;
 use clippy_utils::higher;
 use rustc_ast::Label;
 use rustc_hir::{Expr, ExprKind, LoopSource, Pat};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::impl_lint_pass;
 use rustc_span::Span;
-use utils::{make_iterator_snippet, IncrementVisitor, InitializeVisitor};
+use utils::{IncrementVisitor, InitializeVisitor, make_iterator_snippet};
 
 declare_clippy_lint! {
     /// ### What it does
@@ -188,22 +188,22 @@ declare_clippy_lint! {
     /// The `while let` loop is usually shorter and more
     /// readable.
     ///
-    /// ### Known problems
-    /// Sometimes the wrong binding is displayed ([#383](https://github.com/rust-lang/rust-clippy/issues/383)).
-    ///
     /// ### Example
     /// ```rust,no_run
-    /// # let y = Some(1);
+    /// let y = Some(1);
     /// loop {
     ///     let x = match y {
     ///         Some(x) => x,
     ///         None => break,
     ///     };
-    ///     // .. do something with x
+    ///     // ..
     /// }
-    /// // is easier written as
+    /// ```
+    /// Use instead:
+    /// ```rust,no_run
+    /// let y = Some(1);
     /// while let Some(x) = y {
-    ///     // .. do something with x
+    ///     // ..
     /// };
     /// ```
     #[clippy::version = "pre 1.29.0"]
diff --git a/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs b/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs
index e18e4374667d..20dd5a311dca 100644
--- a/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs
@@ -3,17 +3,17 @@ use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::source::snippet;
 use clippy_utils::ty::has_iter_method;
 use clippy_utils::visitors::is_local_used;
-use clippy_utils::{contains_name, higher, is_integer_const, sugg, SpanlessEq};
+use clippy_utils::{SpanlessEq, contains_name, higher, is_integer_const, sugg};
 use rustc_ast::ast;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_errors::Applicability;
 use rustc_hir::def::{DefKind, Res};
-use rustc_hir::intravisit::{walk_expr, Visitor};
+use rustc_hir::intravisit::{Visitor, walk_expr};
 use rustc_hir::{BinOpKind, BorrowKind, Closure, Expr, ExprKind, HirId, Mutability, Pat, PatKind, QPath};
 use rustc_lint::LateContext;
 use rustc_middle::middle::region;
 use rustc_middle::ty::{self, Ty};
-use rustc_span::symbol::{sym, Symbol};
+use rustc_span::symbol::{Symbol, sym};
 use std::{iter, mem};
 
 /// Checks for looping over a range and then indexing a sequence with it.
diff --git a/src/tools/clippy/clippy_lints/src/loops/never_loop.rs b/src/tools/clippy/clippy_lints/src/loops/never_loop.rs
index 313a5bfefbc8..1c55e3e22e8c 100644
--- a/src/tools/clippy/clippy_lints/src/loops/never_loop.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/never_loop.rs
@@ -1,5 +1,5 @@
-use super::utils::make_iterator_snippet;
 use super::NEVER_LOOP;
+use super::utils::make_iterator_snippet;
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::higher::ForLoop;
 use clippy_utils::macros::root_macro_call_first_node;
@@ -7,7 +7,7 @@ use clippy_utils::source::snippet;
 use rustc_errors::Applicability;
 use rustc_hir::{Block, Destination, Expr, ExprKind, HirId, InlineAsmOperand, Pat, Stmt, StmtKind};
 use rustc_lint::LateContext;
-use rustc_span::{sym, Span};
+use rustc_span::{Span, sym};
 use std::iter::once;
 
 pub(super) fn check<'tcx>(
diff --git a/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs b/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs
index 9185cf1f8b2e..5662d3013e15 100644
--- a/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs
@@ -6,11 +6,11 @@ use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
 use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::Applicability;
 use rustc_hir::def::{DefKind, Res};
-use rustc_hir::intravisit::{walk_expr, Visitor};
+use rustc_hir::intravisit::{Visitor, walk_expr};
 use rustc_hir::{BindingMode, Block, Expr, ExprKind, HirId, Mutability, Node, Pat, PatKind, Stmt, StmtKind};
 use rustc_lint::LateContext;
-use rustc_span::symbol::sym;
 use rustc_span::SyntaxContext;
+use rustc_span::symbol::sym;
 
 /// Detects for loop pushing the same item into a Vec
 pub(super) fn check<'tcx>(
diff --git a/src/tools/clippy/clippy_lints/src/loops/single_element_loop.rs b/src/tools/clippy/clippy_lints/src/loops/single_element_loop.rs
index 2b41911e8ec7..70f76ced09a4 100644
--- a/src/tools/clippy/clippy_lints/src/loops/single_element_loop.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/single_element_loop.rs
@@ -2,10 +2,10 @@ use super::SINGLE_ELEMENT_LOOP;
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::{indent_of, snippet, snippet_with_applicability};
 use clippy_utils::visitors::contains_break_or_continue;
-use rustc_ast::util::parser::PREC_PREFIX;
 use rustc_ast::Mutability;
+use rustc_ast::util::parser::PREC_PREFIX;
 use rustc_errors::Applicability;
-use rustc_hir::{is_range_literal, BorrowKind, Expr, ExprKind, Pat, PatKind};
+use rustc_hir::{BorrowKind, Expr, ExprKind, Pat, PatKind, is_range_literal};
 use rustc_lint::LateContext;
 use rustc_span::edition::Edition;
 use rustc_span::sym;
diff --git a/src/tools/clippy/clippy_lints/src/loops/utils.rs b/src/tools/clippy/clippy_lints/src/loops/utils.rs
index 7b45cc95431c..9a89a41d2b39 100644
--- a/src/tools/clippy/clippy_lints/src/loops/utils.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/utils.rs
@@ -2,13 +2,13 @@ use clippy_utils::ty::{has_iter_method, implements_trait};
 use clippy_utils::{get_parent_expr, is_integer_const, path_to_local, path_to_local_id, sugg};
 use rustc_ast::ast::{LitIntType, LitKind};
 use rustc_errors::Applicability;
-use rustc_hir::intravisit::{walk_expr, walk_local, Visitor};
+use rustc_hir::intravisit::{Visitor, walk_expr, walk_local};
 use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, HirId, HirIdMap, LetStmt, Mutability, PatKind};
 use rustc_lint::LateContext;
 use rustc_middle::hir::nested_filter;
 use rustc_middle::ty::{self, Ty};
 use rustc_span::source_map::Spanned;
-use rustc_span::symbol::{sym, Symbol};
+use rustc_span::symbol::{Symbol, sym};
 
 #[derive(Debug, PartialEq, Eq)]
 enum IncrementVisitorVarState {
diff --git a/src/tools/clippy/clippy_lints/src/loops/while_immutable_condition.rs b/src/tools/clippy/clippy_lints/src/loops/while_immutable_condition.rs
index cc1bd5929d0f..eab096e9a227 100644
--- a/src/tools/clippy/clippy_lints/src/loops/while_immutable_condition.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/while_immutable_condition.rs
@@ -4,7 +4,7 @@ use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::usage::mutated_variables;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::DefIdMap;
-use rustc_hir::intravisit::{walk_expr, Visitor};
+use rustc_hir::intravisit::{Visitor, walk_expr};
 use rustc_hir::{Expr, ExprKind, HirIdSet, QPath};
 use rustc_lint::LateContext;
 use std::ops::ControlFlow;
diff --git a/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs b/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs
index c171fa1c622a..7d9fbaf3ceab 100644
--- a/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs
@@ -5,13 +5,13 @@ use clippy_utils::visitors::is_res_used;
 use clippy_utils::{get_enclosing_loop_or_multi_call_closure, higher, is_refutable, is_res_lang_ctor, is_trait_method};
 use rustc_errors::Applicability;
 use rustc_hir::def::Res;
-use rustc_hir::intravisit::{walk_expr, Visitor};
+use rustc_hir::intravisit::{Visitor, walk_expr};
 use rustc_hir::{Closure, Expr, ExprKind, HirId, LangItem, LetStmt, Mutability, PatKind, UnOp};
 use rustc_lint::LateContext;
 use rustc_middle::hir::nested_filter::OnlyBodies;
 use rustc_middle::ty::adjustment::Adjust;
-use rustc_span::symbol::sym;
 use rustc_span::Symbol;
+use rustc_span::symbol::sym;
 
 pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
     if let Some(higher::WhileLet { if_then, let_pat, let_expr, label, .. }) = higher::WhileLet::hir(expr)
diff --git a/src/tools/clippy/clippy_lints/src/macro_metavars_in_unsafe.rs b/src/tools/clippy/clippy_lints/src/macro_metavars_in_unsafe.rs
index e215097142be..ccc554042d68 100644
--- a/src/tools/clippy/clippy_lints/src/macro_metavars_in_unsafe.rs
+++ b/src/tools/clippy/clippy_lints/src/macro_metavars_in_unsafe.rs
@@ -3,13 +3,13 @@ use clippy_utils::diagnostics::span_lint_hir_and_then;
 use clippy_utils::is_lint_allowed;
 use itertools::Itertools;
 use rustc_hir::def_id::LocalDefId;
-use rustc_hir::intravisit::{walk_block, walk_expr, walk_stmt, Visitor};
+use rustc_hir::intravisit::{Visitor, walk_block, walk_expr, walk_stmt};
 use rustc_hir::{BlockCheckMode, Expr, ExprKind, HirId, Stmt, UnsafeSource};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::impl_lint_pass;
-use rustc_span::{sym, Span, SyntaxContext};
-use std::collections::btree_map::Entry;
+use rustc_span::{Span, SyntaxContext, sym};
 use std::collections::BTreeMap;
+use std::collections::btree_map::Entry;
 
 declare_clippy_lint! {
     /// ### What it does
diff --git a/src/tools/clippy/clippy_lints/src/macro_use.rs b/src/tools/clippy/clippy_lints/src/macro_use.rs
index 067384b09015..bd6b3f1a47b1 100644
--- a/src/tools/clippy/clippy_lints/src/macro_use.rs
+++ b/src/tools/clippy/clippy_lints/src/macro_use.rs
@@ -8,7 +8,7 @@ use rustc_hir as hir;
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_session::impl_lint_pass;
 use rustc_span::edition::Edition;
-use rustc_span::{sym, Span};
+use rustc_span::{Span, sym};
 use std::collections::BTreeMap;
 
 declare_clippy_lint! {
diff --git a/src/tools/clippy/clippy_lints/src/manual_async_fn.rs b/src/tools/clippy/clippy_lints/src/manual_async_fn.rs
index 61723aec5904..fc3bba9e5123 100644
--- a/src/tools/clippy/clippy_lints/src/manual_async_fn.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_async_fn.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::source::{position_before_rarrow, snippet_block, SpanRangeExt};
+use clippy_utils::source::{SpanRangeExt, position_before_rarrow, snippet_block};
 use rustc_errors::Applicability;
 use rustc_hir::intravisit::FnKind;
 use rustc_hir::{
@@ -9,7 +9,7 @@ use rustc_hir::{
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::declare_lint_pass;
 use rustc_span::def_id::LocalDefId;
-use rustc_span::{sym, Span};
+use rustc_span::{Span, sym};
 
 declare_clippy_lint! {
     /// ### What it does
diff --git a/src/tools/clippy/clippy_lints/src/manual_bits.rs b/src/tools/clippy/clippy_lints/src/manual_bits.rs
index 5cbdc7f864a7..c0e87e8a1fae 100644
--- a/src/tools/clippy/clippy_lints/src/manual_bits.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_bits.rs
@@ -1,5 +1,5 @@
-use clippy_config::msrvs::{self, Msrv};
 use clippy_config::Conf;
+use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::get_parent_expr;
 use clippy_utils::source::snippet_with_context;
diff --git a/src/tools/clippy/clippy_lints/src/manual_clamp.rs b/src/tools/clippy/clippy_lints/src/manual_clamp.rs
index 4123c933660b..788649fd4f90 100644
--- a/src/tools/clippy/clippy_lints/src/manual_clamp.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_clamp.rs
@@ -1,5 +1,5 @@
-use clippy_config::msrvs::{self, Msrv};
 use clippy_config::Conf;
+use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::consts::{ConstEvalCtxt, Constant};
 use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then};
 use clippy_utils::higher::If;
@@ -7,8 +7,8 @@ use clippy_utils::sugg::Sugg;
 use clippy_utils::ty::implements_trait;
 use clippy_utils::visitors::is_const_evaluatable;
 use clippy_utils::{
-    eq_expr_value, is_diag_trait_item, is_in_const_context, is_trait_method, path_res, path_to_local_id, peel_blocks,
-    peel_blocks_with_stmt, MaybePath,
+    MaybePath, eq_expr_value, is_diag_trait_item, is_in_const_context, is_trait_method, path_res, path_to_local_id,
+    peel_blocks, peel_blocks_with_stmt,
 };
 use itertools::Itertools;
 use rustc_errors::{Applicability, Diag};
@@ -17,8 +17,8 @@ use rustc_hir::{Arm, BinOpKind, Block, Expr, ExprKind, HirId, PatKind, PathSegme
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::Ty;
 use rustc_session::impl_lint_pass;
-use rustc_span::symbol::sym;
 use rustc_span::Span;
+use rustc_span::symbol::sym;
 use std::cmp::Ordering;
 use std::ops::Deref;
 
diff --git a/src/tools/clippy/clippy_lints/src/manual_div_ceil.rs b/src/tools/clippy/clippy_lints/src/manual_div_ceil.rs
new file mode 100644
index 000000000000..00e02e2a3365
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/manual_div_ceil.rs
@@ -0,0 +1,158 @@
+use clippy_config::msrvs::{self, Msrv};
+use clippy_utils::SpanlessEq;
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::sugg::Sugg;
+use rustc_ast::{BinOpKind, LitKind};
+use rustc_data_structures::packed::Pu128;
+use rustc_errors::Applicability;
+use rustc_hir::{Expr, ExprKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty::{self};
+use rustc_session::impl_lint_pass;
+use rustc_span::symbol::Symbol;
+
+use clippy_config::Conf;
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for an expression like `(x + (y - 1)) / y` which is a common manual reimplementation
+    /// of `x.div_ceil(y)`.
+    ///
+    /// ### Why is this bad?
+    /// It's simpler, clearer and more readable.
+    ///
+    /// ### Example
+    /// ```no_run
+    /// let x: i32 = 7;
+    /// let y: i32 = 4;
+    /// let div = (x + (y - 1)) / y;
+    /// ```
+    /// Use instead:
+    /// ```no_run
+    /// #![feature(int_roundings)]
+    /// let x: i32 = 7;
+    /// let y: i32 = 4;
+    /// let div = x.div_ceil(y);
+    /// ```
+    #[clippy::version = "1.81.0"]
+    pub MANUAL_DIV_CEIL,
+    complexity,
+    "manually reimplementing `div_ceil`"
+}
+
+pub struct ManualDivCeil {
+    msrv: Msrv,
+}
+
+impl ManualDivCeil {
+    #[must_use]
+    pub fn new(conf: &'static Conf) -> Self {
+        Self {
+            msrv: conf.msrv.clone(),
+        }
+    }
+}
+
+impl_lint_pass!(ManualDivCeil => [MANUAL_DIV_CEIL]);
+
+impl<'tcx> LateLintPass<'tcx> for ManualDivCeil {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) {
+        if !self.msrv.meets(msrvs::MANUAL_DIV_CEIL) {
+            return;
+        }
+
+        let mut applicability = Applicability::MachineApplicable;
+
+        if let ExprKind::Binary(div_op, div_lhs, div_rhs) = expr.kind
+            && div_op.node == BinOpKind::Div
+            && check_int_ty_and_feature(cx, div_lhs)
+            && check_int_ty_and_feature(cx, div_rhs)
+            && let ExprKind::Binary(inner_op, inner_lhs, inner_rhs) = div_lhs.kind
+        {
+            // (x + (y - 1)) / y
+            if let ExprKind::Binary(sub_op, sub_lhs, sub_rhs) = inner_rhs.kind
+                && inner_op.node == BinOpKind::Add
+                && sub_op.node == BinOpKind::Sub
+                && check_literal(sub_rhs)
+                && check_eq_expr(cx, sub_lhs, div_rhs)
+            {
+                build_suggestion(cx, expr, inner_lhs, div_rhs, &mut applicability);
+                return;
+            }
+
+            // ((y - 1) + x) / y
+            if let ExprKind::Binary(sub_op, sub_lhs, sub_rhs) = inner_lhs.kind
+                && inner_op.node == BinOpKind::Add
+                && sub_op.node == BinOpKind::Sub
+                && check_literal(sub_rhs)
+                && check_eq_expr(cx, sub_lhs, div_rhs)
+            {
+                build_suggestion(cx, expr, inner_rhs, div_rhs, &mut applicability);
+                return;
+            }
+
+            // (x + y - 1) / y
+            if let ExprKind::Binary(add_op, add_lhs, add_rhs) = inner_lhs.kind
+                && inner_op.node == BinOpKind::Sub
+                && add_op.node == BinOpKind::Add
+                && check_literal(inner_rhs)
+                && check_eq_expr(cx, add_rhs, div_rhs)
+            {
+                build_suggestion(cx, expr, add_lhs, div_rhs, &mut applicability);
+            }
+        }
+    }
+
+    extract_msrv_attr!(LateContext);
+}
+
+fn check_int_ty_and_feature(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
+    let expr_ty = cx.typeck_results().expr_ty(expr);
+    match expr_ty.peel_refs().kind() {
+        ty::Uint(_) => true,
+        ty::Int(_) => cx
+            .tcx
+            .features()
+            .declared_features
+            .contains(&Symbol::intern("int_roundings")),
+
+        _ => false,
+    }
+}
+
+fn check_literal(expr: &Expr<'_>) -> bool {
+    if let ExprKind::Lit(lit) = expr.kind
+        && let LitKind::Int(Pu128(1), _) = lit.node
+    {
+        return true;
+    }
+    false
+}
+
+fn check_eq_expr(cx: &LateContext<'_>, lhs: &Expr<'_>, rhs: &Expr<'_>) -> bool {
+    SpanlessEq::new(cx).eq_expr(lhs, rhs)
+}
+
+fn build_suggestion(
+    cx: &LateContext<'_>,
+    expr: &Expr<'_>,
+    lhs: &Expr<'_>,
+    rhs: &Expr<'_>,
+    applicability: &mut Applicability,
+) {
+    let dividend_sugg = Sugg::hir_with_applicability(cx, lhs, "..", applicability).maybe_par();
+    let divisor_snippet = snippet_with_applicability(cx, rhs.span.source_callsite(), "..", applicability);
+
+    let sugg = format!("{dividend_sugg}.div_ceil({divisor_snippet})");
+
+    span_lint_and_sugg(
+        cx,
+        MANUAL_DIV_CEIL,
+        expr.span,
+        "manually reimplementing `div_ceil`",
+        "consider using `.div_ceil()`",
+        sugg,
+        *applicability,
+    );
+}
diff --git a/src/tools/clippy/clippy_lints/src/manual_hash_one.rs b/src/tools/clippy/clippy_lints/src/manual_hash_one.rs
index 11716a539ab5..c62bd65bea68 100644
--- a/src/tools/clippy/clippy_lints/src/manual_hash_one.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_hash_one.rs
@@ -1,5 +1,5 @@
-use clippy_config::msrvs::{self, Msrv};
 use clippy_config::Conf;
+use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::span_lint_hir_and_then;
 use clippy_utils::source::SpanRangeExt;
 use clippy_utils::visitors::{is_local_used, local_used_once};
diff --git a/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs b/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs
index 31c37c3bc3b1..24207705743d 100644
--- a/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs
@@ -1,17 +1,17 @@
-use clippy_config::msrvs::{self, Msrv};
 use clippy_config::Conf;
+use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::macros::matching_root_macro_call;
 use clippy_utils::sugg::Sugg;
 use clippy_utils::{higher, is_in_const_context, path_to_local, peel_ref_operators};
-use rustc_ast::ast::RangeLimits;
 use rustc_ast::LitKind::{Byte, Char};
+use rustc_ast::ast::RangeLimits;
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind, Node, Param, PatKind, RangeEnd};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty;
 use rustc_session::impl_lint_pass;
-use rustc_span::{sym, Span};
+use rustc_span::{Span, sym};
 
 declare_clippy_lint! {
     /// ### What it does
diff --git a/src/tools/clippy/clippy_lints/src/manual_is_power_of_two.rs b/src/tools/clippy/clippy_lints/src/manual_is_power_of_two.rs
new file mode 100644
index 000000000000..da2a982ee170
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/manual_is_power_of_two.rs
@@ -0,0 +1,142 @@
+use clippy_utils::SpanlessEq;
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_applicability;
+use rustc_ast::LitKind;
+use rustc_data_structures::packed::Pu128;
+use rustc_errors::Applicability;
+use rustc_hir::{BinOpKind, Expr, ExprKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty::Uint;
+use rustc_session::declare_lint_pass;
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for expressions like `x.count_ones() == 1` or `x & (x - 1) == 0`, with x and unsigned integer, which are manual
+    /// reimplementations of `x.is_power_of_two()`.
+    /// ### Why is this bad?
+    /// Manual reimplementations of `is_power_of_two` increase code complexity for little benefit.
+    /// ### Example
+    /// ```no_run
+    /// let a: u32 = 4;
+    /// let result = a.count_ones() == 1;
+    /// ```
+    /// Use instead:
+    /// ```no_run
+    /// let a: u32 = 4;
+    /// let result = a.is_power_of_two();
+    /// ```
+    #[clippy::version = "1.82.0"]
+    pub MANUAL_IS_POWER_OF_TWO,
+    complexity,
+    "manually reimplementing `is_power_of_two`"
+}
+
+declare_lint_pass!(ManualIsPowerOfTwo => [MANUAL_IS_POWER_OF_TWO]);
+
+impl LateLintPass<'_> for ManualIsPowerOfTwo {
+    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
+        let mut applicability = Applicability::MachineApplicable;
+
+        if let ExprKind::Binary(bin_op, left, right) = expr.kind
+            && bin_op.node == BinOpKind::Eq
+        {
+            // a.count_ones() == 1
+            if let ExprKind::MethodCall(method_name, reciever, _, _) = left.kind
+                && method_name.ident.as_str() == "count_ones"
+                && let &Uint(_) = cx.typeck_results().expr_ty(reciever).kind()
+                && check_lit(right, 1)
+            {
+                build_sugg(cx, expr, reciever, &mut applicability);
+            }
+
+            // 1 == a.count_ones()
+            if let ExprKind::MethodCall(method_name, reciever, _, _) = right.kind
+                && method_name.ident.as_str() == "count_ones"
+                && let &Uint(_) = cx.typeck_results().expr_ty(reciever).kind()
+                && check_lit(left, 1)
+            {
+                build_sugg(cx, expr, reciever, &mut applicability);
+            }
+
+            // a & (a - 1) == 0
+            if let ExprKind::Binary(op1, left1, right1) = left.kind
+                && op1.node == BinOpKind::BitAnd
+                && let ExprKind::Binary(op2, left2, right2) = right1.kind
+                && op2.node == BinOpKind::Sub
+                && check_eq_expr(cx, left1, left2)
+                && let &Uint(_) = cx.typeck_results().expr_ty(left1).kind()
+                && check_lit(right2, 1)
+                && check_lit(right, 0)
+            {
+                build_sugg(cx, expr, left1, &mut applicability);
+            }
+
+            // (a - 1) & a == 0;
+            if let ExprKind::Binary(op1, left1, right1) = left.kind
+                && op1.node == BinOpKind::BitAnd
+                && let ExprKind::Binary(op2, left2, right2) = left1.kind
+                && op2.node == BinOpKind::Sub
+                && check_eq_expr(cx, right1, left2)
+                && let &Uint(_) = cx.typeck_results().expr_ty(right1).kind()
+                && check_lit(right2, 1)
+                && check_lit(right, 0)
+            {
+                build_sugg(cx, expr, right1, &mut applicability);
+            }
+
+            // 0 == a & (a - 1);
+            if let ExprKind::Binary(op1, left1, right1) = right.kind
+                && op1.node == BinOpKind::BitAnd
+                && let ExprKind::Binary(op2, left2, right2) = right1.kind
+                && op2.node == BinOpKind::Sub
+                && check_eq_expr(cx, left1, left2)
+                && let &Uint(_) = cx.typeck_results().expr_ty(left1).kind()
+                && check_lit(right2, 1)
+                && check_lit(left, 0)
+            {
+                build_sugg(cx, expr, left1, &mut applicability);
+            }
+
+            // 0 == (a - 1) & a
+            if let ExprKind::Binary(op1, left1, right1) = right.kind
+                && op1.node == BinOpKind::BitAnd
+                && let ExprKind::Binary(op2, left2, right2) = left1.kind
+                && op2.node == BinOpKind::Sub
+                && check_eq_expr(cx, right1, left2)
+                && let &Uint(_) = cx.typeck_results().expr_ty(right1).kind()
+                && check_lit(right2, 1)
+                && check_lit(left, 0)
+            {
+                build_sugg(cx, expr, right1, &mut applicability);
+            }
+        }
+    }
+}
+
+fn build_sugg(cx: &LateContext<'_>, expr: &Expr<'_>, reciever: &Expr<'_>, applicability: &mut Applicability) {
+    let snippet = snippet_with_applicability(cx, reciever.span, "..", applicability);
+
+    span_lint_and_sugg(
+        cx,
+        MANUAL_IS_POWER_OF_TWO,
+        expr.span,
+        "manually reimplementing `is_power_of_two`",
+        "consider using `.is_power_of_two()`",
+        format!("{snippet}.is_power_of_two()"),
+        *applicability,
+    );
+}
+
+fn check_lit(expr: &Expr<'_>, expected_num: u128) -> bool {
+    if let ExprKind::Lit(lit) = expr.kind
+        && let LitKind::Int(Pu128(num), _) = lit.node
+        && num == expected_num
+    {
+        return true;
+    }
+    false
+}
+
+fn check_eq_expr(cx: &LateContext<'_>, lhs: &Expr<'_>, rhs: &Expr<'_>) -> bool {
+    SpanlessEq::new(cx).eq_expr(lhs, rhs)
+}
diff --git a/src/tools/clippy/clippy_lints/src/manual_let_else.rs b/src/tools/clippy/clippy_lints/src/manual_let_else.rs
index ebebdf679a86..17185df5d76f 100644
--- a/src/tools/clippy/clippy_lints/src/manual_let_else.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_let_else.rs
@@ -1,4 +1,4 @@
-use crate::question_mark::{QuestionMark, QUESTION_MARK};
+use crate::question_mark::{QUESTION_MARK, QuestionMark};
 use clippy_config::msrvs;
 use clippy_config::types::MatchLintBehaviour;
 use clippy_utils::diagnostics::span_lint_and_then;
@@ -12,8 +12,8 @@ use rustc_hir::{Expr, ExprKind, MatchSource, Pat, PatKind, QPath, Stmt, StmtKind
 use rustc_lint::{LateContext, LintContext};
 use rustc_middle::lint::in_external_macro;
 
-use rustc_span::symbol::{sym, Symbol};
 use rustc_span::Span;
+use rustc_span::symbol::{Symbol, sym};
 use std::slice;
 
 declare_clippy_lint! {
diff --git a/src/tools/clippy/clippy_lints/src/manual_main_separator_str.rs b/src/tools/clippy/clippy_lints/src/manual_main_separator_str.rs
index 5198d7838a28..85e99a92cf59 100644
--- a/src/tools/clippy/clippy_lints/src/manual_main_separator_str.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_main_separator_str.rs
@@ -1,5 +1,5 @@
-use clippy_config::msrvs::{self, Msrv};
 use clippy_config::Conf;
+use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::{is_trait_method, peel_hir_expr_refs};
 use rustc_errors::Applicability;
diff --git a/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs b/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs
index 6b1d90483ccd..25868ccae400 100644
--- a/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs
@@ -1,18 +1,18 @@
-use clippy_config::msrvs::{self, Msrv};
 use clippy_config::Conf;
+use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then};
 use clippy_utils::is_doc_hidden;
-use clippy_utils::source::SpanRangeExt;
-use rustc_ast::ast::{self, VisibilityKind};
+use clippy_utils::source::snippet_indent;
+use itertools::Itertools;
 use rustc_ast::attr;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::Applicability;
 use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
-use rustc_hir::{self as hir, Expr, ExprKind, QPath};
-use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
+use rustc_hir::{Expr, ExprKind, Item, ItemKind, QPath, TyKind, VariantData};
+use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::impl_lint_pass;
-use rustc_span::def_id::{DefId, LocalDefId};
-use rustc_span::{sym, Span};
+use rustc_span::def_id::LocalDefId;
+use rustc_span::{Span, sym};
 
 declare_clippy_lint! {
     /// ### What it does
@@ -62,29 +62,13 @@ declare_clippy_lint! {
     "manual implementations of the non-exhaustive pattern can be simplified using #[non_exhaustive]"
 }
 
-#[expect(clippy::module_name_repetitions)]
-pub struct ManualNonExhaustiveStruct {
+pub struct ManualNonExhaustive {
     msrv: Msrv,
-}
-
-impl ManualNonExhaustiveStruct {
-    pub fn new(conf: &'static Conf) -> Self {
-        Self {
-            msrv: conf.msrv.clone(),
-        }
-    }
-}
-
-impl_lint_pass!(ManualNonExhaustiveStruct => [MANUAL_NON_EXHAUSTIVE]);
-
-#[expect(clippy::module_name_repetitions)]
-pub struct ManualNonExhaustiveEnum {
-    msrv: Msrv,
-    constructed_enum_variants: FxHashSet<(DefId, DefId)>,
+    constructed_enum_variants: FxHashSet,
     potential_enums: Vec<(LocalDefId, LocalDefId, Span, Span)>,
 }
 
-impl ManualNonExhaustiveEnum {
+impl ManualNonExhaustive {
     pub fn new(conf: &'static Conf) -> Self {
         Self {
             msrv: conf.msrv.clone(),
@@ -94,96 +78,78 @@ impl ManualNonExhaustiveEnum {
     }
 }
 
-impl_lint_pass!(ManualNonExhaustiveEnum => [MANUAL_NON_EXHAUSTIVE]);
-
-impl EarlyLintPass for ManualNonExhaustiveStruct {
-    fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
-        if let ast::ItemKind::Struct(variant_data, _) = &item.kind
-            && let (fields, delimiter) = match variant_data {
-                ast::VariantData::Struct { fields, .. } => (&**fields, '{'),
-                ast::VariantData::Tuple(fields, _) => (&**fields, '('),
-                ast::VariantData::Unit(_) => return,
-            }
-            && fields.len() > 1
-            && self.msrv.meets(msrvs::NON_EXHAUSTIVE)
-        {
-            let mut iter = fields.iter().filter_map(|f| match f.vis.kind {
-                VisibilityKind::Public => None,
-                VisibilityKind::Inherited => Some(Ok(f)),
-                VisibilityKind::Restricted { .. } => Some(Err(())),
-            });
-            if let Some(Ok(field)) = iter.next()
-                && iter.next().is_none()
-                && field.ty.kind.is_unit()
-            {
-                span_lint_and_then(
-                    cx,
-                    MANUAL_NON_EXHAUSTIVE,
-                    item.span,
-                    "this seems like a manual implementation of the non-exhaustive pattern",
-                    |diag| {
-                        if !item.attrs.iter().any(|attr| attr.has_name(sym::non_exhaustive))
-                            && let header_span = cx.sess().source_map().span_until_char(item.span, delimiter)
-                            && let Some(snippet) = header_span.get_source_text(cx)
-                        {
-                            diag.span_suggestion(
-                                header_span,
-                                "add the attribute",
-                                format!("#[non_exhaustive] {snippet}"),
-                                Applicability::Unspecified,
-                            );
-                        }
-                        diag.span_help(field.span, "remove this field");
-                    },
-                );
-            }
-        }
-    }
-
-    extract_msrv_attr!(EarlyContext);
-}
+impl_lint_pass!(ManualNonExhaustive => [MANUAL_NON_EXHAUSTIVE]);
 
-impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustiveEnum {
-    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
-        if !self.msrv.meets(msrvs::NON_EXHAUSTIVE) {
+impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustive {
+    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
+        if !self.msrv.meets(msrvs::NON_EXHAUSTIVE) || !cx.effective_visibilities.is_exported(item.owner_id.def_id) {
             return;
         }
 
-        if let hir::ItemKind::Enum(def, _) = &item.kind
-            && def.variants.len() > 1
-        {
-            let mut iter = def.variants.iter().filter_map(|v| {
-                (matches!(v.data, hir::VariantData::Unit(_, _))
-                    && is_doc_hidden(cx.tcx.hir().attrs(v.hir_id))
-                    && !attr::contains_name(cx.tcx.hir().attrs(item.hir_id()), sym::non_exhaustive))
-                .then_some((v.def_id, v.span))
-            });
-            if let Some((id, span)) = iter.next()
-                && iter.next().is_none()
-            {
-                self.potential_enums.push((item.owner_id.def_id, id, item.span, span));
-            }
+        match item.kind {
+            ItemKind::Enum(def, _) if def.variants.len() > 1 => {
+                let iter = def.variants.iter().filter_map(|v| {
+                    (matches!(v.data, VariantData::Unit(_, _)) && is_doc_hidden(cx.tcx.hir().attrs(v.hir_id)))
+                        .then_some((v.def_id, v.span))
+                });
+                if let Ok((id, span)) = iter.exactly_one()
+                    && !attr::contains_name(cx.tcx.hir().attrs(item.hir_id()), sym::non_exhaustive)
+                {
+                    self.potential_enums.push((item.owner_id.def_id, id, item.span, span));
+                }
+            },
+            ItemKind::Struct(variant_data, _) => {
+                let fields = variant_data.fields();
+                let private_fields = fields
+                    .iter()
+                    .filter(|field| !cx.effective_visibilities.is_exported(field.def_id));
+                if fields.len() > 1
+                    && let Ok(field) = private_fields.exactly_one()
+                    && let TyKind::Tup([]) = field.ty.kind
+                {
+                    span_lint_and_then(
+                        cx,
+                        MANUAL_NON_EXHAUSTIVE,
+                        item.span,
+                        "this seems like a manual implementation of the non-exhaustive pattern",
+                        |diag| {
+                            if let Some(non_exhaustive) =
+                                attr::find_by_name(cx.tcx.hir().attrs(item.hir_id()), sym::non_exhaustive)
+                            {
+                                diag.span_note(non_exhaustive.span, "the struct is already non-exhaustive");
+                            } else {
+                                let indent = snippet_indent(cx, item.span).unwrap_or_default();
+                                diag.span_suggestion_verbose(
+                                    item.span.shrink_to_lo(),
+                                    "use the `#[non_exhaustive]` attribute instead",
+                                    format!("#[non_exhaustive]\n{indent}"),
+                                    Applicability::MaybeIncorrect,
+                                );
+                            }
+                            diag.span_help(field.span, "remove this field");
+                        },
+                    );
+                }
+            },
+            _ => {},
         }
     }
 
     fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
         if let ExprKind::Path(QPath::Resolved(None, p)) = &e.kind
-            && let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Const), id) = p.res
+            && let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Const), ctor_id) = p.res
+            && let Some(local_ctor) = ctor_id.as_local()
         {
-            let variant_id = cx.tcx.parent(id);
-            let enum_id = cx.tcx.parent(variant_id);
-
-            self.constructed_enum_variants.insert((enum_id, variant_id));
+            let variant_id = cx.tcx.local_parent(local_ctor);
+            self.constructed_enum_variants.insert(variant_id);
         }
     }
 
     fn check_crate_post(&mut self, cx: &LateContext<'tcx>) {
-        for &(enum_id, _, enum_span, variant_span) in
-            self.potential_enums.iter().filter(|&&(enum_id, variant_id, _, _)| {
-                !self
-                    .constructed_enum_variants
-                    .contains(&(enum_id.to_def_id(), variant_id.to_def_id()))
-            })
+        for &(enum_id, _, enum_span, variant_span) in self
+            .potential_enums
+            .iter()
+            .filter(|(_, variant_id, _, _)| !self.constructed_enum_variants.contains(variant_id))
         {
             let hir_id = cx.tcx.local_def_id_to_hir_id(enum_id);
             span_lint_hir_and_then(
@@ -193,15 +159,13 @@ impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustiveEnum {
                 enum_span,
                 "this seems like a manual implementation of the non-exhaustive pattern",
                 |diag| {
-                    let header_span = cx.sess().source_map().span_until_char(enum_span, '{');
-                    if let Some(snippet) = header_span.get_source_text(cx) {
-                        diag.span_suggestion(
-                            header_span,
-                            "add the attribute",
-                            format!("#[non_exhaustive] {snippet}"),
-                            Applicability::Unspecified,
-                        );
-                    }
+                    let indent = snippet_indent(cx, enum_span).unwrap_or_default();
+                    diag.span_suggestion_verbose(
+                        enum_span.shrink_to_lo(),
+                        "use the `#[non_exhaustive]` attribute instead",
+                        format!("#[non_exhaustive]\n{indent}"),
+                        Applicability::MaybeIncorrect,
+                    );
                     diag.span_help(variant_span, "remove this variant");
                 },
             );
diff --git a/src/tools/clippy/clippy_lints/src/manual_range_patterns.rs b/src/tools/clippy/clippy_lints/src/manual_range_patterns.rs
index 9afb2b5bc84c..ffa3eacf3544 100644
--- a/src/tools/clippy/clippy_lints/src/manual_range_patterns.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_range_patterns.rs
@@ -7,7 +7,7 @@ use rustc_hir::{Expr, ExprKind, PatKind, RangeEnd, UnOp};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
 use rustc_session::declare_lint_pass;
-use rustc_span::{Span, DUMMY_SP};
+use rustc_span::{DUMMY_SP, Span};
 
 declare_clippy_lint! {
     /// ### What it does
@@ -77,9 +77,10 @@ impl Num {
 impl LateLintPass<'_> for ManualRangePatterns {
     fn check_pat(&mut self, cx: &LateContext<'_>, pat: &'_ rustc_hir::Pat<'_>) {
         // a pattern like 1 | 2 seems fine, lint if there are at least 3 alternatives
-        // or at least one range
+        // or more then one range (exclude triggering on stylistic using OR with one element
+        // like described https://github.com/rust-lang/rust-clippy/issues/11825)
         if let PatKind::Or(pats) = pat.kind
-            && (pats.len() >= 3 || pats.iter().any(|p| matches!(p.kind, PatKind::Range(..))))
+            && (pats.len() >= 3 || (pats.len() > 1 && pats.iter().any(|p| matches!(p.kind, PatKind::Range(..)))))
             && !in_external_macro(cx.sess(), pat.span)
         {
             let mut min = Num::dummy(i128::MAX);
diff --git a/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs b/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs
index 6cf5d272d7d8..86293169ea28 100644
--- a/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs
@@ -1,5 +1,5 @@
-use clippy_config::msrvs::{self, Msrv};
 use clippy_config::Conf;
+use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::consts::{ConstEvalCtxt, FullInt};
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet_with_context;
diff --git a/src/tools/clippy/clippy_lints/src/manual_retain.rs b/src/tools/clippy/clippy_lints/src/manual_retain.rs
index a35b0f914e05..a60163be770d 100644
--- a/src/tools/clippy/clippy_lints/src/manual_retain.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_retain.rs
@@ -1,17 +1,17 @@
-use clippy_config::msrvs::{self, Msrv};
 use clippy_config::Conf;
+use clippy_config::msrvs::{self, Msrv};
+use clippy_utils::SpanlessEq;
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet;
 use clippy_utils::ty::{get_type_diagnostic_name, is_type_lang_item};
-use clippy_utils::SpanlessEq;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
-use rustc_hir::def_id::DefId;
 use rustc_hir::ExprKind::Assign;
+use rustc_hir::def_id::DefId;
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::impl_lint_pass;
-use rustc_span::symbol::{sym, Symbol};
 use rustc_span::Span;
+use rustc_span::symbol::{Symbol, sym};
 
 const ACCEPTABLE_METHODS: [Symbol; 5] = [
     sym::binaryheap_iter,
diff --git a/src/tools/clippy/clippy_lints/src/manual_string_new.rs b/src/tools/clippy/clippy_lints/src/manual_string_new.rs
index 781db4b97f08..198f7aaddc71 100644
--- a/src/tools/clippy/clippy_lints/src/manual_string_new.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_string_new.rs
@@ -5,7 +5,7 @@ use rustc_hir::{Expr, ExprKind, PathSegment, QPath, TyKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty;
 use rustc_session::declare_lint_pass;
-use rustc_span::{sym, symbol, Span};
+use rustc_span::{Span, sym, symbol};
 
 declare_clippy_lint! {
     /// ### What it does
diff --git a/src/tools/clippy/clippy_lints/src/manual_strip.rs b/src/tools/clippy/clippy_lints/src/manual_strip.rs
index 02d433ecc5aa..9aceca66bf77 100644
--- a/src/tools/clippy/clippy_lints/src/manual_strip.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_strip.rs
@@ -1,5 +1,5 @@
-use clippy_config::msrvs::{self, Msrv};
 use clippy_config::Conf;
+use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::consts::{ConstEvalCtxt, Constant};
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::source::snippet;
@@ -8,13 +8,13 @@ use clippy_utils::{eq_expr_value, higher};
 use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
 use rustc_hir::def::Res;
-use rustc_hir::intravisit::{walk_expr, Visitor};
+use rustc_hir::intravisit::{Visitor, walk_expr};
 use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty;
 use rustc_session::impl_lint_pass;
 use rustc_span::source_map::Spanned;
-use rustc_span::{sym, Span};
+use rustc_span::{Span, sym};
 use std::iter;
 
 declare_clippy_lint! {
diff --git a/src/tools/clippy/clippy_lints/src/map_unit_fn.rs b/src/tools/clippy/clippy_lints/src/map_unit_fn.rs
index 2db71b1f7a38..a97dbbbc33fd 100644
--- a/src/tools/clippy/clippy_lints/src/map_unit_fn.rs
+++ b/src/tools/clippy/clippy_lints/src/map_unit_fn.rs
@@ -7,7 +7,7 @@ use rustc_hir as hir;
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::{self, Ty};
 use rustc_session::declare_lint_pass;
-use rustc_span::{sym, Span};
+use rustc_span::{Span, sym};
 
 declare_clippy_lint! {
     /// ### What it does
diff --git a/src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs b/src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs
index b666e77c09ec..50e6dfc6298d 100644
--- a/src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs
@@ -4,7 +4,7 @@ use clippy_utils::higher::IfLetOrMatch;
 use clippy_utils::source::snippet;
 use clippy_utils::visitors::is_local_used;
 use clippy_utils::{
-    is_res_lang_ctor, is_unit_expr, path_to_local, peel_blocks_with_stmt, peel_ref_operators, SpanlessEq,
+    SpanlessEq, is_res_lang_ctor, is_unit_expr, path_to_local, peel_blocks_with_stmt, peel_ref_operators,
 };
 use rustc_errors::MultiSpan;
 use rustc_hir::LangItem::OptionNone;
@@ -12,7 +12,7 @@ use rustc_hir::{Arm, Expr, HirId, Pat, PatKind};
 use rustc_lint::LateContext;
 use rustc_span::Span;
 
-use super::{pat_contains_disallowed_or, COLLAPSIBLE_MATCH};
+use super::{COLLAPSIBLE_MATCH, pat_contains_disallowed_or};
 
 pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], msrv: &Msrv) {
     if let Some(els_arm) = arms.iter().rfind(|arm| arm_is_wild_like(cx, arm)) {
diff --git a/src/tools/clippy/clippy_lints/src/matches/manual_filter.rs b/src/tools/clippy/clippy_lints/src/matches/manual_filter.rs
index 619ec83127ab..cfa054706d6b 100644
--- a/src/tools/clippy/clippy_lints/src/matches/manual_filter.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/manual_filter.rs
@@ -6,10 +6,10 @@ use clippy_utils::{is_res_lang_ctor, path_res, path_to_local_id};
 use rustc_hir::LangItem::{OptionNone, OptionSome};
 use rustc_hir::{Arm, Expr, ExprKind, HirId, Pat, PatKind};
 use rustc_lint::LateContext;
-use rustc_span::{sym, SyntaxContext};
+use rustc_span::{SyntaxContext, sym};
 
-use super::manual_utils::{check_with, SomeExpr};
 use super::MANUAL_FILTER;
+use super::manual_utils::{SomeExpr, check_with};
 
 // Function called on the  of `[&+]Some((ref | ref mut) x) => `
 // Need to check if it's of the form `=if  {} else {}`
diff --git a/src/tools/clippy/clippy_lints/src/matches/manual_map.rs b/src/tools/clippy/clippy_lints/src/matches/manual_map.rs
index ed3d8b09fdcd..de57d1eee924 100644
--- a/src/tools/clippy/clippy_lints/src/matches/manual_map.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/manual_map.rs
@@ -1,5 +1,5 @@
-use super::manual_utils::{check_with, SomeExpr};
 use super::MANUAL_MAP;
+use super::manual_utils::{SomeExpr, check_with};
 use clippy_utils::diagnostics::span_lint_and_sugg;
 
 use clippy_utils::{is_res_lang_ctor, path_res};
diff --git a/src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs b/src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs
index d5d83df93471..59d375200117 100644
--- a/src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs
@@ -1,12 +1,12 @@
 use clippy_utils::consts::ConstEvalCtxt;
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::{indent_of, reindent_multiline, SpanRangeExt};
+use clippy_utils::source::{SpanRangeExt, indent_of, reindent_multiline};
 use clippy_utils::ty::is_type_diagnostic_item;
 use clippy_utils::usage::contains_return_break_continue_macro;
 use clippy_utils::{is_res_lang_ctor, path_to_local_id, peel_blocks, sugg};
 use rustc_errors::Applicability;
-use rustc_hir::def::{DefKind, Res};
 use rustc_hir::LangItem::{OptionNone, ResultErr};
+use rustc_hir::def::{DefKind, Res};
 use rustc_hir::{Arm, Expr, Pat, PatKind};
 use rustc_lint::LateContext;
 use rustc_middle::ty::Ty;
diff --git a/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs b/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs
index be80aebed6df..d38560998a5a 100644
--- a/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs
@@ -4,16 +4,16 @@ use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
 use clippy_utils::sugg::Sugg;
 use clippy_utils::ty::{is_copy, is_type_diagnostic_item, peel_mid_ty_refs_is_mutable, type_is_unsafe_function};
 use clippy_utils::{
-    can_move_expr_to_closure, is_else_clause, is_lint_allowed, is_res_lang_ctor, path_res, path_to_local_id,
-    peel_blocks, peel_hir_expr_refs, peel_hir_expr_while, CaptureKind,
+    CaptureKind, can_move_expr_to_closure, is_else_clause, is_lint_allowed, is_res_lang_ctor, path_res,
+    path_to_local_id, peel_blocks, peel_hir_expr_refs, peel_hir_expr_while,
 };
 use rustc_ast::util::parser::PREC_UNAMBIGUOUS;
 use rustc_errors::Applicability;
-use rustc_hir::def::Res;
 use rustc_hir::LangItem::{OptionNone, OptionSome};
+use rustc_hir::def::Res;
 use rustc_hir::{BindingMode, Expr, ExprKind, HirId, Mutability, Pat, PatKind, Path, QPath};
 use rustc_lint::LateContext;
-use rustc_span::{sym, SyntaxContext};
+use rustc_span::{SyntaxContext, sym};
 
 #[expect(clippy::too_many_arguments)]
 #[expect(clippy::too_many_lines)]
diff --git a/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs b/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs
index da8c918a62bb..f9ffbc5dc0b8 100644
--- a/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_hir_and_then;
 use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::{is_lint_allowed, path_to_local, search_same, SpanlessEq, SpanlessHash};
+use clippy_utils::{SpanlessEq, SpanlessHash, is_lint_allowed, path_to_local, search_same};
 use core::cmp::Ordering;
 use core::{iter, slice};
 use rustc_arena::DroplessArena;
diff --git a/src/tools/clippy/clippy_lints/src/matches/match_str_case_mismatch.rs b/src/tools/clippy/clippy_lints/src/matches/match_str_case_mismatch.rs
index 322e9c3ebe5b..40518ce2ca77 100644
--- a/src/tools/clippy/clippy_lints/src/matches/match_str_case_mismatch.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/match_str_case_mismatch.rs
@@ -2,12 +2,12 @@ use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::ty::is_type_lang_item;
 use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
-use rustc_hir::intravisit::{walk_expr, Visitor};
+use rustc_hir::intravisit::{Visitor, walk_expr};
 use rustc_hir::{Arm, Expr, ExprKind, LangItem, PatKind};
 use rustc_lint::LateContext;
 use rustc_middle::ty;
-use rustc_span::symbol::Symbol;
 use rustc_span::Span;
+use rustc_span::symbol::Symbol;
 
 use super::MATCH_STR_CASE_MISMATCH;
 
diff --git a/src/tools/clippy/clippy_lints/src/matches/mod.rs b/src/tools/clippy/clippy_lints/src/matches/mod.rs
index cf5377e0725d..686fc4a0fa0a 100644
--- a/src/tools/clippy/clippy_lints/src/matches/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/mod.rs
@@ -24,8 +24,8 @@ mod single_match;
 mod try_err;
 mod wild_in_or_pats;
 
-use clippy_config::msrvs::{self, Msrv};
 use clippy_config::Conf;
+use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::source::walk_span_to_context;
 use clippy_utils::{higher, is_direct_expn_of, is_in_const_context, is_span_match, span_contains_cfg};
 use rustc_hir::{Arm, Expr, ExprKind, LetStmt, MatchSource, Pat, PatKind};
diff --git a/src/tools/clippy/clippy_lints/src/matches/overlapping_arms.rs b/src/tools/clippy/clippy_lints/src/matches/overlapping_arms.rs
index 71f211be925b..6a4c553cee0e 100644
--- a/src/tools/clippy/clippy_lints/src/matches/overlapping_arms.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/overlapping_arms.rs
@@ -1,4 +1,4 @@
-use clippy_utils::consts::{mir_to_const, ConstEvalCtxt, FullInt};
+use clippy_utils::consts::{ConstEvalCtxt, FullInt, mir_to_const};
 use clippy_utils::diagnostics::span_lint_and_note;
 use core::cmp::Ordering;
 use rustc_hir::{Arm, Expr, PatKind, RangeEnd};
diff --git a/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs b/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs
index 8222c3057f73..564c598a334c 100644
--- a/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs
@@ -10,11 +10,11 @@ use rustc_hir::def::{DefKind, Res};
 use rustc_hir::{Arm, BinOpKind, Expr, ExprKind, MatchSource, Node, PatKind, UnOp};
 use rustc_lint::LateContext;
 use rustc_span::symbol::Ident;
-use rustc_span::{sym, Span, Symbol};
+use rustc_span::{Span, Symbol, sym};
 use std::borrow::Cow;
 use std::ops::ControlFlow;
 
-use super::{pat_contains_disallowed_or, REDUNDANT_GUARDS};
+use super::{REDUNDANT_GUARDS, pat_contains_disallowed_or};
 
 pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'tcx>], msrv: &Msrv) {
     for outer_arm in arms {
diff --git a/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs b/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs
index 0e4b2d9d34a0..b646e87a4393 100644
--- a/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs
@@ -1,18 +1,18 @@
 use super::REDUNDANT_PATTERN_MATCHING;
 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
 use clippy_utils::source::{snippet, walk_span_to_context};
-use clippy_utils::sugg::{make_unop, Sugg};
+use clippy_utils::sugg::{Sugg, make_unop};
 use clippy_utils::ty::{is_type_diagnostic_item, needs_ordered_drop};
 use clippy_utils::visitors::{any_temporaries_need_ordered_drop, for_each_expr_without_closures};
 use clippy_utils::{higher, is_expn_of, is_trait_method};
 use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
-use rustc_hir::def::{DefKind, Res};
 use rustc_hir::LangItem::{self, OptionNone, OptionSome, PollPending, PollReady, ResultErr, ResultOk};
+use rustc_hir::def::{DefKind, Res};
 use rustc_hir::{Arm, Expr, ExprKind, Node, Pat, PatKind, QPath, UnOp};
 use rustc_lint::LateContext;
 use rustc_middle::ty::{self, GenericArgKind, Ty};
-use rustc_span::{sym, Span, Symbol};
+use rustc_span::{Span, Symbol, sym};
 use std::fmt::Write;
 use std::ops::ControlFlow;
 
diff --git a/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs b/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs
index 9c2f42d21870..537f7272f7f3 100644
--- a/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs
@@ -8,7 +8,7 @@ use clippy_utils::{get_attr, is_lint_allowed};
 use itertools::Itertools;
 use rustc_ast::Mutability;
 use rustc_errors::{Applicability, Diag};
-use rustc_hir::intravisit::{walk_expr, Visitor};
+use rustc_hir::intravisit::{Visitor, walk_expr};
 use rustc_hir::{Arm, Expr, ExprKind, MatchSource};
 use rustc_lint::{LateContext, LintContext};
 use rustc_middle::ty::{GenericArgKind, Region, RegionKind, Ty, TyCtxt, TypeVisitable, TypeVisitor};
diff --git a/src/tools/clippy/clippy_lints/src/matches/single_match.rs b/src/tools/clippy/clippy_lints/src/matches/single_match.rs
index b6930f7b9d10..2eda238ae8cd 100644
--- a/src/tools/clippy/clippy_lints/src/matches/single_match.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/single_match.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::{expr_block, snippet, SpanRangeExt};
+use clippy_utils::source::{SpanRangeExt, expr_block, snippet, snippet_block_with_context};
 use clippy_utils::ty::implements_trait;
 use clippy_utils::{
     is_lint_allowed, is_unit_expr, peel_blocks, peel_hir_pat_refs, peel_middle_ty_refs, peel_n_hir_expr_refs,
@@ -8,11 +8,11 @@ use core::ops::ControlFlow;
 use rustc_arena::DroplessArena;
 use rustc_errors::Applicability;
 use rustc_hir::def::{DefKind, Res};
-use rustc_hir::intravisit::{walk_pat, Visitor};
-use rustc_hir::{Arm, Expr, ExprKind, HirId, Pat, PatKind, QPath};
+use rustc_hir::intravisit::{Visitor, walk_pat};
+use rustc_hir::{Arm, Expr, ExprKind, HirId, Node, Pat, PatKind, QPath, StmtKind};
 use rustc_lint::LateContext;
 use rustc_middle::ty::{self, AdtDef, ParamEnv, TyCtxt, TypeckResults, VariantDef};
-use rustc_span::{sym, Span};
+use rustc_span::{Span, sym};
 
 use super::{MATCH_BOOL, SINGLE_MATCH, SINGLE_MATCH_ELSE};
 
@@ -91,6 +91,29 @@ fn report_single_pattern(cx: &LateContext<'_>, ex: &Expr<'_>, arm: &Arm<'_>, exp
         format!(" else {}", expr_block(cx, els, ctxt, "..", Some(expr.span), &mut app))
     });
 
+    if snippet(cx, ex.span, "..") == snippet(cx, arm.pat.span, "..") {
+        let msg = "this pattern is irrefutable, `match` is useless";
+        let (sugg, help) = if is_unit_expr(arm.body) {
+            (String::new(), "`match` expression can be removed")
+        } else {
+            let mut sugg = snippet_block_with_context(cx, arm.body.span, ctxt, "..", Some(expr.span), &mut app)
+                .0
+                .to_string();
+            if let Node::Stmt(stmt) = cx.tcx.parent_hir_node(expr.hir_id)
+                && let StmtKind::Expr(_) = stmt.kind
+                && match arm.body.kind {
+                    ExprKind::Block(block, _) => block.span.from_expansion(),
+                    _ => true,
+                }
+            {
+                sugg.push(';');
+            }
+            (sugg, "try")
+        };
+        span_lint_and_sugg(cx, lint, expr.span, msg, help, sugg.to_string(), app);
+        return;
+    }
+
     let (pat, pat_ref_count) = peel_hir_pat_refs(arm.pat);
     let (msg, sugg) = if let PatKind::Path(_) | PatKind::Lit(_) = pat.kind
         && let (ty, ty_ref_count) = peel_middle_ty_refs(cx.typeck_results().expr_ty(ex))
diff --git a/src/tools/clippy/clippy_lints/src/mem_replace.rs b/src/tools/clippy/clippy_lints/src/mem_replace.rs
index 72a5945ad9bc..146748734cff 100644
--- a/src/tools/clippy/clippy_lints/src/mem_replace.rs
+++ b/src/tools/clippy/clippy_lints/src/mem_replace.rs
@@ -1,5 +1,5 @@
-use clippy_config::msrvs::{self, Msrv};
 use clippy_config::Conf;
+use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then};
 use clippy_utils::source::{snippet, snippet_with_applicability};
 use clippy_utils::sugg::Sugg;
@@ -13,8 +13,8 @@ use rustc_hir::{Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::lint::in_external_macro;
 use rustc_session::impl_lint_pass;
-use rustc_span::symbol::sym;
 use rustc_span::Span;
+use rustc_span::symbol::sym;
 
 declare_clippy_lint! {
     /// ### What it does
diff --git a/src/tools/clippy/clippy_lints/src/methods/bind_instead_of_map.rs b/src/tools/clippy/clippy_lints/src/methods/bind_instead_of_map.rs
index 2a8a5fcc3b6f..91a5de16e967 100644
--- a/src/tools/clippy/clippy_lints/src/methods/bind_instead_of_map.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/bind_instead_of_map.rs
@@ -1,4 +1,4 @@
-use super::{contains_return, BIND_INSTEAD_OF_MAP};
+use super::{BIND_INSTEAD_OF_MAP, contains_return};
 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
 use clippy_utils::peel_blocks;
 use clippy_utils::source::{snippet, snippet_with_context};
diff --git a/src/tools/clippy/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs b/src/tools/clippy/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs
index 740cce0a6c23..76bdbe55e2f3 100644
--- a/src/tools/clippy/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs
@@ -1,12 +1,12 @@
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::source::{indent_of, reindent_multiline, SpanRangeExt};
+use clippy_utils::source::{SpanRangeExt, indent_of, reindent_multiline};
 use clippy_utils::ty::is_type_lang_item;
 use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind, LangItem};
 use rustc_lint::LateContext;
-use rustc_span::source_map::Spanned;
 use rustc_span::Span;
+use rustc_span::source_map::Spanned;
 
 use super::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS;
 
diff --git a/src/tools/clippy/clippy_lints/src/methods/clear_with_drain.rs b/src/tools/clippy/clippy_lints/src/methods/clear_with_drain.rs
index 5389861245a2..6e24cabca8bb 100644
--- a/src/tools/clippy/clippy_lints/src/methods/clear_with_drain.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/clear_with_drain.rs
@@ -4,8 +4,8 @@ use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item};
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind, LangItem, QPath};
 use rustc_lint::LateContext;
-use rustc_span::symbol::sym;
 use rustc_span::Span;
+use rustc_span::symbol::sym;
 
 use super::CLEAR_WITH_DRAIN;
 
diff --git a/src/tools/clippy/clippy_lints/src/methods/clone_on_copy.rs b/src/tools/clippy/clippy_lints/src/methods/clone_on_copy.rs
index e7a2060be046..79a473e0e6f5 100644
--- a/src/tools/clippy/clippy_lints/src/methods/clone_on_copy.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/clone_on_copy.rs
@@ -7,7 +7,7 @@ use rustc_lint::LateContext;
 use rustc_middle::ty::adjustment::Adjust;
 use rustc_middle::ty::print::with_forced_trimmed_paths;
 use rustc_middle::ty::{self};
-use rustc_span::symbol::{sym, Symbol};
+use rustc_span::symbol::{Symbol, sym};
 
 use super::CLONE_ON_COPY;
 
diff --git a/src/tools/clippy/clippy_lints/src/methods/clone_on_ref_ptr.rs b/src/tools/clippy/clippy_lints/src/methods/clone_on_ref_ptr.rs
index e0826b53004b..96e2de0dc1cb 100644
--- a/src/tools/clippy/clippy_lints/src/methods/clone_on_ref_ptr.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/clone_on_ref_ptr.rs
@@ -4,7 +4,7 @@ use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
 use rustc_middle::ty;
-use rustc_span::symbol::{sym, Symbol};
+use rustc_span::symbol::{Symbol, sym};
 
 use super::CLONE_ON_REF_PTR;
 
diff --git a/src/tools/clippy/clippy_lints/src/methods/cloned_instead_of_copied.rs b/src/tools/clippy/clippy_lints/src/methods/cloned_instead_of_copied.rs
index fcafa1622365..fa04f74eec10 100644
--- a/src/tools/clippy/clippy_lints/src/methods/cloned_instead_of_copied.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/cloned_instead_of_copied.rs
@@ -6,7 +6,7 @@ use rustc_errors::Applicability;
 use rustc_hir::Expr;
 use rustc_lint::LateContext;
 use rustc_middle::ty;
-use rustc_span::{sym, Span};
+use rustc_span::{Span, sym};
 
 use super::CLONED_INSTEAD_OF_COPIED;
 
diff --git a/src/tools/clippy/clippy_lints/src/methods/collapsible_str_replace.rs b/src/tools/clippy/clippy_lints/src/methods/collapsible_str_replace.rs
index 1fab6c0e499d..f7bf8764bdeb 100644
--- a/src/tools/clippy/clippy_lints/src/methods/collapsible_str_replace.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/collapsible_str_replace.rs
@@ -8,7 +8,7 @@ use rustc_hir as hir;
 use rustc_lint::LateContext;
 use std::collections::VecDeque;
 
-use super::{method_call, COLLAPSIBLE_STR_REPLACE};
+use super::{COLLAPSIBLE_STR_REPLACE, method_call};
 
 pub(super) fn check<'tcx>(
     cx: &LateContext<'tcx>,
diff --git a/src/tools/clippy/clippy_lints/src/methods/drain_collect.rs b/src/tools/clippy/clippy_lints/src/methods/drain_collect.rs
index 56171a134522..10360b4817bc 100644
--- a/src/tools/clippy/clippy_lints/src/methods/drain_collect.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/drain_collect.rs
@@ -8,7 +8,7 @@ use rustc_hir::{Expr, ExprKind, LangItem, Path, QPath};
 use rustc_lint::LateContext;
 use rustc_middle::ty;
 use rustc_middle::ty::Ty;
-use rustc_span::{sym, Symbol};
+use rustc_span::{Symbol, sym};
 
 /// Checks if both types match the given diagnostic item, e.g.:
 ///
diff --git a/src/tools/clippy/clippy_lints/src/methods/err_expect.rs b/src/tools/clippy/clippy_lints/src/methods/err_expect.rs
index dc978c8a5845..3b1adb16b80e 100644
--- a/src/tools/clippy/clippy_lints/src/methods/err_expect.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/err_expect.rs
@@ -6,7 +6,7 @@ use rustc_errors::Applicability;
 use rustc_lint::LateContext;
 use rustc_middle::ty;
 use rustc_middle::ty::Ty;
-use rustc_span::{sym, Span};
+use rustc_span::{Span, sym};
 
 pub(super) fn check(
     cx: &LateContext<'_>,
diff --git a/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs b/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs
index c9f56e1d9809..2922086522c2 100644
--- a/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs
@@ -1,13 +1,13 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::macros::{format_args_inputs_span, root_macro_call_first_node, FormatArgsStorage};
+use clippy_utils::macros::{FormatArgsStorage, format_args_inputs_span, root_macro_call_first_node};
 use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item};
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
 use rustc_middle::ty;
-use rustc_span::symbol::sym;
 use rustc_span::Span;
+use rustc_span::symbol::sym;
 use std::borrow::Cow;
 
 use super::EXPECT_FUN_CALL;
diff --git a/src/tools/clippy/clippy_lints/src/methods/filetype_is_file.rs b/src/tools/clippy/clippy_lints/src/methods/filetype_is_file.rs
index 2ab0401947c4..35008c39c084 100644
--- a/src/tools/clippy/clippy_lints/src/methods/filetype_is_file.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/filetype_is_file.rs
@@ -3,7 +3,7 @@ use clippy_utils::get_parent_expr;
 use clippy_utils::ty::is_type_diagnostic_item;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
-use rustc_span::{sym, Span};
+use rustc_span::{Span, sym};
 
 use super::FILETYPE_IS_FILE;
 
diff --git a/src/tools/clippy/clippy_lints/src/methods/filter_map.rs b/src/tools/clippy/clippy_lints/src/methods/filter_map.rs
index 02a11257007a..06482a743ddf 100644
--- a/src/tools/clippy/clippy_lints/src/methods/filter_map.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/filter_map.rs
@@ -2,7 +2,7 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
 use clippy_utils::macros::{is_panic, matching_root_macro_call, root_macro_call};
 use clippy_utils::source::{indent_of, reindent_multiline, snippet};
 use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{higher, is_trait_method, path_to_local_id, peel_blocks, SpanlessEq};
+use clippy_utils::{SpanlessEq, higher, is_trait_method, path_to_local_id, peel_blocks};
 use hir::{Body, HirId, MatchSource, Pat};
 use rustc_errors::Applicability;
 use rustc_hir as hir;
@@ -10,8 +10,8 @@ use rustc_hir::def::Res;
 use rustc_hir::{Closure, Expr, ExprKind, PatKind, PathSegment, QPath, UnOp};
 use rustc_lint::LateContext;
 use rustc_middle::ty::adjustment::Adjust;
-use rustc_span::symbol::{sym, Ident, Symbol};
 use rustc_span::Span;
+use rustc_span::symbol::{Ident, Symbol, sym};
 use std::borrow::Cow;
 
 use super::{MANUAL_FILTER_MAP, MANUAL_FIND_MAP, OPTION_FILTER_MAP, RESULT_FILTER_MAP};
diff --git a/src/tools/clippy/clippy_lints/src/methods/filter_map_bool_then.rs b/src/tools/clippy/clippy_lints/src/methods/filter_map_bool_then.rs
index 77a62fbb4fb9..129e69254289 100644
--- a/src/tools/clippy/clippy_lints/src/methods/filter_map_bool_then.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/filter_map_bool_then.rs
@@ -7,9 +7,9 @@ use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::{LateContext, LintContext};
 use rustc_middle::lint::in_external_macro;
-use rustc_middle::ty::adjustment::Adjust;
 use rustc_middle::ty::Binder;
-use rustc_span::{sym, Span};
+use rustc_middle::ty::adjustment::Adjust;
+use rustc_span::{Span, sym};
 
 pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, arg: &Expr<'_>, call_span: Span) {
     if !in_external_macro(cx.sess(), expr.span)
diff --git a/src/tools/clippy/clippy_lints/src/methods/filter_map_identity.rs b/src/tools/clippy/clippy_lints/src/methods/filter_map_identity.rs
index 999df875c753..cf7f276dabb1 100644
--- a/src/tools/clippy/clippy_lints/src/methods/filter_map_identity.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/filter_map_identity.rs
@@ -3,7 +3,7 @@ use clippy_utils::{is_expr_identity_function, is_expr_untyped_identity_function,
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
-use rustc_span::{sym, Span};
+use rustc_span::{Span, sym};
 
 use super::FILTER_MAP_IDENTITY;
 
diff --git a/src/tools/clippy/clippy_lints/src/methods/flat_map_identity.rs b/src/tools/clippy/clippy_lints/src/methods/flat_map_identity.rs
index 651ea34f9d0e..0c2ecfbc8ffd 100644
--- a/src/tools/clippy/clippy_lints/src/methods/flat_map_identity.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/flat_map_identity.rs
@@ -3,7 +3,7 @@ use clippy_utils::{is_expr_untyped_identity_function, is_trait_method};
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
-use rustc_span::{sym, Span};
+use rustc_span::{Span, sym};
 
 use super::FLAT_MAP_IDENTITY;
 
diff --git a/src/tools/clippy/clippy_lints/src/methods/flat_map_option.rs b/src/tools/clippy/clippy_lints/src/methods/flat_map_option.rs
index 0a4a381b8618..3242dcadb701 100644
--- a/src/tools/clippy/clippy_lints/src/methods/flat_map_option.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/flat_map_option.rs
@@ -4,7 +4,7 @@ use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
 use rustc_middle::ty;
-use rustc_span::{sym, Span};
+use rustc_span::{Span, sym};
 
 use super::FLAT_MAP_OPTION;
 use clippy_utils::ty::is_type_diagnostic_item;
diff --git a/src/tools/clippy/clippy_lints/src/methods/get_last_with_len.rs b/src/tools/clippy/clippy_lints/src/methods/get_last_with_len.rs
index 620376511342..5f6fb4c821d5 100644
--- a/src/tools/clippy/clippy_lints/src/methods/get_last_with_len.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/get_last_with_len.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::{is_integer_literal, SpanlessEq};
+use clippy_utils::{SpanlessEq, is_integer_literal};
 use rustc_errors::Applicability;
 use rustc_hir::{BinOpKind, Expr, ExprKind};
 use rustc_lint::LateContext;
diff --git a/src/tools/clippy/clippy_lints/src/methods/inefficient_to_string.rs b/src/tools/clippy/clippy_lints/src/methods/inefficient_to_string.rs
index 230a8eb2ec47..4ed7de81ea3d 100644
--- a/src/tools/clippy/clippy_lints/src/methods/inefficient_to_string.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/inefficient_to_string.rs
@@ -5,7 +5,7 @@ use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
 use rustc_middle::ty::{self, Ty};
-use rustc_span::symbol::{sym, Symbol};
+use rustc_span::symbol::{Symbol, sym};
 
 use super::INEFFICIENT_TO_STRING;
 
diff --git a/src/tools/clippy/clippy_lints/src/methods/inspect_for_each.rs b/src/tools/clippy/clippy_lints/src/methods/inspect_for_each.rs
index ad4b6fa130e3..3706f3b670ba 100644
--- a/src/tools/clippy/clippy_lints/src/methods/inspect_for_each.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/inspect_for_each.rs
@@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_help;
 use clippy_utils::is_trait_method;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
-use rustc_span::{sym, Span};
+use rustc_span::{Span, sym};
 
 use super::INSPECT_FOR_EACH;
 
diff --git a/src/tools/clippy/clippy_lints/src/methods/into_iter_on_ref.rs b/src/tools/clippy/clippy_lints/src/methods/into_iter_on_ref.rs
index bbc7ce8d78ab..bedeb63367d0 100644
--- a/src/tools/clippy/clippy_lints/src/methods/into_iter_on_ref.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/into_iter_on_ref.rs
@@ -5,8 +5,8 @@ use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
 use rustc_middle::ty::{self, Ty};
-use rustc_span::symbol::{sym, Symbol};
 use rustc_span::Span;
+use rustc_span::symbol::{Symbol, sym};
 
 use super::INTO_ITER_ON_REF;
 
diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_filter.rs b/src/tools/clippy/clippy_lints/src/methods/iter_filter.rs
index b93d51eac647..f6612c984a7e 100644
--- a/src/tools/clippy/clippy_lints/src/methods/iter_filter.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/iter_filter.rs
@@ -10,8 +10,8 @@ use clippy_utils::{get_parent_expr, is_trait_method, peel_blocks, span_contains_
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_hir::QPath;
-use rustc_span::symbol::{sym, Ident, Symbol};
 use rustc_span::Span;
+use rustc_span::symbol::{Ident, Symbol, sym};
 use std::borrow::Cow;
 
 ///
diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs b/src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs
index 33de3b87abc8..390dd24b5058 100644
--- a/src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs
@@ -14,7 +14,6 @@ use rustc_span::sym;
 /// - `hashmap.into_iter().map(|(_, v)| v)`
 ///
 /// on `HashMaps` and `BTreeMaps` in std
-
 pub(super) fn check<'tcx>(
     cx: &LateContext<'tcx>,
     map_type: &'tcx str,     // iter / into_iter
diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_nth.rs b/src/tools/clippy/clippy_lints/src/methods/iter_nth.rs
index e31fa2f777d8..82bda5d95122 100644
--- a/src/tools/clippy/clippy_lints/src/methods/iter_nth.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/iter_nth.rs
@@ -3,8 +3,8 @@ use clippy_utils::ty::get_type_diagnostic_name;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
-use rustc_span::symbol::sym;
 use rustc_span::Span;
+use rustc_span::symbol::sym;
 
 use super::ITER_NTH;
 
diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs b/src/tools/clippy/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs
index 7f6b666e434e..9d562f5e51d2 100644
--- a/src/tools/clippy/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs
@@ -5,9 +5,9 @@ use clippy_utils::source::snippet;
 use clippy_utils::{get_expr_use_or_unification_node, is_res_lang_ctor, path_res, std_or_core};
 
 use rustc_errors::Applicability;
+use rustc_hir::LangItem::{OptionNone, OptionSome};
 use rustc_hir::def_id::DefId;
 use rustc_hir::hir_id::HirId;
-use rustc_hir::LangItem::{OptionNone, OptionSome};
 use rustc_hir::{Expr, ExprKind, Node};
 use rustc_lint::LateContext;
 
diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_with_drain.rs b/src/tools/clippy/clippy_lints/src/methods/iter_with_drain.rs
index 1378a07cbc47..163058713377 100644
--- a/src/tools/clippy/clippy_lints/src/methods/iter_with_drain.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/iter_with_drain.rs
@@ -3,8 +3,8 @@ use clippy_utils::is_range_full;
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind, QPath};
 use rustc_lint::LateContext;
-use rustc_span::symbol::sym;
 use rustc_span::Span;
+use rustc_span::symbol::sym;
 
 use super::ITER_WITH_DRAIN;
 
diff --git a/src/tools/clippy/clippy_lints/src/methods/join_absolute_paths.rs b/src/tools/clippy/clippy_lints/src/methods/join_absolute_paths.rs
index 2dad7fcf3c12..2ad070793cbb 100644
--- a/src/tools/clippy/clippy_lints/src/methods/join_absolute_paths.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/join_absolute_paths.rs
@@ -6,8 +6,8 @@ use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::LateContext;
-use rustc_span::symbol::sym;
 use rustc_span::Span;
+use rustc_span::symbol::sym;
 
 use super::JOIN_ABSOLUTE_PATHS;
 
diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_c_str_literals.rs b/src/tools/clippy/clippy_lints/src/methods/manual_c_str_literals.rs
index cb9fb373c10a..96af9db1af70 100644
--- a/src/tools/clippy/clippy_lints/src/methods/manual_c_str_literals.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/manual_c_str_literals.rs
@@ -6,7 +6,7 @@ use rustc_ast::{LitKind, StrStyle};
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind, Node, QPath, TyKind};
 use rustc_lint::LateContext;
-use rustc_span::{sym, Span, Symbol};
+use rustc_span::{Span, Symbol, sym};
 
 use super::MANUAL_C_STR_LITERALS;
 
diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_inspect.rs b/src/tools/clippy/clippy_lints/src/methods/manual_inspect.rs
index cac2e11f5916..64c19c327b25 100644
--- a/src/tools/clippy/clippy_lints/src/methods/manual_inspect.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/manual_inspect.rs
@@ -3,13 +3,13 @@ use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::source::{IntoSpan, SpanRangeExt};
 use clippy_utils::ty::get_field_by_name;
 use clippy_utils::visitors::{for_each_expr, for_each_expr_without_closures};
-use clippy_utils::{expr_use_ctxt, is_diag_item_method, is_diag_trait_item, path_to_local_id, ExprUseNode};
+use clippy_utils::{ExprUseNode, expr_use_ctxt, is_diag_item_method, is_diag_trait_item, path_to_local_id};
 use core::ops::ControlFlow;
 use rustc_errors::Applicability;
 use rustc_hir::{BindingMode, BorrowKind, ByRef, ClosureKind, Expr, ExprKind, Mutability, Node, PatKind};
 use rustc_lint::LateContext;
 use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability};
-use rustc_span::{sym, Span, Symbol, DUMMY_SP};
+use rustc_span::{DUMMY_SP, Span, Symbol, sym};
 
 use super::MANUAL_INSPECT;
 
diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_is_variant_and.rs b/src/tools/clippy/clippy_lints/src/methods/manual_is_variant_and.rs
index d29acd4622a0..c377abd62377 100644
--- a/src/tools/clippy/clippy_lints/src/methods/manual_is_variant_and.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/manual_is_variant_and.rs
@@ -4,7 +4,7 @@ use clippy_utils::source::snippet;
 use clippy_utils::ty::is_type_diagnostic_item;
 use rustc_errors::Applicability;
 use rustc_lint::LateContext;
-use rustc_span::{sym, Span};
+use rustc_span::{Span, sym};
 
 use super::MANUAL_IS_VARIANT_AND;
 
diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_ok_or.rs b/src/tools/clippy/clippy_lints/src/methods/manual_ok_or.rs
index b1a8e1e5e470..4321dd6b0e09 100644
--- a/src/tools/clippy/clippy_lints/src/methods/manual_ok_or.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/manual_ok_or.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::{indent_of, reindent_multiline, SpanRangeExt};
+use clippy_utils::source::{SpanRangeExt, indent_of, reindent_multiline};
 use clippy_utils::ty::is_type_diagnostic_item;
 use clippy_utils::{is_res_lang_ctor, path_res, path_to_local_id};
 use rustc_errors::Applicability;
diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_try_fold.rs b/src/tools/clippy/clippy_lints/src/methods/manual_try_fold.rs
index 11fba35c16ea..31449d417701 100644
--- a/src/tools/clippy/clippy_lints/src/methods/manual_try_fold.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/manual_try_fold.rs
@@ -8,7 +8,7 @@ use rustc_hir::def::{DefKind, Res};
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::{LateContext, LintContext};
 use rustc_middle::lint::in_external_macro;
-use rustc_span::{sym, Span};
+use rustc_span::{Span, sym};
 
 use super::MANUAL_TRY_FOLD;
 
diff --git a/src/tools/clippy/clippy_lints/src/methods/map_clone.rs b/src/tools/clippy/clippy_lints/src/methods/map_clone.rs
index 08ce7e204dd2..ac378ff3702b 100644
--- a/src/tools/clippy/clippy_lints/src/methods/map_clone.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/map_clone.rs
@@ -11,7 +11,7 @@ use rustc_middle::mir::Mutability;
 use rustc_middle::ty;
 use rustc_middle::ty::adjustment::Adjust;
 use rustc_span::symbol::Ident;
-use rustc_span::{sym, Span};
+use rustc_span::{Span, sym};
 
 use super::MAP_CLONE;
 
diff --git a/src/tools/clippy/clippy_lints/src/methods/map_flatten.rs b/src/tools/clippy/clippy_lints/src/methods/map_flatten.rs
index 22a03825194e..07a7a12b1627 100644
--- a/src/tools/clippy/clippy_lints/src/methods/map_flatten.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/map_flatten.rs
@@ -6,8 +6,8 @@ use rustc_errors::Applicability;
 use rustc_hir::Expr;
 use rustc_lint::LateContext;
 use rustc_middle::ty;
-use rustc_span::symbol::sym;
 use rustc_span::Span;
+use rustc_span::symbol::sym;
 
 use super::MAP_FLATTEN;
 
diff --git a/src/tools/clippy/clippy_lints/src/methods/map_identity.rs b/src/tools/clippy/clippy_lints/src/methods/map_identity.rs
index 5dd7b1b02ade..1f204de01da0 100644
--- a/src/tools/clippy/clippy_lints/src/methods/map_identity.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/map_identity.rs
@@ -4,7 +4,7 @@ use clippy_utils::{is_expr_untyped_identity_function, is_trait_method};
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
-use rustc_span::{sym, Span};
+use rustc_span::{Span, sym};
 
 use super::MAP_IDENTITY;
 
diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs
index f61bb3a6bf48..7696dd16b251 100644
--- a/src/tools/clippy/clippy_lints/src/methods/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs
@@ -111,6 +111,7 @@ mod uninit_assumed_init;
 mod unit_hash;
 mod unnecessary_fallible_conversions;
 mod unnecessary_filter_map;
+mod unnecessary_first_then_check;
 mod unnecessary_fold;
 mod unnecessary_get_then_check;
 mod unnecessary_iter_cloned;
@@ -131,8 +132,8 @@ mod waker_clone_wake;
 mod wrong_self_convention;
 mod zst_offset;
 
-use clippy_config::msrvs::{self, Msrv};
 use clippy_config::Conf;
+use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::consts::{ConstEvalCtxt, Constant};
 use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
 use clippy_utils::macros::FormatArgsStorage;
@@ -146,7 +147,7 @@ use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
 use rustc_middle::ty::{self, TraitRef, Ty};
 use rustc_session::impl_lint_pass;
-use rustc_span::{sym, Span};
+use rustc_span::{Span, sym};
 
 declare_clippy_lint! {
     /// ### What it does
@@ -441,6 +442,17 @@ declare_clippy_lint! {
     ///     }
     /// }
     /// ```
+    ///
+    /// Use instead:
+    /// ```no_run
+    /// # struct X;
+    /// impl X {
+    ///     fn as_str(&self) -> &'static str {
+    ///         // ..
+    /// # ""
+    ///     }
+    /// }
+    /// ```
     #[clippy::version = "pre 1.29.0"]
     pub WRONG_SELF_CONVENTION,
     style,
@@ -3964,7 +3976,7 @@ declare_clippy_lint! {
     /// ```no_run
     /// let _ = 0;
     /// ```
-    #[clippy::version = "1.78.0"]
+    #[clippy::version = "1.81.0"]
     pub UNNECESSARY_MIN_OR_MAX,
     complexity,
     "using 'min()/max()' when there is no need for it"
@@ -4025,7 +4037,7 @@ declare_clippy_lint! {
     /// ```
     #[clippy::version = "1.78.0"]
     pub MANUAL_C_STR_LITERALS,
-    pedantic,
+    complexity,
     r#"creating a `CStr` through functions when `c""` literals can be used"#
 }
 
@@ -4099,7 +4111,7 @@ declare_clippy_lint! {
     /// ```no_run
     /// "foo".is_ascii();
     /// ```
-    #[clippy::version = "1.80.0"]
+    #[clippy::version = "1.81.0"]
     pub NEEDLESS_CHARACTER_ITERATION,
     suspicious,
     "is_ascii() called on a char iterator"
@@ -4126,6 +4138,34 @@ declare_clippy_lint! {
     "use of `map` returning the original item"
 }
 
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks the usage of `.first().is_some()` or `.first().is_none()` to check if a slice is
+    /// empty.
+    ///
+    /// ### Why is this bad?
+    /// Using `.is_empty()` is shorter and better communicates the intention.
+    ///
+    /// ### Example
+    /// ```no_run
+    /// let v = vec![1, 2, 3];
+    /// if v.first().is_none() {
+    ///     // The vector is empty...
+    /// }
+    /// ```
+    /// Use instead:
+    /// ```no_run
+    /// let v = vec![1, 2, 3];
+    /// if v.is_empty() {
+    ///     // The vector is empty...
+    /// }
+    /// ```
+    #[clippy::version = "1.83.0"]
+    pub UNNECESSARY_FIRST_THEN_CHECK,
+    complexity,
+    "calling `.first().is_some()` or `.first().is_none()` instead of `.is_empty()`"
+}
+
 pub struct Methods {
     avoid_breaking_exported_api: bool,
     msrv: Msrv,
@@ -4283,6 +4323,7 @@ impl_lint_pass!(Methods => [
     UNNECESSARY_RESULT_MAP_OR_ELSE,
     MANUAL_C_STR_LITERALS,
     UNNECESSARY_GET_THEN_CHECK,
+    UNNECESSARY_FIRST_THEN_CHECK,
     NEEDLESS_CHARACTER_ITERATION,
     MANUAL_INSPECT,
     UNNECESSARY_MIN_OR_MAX,
@@ -5055,6 +5096,9 @@ fn check_is_some_is_none(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>,
         Some(("get", f_recv, [arg], _, _)) => {
             unnecessary_get_then_check::check(cx, call_span, recv, f_recv, arg, is_some);
         },
+        Some(("first", f_recv, [], _, _)) => {
+            unnecessary_first_then_check::check(cx, call_span, recv, f_recv, is_some);
+        },
         _ => {},
     }
 }
@@ -5130,12 +5174,9 @@ impl ShouldImplTraitCase {
     fn lifetime_param_cond(&self, impl_item: &hir::ImplItem<'_>) -> bool {
         self.lint_explicit_lifetime
             || !impl_item.generics.params.iter().any(|p| {
-                matches!(
-                    p.kind,
-                    hir::GenericParamKind::Lifetime {
-                        kind: hir::LifetimeParamKind::Explicit
-                    }
-                )
+                matches!(p.kind, hir::GenericParamKind::Lifetime {
+                    kind: hir::LifetimeParamKind::Explicit
+                })
             })
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/methods/mut_mutex_lock.rs b/src/tools/clippy/clippy_lints/src/methods/mut_mutex_lock.rs
index 1a0fce2876d4..83e8370f939b 100644
--- a/src/tools/clippy/clippy_lints/src/methods/mut_mutex_lock.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/mut_mutex_lock.rs
@@ -5,7 +5,7 @@ use rustc_errors::Applicability;
 use rustc_hir::{Expr, Mutability};
 use rustc_lint::LateContext;
 use rustc_middle::ty;
-use rustc_span::{sym, Span};
+use rustc_span::{Span, sym};
 
 use super::MUT_MUTEX_LOCK;
 
diff --git a/src/tools/clippy/clippy_lints/src/methods/needless_character_iteration.rs b/src/tools/clippy/clippy_lints/src/methods/needless_character_iteration.rs
index 332da722a37d..348f740e7ddf 100644
--- a/src/tools/clippy/clippy_lints/src/methods/needless_character_iteration.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/needless_character_iteration.rs
@@ -4,8 +4,8 @@ use rustc_lint::LateContext;
 use rustc_middle::ty;
 use rustc_span::Span;
 
-use super::utils::get_last_chain_binding_hir_id;
 use super::NEEDLESS_CHARACTER_ITERATION;
+use super::utils::get_last_chain_binding_hir_id;
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::SpanRangeExt;
 use clippy_utils::{match_def_path, path_to_local_id, peel_blocks};
diff --git a/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs b/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs
index f61923e5bf56..421c7a5e070d 100644
--- a/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs
@@ -4,12 +4,12 @@ use clippy_utils::source::{snippet, snippet_with_applicability};
 use clippy_utils::sugg::Sugg;
 use clippy_utils::ty::{get_type_diagnostic_name, make_normalized_projection, make_projection};
 use clippy_utils::{
-    can_move_expr_to_closure, fn_def_id, get_enclosing_block, higher, is_trait_method, path_to_local, path_to_local_id,
-    CaptureKind,
+    CaptureKind, can_move_expr_to_closure, fn_def_id, get_enclosing_block, higher, is_trait_method, path_to_local,
+    path_to_local_id,
 };
 use rustc_data_structures::fx::FxHashMap;
 use rustc_errors::{Applicability, MultiSpan};
-use rustc_hir::intravisit::{walk_block, walk_expr, Visitor};
+use rustc_hir::intravisit::{Visitor, walk_block, walk_expr};
 use rustc_hir::{
     BindingMode, Block, Expr, ExprKind, HirId, HirIdSet, LetStmt, Mutability, Node, PatKind, Stmt, StmtKind,
 };
@@ -17,7 +17,7 @@ use rustc_lint::LateContext;
 use rustc_middle::hir::nested_filter;
 use rustc_middle::ty::{self, AssocKind, ClauseKind, EarlyBinder, GenericArg, GenericArgKind, Ty};
 use rustc_span::symbol::Ident;
-use rustc_span::{sym, Span};
+use rustc_span::{Span, sym};
 
 const NEEDLESS_COLLECT_MSG: &str = "avoid using `collect()` when not needed";
 
diff --git a/src/tools/clippy/clippy_lints/src/methods/needless_option_as_deref.rs b/src/tools/clippy/clippy_lints/src/methods/needless_option_as_deref.rs
index 9f714fdd47bb..538aa9097a40 100644
--- a/src/tools/clippy/clippy_lints/src/methods/needless_option_as_deref.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/needless_option_as_deref.rs
@@ -4,8 +4,8 @@ use clippy_utils::source::SpanRangeExt;
 use clippy_utils::ty::is_type_diagnostic_item;
 use clippy_utils::usage::local_used_after_expr;
 use rustc_errors::Applicability;
-use rustc_hir::def::Res;
 use rustc_hir::Expr;
+use rustc_hir::def::Res;
 use rustc_lint::LateContext;
 use rustc_span::sym;
 
diff --git a/src/tools/clippy/clippy_lints/src/methods/no_effect_replace.rs b/src/tools/clippy/clippy_lints/src/methods/no_effect_replace.rs
index a301a5f7d65b..32f32f1b2167 100644
--- a/src/tools/clippy/clippy_lints/src/methods/no_effect_replace.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/no_effect_replace.rs
@@ -1,6 +1,6 @@
+use clippy_utils::SpanlessEq;
 use clippy_utils::diagnostics::span_lint;
 use clippy_utils::ty::is_type_lang_item;
-use clippy_utils::SpanlessEq;
 use rustc_ast::LitKind;
 use rustc_hir::{ExprKind, LangItem};
 use rustc_lint::LateContext;
diff --git a/src/tools/clippy/clippy_lints/src/methods/open_options.rs b/src/tools/clippy/clippy_lints/src/methods/open_options.rs
index f1a3c81cebbd..da084871402a 100644
--- a/src/tools/clippy/clippy_lints/src/methods/open_options.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/open_options.rs
@@ -8,7 +8,7 @@ use rustc_hir::{Expr, ExprKind};
 use rustc_lint::LateContext;
 use rustc_middle::ty::Ty;
 use rustc_span::source_map::Spanned;
-use rustc_span::{sym, Span};
+use rustc_span::{Span, sym};
 
 use super::{NONSENSICAL_OPEN_OPTIONS, SUSPICIOUS_OPEN_OPTIONS};
 
@@ -126,17 +126,13 @@ fn get_open_options(
         && let ExprKind::Path(path) = callee.kind
         && let Some(did) = cx.qpath_res(&path, callee.hir_id).opt_def_id()
     {
-        let std_file_options = [
-            sym::file_options,
-            sym::open_options_new,
-        ];
+        let std_file_options = [sym::file_options, sym::open_options_new];
 
-        let tokio_file_options: &[&[&str]] = &[
-            &paths::TOKIO_IO_OPEN_OPTIONS_NEW,
-            &paths::TOKIO_FILE_OPTIONS,
-        ];
+        let tokio_file_options: &[&[&str]] = &[&paths::TOKIO_IO_OPEN_OPTIONS_NEW, &paths::TOKIO_FILE_OPTIONS];
 
-        let is_std_options = std_file_options.into_iter().any(|sym| cx.tcx.is_diagnostic_item(sym, did));
+        let is_std_options = std_file_options
+            .into_iter()
+            .any(|sym| cx.tcx.is_diagnostic_item(sym, did));
         is_std_options || match_any_def_paths(cx, did, tokio_file_options).is_some()
     } else {
         false
diff --git a/src/tools/clippy/clippy_lints/src/methods/option_as_ref_cloned.rs b/src/tools/clippy/clippy_lints/src/methods/option_as_ref_cloned.rs
index ba167f9d9c23..9b22494888fb 100644
--- a/src/tools/clippy/clippy_lints/src/methods/option_as_ref_cloned.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/option_as_ref_cloned.rs
@@ -3,9 +3,9 @@ use clippy_utils::ty::is_type_diagnostic_item;
 use rustc_errors::Applicability;
 use rustc_hir::Expr;
 use rustc_lint::LateContext;
-use rustc_span::{sym, Span};
+use rustc_span::{Span, sym};
 
-use super::{method_call, OPTION_AS_REF_CLONED};
+use super::{OPTION_AS_REF_CLONED, method_call};
 
 pub(super) fn check(cx: &LateContext<'_>, cloned_recv: &Expr<'_>, cloned_ident_span: Span) {
     if let Some((method @ ("as_ref" | "as_mut"), as_ref_recv, [], as_ref_ident_span, _)) = method_call(cloned_recv)
diff --git a/src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs b/src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs
index 9a18d8a14219..389e02056b2b 100644
--- a/src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs
@@ -7,7 +7,7 @@ use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
 use rustc_middle::ty;
-use rustc_span::{sym, Symbol};
+use rustc_span::{Symbol, sym};
 
 use super::OPTION_AS_REF_DEREF;
 
@@ -48,7 +48,9 @@ pub(super) fn check(
                 .map_or(false, |fun_def_id| {
                     cx.tcx.is_diagnostic_item(sym::deref_method, fun_def_id)
                         || cx.tcx.is_diagnostic_item(sym::deref_mut_method, fun_def_id)
-                        || deref_aliases.iter().any(|&sym| cx.tcx.is_diagnostic_item(sym, fun_def_id))
+                        || deref_aliases
+                            .iter()
+                            .any(|&sym| cx.tcx.is_diagnostic_item(sym, fun_def_id))
                 })
         },
         hir::ExprKind::Closure(&hir::Closure { body, .. }) => {
@@ -69,7 +71,9 @@ pub(super) fn check(
                         let method_did = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id).unwrap();
                         cx.tcx.is_diagnostic_item(sym::deref_method, method_did)
                             || cx.tcx.is_diagnostic_item(sym::deref_mut_method, method_did)
-                            || deref_aliases.iter().any(|&sym| cx.tcx.is_diagnostic_item(sym, method_did))
+                            || deref_aliases
+                                .iter()
+                                .any(|&sym| cx.tcx.is_diagnostic_item(sym, method_did))
                     } else {
                         false
                     }
diff --git a/src/tools/clippy/clippy_lints/src/methods/option_map_unwrap_or.rs b/src/tools/clippy/clippy_lints/src/methods/option_map_unwrap_or.rs
index ed3bed42eb3a..b160ab6de8e9 100644
--- a/src/tools/clippy/clippy_lints/src/methods/option_map_unwrap_or.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/option_map_unwrap_or.rs
@@ -5,11 +5,11 @@ use clippy_utils::ty::{is_copy, is_type_diagnostic_item};
 use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::Applicability;
 use rustc_hir::def::Res;
-use rustc_hir::intravisit::{walk_path, Visitor};
+use rustc_hir::intravisit::{Visitor, walk_path};
 use rustc_hir::{ExprKind, HirId, Node, PatKind, Path, QPath};
 use rustc_lint::LateContext;
 use rustc_middle::hir::nested_filter;
-use rustc_span::{sym, Span};
+use rustc_span::{Span, sym};
 use std::ops::ControlFlow;
 
 use super::MAP_UNWRAP_OR;
diff --git a/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs b/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs
index e4326cb958e5..c60a839432c9 100644
--- a/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs
@@ -11,8 +11,8 @@ use clippy_utils::{
 use rustc_errors::Applicability;
 use rustc_lint::LateContext;
 use rustc_middle::ty;
-use rustc_span::symbol::{self, sym, Symbol};
 use rustc_span::Span;
+use rustc_span::symbol::{self, Symbol, sym};
 use {rustc_ast as ast, rustc_hir as hir};
 
 use super::{OR_FUN_CALL, UNWRAP_OR_DEFAULT};
@@ -78,7 +78,6 @@ pub(super) fn check<'tcx>(
             cx.tcx
                 .inherent_impls(adt_def.did())
                 .into_iter()
-                .flatten()
                 .flat_map(|impl_id| cx.tcx.associated_items(impl_id).filter_by_name_unhygienic(sugg))
                 .find_map(|assoc| {
                     if assoc.fn_has_self_parameter
diff --git a/src/tools/clippy/clippy_lints/src/methods/or_then_unwrap.rs b/src/tools/clippy/clippy_lints/src/methods/or_then_unwrap.rs
index 7b0bdcf99e73..3e64e15dc860 100644
--- a/src/tools/clippy/clippy_lints/src/methods/or_then_unwrap.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/or_then_unwrap.rs
@@ -6,7 +6,7 @@ use rustc_errors::Applicability;
 use rustc_hir::lang_items::LangItem;
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::LateContext;
-use rustc_span::{sym, Span};
+use rustc_span::{Span, sym};
 
 use super::OR_THEN_UNWRAP;
 
diff --git a/src/tools/clippy/clippy_lints/src/methods/range_zip_with_len.rs b/src/tools/clippy/clippy_lints/src/methods/range_zip_with_len.rs
index 28ca76832eb3..f4d206c5307c 100644
--- a/src/tools/clippy/clippy_lints/src/methods/range_zip_with_len.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/range_zip_with_len.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint;
 use clippy_utils::source::snippet;
-use clippy_utils::{higher, is_integer_const, is_trait_method, SpanlessEq};
+use clippy_utils::{SpanlessEq, higher, is_integer_const, is_trait_method};
 use rustc_hir::{Expr, ExprKind, QPath};
 use rustc_lint::LateContext;
 use rustc_span::sym;
diff --git a/src/tools/clippy/clippy_lints/src/methods/search_is_some.rs b/src/tools/clippy/clippy_lints/src/methods/search_is_some.rs
index 3b2dd285b8c9..4ab165a5528b 100644
--- a/src/tools/clippy/clippy_lints/src/methods/search_is_some.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/search_is_some.rs
@@ -8,8 +8,8 @@ use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_hir::PatKind;
 use rustc_lint::LateContext;
-use rustc_span::symbol::sym;
 use rustc_span::Span;
+use rustc_span::symbol::sym;
 
 use super::SEARCH_IS_SOME;
 
diff --git a/src/tools/clippy/clippy_lints/src/methods/seek_from_current.rs b/src/tools/clippy/clippy_lints/src/methods/seek_from_current.rs
index 8bc535ac47a8..7ef07fe899c0 100644
--- a/src/tools/clippy/clippy_lints/src/methods/seek_from_current.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/seek_from_current.rs
@@ -6,9 +6,9 @@ use rustc_lint::LateContext;
 use rustc_span::sym;
 
 use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::is_enum_variant_ctor;
 use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::ty::implements_trait;
-use clippy_utils::is_enum_variant_ctor;
 
 use super::SEEK_FROM_CURRENT;
 
diff --git a/src/tools/clippy/clippy_lints/src/methods/seek_to_start_instead_of_rewind.rs b/src/tools/clippy/clippy_lints/src/methods/seek_to_start_instead_of_rewind.rs
index 7c09cc252e13..9c966f010f13 100644
--- a/src/tools/clippy/clippy_lints/src/methods/seek_to_start_instead_of_rewind.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/seek_to_start_instead_of_rewind.rs
@@ -1,12 +1,12 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::ty::implements_trait;
-use clippy_utils::{is_expr_used_or_unified, is_enum_variant_ctor};
+use clippy_utils::{is_enum_variant_ctor, is_expr_used_or_unified};
 use rustc_ast::ast::{LitIntType, LitKind};
 use rustc_data_structures::packed::Pu128;
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::LateContext;
-use rustc_span::{sym, Span};
+use rustc_span::{Span, sym};
 
 use super::SEEK_TO_START_INSTEAD_OF_REWIND;
 
diff --git a/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs b/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs
index 12cabd43cb1b..69032776b2b9 100644
--- a/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs
@@ -3,7 +3,7 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant};
 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
 use clippy_utils::source::snippet_with_context;
 use clippy_utils::usage::local_used_after_expr;
-use clippy_utils::visitors::{for_each_expr, Descend};
+use clippy_utils::visitors::{Descend, for_each_expr};
 use clippy_utils::{is_diag_item_method, match_def_path, path_to_local_id, paths};
 use core::ops::ControlFlow;
 use rustc_errors::Applicability;
@@ -12,7 +12,7 @@ use rustc_hir::{
 };
 use rustc_lint::LateContext;
 use rustc_middle::ty;
-use rustc_span::{sym, Span, Symbol, SyntaxContext};
+use rustc_span::{Span, Symbol, SyntaxContext, sym};
 
 use super::{MANUAL_SPLIT_ONCE, NEEDLESS_SPLITN};
 
diff --git a/src/tools/clippy/clippy_lints/src/methods/suspicious_command_arg_space.rs b/src/tools/clippy/clippy_lints/src/methods/suspicious_command_arg_space.rs
index 38f2c9169124..c60a49067ec0 100644
--- a/src/tools/clippy/clippy_lints/src/methods/suspicious_command_arg_space.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/suspicious_command_arg_space.rs
@@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::ty::is_type_diagnostic_item;
 use rustc_errors::{Applicability, Diag};
 use rustc_lint::LateContext;
-use rustc_span::{sym, Span};
+use rustc_span::{Span, sym};
 use {rustc_ast as ast, rustc_hir as hir};
 
 use super::SUSPICIOUS_COMMAND_ARG_SPACE;
diff --git a/src/tools/clippy/clippy_lints/src/methods/type_id_on_box.rs b/src/tools/clippy/clippy_lints/src/methods/type_id_on_box.rs
index db8cc4595d4d..e67ba5c4d314 100644
--- a/src/tools/clippy/clippy_lints/src/methods/type_id_on_box.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/type_id_on_box.rs
@@ -7,7 +7,7 @@ use rustc_lint::LateContext;
 use rustc_middle::ty::adjustment::{Adjust, Adjustment};
 use rustc_middle::ty::print::with_forced_trimmed_paths;
 use rustc_middle::ty::{self, ExistentialPredicate, Ty};
-use rustc_span::{sym, Span};
+use rustc_span::{Span, sym};
 
 /// Checks if the given type is `dyn Any`, or a trait object that has `Any` as a supertrait.
 /// Only in those cases will its vtable have a `type_id` method that returns the implementor's
diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_fallible_conversions.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_fallible_conversions.rs
index d46b584f8f44..ce81282ddfeb 100644
--- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_fallible_conversions.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_fallible_conversions.rs
@@ -6,7 +6,7 @@ use rustc_hir::{Expr, ExprKind, QPath};
 use rustc_lint::LateContext;
 use rustc_middle::ty;
 use rustc_middle::ty::print::with_forced_trimmed_paths;
-use rustc_span::{sym, Span};
+use rustc_span::{Span, sym};
 
 use super::UNNECESSARY_FALLIBLE_CONVERSIONS;
 
diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs
index c9b9d98dbe60..ca46da81fa05 100644
--- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs
@@ -2,7 +2,7 @@ use super::utils::clone_or_copy_needed;
 use clippy_utils::diagnostics::span_lint;
 use clippy_utils::ty::is_copy;
 use clippy_utils::usage::mutated_variables;
-use clippy_utils::visitors::{for_each_expr_without_closures, Descend};
+use clippy_utils::visitors::{Descend, for_each_expr_without_closures};
 use clippy_utils::{is_res_lang_ctor, is_trait_method, path_res, path_to_local_id};
 use core::ops::ControlFlow;
 use rustc_hir as hir;
diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_first_then_check.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_first_then_check.rs
new file mode 100644
index 000000000000..7ae1bb54e609
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_first_then_check.rs
@@ -0,0 +1,56 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::SpanRangeExt;
+
+use rustc_errors::Applicability;
+use rustc_hir::{Expr, ExprKind};
+use rustc_lint::LateContext;
+use rustc_span::Span;
+
+use super::UNNECESSARY_FIRST_THEN_CHECK;
+
+pub(super) fn check(
+    cx: &LateContext<'_>,
+    call_span: Span,
+    first_call: &Expr<'_>,
+    first_caller: &Expr<'_>,
+    is_some: bool,
+) {
+    if !cx
+        .typeck_results()
+        .expr_ty_adjusted(first_caller)
+        .peel_refs()
+        .is_slice()
+    {
+        return;
+    }
+
+    let ExprKind::MethodCall(_, _, _, first_call_span) = first_call.kind else {
+        return;
+    };
+
+    let both_calls_span = first_call_span.with_hi(call_span.hi());
+    if let Some(both_calls_snippet) = both_calls_span.get_source_text(cx)
+        && let Some(first_caller_snippet) = first_caller.span.get_source_text(cx)
+    {
+        let (sugg_span, suggestion) = if is_some {
+            (
+                first_caller.span.with_hi(call_span.hi()),
+                format!("!{first_caller_snippet}.is_empty()"),
+            )
+        } else {
+            (both_calls_span, "is_empty()".to_owned())
+        };
+        span_lint_and_sugg(
+            cx,
+            UNNECESSARY_FIRST_THEN_CHECK,
+            sugg_span,
+            format!(
+                "unnecessary use of `{both_calls_snippet}` to check if slice {}",
+                if is_some { "is not empty" } else { "is empty" }
+            ),
+            "replace this with",
+            suggestion,
+            Applicability::MaybeIncorrect,
+        );
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_fold.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_fold.rs
index ccc8d17970ec..b5d8972d7aad 100644
--- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_fold.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_fold.rs
@@ -8,7 +8,7 @@ use rustc_hir as hir;
 use rustc_hir::PatKind;
 use rustc_lint::LateContext;
 use rustc_middle::ty;
-use rustc_span::{sym, Span};
+use rustc_span::{Span, sym};
 
 use super::UNNECESSARY_FOLD;
 
@@ -123,58 +123,32 @@ pub(super) fn check(
     if let hir::ExprKind::Lit(lit) = init.kind {
         match lit.node {
             ast::LitKind::Bool(false) => {
-                check_fold_with_op(
-                    cx,
-                    expr,
-                    acc,
-                    fold_span,
-                    hir::BinOpKind::Or,
-                    Replacement {
-                        has_args: true,
-                        has_generic_return: false,
-                        method_name: "any",
-                    },
-                );
+                check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::Or, Replacement {
+                    has_args: true,
+                    has_generic_return: false,
+                    method_name: "any",
+                });
             },
             ast::LitKind::Bool(true) => {
-                check_fold_with_op(
-                    cx,
-                    expr,
-                    acc,
-                    fold_span,
-                    hir::BinOpKind::And,
-                    Replacement {
-                        has_args: true,
-                        has_generic_return: false,
-                        method_name: "all",
-                    },
-                );
+                check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::And, Replacement {
+                    has_args: true,
+                    has_generic_return: false,
+                    method_name: "all",
+                });
             },
-            ast::LitKind::Int(Pu128(0), _) => check_fold_with_op(
-                cx,
-                expr,
-                acc,
-                fold_span,
-                hir::BinOpKind::Add,
-                Replacement {
+            ast::LitKind::Int(Pu128(0), _) => {
+                check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::Add, Replacement {
                     has_args: false,
                     has_generic_return: needs_turbofish(cx, expr),
                     method_name: "sum",
-                },
-            ),
+                });
+            },
             ast::LitKind::Int(Pu128(1), _) => {
-                check_fold_with_op(
-                    cx,
-                    expr,
-                    acc,
-                    fold_span,
-                    hir::BinOpKind::Mul,
-                    Replacement {
-                        has_args: false,
-                        has_generic_return: needs_turbofish(cx, expr),
-                        method_name: "product",
-                    },
-                );
+                check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::Mul, Replacement {
+                    has_args: false,
+                    has_generic_return: needs_turbofish(cx, expr),
+                    method_name: "product",
+                });
             },
             _ => (),
         }
diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_get_then_check.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_get_then_check.rs
index 64eb84117954..39fce2c40c91 100644
--- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_get_then_check.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_get_then_check.rs
@@ -6,7 +6,7 @@ use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::LateContext;
 use rustc_middle::ty::Ty;
-use rustc_span::{sym, Span};
+use rustc_span::{Span, sym};
 
 use super::UNNECESSARY_GET_THEN_CHECK;
 
diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_iter_cloned.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_iter_cloned.rs
index 4d18bc7ac777..029704882dde 100644
--- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_iter_cloned.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_iter_cloned.rs
@@ -10,7 +10,7 @@ use rustc_errors::Applicability;
 use rustc_hir::def_id::DefId;
 use rustc_hir::{BindingMode, Expr, ExprKind, Node, PatKind};
 use rustc_lint::LateContext;
-use rustc_span::{sym, Symbol};
+use rustc_span::{Symbol, sym};
 
 use super::UNNECESSARY_TO_OWNED;
 
diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_literal_unwrap.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_literal_unwrap.rs
index 494d71fc053f..6dc8adb42dfb 100644
--- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_literal_unwrap.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_literal_unwrap.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::{is_res_lang_ctor, last_path_segment, path_res, MaybePath};
+use clippy_utils::{MaybePath, is_res_lang_ctor, last_path_segment, path_res};
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_min_or_max.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_min_or_max.rs
index 86c0a6322b66..062d1348555c 100644
--- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_min_or_max.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_min_or_max.rs
@@ -1,16 +1,15 @@
 use std::cmp::Ordering;
 
 use super::UNNECESSARY_MIN_OR_MAX;
-use clippy_utils::diagnostics::span_lint_and_sugg;
-
 use clippy_utils::consts::{ConstEvalCtxt, Constant, ConstantSource, FullInt};
+use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet;
 
 use rustc_errors::Applicability;
 use rustc_hir::Expr;
 use rustc_lint::LateContext;
 use rustc_middle::ty;
-use rustc_span::Span;
+use rustc_span::{Span, sym};
 
 pub(super) fn check<'tcx>(
     cx: &LateContext<'tcx>,
@@ -21,26 +20,30 @@ pub(super) fn check<'tcx>(
 ) {
     let typeck_results = cx.typeck_results();
     let ecx = ConstEvalCtxt::with_env(cx.tcx, cx.param_env, typeck_results);
-    if let Some((left, ConstantSource::Local | ConstantSource::CoreConstant)) = ecx.eval_with_source(recv)
-        && let Some((right, ConstantSource::Local | ConstantSource::CoreConstant)) = ecx.eval_with_source(arg)
+    if let Some(id) = typeck_results.type_dependent_def_id(expr.hir_id)
+        && (cx.tcx.is_diagnostic_item(sym::cmp_ord_min, id) || cx.tcx.is_diagnostic_item(sym::cmp_ord_max, id))
     {
-        let Some(ord) = Constant::partial_cmp(cx.tcx, typeck_results.expr_ty(recv), &left, &right) else {
-            return;
-        };
+        if let Some((left, ConstantSource::Local | ConstantSource::CoreConstant)) = ecx.eval_with_source(recv)
+            && let Some((right, ConstantSource::Local | ConstantSource::CoreConstant)) = ecx.eval_with_source(arg)
+        {
+            let Some(ord) = Constant::partial_cmp(cx.tcx, typeck_results.expr_ty(recv), &left, &right) else {
+                return;
+            };
 
-        lint(cx, expr, name, recv.span, arg.span, ord);
-    } else if let Some(extrema) = detect_extrema(cx, recv) {
-        let ord = match extrema {
-            Extrema::Minimum => Ordering::Less,
-            Extrema::Maximum => Ordering::Greater,
-        };
-        lint(cx, expr, name, recv.span, arg.span, ord);
-    } else if let Some(extrema) = detect_extrema(cx, arg) {
-        let ord = match extrema {
-            Extrema::Minimum => Ordering::Greater,
-            Extrema::Maximum => Ordering::Less,
-        };
-        lint(cx, expr, name, recv.span, arg.span, ord);
+            lint(cx, expr, name, recv.span, arg.span, ord);
+        } else if let Some(extrema) = detect_extrema(cx, recv) {
+            let ord = match extrema {
+                Extrema::Minimum => Ordering::Less,
+                Extrema::Maximum => Ordering::Greater,
+            };
+            lint(cx, expr, name, recv.span, arg.span, ord);
+        } else if let Some(extrema) = detect_extrema(cx, arg) {
+            let ord = match extrema {
+                Extrema::Minimum => Ordering::Greater,
+                Extrema::Maximum => Ordering::Less,
+            };
+            lint(cx, expr, name, recv.span, arg.span, ord);
+        }
     }
 }
 
diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_result_map_or_else.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_result_map_or_else.rs
index c14a87c15341..dc50717112d8 100644
--- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_result_map_or_else.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_result_map_or_else.rs
@@ -8,8 +8,8 @@ use rustc_hir::{Closure, Expr, ExprKind, HirId, QPath};
 use rustc_lint::LateContext;
 use rustc_span::symbol::sym;
 
-use super::utils::get_last_chain_binding_hir_id;
 use super::UNNECESSARY_RESULT_MAP_OR_ELSE;
+use super::utils::get_last_chain_binding_hir_id;
 
 fn emit_lint(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, def_arg: &Expr<'_>) {
     let msg = "unused \"map closure\" when calling `Result::map_or_else` value";
diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs
index bf7555a29e22..cfa1fdb81372 100644
--- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs
@@ -2,12 +2,11 @@ use super::implicit_clone::is_clone_like;
 use super::unnecessary_iter_cloned::{self, is_into_iter};
 use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
-use clippy_utils::source::{snippet, SpanRangeExt};
+use clippy_utils::source::{SpanRangeExt, snippet};
 use clippy_utils::ty::{get_iterator_item_ty, implements_trait, is_copy, is_type_diagnostic_item, is_type_lang_item};
 use clippy_utils::visitors::find_all_ret_expressions;
 use clippy_utils::{
-    fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, peel_middle_ty_refs,
-    return_ty,
+    fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, peel_middle_ty_refs, return_ty,
 };
 use rustc_errors::Applicability;
 use rustc_hir::def::{DefKind, Res};
@@ -20,7 +19,7 @@ use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref};
 use rustc_middle::ty::{
     self, ClauseKind, GenericArg, GenericArgKind, GenericArgsRef, ParamTy, ProjectionPredicate, TraitPredicate, Ty,
 };
-use rustc_span::{sym, Symbol};
+use rustc_span::{Symbol, sym};
 use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
 use rustc_trait_selection::traits::{Obligation, ObligationCause};
 
@@ -717,7 +716,7 @@ fn check_if_applicable_to_argument<'tcx>(cx: &LateContext<'tcx>, arg: &Expr<'tcx
 // check that:
 // 1. This is a method with only one argument that doesn't come from a trait.
 // 2. That it has `Borrow` in its generic predicates.
-// 3. `Self` is a std "map type" (ie `HashSet`, `HashMap`, BTreeSet`, `BTreeMap`).
+// 3. `Self` is a std "map type" (ie `HashSet`, `HashMap`, `BTreeSet`, `BTreeMap`).
 fn check_borrow_predicate<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
     if let ExprKind::MethodCall(_, caller, &[arg], _) = expr.kind
         && let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id)
diff --git a/src/tools/clippy/clippy_lints/src/methods/unused_enumerate_index.rs b/src/tools/clippy/clippy_lints/src/methods/unused_enumerate_index.rs
index ee5177d1ffaa..6c2ae9cc6bff 100644
--- a/src/tools/clippy/clippy_lints/src/methods/unused_enumerate_index.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/unused_enumerate_index.rs
@@ -1,11 +1,11 @@
 use clippy_utils::diagnostics::span_lint_hir_and_then;
-use clippy_utils::source::{snippet, SpanRangeExt};
+use clippy_utils::source::{SpanRangeExt, snippet};
 use clippy_utils::{expr_or_init, is_trait_method, pat_is_wild};
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind, FnDecl, PatKind, TyKind};
 use rustc_lint::LateContext;
 use rustc_middle::ty::AdtDef;
-use rustc_span::{sym, Span};
+use rustc_span::{Span, sym};
 
 use crate::loops::UNUSED_ENUMERATE_INDEX;
 
diff --git a/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs b/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs
index ebad4ae6ee95..eafe7486bb05 100644
--- a/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs
@@ -9,7 +9,7 @@ use rustc_hir::{self as hir, LangItem};
 use rustc_lint::LateContext;
 use rustc_middle::ty::adjustment::Adjust;
 use rustc_middle::ty::{Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor};
-use rustc_span::{sym, Span};
+use rustc_span::{Span, sym};
 
 use core::ops::ControlFlow;
 
diff --git a/src/tools/clippy/clippy_lints/src/methods/utils.rs b/src/tools/clippy/clippy_lints/src/methods/utils.rs
index fe860e5ae260..4e33dc1df54d 100644
--- a/src/tools/clippy/clippy_lints/src/methods/utils.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/utils.rs
@@ -1,12 +1,12 @@
 use clippy_utils::ty::is_type_diagnostic_item;
 use clippy_utils::{get_parent_expr, path_to_local_id, usage};
-use rustc_hir::intravisit::{walk_expr, Visitor};
+use rustc_hir::intravisit::{Visitor, walk_expr};
 use rustc_hir::{BorrowKind, Expr, ExprKind, HirId, Mutability, Pat, QPath, Stmt, StmtKind};
 use rustc_lint::LateContext;
 use rustc_middle::hir::nested_filter;
 use rustc_middle::ty::{self, Ty};
-use rustc_span::symbol::sym;
 use rustc_span::Span;
+use rustc_span::symbol::sym;
 
 pub(super) fn derefs_to_slice<'tcx>(
     cx: &LateContext<'tcx>,
diff --git a/src/tools/clippy/clippy_lints/src/methods/vec_resize_to_zero.rs b/src/tools/clippy/clippy_lints/src/methods/vec_resize_to_zero.rs
index 3e271a606115..5ea4ada128a2 100644
--- a/src/tools/clippy/clippy_lints/src/methods/vec_resize_to_zero.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/vec_resize_to_zero.rs
@@ -6,7 +6,7 @@ use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::LateContext;
 use rustc_span::source_map::Spanned;
-use rustc_span::{sym, Span};
+use rustc_span::{Span, sym};
 
 use super::VEC_RESIZE_TO_ZERO;
 
diff --git a/src/tools/clippy/clippy_lints/src/methods/waker_clone_wake.rs b/src/tools/clippy/clippy_lints/src/methods/waker_clone_wake.rs
index 9b64cc7589cc..b5f34a9be2e2 100644
--- a/src/tools/clippy/clippy_lints/src/methods/waker_clone_wake.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/waker_clone_wake.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::is_trait_method;
+use clippy_utils::source::snippet_with_applicability;
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::LateContext;
diff --git a/src/tools/clippy/clippy_lints/src/min_ident_chars.rs b/src/tools/clippy/clippy_lints/src/min_ident_chars.rs
index c83e5198c274..a99e21d938c6 100644
--- a/src/tools/clippy/clippy_lints/src/min_ident_chars.rs
+++ b/src/tools/clippy/clippy_lints/src/min_ident_chars.rs
@@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint;
 use clippy_utils::is_from_proc_macro;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_hir::def::{DefKind, Res};
-use rustc_hir::intravisit::{walk_item, walk_trait_item, Visitor};
+use rustc_hir::intravisit::{Visitor, walk_item, walk_trait_item};
 use rustc_hir::{GenericParamKind, HirId, Item, ItemKind, ItemLocalId, Node, Pat, PatKind, TraitItem, UsePath};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
diff --git a/src/tools/clippy/clippy_lints/src/misc.rs b/src/tools/clippy/clippy_lints/src/misc.rs
index a9aafe7ed56c..408dbef9cb16 100644
--- a/src/tools/clippy/clippy_lints/src/misc.rs
+++ b/src/tools/clippy/clippy_lints/src/misc.rs
@@ -2,11 +2,12 @@ use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir, span_lint_hir
 use clippy_utils::source::{snippet, snippet_with_context};
 use clippy_utils::sugg::Sugg;
 use clippy_utils::{
-    fulfill_or_allowed, get_parent_expr, in_automatically_derived, is_lint_allowed, iter_input_pats, last_path_segment,
-    SpanlessEq,
+    SpanlessEq, fulfill_or_allowed, get_parent_expr, in_automatically_derived, is_lint_allowed, iter_input_pats,
+    last_path_segment,
 };
 use rustc_errors::Applicability;
 use rustc_hir::def::Res;
+use rustc_hir::def_id::LOCAL_CRATE;
 use rustc_hir::intravisit::FnKind;
 use rustc_hir::{
     BinOpKind, BindingMode, Body, ByRef, Expr, ExprKind, FnDecl, Mutability, PatKind, QPath, Stmt, StmtKind,
@@ -14,8 +15,8 @@ use rustc_hir::{
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
 use rustc_session::declare_lint_pass;
-use rustc_span::def_id::LocalDefId;
 use rustc_span::Span;
+use rustc_span::def_id::LocalDefId;
 
 use crate::ref_patterns::REF_PATTERNS;
 
@@ -80,6 +81,45 @@ declare_clippy_lint! {
     "using a binding which is prefixed with an underscore"
 }
 
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for the use of item with a single leading
+    /// underscore.
+    ///
+    /// ### Why is this bad?
+    /// A single leading underscore is usually used to indicate
+    /// that a item will not be used. Using such a item breaks this
+    /// expectation.
+    ///
+    /// ### Example
+    /// ```no_run
+    /// fn _foo() {}
+    ///
+    /// struct _FooStruct {}
+    ///
+    /// fn main() {
+    ///     _foo();
+    ///     let _ = _FooStruct{};
+    /// }
+    /// ```
+    ///
+    /// Use instead:
+    /// ```no_run
+    /// fn foo() {}
+    ///
+    /// struct FooStruct {}
+    ///
+    /// fn main() {
+    ///     foo();
+    ///     let _ = FooStruct{};
+    /// }
+    /// ```
+    #[clippy::version = "pre 1.29.0"]
+    pub USED_UNDERSCORE_ITEMS,
+    pedantic,
+    "using a item which is prefixed with an underscore"
+}
+
 declare_clippy_lint! {
     /// ### What it does
     /// Checks for the use of short circuit boolean conditions as
@@ -104,6 +144,7 @@ declare_clippy_lint! {
 declare_lint_pass!(LintPass => [
     TOPLEVEL_REF_ARG,
     USED_UNDERSCORE_BINDING,
+    USED_UNDERSCORE_ITEMS,
     SHORT_CIRCUIT_STATEMENT,
 ]);
 
@@ -205,51 +246,104 @@ impl<'tcx> LateLintPass<'tcx> for LintPass {
         {
             return;
         }
-        let (definition_hir_id, ident) = match expr.kind {
-            ExprKind::Path(ref qpath) => {
-                if let QPath::Resolved(None, path) = qpath
-                    && let Res::Local(id) = path.res
-                    && is_used(cx, expr)
-                {
-                    (id, last_path_segment(qpath).ident)
-                } else {
-                    return;
-                }
-            },
-            ExprKind::Field(recv, ident) => {
-                if let Some(adt_def) = cx.typeck_results().expr_ty_adjusted(recv).ty_adt_def()
-                    && let Some(field) = adt_def.all_fields().find(|field| field.name == ident.name)
-                    && let Some(local_did) = field.did.as_local()
-                    && !cx.tcx.type_of(field.did).skip_binder().is_phantom_data()
-                {
-                    (cx.tcx.local_def_id_to_hir_id(local_did), ident)
-                } else {
-                    return;
-                }
+
+        used_underscore_binding(cx, expr);
+        used_underscore_items(cx, expr);
+    }
+}
+
+fn used_underscore_items<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+    let (def_id, ident) = match expr.kind {
+        ExprKind::Call(func, ..) => {
+            if let ExprKind::Path(QPath::Resolved(.., path)) = func.kind
+                && let Some(last_segment) = path.segments.last()
+                && let Res::Def(_, def_id) = last_segment.res
+            {
+                (def_id, last_segment.ident)
+            } else {
+                return;
+            }
+        },
+        ExprKind::MethodCall(path, ..) => {
+            if let Some(def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) {
+                (def_id, path.ident)
+            } else {
+                return;
+            }
+        },
+        ExprKind::Struct(QPath::Resolved(_, path), ..) => {
+            if let Some(last_segment) = path.segments.last()
+                && let Res::Def(_, def_id) = last_segment.res
+            {
+                (def_id, last_segment.ident)
+            } else {
+                return;
+            }
+        },
+        _ => return,
+    };
+
+    let name = ident.name.as_str();
+    let definition_span = cx.tcx.def_span(def_id);
+    if name.starts_with('_')
+        && !name.starts_with("__")
+        && !definition_span.from_expansion()
+        && def_id.krate == LOCAL_CRATE
+    {
+        span_lint_and_then(
+            cx,
+            USED_UNDERSCORE_ITEMS,
+            expr.span,
+            "used underscore-prefixed item".to_string(),
+            |diag| {
+                diag.span_note(definition_span, "item is defined here".to_string());
             },
-            _ => return,
-        };
+        );
+    }
+}
 
-        let name = ident.name.as_str();
-        if name.starts_with('_')
-            && !name.starts_with("__")
-            && let definition_span = cx.tcx.hir().span(definition_hir_id)
-            && !definition_span.from_expansion()
-            && !fulfill_or_allowed(cx, USED_UNDERSCORE_BINDING, [expr.hir_id, definition_hir_id])
-        {
-            span_lint_and_then(
-                cx,
-                USED_UNDERSCORE_BINDING,
-                expr.span,
-                format!(
-                    "used binding `{name}` which is prefixed with an underscore. A leading \
-                     underscore signals that a binding will not be used"
-                ),
-                |diag| {
-                    diag.span_note(definition_span, format!("`{name}` is defined here"));
-                },
-            );
-        }
+fn used_underscore_binding<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+    let (definition_hir_id, ident) = match expr.kind {
+        ExprKind::Path(ref qpath) => {
+            if let QPath::Resolved(None, path) = qpath
+                && let Res::Local(id) = path.res
+                && is_used(cx, expr)
+            {
+                (id, last_path_segment(qpath).ident)
+            } else {
+                return;
+            }
+        },
+        ExprKind::Field(recv, ident) => {
+            if let Some(adt_def) = cx.typeck_results().expr_ty_adjusted(recv).ty_adt_def()
+                && let Some(field) = adt_def.all_fields().find(|field| field.name == ident.name)
+                && let Some(local_did) = field.did.as_local()
+                && !cx.tcx.type_of(field.did).skip_binder().is_phantom_data()
+            {
+                (cx.tcx.local_def_id_to_hir_id(local_did), ident)
+            } else {
+                return;
+            }
+        },
+        _ => return,
+    };
+
+    let name = ident.name.as_str();
+    if name.starts_with('_')
+        && !name.starts_with("__")
+        && let definition_span = cx.tcx.hir().span(definition_hir_id)
+        && !definition_span.from_expansion()
+        && !fulfill_or_allowed(cx, USED_UNDERSCORE_BINDING, [expr.hir_id, definition_hir_id])
+    {
+        span_lint_and_then(
+            cx,
+            USED_UNDERSCORE_BINDING,
+            expr.span,
+            "used underscore-prefixed binding".to_string(),
+            |diag| {
+                diag.span_note(definition_span, "binding is defined here".to_string());
+            },
+        );
     }
 }
 
diff --git a/src/tools/clippy/clippy_lints/src/missing_assert_message.rs b/src/tools/clippy/clippy_lints/src/missing_assert_message.rs
index a9ea11f4c2b0..86348f04600b 100644
--- a/src/tools/clippy/clippy_lints/src/missing_assert_message.rs
+++ b/src/tools/clippy/clippy_lints/src/missing_assert_message.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::is_in_test;
-use clippy_utils::macros::{find_assert_args, find_assert_eq_args, root_macro_call_first_node, PanicExpn};
+use clippy_utils::macros::{PanicExpn, find_assert_args, find_assert_eq_args, root_macro_call_first_node};
 use rustc_hir::Expr;
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::declare_lint_pass;
diff --git a/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs b/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs
index 94c91b095171..b40d7eba15e5 100644
--- a/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs
+++ b/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs
@@ -1,7 +1,7 @@
 use std::mem;
 use std::ops::ControlFlow;
 
-use clippy_utils::comparisons::{normalize_comparison, Rel};
+use clippy_utils::comparisons::{Rel, normalize_comparison};
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::source::snippet;
 use clippy_utils::visitors::for_each_expr_without_closures;
@@ -14,7 +14,7 @@ use rustc_hir::{BinOp, Block, Body, Expr, ExprKind, UnOp};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::declare_lint_pass;
 use rustc_span::source_map::Spanned;
-use rustc_span::{sym, Span};
+use rustc_span::{Span, sym};
 
 declare_clippy_lint! {
     /// ### What it does
diff --git a/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs b/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs
index 859afe1b9636..eea0459e026e 100644
--- a/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs
+++ b/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs
@@ -1,5 +1,5 @@
-use clippy_config::msrvs::{self, Msrv};
 use clippy_config::Conf;
+use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::qualify_min_const_fn::is_min_const_fn;
 use clippy_utils::{fn_has_unsatisfiable_preds, is_entrypoint_fn, is_from_proc_macro, trait_ref_of_method};
@@ -11,8 +11,8 @@ use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::lint::in_external_macro;
 use rustc_middle::ty;
 use rustc_session::impl_lint_pass;
-use rustc_span::def_id::LocalDefId;
 use rustc_span::Span;
+use rustc_span::def_id::LocalDefId;
 use rustc_target::spec::abi::Abi;
 
 declare_clippy_lint! {
diff --git a/src/tools/clippy/clippy_lints/src/missing_const_for_thread_local.rs b/src/tools/clippy/clippy_lints/src/missing_const_for_thread_local.rs
index 954216038fb5..c2f524a63531 100644
--- a/src/tools/clippy/clippy_lints/src/missing_const_for_thread_local.rs
+++ b/src/tools/clippy/clippy_lints/src/missing_const_for_thread_local.rs
@@ -1,12 +1,12 @@
-use clippy_config::msrvs::{self, Msrv};
 use clippy_config::Conf;
+use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::macros::macro_backtrace;
 use clippy_utils::qualify_min_const_fn::is_min_const_fn;
 use clippy_utils::source::snippet;
 use clippy_utils::{fn_has_unsatisfiable_preds, peel_blocks};
 use rustc_errors::Applicability;
-use rustc_hir::{intravisit, Expr, ExprKind};
+use rustc_hir::{Expr, ExprKind, intravisit};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::impl_lint_pass;
 use rustc_span::sym::{self, thread_local_macro};
diff --git a/src/tools/clippy/clippy_lints/src/missing_doc.rs b/src/tools/clippy/clippy_lints/src/missing_doc.rs
index 2166b0fe5a06..64fc1a8a1a58 100644
--- a/src/tools/clippy/clippy_lints/src/missing_doc.rs
+++ b/src/tools/clippy/clippy_lints/src/missing_doc.rs
@@ -17,7 +17,7 @@ use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::ty::Visibility;
 use rustc_session::impl_lint_pass;
 use rustc_span::def_id::CRATE_DEF_ID;
-use rustc_span::{sym, Span};
+use rustc_span::{Span, sym};
 
 declare_clippy_lint! {
     /// ### What it does
diff --git a/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs b/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs
index 77595b121aad..fc01b6753f1c 100644
--- a/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs
+++ b/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs
@@ -3,7 +3,7 @@ use std::ops::ControlFlow;
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::is_path_lang_item;
 use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::visitors::{for_each_expr, Visitable};
+use clippy_utils::visitors::{Visitable, for_each_expr};
 use rustc_ast::LitKind;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_hir::def::{DefKind, Res};
@@ -13,7 +13,7 @@ use rustc_hir::{
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::{Ty, TypeckResults};
 use rustc_session::declare_lint_pass;
-use rustc_span::{sym, Span, Symbol};
+use rustc_span::{Span, Symbol, sym};
 
 declare_clippy_lint! {
     /// ### What it does
diff --git a/src/tools/clippy/clippy_lints/src/missing_inline.rs b/src/tools/clippy/clippy_lints/src/missing_inline.rs
index 33a14d8b7fed..d342be4545ce 100644
--- a/src/tools/clippy/clippy_lints/src/missing_inline.rs
+++ b/src/tools/clippy/clippy_lints/src/missing_inline.rs
@@ -3,7 +3,7 @@ use rustc_ast::ast;
 use rustc_hir as hir;
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_session::declare_lint_pass;
-use rustc_span::{sym, Span};
+use rustc_span::{Span, sym};
 
 declare_clippy_lint! {
     /// ### What it does
diff --git a/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs b/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs
index 0b3769ecb7cc..d333b71edb1a 100644
--- a/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs
+++ b/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs
@@ -1,6 +1,7 @@
 use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
+use clippy_utils::macros::root_macro_call_first_node;
 use clippy_utils::{get_parent_expr, path_to_local, path_to_local_id};
-use rustc_hir::intravisit::{walk_expr, Visitor};
+use rustc_hir::intravisit::{Visitor, walk_expr};
 use rustc_hir::{BinOpKind, Block, Expr, ExprKind, HirId, LetStmt, Node, Stmt, StmtKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty;
@@ -134,6 +135,11 @@ impl<'a, 'tcx> DivergenceVisitor<'a, 'tcx> {
     }
 
     fn report_diverging_sub_expr(&mut self, e: &Expr<'_>) {
+        if let Some(macro_call) = root_macro_call_first_node(self.cx, e) {
+            if self.cx.tcx.item_name(macro_call.def_id).as_str() == "todo" {
+                return;
+            }
+        }
         span_lint(self.cx, DIVERGING_SUB_EXPRESSION, e.span, "sub-expression diverges");
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs b/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs
index 0bde0da3cd81..12bcc608174f 100644
--- a/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs
+++ b/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::visitors::{for_each_expr, Descend, Visitable};
+use clippy_utils::visitors::{Descend, Visitable, for_each_expr};
 use core::ops::ControlFlow::Continue;
 use hir::def::{DefKind, Res};
 use hir::{BlockCheckMode, ExprKind, QPath, Safety, UnOp};
@@ -153,19 +153,16 @@ fn collect_unsafe_exprs<'tcx>(
             ExprKind::AssignOp(_, lhs, rhs) | ExprKind::Assign(lhs, rhs, _) => {
                 if matches!(
                     lhs.kind,
-                    ExprKind::Path(QPath::Resolved(
-                        _,
-                        hir::Path {
-                            res: Res::Def(
-                                DefKind::Static {
-                                    mutability: Mutability::Mut,
-                                    ..
-                                },
-                                _
-                            ),
-                            ..
-                        }
-                    ))
+                    ExprKind::Path(QPath::Resolved(_, hir::Path {
+                        res: Res::Def(
+                            DefKind::Static {
+                                mutability: Mutability::Mut,
+                                ..
+                            },
+                            _
+                        ),
+                        ..
+                    }))
                 ) {
                     unsafe_ops.push(("modification of a mutable static occurs here", expr.span));
                     collect_unsafe_exprs(cx, rhs, unsafe_ops);
diff --git a/src/tools/clippy/clippy_lints/src/mut_key.rs b/src/tools/clippy/clippy_lints/src/mut_key.rs
index f52b3a6a5a16..8118c14bd4a2 100644
--- a/src/tools/clippy/clippy_lints/src/mut_key.rs
+++ b/src/tools/clippy/clippy_lints/src/mut_key.rs
@@ -6,9 +6,9 @@ use rustc_hir as hir;
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_session::impl_lint_pass;
+use rustc_span::Span;
 use rustc_span::def_id::LocalDefId;
 use rustc_span::symbol::sym;
-use rustc_span::Span;
 use std::iter;
 
 declare_clippy_lint! {
diff --git a/src/tools/clippy/clippy_lints/src/mutable_debug_assertion.rs b/src/tools/clippy/clippy_lints/src/mutable_debug_assertion.rs
index e92ba93942ef..785bf70a3ec9 100644
--- a/src/tools/clippy/clippy_lints/src/mutable_debug_assertion.rs
+++ b/src/tools/clippy/clippy_lints/src/mutable_debug_assertion.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint;
 use clippy_utils::macros::{find_assert_eq_args, root_macro_call_first_node};
-use rustc_hir::intravisit::{walk_expr, Visitor};
+use rustc_hir::intravisit::{Visitor, walk_expr};
 use rustc_hir::{BorrowKind, Expr, ExprKind, MatchSource, Mutability};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::hir::nested_filter;
diff --git a/src/tools/clippy/clippy_lints/src/needless_arbitrary_self_type.rs b/src/tools/clippy/clippy_lints/src/needless_arbitrary_self_type.rs
index 60c44382059a..3c47d0edfdc5 100644
--- a/src/tools/clippy/clippy_lints/src/needless_arbitrary_self_type.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_arbitrary_self_type.rs
@@ -3,8 +3,8 @@ use rustc_ast::ast::{BindingMode, ByRef, Lifetime, Mutability, Param, PatKind, P
 use rustc_errors::Applicability;
 use rustc_lint::{EarlyContext, EarlyLintPass};
 use rustc_session::declare_lint_pass;
-use rustc_span::symbol::kw;
 use rustc_span::Span;
+use rustc_span::symbol::kw;
 
 declare_clippy_lint! {
     /// ### What it does
diff --git a/src/tools/clippy/clippy_lints/src/needless_bool.rs b/src/tools/clippy/clippy_lints/src/needless_bool.rs
index df155a7a4125..2eacd6875d6b 100644
--- a/src/tools/clippy/clippy_lints/src/needless_bool.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_bool.rs
@@ -2,16 +2,16 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
 use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::sugg::Sugg;
 use clippy_utils::{
-    get_parent_expr, higher, is_block_like, is_else_clause, is_expn_of, is_parent_stmt, is_receiver_of_method_call,
-    peel_blocks, peel_blocks_with_stmt, span_extract_comment, SpanlessEq,
+    SpanlessEq, get_parent_expr, higher, is_block_like, is_else_clause, is_expn_of, is_parent_stmt,
+    is_receiver_of_method_call, peel_blocks, peel_blocks_with_stmt, span_extract_comment,
 };
 use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
 use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::declare_lint_pass;
-use rustc_span::source_map::Spanned;
 use rustc_span::Span;
+use rustc_span::source_map::Spanned;
 
 declare_clippy_lint! {
     /// ### What it does
diff --git a/src/tools/clippy/clippy_lints/src/needless_borrows_for_generic_args.rs b/src/tools/clippy/clippy_lints/src/needless_borrows_for_generic_args.rs
index 32e7fde03b2c..f6db12ed84e8 100644
--- a/src/tools/clippy/clippy_lints/src/needless_borrows_for_generic_args.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_borrows_for_generic_args.rs
@@ -1,10 +1,10 @@
-use clippy_config::msrvs::{self, Msrv};
 use clippy_config::Conf;
+use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::mir::{enclosing_mir, expr_local, local_assignments, used_exactly_once, PossibleBorrowerMap};
+use clippy_utils::mir::{PossibleBorrowerMap, enclosing_mir, expr_local, local_assignments, used_exactly_once};
 use clippy_utils::source::snippet_with_context;
 use clippy_utils::ty::{implements_trait, is_copy};
-use clippy_utils::{expr_use_ctxt, peel_n_hir_expr_refs, DefinedTy, ExprUseNode};
+use clippy_utils::{DefinedTy, ExprUseNode, expr_use_ctxt, peel_n_hir_expr_refs};
 use rustc_errors::Applicability;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::{DefId, LocalDefId};
diff --git a/src/tools/clippy/clippy_lints/src/needless_for_each.rs b/src/tools/clippy/clippy_lints/src/needless_for_each.rs
index 6390e51f916b..b54eb164e81d 100644
--- a/src/tools/clippy/clippy_lints/src/needless_for_each.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_for_each.rs
@@ -1,9 +1,9 @@
 use rustc_errors::Applicability;
-use rustc_hir::intravisit::{walk_expr, Visitor};
+use rustc_hir::intravisit::{Visitor, walk_expr};
 use rustc_hir::{Block, BlockCheckMode, Closure, Expr, ExprKind, Stmt, StmtKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::declare_lint_pass;
-use rustc_span::{sym, Span};
+use rustc_span::{Span, sym};
 
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::is_trait_method;
diff --git a/src/tools/clippy/clippy_lints/src/needless_maybe_sized.rs b/src/tools/clippy/clippy_lints/src/needless_maybe_sized.rs
index 62e41088f370..bb44ff37b203 100644
--- a/src/tools/clippy/clippy_lints/src/needless_maybe_sized.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_maybe_sized.rs
@@ -26,7 +26,7 @@ declare_clippy_lint! {
     ///
     /// // or choose alternative bounds for `T` so that it can be unsized
     /// ```
-    #[clippy::version = "1.79.0"]
+    #[clippy::version = "1.81.0"]
     pub NEEDLESS_MAYBE_SIZED,
     suspicious,
     "a `?Sized` bound that is unusable due to a `Sized` requirement"
diff --git a/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs b/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs
index d543fd467abe..19cbf5959084 100644
--- a/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs
@@ -17,9 +17,9 @@ use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::mir::FakeReadCause;
 use rustc_middle::ty::{self, Ty, TyCtxt, UpvarId, UpvarPath};
 use rustc_session::impl_lint_pass;
+use rustc_span::Span;
 use rustc_span::def_id::LocalDefId;
 use rustc_span::symbol::kw;
-use rustc_span::Span;
 use rustc_target::spec::abi::Abi;
 
 declare_clippy_lint! {
diff --git a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs
index addb4b1aee80..0775d7abdbb3 100644
--- a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::is_self;
 use clippy_utils::ptr::get_spans;
-use clippy_utils::source::{snippet, SpanRangeExt};
+use clippy_utils::source::{SpanRangeExt, snippet};
 use clippy_utils::ty::{
     implements_trait, implements_trait_with_env_from_iter, is_copy, is_type_diagnostic_item, is_type_lang_item,
 };
@@ -19,7 +19,7 @@ use rustc_middle::ty::{self, Ty, TypeVisitableExt};
 use rustc_session::declare_lint_pass;
 use rustc_span::def_id::LocalDefId;
 use rustc_span::symbol::kw;
-use rustc_span::{sym, Span};
+use rustc_span::{Span, sym};
 use rustc_target::spec::abi::Abi;
 use rustc_trait_selection::traits;
 use rustc_trait_selection::traits::misc::type_allowed_to_implement_copy;
@@ -129,7 +129,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
             })
             .collect::>();
 
-        // Collect moved variables and spans which will need dereferencings from the
+        // Collect moved variables and spans which will need dereferencing from the
         // function body.
         let MovedVariablesCtxt { moved_vars } = {
             let mut ctx = MovedVariablesCtxt::default();
@@ -148,12 +148,13 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
                 return;
             }
 
-            // Ignore `self`s.
-            if idx == 0 {
-                if let PatKind::Binding(.., ident, _) = arg.pat.kind {
-                    if ident.name == kw::SelfLower {
-                        continue;
-                    }
+            // Ignore `self`s and params whose variable name starts with an underscore
+            if let PatKind::Binding(.., ident, _) = arg.pat.kind {
+                if idx == 0 && ident.name == kw::SelfLower {
+                    continue;
+                }
+                if ident.name.as_str().starts_with('_') {
+                    continue;
                 }
             }
 
@@ -181,14 +182,9 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
                 && !is_copy(cx, ty)
                 && ty.is_sized(cx.tcx, cx.param_env)
                 && !allowed_traits.iter().any(|&t| {
-                    implements_trait_with_env_from_iter(
-                        cx.tcx,
-                        cx.param_env,
-                        ty,
-                        t,
-                        None,
-                        [Option::>::None],
-                    )
+                    implements_trait_with_env_from_iter(cx.tcx, cx.param_env, ty, t, None, [Option::<
+                        ty::GenericArg<'tcx>,
+                    >::None])
                 })
                 && !implements_borrow_trait
                 && !all_borrowable_trait
@@ -207,7 +203,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
                             )
                             .is_ok()
                             {
-                                diag.span_help(span, "consider marking this type as `Copy`");
+                                diag.span_help(span, "or consider marking this type as `Copy`");
                             }
                         }
                     }
diff --git a/src/tools/clippy/clippy_lints/src/no_effect.rs b/src/tools/clippy/clippy_lints/src/no_effect.rs
index b181791699a5..392cfcb813e8 100644
--- a/src/tools/clippy/clippy_lints/src/no_effect.rs
+++ b/src/tools/clippy/clippy_lints/src/no_effect.rs
@@ -7,8 +7,8 @@ use clippy_utils::{
 use rustc_errors::Applicability;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::{
-    is_range_literal, BinOpKind, BlockCheckMode, Expr, ExprKind, HirId, HirIdMap, ItemKind, LocalSource, Node, PatKind,
-    Stmt, StmtKind, UnsafeSource,
+    BinOpKind, BlockCheckMode, Expr, ExprKind, HirId, HirIdMap, ItemKind, LocalSource, Node, PatKind, Stmt, StmtKind,
+    UnsafeSource, is_range_literal,
 };
 use rustc_infer::infer::TyCtxtInferExt as _;
 use rustc_lint::{LateContext, LateLintPass, LintContext};
diff --git a/src/tools/clippy/clippy_lints/src/non_copy_const.rs b/src/tools/clippy/clippy_lints/src/non_copy_const.rs
index 25f547f1a43c..5e20b4064260 100644
--- a/src/tools/clippy/clippy_lints/src/non_copy_const.rs
+++ b/src/tools/clippy/clippy_lints/src/non_copy_const.rs
@@ -4,7 +4,7 @@ use clippy_config::Conf;
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::is_in_const_context;
 use clippy_utils::macros::macro_backtrace;
-use clippy_utils::ty::{implements_trait, InteriorMut};
+use clippy_utils::ty::{InteriorMut, implements_trait};
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::DefId;
 use rustc_hir::{
@@ -15,7 +15,7 @@ use rustc_middle::mir::interpret::{ErrorHandled, EvalToValTreeResult, GlobalId};
 use rustc_middle::ty::adjustment::Adjust;
 use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_session::impl_lint_pass;
-use rustc_span::{sym, Span, DUMMY_SP};
+use rustc_span::{DUMMY_SP, Span, sym};
 use rustc_target::abi::VariantIdx;
 
 // FIXME: this is a correctness problem but there's no suitable
diff --git a/src/tools/clippy/clippy_lints/src/non_expressive_names.rs b/src/tools/clippy/clippy_lints/src/non_expressive_names.rs
index 832518d2d35b..d85032e9eee8 100644
--- a/src/tools/clippy/clippy_lints/src/non_expressive_names.rs
+++ b/src/tools/clippy/clippy_lints/src/non_expressive_names.rs
@@ -3,12 +3,12 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
 use rustc_ast::ast::{
     self, Arm, AssocItem, AssocItemKind, Attribute, Block, FnDecl, Item, ItemKind, Local, Pat, PatKind,
 };
-use rustc_ast::visit::{walk_block, walk_expr, walk_pat, Visitor};
+use rustc_ast::visit::{Visitor, walk_block, walk_expr, walk_pat};
 use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
 use rustc_session::impl_lint_pass;
 use rustc_span::symbol::{Ident, Symbol};
-use rustc_span::{sym, Span};
+use rustc_span::{Span, sym};
 use std::cmp::Ordering;
 
 declare_clippy_lint! {
diff --git a/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs b/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs
index cfc15d927158..3f156aa55104 100644
--- a/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs
+++ b/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::{snippet_with_applicability, SpanRangeExt};
+use clippy_utils::source::{SpanRangeExt, snippet_with_applicability};
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
diff --git a/src/tools/clippy/clippy_lints/src/non_zero_suggestions.rs b/src/tools/clippy/clippy_lints/src/non_zero_suggestions.rs
new file mode 100644
index 000000000000..90a9f2e994b8
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/non_zero_suggestions.rs
@@ -0,0 +1,143 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet;
+use rustc_ast::ast::BinOpKind;
+use rustc_errors::Applicability;
+use rustc_hir::{Expr, ExprKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty::{self, Ty};
+use rustc_session::declare_lint_pass;
+use rustc_span::symbol::sym;
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for conversions from `NonZero` types to regular integer types,
+    /// and suggests using `NonZero` types for the target as well.
+    ///
+    /// ### Why is this bad?
+    /// Converting from `NonZero` types to regular integer types and then back to `NonZero`
+    /// types is less efficient and loses the type-safety guarantees provided by `NonZero` types.
+    /// Using `NonZero` types consistently can lead to more optimized code and prevent
+    /// certain classes of errors related to zero values.
+    ///
+    /// ### Example
+    /// ```no_run
+    /// use std::num::{NonZeroU32, NonZeroU64};
+    ///
+    /// fn example(x: u64, y: NonZeroU32) {
+    ///     // Bad: Converting NonZeroU32 to u64 unnecessarily
+    ///     let r1 = x / u64::from(y.get());
+    ///     let r2 = x % u64::from(y.get());
+    /// }
+    /// ```
+    /// Use instead:
+    /// ```no_run
+    /// use std::num::{NonZeroU32, NonZeroU64};
+    ///
+    /// fn example(x: u64, y: NonZeroU32) {
+    ///     // Good: Preserving the NonZero property
+    ///     let r1 = x / NonZeroU64::from(y);
+    ///     let r2 = x % NonZeroU64::from(y);
+    /// }
+    /// ```
+    #[clippy::version = "1.81.0"]
+    pub NON_ZERO_SUGGESTIONS,
+    restriction,
+    "suggests using `NonZero#` from `u#` or `i#` for more efficient and type-safe conversions"
+}
+
+declare_lint_pass!(NonZeroSuggestions => [NON_ZERO_SUGGESTIONS]);
+
+impl<'tcx> LateLintPass<'tcx> for NonZeroSuggestions {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
+        if let ExprKind::Binary(op, _, rhs) = expr.kind
+            && matches!(op.node, BinOpKind::Div | BinOpKind::Rem)
+        {
+            check_non_zero_conversion(cx, rhs, Applicability::MachineApplicable);
+        } else {
+            // Check if the parent expression is a binary operation
+            let parent_is_binary = cx.tcx.hir().parent_iter(expr.hir_id).any(|(_, node)| {
+                matches!(node, rustc_hir::Node::Expr(parent_expr) if matches!(parent_expr.kind, ExprKind::Binary(..)))
+            });
+
+            if !parent_is_binary {
+                check_non_zero_conversion(cx, expr, Applicability::MaybeIncorrect);
+            }
+        }
+    }
+}
+
+fn check_non_zero_conversion(cx: &LateContext<'_>, expr: &Expr<'_>, applicability: Applicability) {
+    // Check if the expression is a function call with one argument
+    if let ExprKind::Call(func, [arg]) = expr.kind
+        && let ExprKind::Path(qpath) = &func.kind
+        && let Some(def_id) = cx.qpath_res(qpath, func.hir_id).opt_def_id()
+        && let ExprKind::MethodCall(rcv_path, receiver, _, _) = &arg.kind
+        && rcv_path.ident.name.as_str() == "get"
+    {
+        let fn_name = cx.tcx.item_name(def_id);
+        let target_ty = cx.typeck_results().expr_ty(expr);
+        let receiver_ty = cx.typeck_results().expr_ty(receiver);
+
+        // Check if the receiver type is a NonZero type
+        if let ty::Adt(adt_def, _) = receiver_ty.kind()
+            && adt_def.is_struct()
+            && cx.tcx.get_diagnostic_name(adt_def.did()) == Some(sym::NonZero)
+        {
+            if let Some(target_non_zero_type) = get_target_non_zero_type(target_ty) {
+                let arg_snippet = get_arg_snippet(cx, arg, rcv_path);
+                suggest_non_zero_conversion(cx, expr, fn_name, target_non_zero_type, &arg_snippet, applicability);
+            }
+        }
+    }
+}
+
+fn get_arg_snippet(cx: &LateContext<'_>, arg: &Expr<'_>, rcv_path: &rustc_hir::PathSegment<'_>) -> String {
+    let arg_snippet = snippet(cx, arg.span, "..");
+    if let Some(index) = arg_snippet.rfind(&format!(".{}", rcv_path.ident.name)) {
+        arg_snippet[..index].trim().to_string()
+    } else {
+        arg_snippet.to_string()
+    }
+}
+
+fn suggest_non_zero_conversion(
+    cx: &LateContext<'_>,
+    expr: &Expr<'_>,
+    fn_name: rustc_span::Symbol,
+    target_non_zero_type: &str,
+    arg_snippet: &str,
+    applicability: Applicability,
+) {
+    let suggestion = format!("{target_non_zero_type}::{fn_name}({arg_snippet})");
+    span_lint_and_sugg(
+        cx,
+        NON_ZERO_SUGGESTIONS,
+        expr.span,
+        format!("consider using `{target_non_zero_type}::{fn_name}()` for more efficient and type-safe conversion"),
+        "replace with",
+        suggestion,
+        applicability,
+    );
+}
+
+fn get_target_non_zero_type(ty: Ty<'_>) -> Option<&'static str> {
+    match ty.kind() {
+        ty::Uint(uint_ty) => Some(match uint_ty {
+            ty::UintTy::U8 => "NonZeroU8",
+            ty::UintTy::U16 => "NonZeroU16",
+            ty::UintTy::U32 => "NonZeroU32",
+            ty::UintTy::U64 => "NonZeroU64",
+            ty::UintTy::U128 => "NonZeroU128",
+            ty::UintTy::Usize => "NonZeroUsize",
+        }),
+        ty::Int(int_ty) => Some(match int_ty {
+            ty::IntTy::I8 => "NonZeroI8",
+            ty::IntTy::I16 => "NonZeroI16",
+            ty::IntTy::I32 => "NonZeroI32",
+            ty::IntTy::I64 => "NonZeroI64",
+            ty::IntTy::I128 => "NonZeroI128",
+            ty::IntTy::Isize => "NonZeroIsize",
+        }),
+        _ => None,
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/nonstandard_macro_braces.rs b/src/tools/clippy/clippy_lints/src/nonstandard_macro_braces.rs
index 51ba29d7389b..a13391a59457 100644
--- a/src/tools/clippy/clippy_lints/src/nonstandard_macro_braces.rs
+++ b/src/tools/clippy/clippy_lints/src/nonstandard_macro_braces.rs
@@ -1,5 +1,5 @@
-use clippy_config::types::MacroMatcher;
 use clippy_config::Conf;
+use clippy_config::types::MacroMatcher;
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::{SourceText, SpanRangeExt};
 use rustc_ast::ast;
@@ -8,8 +8,8 @@ use rustc_errors::Applicability;
 use rustc_hir::def_id::DefId;
 use rustc_lint::{EarlyContext, EarlyLintPass};
 use rustc_session::impl_lint_pass;
-use rustc_span::hygiene::{ExpnKind, MacroKind};
 use rustc_span::Span;
+use rustc_span::hygiene::{ExpnKind, MacroKind};
 
 declare_clippy_lint! {
     /// ### What it does
diff --git a/src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs b/src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs
index aadd729f32a4..372128ac16f0 100644
--- a/src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs
+++ b/src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs
@@ -9,8 +9,8 @@ use rustc_hir::{Body, Expr, ExprKind, HirId, ImplItem, ImplItemKind, Node, PatKi
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::{self, ConstKind, EarlyBinder, GenericArgKind, GenericArgsRef};
 use rustc_session::impl_lint_pass;
-use rustc_span::symbol::{kw, Ident};
 use rustc_span::Span;
+use rustc_span::symbol::{Ident, kw};
 use std::iter;
 
 declare_clippy_lint! {
diff --git a/src/tools/clippy/clippy_lints/src/operators/absurd_extreme_comparisons.rs b/src/tools/clippy/clippy_lints/src/operators/absurd_extreme_comparisons.rs
index a0de5ea711ca..dbc9948eeedc 100644
--- a/src/tools/clippy/clippy_lints/src/operators/absurd_extreme_comparisons.rs
+++ b/src/tools/clippy/clippy_lints/src/operators/absurd_extreme_comparisons.rs
@@ -2,7 +2,7 @@ use rustc_hir::{BinOpKind, Expr, ExprKind};
 use rustc_lint::LateContext;
 use rustc_middle::ty;
 
-use clippy_utils::comparisons::{normalize_comparison, Rel};
+use clippy_utils::comparisons::{Rel, normalize_comparison};
 use clippy_utils::consts::{ConstEvalCtxt, Constant};
 use clippy_utils::diagnostics::span_lint_and_help;
 use clippy_utils::source::snippet;
diff --git a/src/tools/clippy/clippy_lints/src/operators/const_comparisons.rs b/src/tools/clippy/clippy_lints/src/operators/const_comparisons.rs
index c13175243962..5d94cfab3b02 100644
--- a/src/tools/clippy/clippy_lints/src/operators/const_comparisons.rs
+++ b/src/tools/clippy/clippy_lints/src/operators/const_comparisons.rs
@@ -7,12 +7,12 @@ use rustc_hir::{BinOpKind, Expr, ExprKind};
 use rustc_lint::LateContext;
 use rustc_middle::ty::layout::HasTyCtxt;
 use rustc_middle::ty::{Ty, TypeckResults};
-use rustc_span::source_map::Spanned;
 use rustc_span::Span;
+use rustc_span::source_map::Spanned;
 
+use clippy_utils::SpanlessEq;
 use clippy_utils::diagnostics::span_lint_and_note;
 use clippy_utils::source::snippet;
-use clippy_utils::SpanlessEq;
 
 use super::{IMPOSSIBLE_COMPARISONS, REDUNDANT_COMPARISONS};
 
diff --git a/src/tools/clippy/clippy_lints/src/operators/float_equality_without_abs.rs b/src/tools/clippy/clippy_lints/src/operators/float_equality_without_abs.rs
index be97ad389bf6..34f7dbea84e4 100644
--- a/src/tools/clippy/clippy_lints/src/operators/float_equality_without_abs.rs
+++ b/src/tools/clippy/clippy_lints/src/operators/float_equality_without_abs.rs
@@ -6,7 +6,8 @@ use rustc_hir::def::{DefKind, Res};
 use rustc_hir::{BinOpKind, Expr, ExprKind};
 use rustc_lint::LateContext;
 use rustc_middle::ty;
-use rustc_span::{sym, source_map::Spanned};
+use rustc_span::source_map::Spanned;
+use rustc_span::sym;
 
 use super::FLOAT_EQUALITY_WITHOUT_ABS;
 
diff --git a/src/tools/clippy/clippy_lints/src/option_if_let_else.rs b/src/tools/clippy/clippy_lints/src/option_if_let_else.rs
index ae02d22512e3..6d9e75f51d66 100644
--- a/src/tools/clippy/clippy_lints/src/option_if_let_else.rs
+++ b/src/tools/clippy/clippy_lints/src/option_if_let_else.rs
@@ -1,12 +1,12 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::sugg::Sugg;
 use clippy_utils::{
-    can_move_expr_to_closure, eager_or_lazy, higher, is_else_clause, is_in_const_context, is_res_lang_ctor,
-    peel_blocks, peel_hir_expr_while, CaptureKind,
+    CaptureKind, can_move_expr_to_closure, eager_or_lazy, higher, is_else_clause, is_in_const_context,
+    is_res_lang_ctor, peel_blocks, peel_hir_expr_while,
 };
 use rustc_errors::Applicability;
-use rustc_hir::def::Res;
 use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk};
+use rustc_hir::def::Res;
 use rustc_hir::{Arm, BindingMode, Expr, ExprKind, MatchSource, Mutability, Pat, PatKind, Path, QPath, UnOp};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::declare_lint_pass;
diff --git a/src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs b/src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs
index 381975199d28..eebc62e2a5a9 100644
--- a/src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs
+++ b/src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs
@@ -1,15 +1,15 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::macros::root_macro_call_first_node;
-use clippy_utils::return_ty;
 use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::visitors::{for_each_expr, Descend};
+use clippy_utils::visitors::{Descend, for_each_expr};
+use clippy_utils::{is_inside_always_const_context, return_ty};
 use core::ops::ControlFlow;
 use rustc_hir as hir;
 use rustc_hir::intravisit::FnKind;
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::declare_lint_pass;
 use rustc_span::def_id::LocalDefId;
-use rustc_span::{sym, Span};
+use rustc_span::{Span, sym};
 
 declare_clippy_lint! {
     /// ### What it does
@@ -68,10 +68,12 @@ fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, body: &'tcx hir
         let Some(macro_call) = root_macro_call_first_node(cx, e) else {
             return ControlFlow::Continue(Descend::Yes);
         };
-        if matches!(
-            cx.tcx.item_name(macro_call.def_id).as_str(),
-            "panic" | "assert" | "assert_eq" | "assert_ne"
-        ) {
+        if !is_inside_always_const_context(cx.tcx, e.hir_id)
+            && matches!(
+                cx.tcx.item_name(macro_call.def_id).as_str(),
+                "panic" | "assert" | "assert_eq" | "assert_ne"
+            )
+        {
             panics.push(macro_call.span);
             ControlFlow::Continue(Descend::No)
         } else {
diff --git a/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs b/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs
index 4eefd0065f65..fa5b02a5a41b 100644
--- a/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs
+++ b/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs
@@ -1,8 +1,9 @@
 use clippy_config::Conf;
 use clippy_utils::diagnostics::span_lint;
-use clippy_utils::is_in_test;
 use clippy_utils::macros::{is_panic, root_macro_call_first_node};
-use rustc_hir::Expr;
+use clippy_utils::{is_in_test, match_def_path, paths};
+use rustc_hir::def::{DefKind, Res};
+use rustc_hir::{Expr, ExprKind, QPath};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::impl_lint_pass;
 
@@ -95,10 +96,49 @@ impl_lint_pass!(PanicUnimplemented => [UNIMPLEMENTED, UNREACHABLE, TODO, PANIC])
 
 impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        let Some(macro_call) = root_macro_call_first_node(cx, expr) else {
-            return;
-        };
-        if is_panic(cx, macro_call.def_id) {
+        if let Some(macro_call) = root_macro_call_first_node(cx, expr) {
+            if is_panic(cx, macro_call.def_id) {
+                if cx.tcx.hir().is_inside_const_context(expr.hir_id)
+                    || self.allow_panic_in_tests && is_in_test(cx.tcx, expr.hir_id)
+                {
+                    return;
+                }
+
+                span_lint(
+                    cx,
+                    PANIC,
+                    macro_call.span,
+                    "`panic` should not be present in production code",
+                );
+                return;
+            }
+            match cx.tcx.item_name(macro_call.def_id).as_str() {
+                "todo" => {
+                    span_lint(
+                        cx,
+                        TODO,
+                        macro_call.span,
+                        "`todo` should not be present in production code",
+                    );
+                },
+                "unimplemented" => {
+                    span_lint(
+                        cx,
+                        UNIMPLEMENTED,
+                        macro_call.span,
+                        "`unimplemented` should not be present in production code",
+                    );
+                },
+                "unreachable" => {
+                    span_lint(cx, UNREACHABLE, macro_call.span, "usage of the `unreachable!` macro");
+                },
+                _ => {},
+            }
+        } else if let ExprKind::Call(func, [_]) = expr.kind
+            && let ExprKind::Path(QPath::Resolved(None, expr_path)) = func.kind
+            && let Res::Def(DefKind::Fn, def_id) = expr_path.res
+            && match_def_path(cx, def_id, &paths::PANIC_ANY)
+        {
             if cx.tcx.hir().is_inside_const_context(expr.hir_id)
                 || self.allow_panic_in_tests && is_in_test(cx.tcx, expr.hir_id)
             {
@@ -108,32 +148,10 @@ impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented {
             span_lint(
                 cx,
                 PANIC,
-                macro_call.span,
-                "`panic` should not be present in production code",
+                expr.span,
+                "`panic_any` should not be present in production code",
             );
             return;
         }
-        match cx.tcx.item_name(macro_call.def_id).as_str() {
-            "todo" => {
-                span_lint(
-                    cx,
-                    TODO,
-                    macro_call.span,
-                    "`todo` should not be present in production code",
-                );
-            },
-            "unimplemented" => {
-                span_lint(
-                    cx,
-                    UNIMPLEMENTED,
-                    macro_call.span,
-                    "`unimplemented` should not be present in production code",
-                );
-            },
-            "unreachable" => {
-                span_lint(cx, UNREACHABLE, macro_call.span, "usage of the `unreachable!` macro");
-            },
-            _ => {},
-        }
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs b/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs
index 5ca244f0141c..75d8c09f2b08 100644
--- a/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs
+++ b/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs
@@ -18,7 +18,7 @@ use rustc_middle::ty::layout::LayoutOf;
 use rustc_middle::ty::{self, RegionKind, TyCtxt};
 use rustc_session::impl_lint_pass;
 use rustc_span::def_id::LocalDefId;
-use rustc_span::{sym, Span};
+use rustc_span::{Span, sym};
 use rustc_target::spec::abi::Abi;
 
 declare_clippy_lint! {
diff --git a/src/tools/clippy/clippy_lints/src/pathbuf_init_then_push.rs b/src/tools/clippy/clippy_lints/src/pathbuf_init_then_push.rs
index d7fa48c1e38d..1b9a5a443829 100644
--- a/src/tools/clippy/clippy_lints/src/pathbuf_init_then_push.rs
+++ b/src/tools/clippy/clippy_lints/src/pathbuf_init_then_push.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::path_to_local_id;
-use clippy_utils::source::{snippet, SpanRangeExt};
+use clippy_utils::source::{SpanRangeExt, snippet};
 use clippy_utils::ty::is_type_diagnostic_item;
 use rustc_ast::{LitKind, StrStyle};
 use rustc_errors::Applicability;
@@ -9,7 +9,7 @@ use rustc_hir::{BindingMode, Block, Expr, ExprKind, HirId, LetStmt, PatKind, QPa
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
 use rustc_session::impl_lint_pass;
-use rustc_span::{sym, Span, Symbol};
+use rustc_span::{Span, Symbol, sym};
 
 declare_clippy_lint! {
     /// ### What it does
diff --git a/src/tools/clippy/clippy_lints/src/pattern_type_mismatch.rs b/src/tools/clippy/clippy_lints/src/pattern_type_mismatch.rs
index c1296b04387a..42fbba8ef6dc 100644
--- a/src/tools/clippy/clippy_lints/src/pattern_type_mismatch.rs
+++ b/src/tools/clippy/clippy_lints/src/pattern_type_mismatch.rs
@@ -1,13 +1,13 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use rustc_hir::{
-    intravisit, Body, Expr, ExprKind, FnDecl, LetExpr, LocalSource, Mutability, Pat, PatKind, Stmt, StmtKind,
+    Body, Expr, ExprKind, FnDecl, LetExpr, LocalSource, Mutability, Pat, PatKind, Stmt, StmtKind, intravisit,
 };
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
 use rustc_middle::ty;
 use rustc_session::declare_lint_pass;
-use rustc_span::def_id::LocalDefId;
 use rustc_span::Span;
+use rustc_span::def_id::LocalDefId;
 
 declare_clippy_lint! {
     /// ### What it does
diff --git a/src/tools/clippy/clippy_lints/src/pointers_in_nomem_asm_block.rs b/src/tools/clippy/clippy_lints/src/pointers_in_nomem_asm_block.rs
new file mode 100644
index 000000000000..385f634a1505
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/pointers_in_nomem_asm_block.rs
@@ -0,0 +1,88 @@
+use clippy_utils::diagnostics::span_lint_and_then;
+use rustc_ast::InlineAsmOptions;
+use rustc_hir::{Expr, ExprKind, InlineAsm, InlineAsmOperand};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::declare_lint_pass;
+use rustc_span::Span;
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks if any pointer is being passed to an asm! block with `nomem` option.
+    ///
+    /// ### Why is this bad?
+    /// `nomem` forbids any reads or writes to memory and passing a pointer suggests
+    /// that either of those will happen.
+    ///
+    /// ### Example
+    /// ```no_run
+    /// fn f(p: *mut u32) {
+    ///     unsafe { core::arch::asm!("mov [{p}], 42", p = in(reg) p, options(nomem, nostack)); }
+    /// }
+    /// ```
+    /// Use instead:
+    /// ```no_run
+    /// fn f(p: *mut u32) {
+    ///     unsafe { core::arch::asm!("mov [{p}], 42", p = in(reg) p, options(nostack)); }
+    /// }
+    /// ```
+    #[clippy::version = "1.81.0"]
+    pub POINTERS_IN_NOMEM_ASM_BLOCK,
+    suspicious,
+    "pointers in nomem asm block"
+}
+
+declare_lint_pass!(PointersInNomemAsmBlock => [POINTERS_IN_NOMEM_ASM_BLOCK]);
+
+impl<'tcx> LateLintPass<'tcx> for PointersInNomemAsmBlock {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
+        if let ExprKind::InlineAsm(asm) = &expr.kind {
+            check_asm(cx, asm);
+        }
+    }
+}
+
+fn check_asm(cx: &LateContext<'_>, asm: &InlineAsm<'_>) {
+    if !asm.options.contains(InlineAsmOptions::NOMEM) {
+        return;
+    }
+
+    let spans = asm
+        .operands
+        .iter()
+        .filter(|(op, _span)| has_in_operand_pointer(cx, op))
+        .map(|(_op, span)| *span)
+        .collect::>();
+
+    if spans.is_empty() {
+        return;
+    }
+
+    span_lint_and_then(
+        cx,
+        POINTERS_IN_NOMEM_ASM_BLOCK,
+        spans,
+        "passing pointers to nomem asm block",
+        additional_notes,
+    );
+}
+
+fn has_in_operand_pointer(cx: &LateContext<'_>, asm_op: &InlineAsmOperand<'_>) -> bool {
+    let asm_in_expr = match asm_op {
+        InlineAsmOperand::SymStatic { .. }
+        | InlineAsmOperand::Out { .. }
+        | InlineAsmOperand::Const { .. }
+        | InlineAsmOperand::SymFn { .. }
+        | InlineAsmOperand::Label { .. } => return false,
+        InlineAsmOperand::SplitInOut { in_expr, .. } => in_expr,
+        InlineAsmOperand::In { expr, .. } | InlineAsmOperand::InOut { expr, .. } => expr,
+    };
+
+    // This checks for raw ptrs, refs and function pointers - the last one
+    // also technically counts as reading memory.
+    cx.typeck_results().expr_ty(asm_in_expr).is_any_ptr()
+}
+
+fn additional_notes(diag: &mut rustc_errors::Diag<'_, ()>) {
+    diag.note("`nomem` means that no memory write or read happens inside the asm! block");
+    diag.note("if this is intentional and no pointers are read or written to, consider allowing the lint");
+}
diff --git a/src/tools/clippy/clippy_lints/src/ptr.rs b/src/tools/clippy/clippy_lints/src/ptr.rs
index 125f694996c1..807636bb642b 100644
--- a/src/tools/clippy/clippy_lints/src/ptr.rs
+++ b/src/tools/clippy/clippy_lints/src/ptr.rs
@@ -1,13 +1,11 @@
 use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then, span_lint_hir_and_then};
 use clippy_utils::source::SpanRangeExt;
-use clippy_utils::ty::expr_sig;
 use clippy_utils::visitors::contains_unsafe_block;
 use clippy_utils::{get_expr_use_or_unification_node, is_lint_allowed, path_def_id, path_to_local};
 use hir::LifetimeName;
 use rustc_errors::{Applicability, MultiSpan};
-use rustc_hir::def_id::DefId;
 use rustc_hir::hir_id::{HirId, HirIdMap};
-use rustc_hir::intravisit::{walk_expr, Visitor};
+use rustc_hir::intravisit::{Visitor, walk_expr};
 use rustc_hir::{
     self as hir, AnonConst, BinOpKind, BindingMode, Body, Expr, ExprKind, FnRetTy, FnSig, GenericArg, ImplItemKind,
     ItemKind, Lifetime, Mutability, Node, Param, PatKind, QPath, Safety, TraitFn, TraitItem, TraitItemKind, TyKind,
@@ -19,7 +17,7 @@ use rustc_middle::hir::nested_filter;
 use rustc_middle::ty::{self, Binder, ClauseKind, ExistentialPredicate, List, PredicateKind, Ty};
 use rustc_session::declare_lint_pass;
 use rustc_span::symbol::Symbol;
-use rustc_span::{sym, Span};
+use rustc_span::{Span, sym};
 use rustc_target::spec::abi::Abi;
 use rustc_trait_selection::infer::InferCtxtExt as _;
 use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
@@ -34,7 +32,7 @@ declare_clippy_lint! {
     /// with the appropriate `.to_owned()`/`to_string()` calls.
     ///
     /// ### Why is this bad?
-    /// Requiring the argument to be of the specific size
+    /// Requiring the argument to be of the specific type
     /// makes the function less useful for no benefit; slices in the form of `&[T]`
     /// or `&str` usually suffice and can be obtained from other types, too.
     ///
@@ -323,7 +321,6 @@ struct PtrArg<'tcx> {
     idx: usize,
     emission_id: HirId,
     span: Span,
-    ty_did: DefId,
     ty_name: Symbol,
     method_renames: &'static [(&'static str, &'static str)],
     ref_prefix: RefPrefix,
@@ -411,7 +408,6 @@ impl<'tcx> DerefTy<'tcx> {
     }
 }
 
-#[expect(clippy::too_many_lines)]
 fn check_fn_args<'cx, 'tcx: 'cx>(
     cx: &'cx LateContext<'tcx>,
     fn_sig: ty::FnSig<'tcx>,
@@ -514,7 +510,6 @@ fn check_fn_args<'cx, 'tcx: 'cx>(
                     idx: i,
                     emission_id,
                     span: hir_ty.span,
-                    ty_did: adt.did(),
                     ty_name: name.ident.name,
                     method_renames,
                     ref_prefix: RefPrefix { lt: *lt, mutability },
@@ -610,65 +605,50 @@ fn check_ptr_arg_usage<'tcx>(cx: &LateContext<'tcx>, body: &Body<'tcx>, args: &[
                         set_skip_flag();
                     }
                 },
-                Some((Node::Expr(e), child_id)) => match e.kind {
-                    ExprKind::Call(f, expr_args) => {
-                        let i = expr_args.iter().position(|arg| arg.hir_id == child_id).unwrap_or(0);
-                        if expr_sig(self.cx, f).and_then(|sig| sig.input(i)).map_or(true, |ty| {
-                            match *ty.skip_binder().peel_refs().kind() {
-                                ty::Dynamic(preds, _, _) => !matches_preds(self.cx, args.deref_ty.ty(self.cx), preds),
-                                ty::Param(_) => true,
-                                ty::Adt(def, _) => def.did() == args.ty_did,
-                                _ => false,
-                            }
-                        }) {
-                            // Passed to a function taking the non-dereferenced type.
-                            set_skip_flag();
-                        }
-                    },
-                    ExprKind::MethodCall(name, self_arg, expr_args, _) => {
-                        let i = iter::once(self_arg)
-                            .chain(expr_args.iter())
-                            .position(|arg| arg.hir_id == child_id)
-                            .unwrap_or(0);
-                        if i == 0 {
-                            // Check if the method can be renamed.
-                            let name = name.ident.as_str();
-                            if let Some((_, replacement)) = args.method_renames.iter().find(|&&(x, _)| x == name) {
-                                result.replacements.push(PtrArgReplacement {
-                                    expr_span: e.span,
-                                    self_span: self_arg.span,
-                                    replacement,
-                                });
-                                return;
-                            }
+                Some((Node::Expr(use_expr), child_id)) => {
+                    if let ExprKind::Index(e, ..) = use_expr.kind
+                        && e.hir_id == child_id
+                    {
+                        // Indexing works with both owned and its dereferenced type
+                        return;
+                    }
+
+                    if let ExprKind::MethodCall(name, receiver, ..) = use_expr.kind
+                        && receiver.hir_id == child_id
+                    {
+                        let name = name.ident.as_str();
+
+                        // Check if the method can be renamed.
+                        if let Some((_, replacement)) = args.method_renames.iter().find(|&&(x, _)| x == name) {
+                            result.replacements.push(PtrArgReplacement {
+                                expr_span: use_expr.span,
+                                self_span: receiver.span,
+                                replacement,
+                            });
+                            return;
                         }
 
-                        let Some(id) = self.cx.typeck_results().type_dependent_def_id(e.hir_id) else {
-                            set_skip_flag();
+                        // Some methods exist on both `[T]` and `Vec`, such as `len`, where the receiver type
+                        // doesn't coerce to a slice and our adjusted type check below isn't enough,
+                        // but it would still be valid to call with a slice
+                        if is_allowed_vec_method(self.cx, use_expr) {
                             return;
-                        };
-
-                        match *self.cx.tcx.fn_sig(id).instantiate_identity().skip_binder().inputs()[i]
-                            .peel_refs()
-                            .kind()
-                        {
-                            ty::Dynamic(preds, _, _) if !matches_preds(self.cx, args.deref_ty.ty(self.cx), preds) => {
-                                set_skip_flag();
-                            },
-                            ty::Param(_) => {
-                                set_skip_flag();
-                            },
-                            // If the types match check for methods which exist on both types. e.g. `Vec::len` and
-                            // `slice::len`
-                            ty::Adt(def, _) if def.did() == args.ty_did && !is_allowed_vec_method(self.cx, e) => {
-                                set_skip_flag();
-                            },
-                            _ => (),
                         }
-                    },
-                    // Indexing is fine for currently supported types.
-                    ExprKind::Index(e, _, _) if e.hir_id == child_id => (),
-                    _ => set_skip_flag(),
+                    }
+
+                    let deref_ty = args.deref_ty.ty(self.cx);
+                    let adjusted_ty = self.cx.typeck_results().expr_ty_adjusted(e).peel_refs();
+                    if adjusted_ty == deref_ty {
+                        return;
+                    }
+
+                    if let ty::Dynamic(preds, ..) = adjusted_ty.kind()
+                        && matches_preds(self.cx, deref_ty, preds)
+                    {
+                        return;
+                    }
+
+                    set_skip_flag();
                 },
                 _ => set_skip_flag(),
             }
diff --git a/src/tools/clippy/clippy_lints/src/pub_underscore_fields.rs b/src/tools/clippy/clippy_lints/src/pub_underscore_fields.rs
index 77b707567e4b..db03657c9af4 100644
--- a/src/tools/clippy/clippy_lints/src/pub_underscore_fields.rs
+++ b/src/tools/clippy/clippy_lints/src/pub_underscore_fields.rs
@@ -1,5 +1,5 @@
-use clippy_config::types::PubUnderscoreFieldsBehaviour;
 use clippy_config::Conf;
+use clippy_config::types::PubUnderscoreFieldsBehaviour;
 use clippy_utils::attrs::is_doc_hidden;
 use clippy_utils::diagnostics::span_lint_hir_and_then;
 use clippy_utils::is_path_lang_item;
diff --git a/src/tools/clippy/clippy_lints/src/question_mark.rs b/src/tools/clippy/clippy_lints/src/question_mark.rs
index e1e3ded2c795..aa9a9001afb7 100644
--- a/src/tools/clippy/clippy_lints/src/question_mark.rs
+++ b/src/tools/clippy/clippy_lints/src/question_mark.rs
@@ -1,8 +1,8 @@
 use crate::manual_let_else::MANUAL_LET_ELSE;
 use crate::question_mark_used::QUESTION_MARK_USED;
+use clippy_config::Conf;
 use clippy_config::msrvs::Msrv;
 use clippy_config::types::MatchLintBehaviour;
-use clippy_config::Conf;
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
@@ -12,8 +12,8 @@ use clippy_utils::{
     span_contains_comment,
 };
 use rustc_errors::Applicability;
-use rustc_hir::def::Res;
 use rustc_hir::LangItem::{self, OptionNone, OptionSome, ResultErr, ResultOk};
+use rustc_hir::def::Res;
 use rustc_hir::{
     BindingMode, Block, Body, ByRef, Expr, ExprKind, LetStmt, Mutability, Node, PatKind, PathSegment, QPath, Stmt,
     StmtKind,
diff --git a/src/tools/clippy/clippy_lints/src/ranges.rs b/src/tools/clippy/clippy_lints/src/ranges.rs
index 81189fe517c0..21cd33672624 100644
--- a/src/tools/clippy/clippy_lints/src/ranges.rs
+++ b/src/tools/clippy/clippy_lints/src/ranges.rs
@@ -1,8 +1,8 @@
-use clippy_config::msrvs::{self, Msrv};
 use clippy_config::Conf;
+use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::consts::{ConstEvalCtxt, Constant};
 use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
-use clippy_utils::source::{snippet, snippet_with_applicability, SpanRangeExt};
+use clippy_utils::source::{SpanRangeExt, snippet, snippet_with_applicability};
 use clippy_utils::sugg::Sugg;
 use clippy_utils::{get_parent_expr, higher, is_in_const_context, is_integer_const, path_to_local};
 use rustc_ast::ast::RangeLimits;
@@ -11,8 +11,8 @@ use rustc_hir::{BinOpKind, Expr, ExprKind, HirId};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty;
 use rustc_session::impl_lint_pass;
-use rustc_span::source_map::Spanned;
 use rustc_span::Span;
+use rustc_span::source_map::Spanned;
 use std::cmp::Ordering;
 
 declare_clippy_lint! {
diff --git a/src/tools/clippy/clippy_lints/src/rc_clone_in_vec_init.rs b/src/tools/clippy/clippy_lints/src/rc_clone_in_vec_init.rs
index d0b45b59526f..e877f5d6ed43 100644
--- a/src/tools/clippy/clippy_lints/src/rc_clone_in_vec_init.rs
+++ b/src/tools/clippy/clippy_lints/src/rc_clone_in_vec_init.rs
@@ -8,7 +8,7 @@ use rustc_hir::{Expr, ExprKind, QPath, TyKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty;
 use rustc_session::declare_lint_pass;
-use rustc_span::{sym, Span, Symbol};
+use rustc_span::{Span, Symbol, sym};
 
 declare_clippy_lint! {
     /// ### What it does
diff --git a/src/tools/clippy/clippy_lints/src/read_zero_byte_vec.rs b/src/tools/clippy/clippy_lints/src/read_zero_byte_vec.rs
index 7f4735c6a889..6bd68dd4109d 100644
--- a/src/tools/clippy/clippy_lints/src/read_zero_byte_vec.rs
+++ b/src/tools/clippy/clippy_lints/src/read_zero_byte_vec.rs
@@ -1,13 +1,13 @@
 use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then};
 use clippy_utils::get_enclosing_block;
-use clippy_utils::higher::{get_vec_init_kind, VecInitKind};
+use clippy_utils::higher::{VecInitKind, get_vec_init_kind};
 use clippy_utils::source::snippet;
 
 use hir::{Expr, ExprKind, HirId, LetStmt, PatKind, PathSegment, QPath, StmtKind};
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_hir::def::Res;
-use rustc_hir::intravisit::{walk_expr, Visitor};
+use rustc_hir::intravisit::{Visitor, walk_expr};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::declare_lint_pass;
 
diff --git a/src/tools/clippy/clippy_lints/src/redundant_clone.rs b/src/tools/clippy/clippy_lints/src/redundant_clone.rs
index 4e24ddad83a5..b9e0106fc86b 100644
--- a/src/tools/clippy/clippy_lints/src/redundant_clone.rs
+++ b/src/tools/clippy/clippy_lints/src/redundant_clone.rs
@@ -1,17 +1,17 @@
 use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then};
-use clippy_utils::mir::{visit_local_usage, LocalUsage, PossibleBorrowerMap};
+use clippy_utils::fn_has_unsatisfiable_preds;
+use clippy_utils::mir::{LocalUsage, PossibleBorrowerMap, visit_local_usage};
 use clippy_utils::source::SpanRangeExt;
 use clippy_utils::ty::{has_drop, is_copy, is_type_diagnostic_item, is_type_lang_item, walk_ptrs_ty_depth};
-use clippy_utils::fn_has_unsatisfiable_preds;
 use rustc_errors::Applicability;
 use rustc_hir::intravisit::FnKind;
-use rustc_hir::{def_id, Body, FnDecl, LangItem};
+use rustc_hir::{Body, FnDecl, LangItem, def_id};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::mir;
 use rustc_middle::ty::{self, Ty};
 use rustc_session::declare_lint_pass;
 use rustc_span::def_id::LocalDefId;
-use rustc_span::{sym, BytePos, Span};
+use rustc_span::{BytePos, Span, sym};
 
 macro_rules! unwrap_or_continue {
     ($x:expr) => {
@@ -349,14 +349,10 @@ fn visit_clone_usage(cloned: mir::Local, clone: mir::Local, mir: &mir::Body<'_>,
             local_use_locs: _,
             local_consume_or_mutate_locs: clone_consume_or_mutate_locs,
         },
-    )) = visit_local_usage(
-        &[cloned, clone],
-        mir,
-        mir::Location {
-            block: bb,
-            statement_index: mir.basic_blocks[bb].statements.len(),
-        },
-    )
+    )) = visit_local_usage(&[cloned, clone], mir, mir::Location {
+        block: bb,
+        statement_index: mir.basic_blocks[bb].statements.len(),
+    })
     .map(|mut vec| (vec.remove(0), vec.remove(0)))
     {
         CloneUsage {
diff --git a/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs b/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs
index bad9b979203f..6930a01d48be 100644
--- a/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs
+++ b/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs
@@ -6,7 +6,7 @@ use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_hir::intravisit::{Visitor as HirVisitor, Visitor};
 use rustc_hir::{
-    intravisit as hir_visit, ClosureKind, CoroutineDesugaring, CoroutineKind, CoroutineSource, ExprKind, Node,
+    ClosureKind, CoroutineDesugaring, CoroutineKind, CoroutineSource, ExprKind, Node, intravisit as hir_visit,
 };
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::hir::nested_filter;
diff --git a/src/tools/clippy/clippy_lints/src/redundant_else.rs b/src/tools/clippy/clippy_lints/src/redundant_else.rs
index 3bdf13dbbea6..6a1d40334e75 100644
--- a/src/tools/clippy/clippy_lints/src/redundant_else.rs
+++ b/src/tools/clippy/clippy_lints/src/redundant_else.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_help;
 use rustc_ast::ast::{Block, Expr, ExprKind, Stmt, StmtKind};
-use rustc_ast::visit::{walk_expr, Visitor};
+use rustc_ast::visit::{Visitor, walk_expr};
 use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
 use rustc_session::declare_lint_pass;
diff --git a/src/tools/clippy/clippy_lints/src/redundant_field_names.rs b/src/tools/clippy/clippy_lints/src/redundant_field_names.rs
index 0e637538615e..d0dbff081f90 100644
--- a/src/tools/clippy/clippy_lints/src/redundant_field_names.rs
+++ b/src/tools/clippy/clippy_lints/src/redundant_field_names.rs
@@ -1,5 +1,5 @@
-use clippy_config::msrvs::{self, Msrv};
 use clippy_config::Conf;
+use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use rustc_ast::ast::{Expr, ExprKind};
 use rustc_errors::Applicability;
diff --git a/src/tools/clippy/clippy_lints/src/redundant_locals.rs b/src/tools/clippy/clippy_lints/src/redundant_locals.rs
index d94ca5bc7ec2..4f46ca3c7150 100644
--- a/src/tools/clippy/clippy_lints/src/redundant_locals.rs
+++ b/src/tools/clippy/clippy_lints/src/redundant_locals.rs
@@ -9,8 +9,8 @@ use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
 use rustc_middle::ty::UpvarCapture;
 use rustc_session::declare_lint_pass;
-use rustc_span::symbol::Ident;
 use rustc_span::DesugaringKind;
+use rustc_span::symbol::Ident;
 
 declare_clippy_lint! {
     /// ### What it does
diff --git a/src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs b/src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs
index d6e741dd974b..b27bb2e78afe 100644
--- a/src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs
+++ b/src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs
@@ -1,5 +1,5 @@
-use clippy_config::msrvs::{self, Msrv};
 use clippy_config::Conf;
+use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::source::snippet;
 use rustc_ast::ast::{ConstItem, Item, ItemKind, StaticItem, Ty, TyKind};
diff --git a/src/tools/clippy/clippy_lints/src/reference.rs b/src/tools/clippy/clippy_lints/src/reference.rs
index 2b4ef21fc48e..4bff37216eda 100644
--- a/src/tools/clippy/clippy_lints/src/reference.rs
+++ b/src/tools/clippy/clippy_lints/src/reference.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::{snippet_with_applicability, SpanRangeExt};
+use clippy_utils::source::{SpanRangeExt, snippet_with_applicability};
 use rustc_ast::ast::{Expr, ExprKind, Mutability, UnOp};
 use rustc_errors::Applicability;
 use rustc_lint::{EarlyContext, EarlyLintPass};
diff --git a/src/tools/clippy/clippy_lints/src/regex.rs b/src/tools/clippy/clippy_lints/src/regex.rs
index f6ef02b7c233..12cbdb854eff 100644
--- a/src/tools/clippy/clippy_lints/src/regex.rs
+++ b/src/tools/clippy/clippy_lints/src/regex.rs
@@ -3,7 +3,7 @@ use std::fmt::Display;
 use clippy_utils::consts::{ConstEvalCtxt, Constant};
 use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
 use clippy_utils::source::SpanRangeExt;
-use clippy_utils::{def_path_def_ids, path_def_id, paths};
+use clippy_utils::{def_path_res_with_base, find_crates, path_def_id, paths};
 use rustc_ast::ast::{LitKind, StrStyle};
 use rustc_hir::def_id::DefIdMap;
 use rustc_hir::{BorrowKind, Expr, ExprKind};
@@ -75,11 +75,14 @@ impl<'tcx> LateLintPass<'tcx> for Regex {
         // We don't use `match_def_path` here because that relies on matching the exact path, which changed
         // between regex 1.8 and 1.9
         //
-        // `def_path_def_ids` will resolve through re-exports but is relatively heavy, so we only perform
-        // the operation once and store the results
-        let mut resolve = |path, kind| {
-            for id in def_path_def_ids(cx.tcx, path) {
-                self.definitions.insert(id, kind);
+        // `def_path_res_with_base` will resolve through re-exports but is relatively heavy, so we only
+        // perform the operation once and store the results
+        let regex_crates = find_crates(cx.tcx, sym!(regex));
+        let mut resolve = |path: &[&str], kind: RegexKind| {
+            for res in def_path_res_with_base(cx.tcx, regex_crates.clone(), &path[1..]) {
+                if let Some(id) = res.opt_def_id() {
+                    self.definitions.insert(id, kind);
+                }
             }
         };
 
diff --git a/src/tools/clippy/clippy_lints/src/repeat_vec_with_capacity.rs b/src/tools/clippy/clippy_lints/src/repeat_vec_with_capacity.rs
index 08de10f69b05..f54cafffb83c 100644
--- a/src/tools/clippy/clippy_lints/src/repeat_vec_with_capacity.rs
+++ b/src/tools/clippy/clippy_lints/src/repeat_vec_with_capacity.rs
@@ -8,7 +8,7 @@ use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::declare_lint_pass;
-use rustc_span::{sym, Span};
+use rustc_span::{Span, sym};
 
 declare_clippy_lint! {
     /// ### What it does
diff --git a/src/tools/clippy/clippy_lints/src/reserve_after_initialization.rs b/src/tools/clippy/clippy_lints/src/reserve_after_initialization.rs
index caf3fb8707da..6157adad059c 100644
--- a/src/tools/clippy/clippy_lints/src/reserve_after_initialization.rs
+++ b/src/tools/clippy/clippy_lints/src/reserve_after_initialization.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::higher::{get_vec_init_kind, VecInitKind};
+use clippy_utils::higher::{VecInitKind, get_vec_init_kind};
 use clippy_utils::source::snippet;
 use clippy_utils::{is_from_proc_macro, path_to_local_id};
 use rustc_errors::Applicability;
diff --git a/src/tools/clippy/clippy_lints/src/return_self_not_must_use.rs b/src/tools/clippy/clippy_lints/src/return_self_not_must_use.rs
index 5962e8be9594..42d9cf2c88c1 100644
--- a/src/tools/clippy/clippy_lints/src/return_self_not_must_use.rs
+++ b/src/tools/clippy/clippy_lints/src/return_self_not_must_use.rs
@@ -7,7 +7,7 @@ use rustc_hir::{Body, FnDecl, OwnerId, TraitItem, TraitItemKind};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
 use rustc_session::declare_lint_pass;
-use rustc_span::{sym, Span};
+use rustc_span::{Span, sym};
 
 declare_clippy_lint! {
     /// ### What it does
diff --git a/src/tools/clippy/clippy_lints/src/returns.rs b/src/tools/clippy/clippy_lints/src/returns.rs
index 5ce091b7a7a3..3754fdddedfd 100644
--- a/src/tools/clippy/clippy_lints/src/returns.rs
+++ b/src/tools/clippy/clippy_lints/src/returns.rs
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
-use clippy_utils::source::{snippet_with_context, SpanRangeExt};
+use clippy_utils::source::{SpanRangeExt, snippet_with_context};
 use clippy_utils::sugg::has_enclosing_paren;
-use clippy_utils::visitors::{for_each_expr, Descend};
+use clippy_utils::visitors::{Descend, for_each_expr, for_each_unconsumed_temporary};
 use clippy_utils::{
     binary_expr_needs_parentheses, fn_def_id, is_from_proc_macro, is_inside_let_else, is_res_lang_ctor, path_res,
     path_to_local_id, span_contains_cfg, span_find_starting_semi,
@@ -9,8 +9,8 @@ use clippy_utils::{
 use core::ops::ControlFlow;
 use rustc_ast::NestedMetaItem;
 use rustc_errors::Applicability;
-use rustc_hir::intravisit::FnKind;
 use rustc_hir::LangItem::ResultErr;
+use rustc_hir::intravisit::FnKind;
 use rustc_hir::{
     Block, Body, Expr, ExprKind, FnDecl, HirId, ItemKind, LangItem, MatchSource, Node, OwnerNode, PatKind, QPath, Stmt,
     StmtKind,
@@ -21,7 +21,7 @@ use rustc_middle::ty::adjustment::Adjust;
 use rustc_middle::ty::{self, GenericArgKind, Ty};
 use rustc_session::declare_lint_pass;
 use rustc_span::def_id::LocalDefId;
-use rustc_span::{sym, BytePos, Pos, Span};
+use rustc_span::{BytePos, Pos, Span, sym};
 use std::borrow::Cow;
 use std::fmt::Display;
 
@@ -389,10 +389,24 @@ fn check_final_expr<'tcx>(
                 }
             };
 
-            let borrows = inner.map_or(false, |inner| last_statement_borrows(cx, inner));
-            if borrows {
-                return;
+            if let Some(inner) = inner {
+                if for_each_unconsumed_temporary(cx, inner, |temporary_ty| {
+                    if temporary_ty.has_significant_drop(cx.tcx, cx.param_env)
+                        && temporary_ty
+                            .walk()
+                            .any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(re) if !re.is_static()))
+                    {
+                        ControlFlow::Break(())
+                    } else {
+                        ControlFlow::Continue(())
+                    }
+                })
+                .is_break()
+                {
+                    return;
+                }
             }
+
             if ret_span.from_expansion() {
                 return;
             }
diff --git a/src/tools/clippy/clippy_lints/src/same_name_method.rs b/src/tools/clippy/clippy_lints/src/same_name_method.rs
index 508f3ae6def0..8d31641d4836 100644
--- a/src/tools/clippy/clippy_lints/src/same_name_method.rs
+++ b/src/tools/clippy/clippy_lints/src/same_name_method.rs
@@ -5,8 +5,8 @@ use rustc_hir::{HirId, Impl, ItemKind, Node, Path, QPath, TraitRef, TyKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::AssocKind;
 use rustc_session::declare_lint_pass;
-use rustc_span::symbol::Symbol;
 use rustc_span::Span;
+use rustc_span::symbol::Symbol;
 use std::collections::{BTreeMap, BTreeSet};
 
 declare_clippy_lint! {
@@ -62,13 +62,10 @@ impl<'tcx> LateLintPass<'tcx> for SameNameMethod {
                 && let TyKind::Path(QPath::Resolved(_, Path { res, .. })) = self_ty.kind
             {
                 if !map.contains_key(res) {
-                    map.insert(
-                        *res,
-                        ExistingName {
-                            impl_methods: BTreeMap::new(),
-                            trait_methods: BTreeMap::new(),
-                        },
-                    );
+                    map.insert(*res, ExistingName {
+                        impl_methods: BTreeMap::new(),
+                        trait_methods: BTreeMap::new(),
+                    });
                 }
                 let existing_name = map.get_mut(res).unwrap();
 
diff --git a/src/tools/clippy/clippy_lints/src/set_contains_or_insert.rs b/src/tools/clippy/clippy_lints/src/set_contains_or_insert.rs
index e6fe76493974..1185d67b1258 100644
--- a/src/tools/clippy/clippy_lints/src/set_contains_or_insert.rs
+++ b/src/tools/clippy/clippy_lints/src/set_contains_or_insert.rs
@@ -3,12 +3,12 @@ use std::ops::ControlFlow;
 use clippy_utils::diagnostics::span_lint;
 use clippy_utils::ty::is_type_diagnostic_item;
 use clippy_utils::visitors::for_each_expr;
-use clippy_utils::{higher, peel_hir_expr_while, SpanlessEq};
+use clippy_utils::{SpanlessEq, higher, peel_hir_expr_while};
 use rustc_hir::{Expr, ExprKind, UnOp};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::declare_lint_pass;
 use rustc_span::symbol::Symbol;
-use rustc_span::{sym, Span};
+use rustc_span::{Span, sym};
 
 declare_clippy_lint! {
     /// ### What it does
@@ -42,7 +42,7 @@ declare_clippy_lint! {
     ///     println!("inserted {value:?}");
     /// }
     /// ```
-    #[clippy::version = "1.80.0"]
+    #[clippy::version = "1.81.0"]
     pub SET_CONTAINS_OR_INSERT,
     nursery,
     "call to `::contains` followed by `::insert`"
diff --git a/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs b/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs
index 979d6dc77aed..d1114cb29f7a 100644
--- a/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs
+++ b/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs
@@ -4,13 +4,13 @@ use clippy_utils::{expr_or_init, get_attr, path_to_local, peel_hir_expr_unary};
 use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
 use rustc_errors::Applicability;
 use rustc_hir::def::{DefKind, Res};
-use rustc_hir::intravisit::{walk_expr, Visitor};
+use rustc_hir::intravisit::{Visitor, walk_expr};
 use rustc_hir::{self as hir, HirId};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::ty::{GenericArgKind, Ty};
 use rustc_session::impl_lint_pass;
 use rustc_span::symbol::Ident;
-use rustc_span::{sym, Span, DUMMY_SP};
+use rustc_span::{DUMMY_SP, Span, sym};
 use std::borrow::Cow;
 use std::collections::hash_map::Entry;
 
diff --git a/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs b/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs
index acf44a9bb5ab..c986c3e8aa6e 100644
--- a/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs
+++ b/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg};
 use rustc_ast::node_id::{NodeId, NodeMap};
 use rustc_ast::ptr::P;
-use rustc_ast::visit::{walk_expr, Visitor};
+use rustc_ast::visit::{Visitor, walk_expr};
 use rustc_ast::{Crate, Expr, ExprKind, Item, ItemKind, MacroDef, ModKind, Ty, TyKind, UseTreeKind};
 use rustc_errors::Applicability;
 use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
diff --git a/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs b/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs
index 04c16281ec4b..5129bbf26655 100644
--- a/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs
+++ b/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs
@@ -2,11 +2,10 @@ use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::macros::matching_root_macro_call;
 use clippy_utils::sugg::Sugg;
 use clippy_utils::{
-    get_enclosing_block, is_integer_literal, is_path_diagnostic_item, path_to_local,
-    path_to_local_id, SpanlessEq,
+    SpanlessEq, get_enclosing_block, is_integer_literal, is_path_diagnostic_item, path_to_local, path_to_local_id,
 };
 use rustc_errors::Applicability;
-use rustc_hir::intravisit::{walk_block, walk_expr, walk_stmt, Visitor};
+use rustc_hir::intravisit::{Visitor, walk_block, walk_expr, walk_stmt};
 use rustc_hir::{BindingMode, Block, Expr, ExprKind, HirId, PatKind, Stmt, StmtKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::declare_lint_pass;
diff --git a/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs b/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs
index 44283a49e84b..8dd998587932 100644
--- a/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs
+++ b/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs
@@ -1,5 +1,5 @@
-use clippy_config::msrvs::Msrv;
 use clippy_config::Conf;
+use clippy_config::msrvs::Msrv;
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::is_from_proc_macro;
 use rustc_attr::{StabilityLevel, StableSince};
@@ -11,7 +11,7 @@ use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
 use rustc_session::impl_lint_pass;
 use rustc_span::symbol::kw;
-use rustc_span::{sym, Span};
+use rustc_span::{Span, sym};
 
 declare_clippy_lint! {
     /// ### What it does
diff --git a/src/tools/clippy/clippy_lints/src/string_patterns.rs b/src/tools/clippy/clippy_lints/src/string_patterns.rs
index 7e211d64da14..ba2ddac2ec33 100644
--- a/src/tools/clippy/clippy_lints/src/string_patterns.rs
+++ b/src/tools/clippy/clippy_lints/src/string_patterns.rs
@@ -1,13 +1,13 @@
 use std::ops::ControlFlow;
 
-use clippy_config::msrvs::{self, Msrv};
 use clippy_config::Conf;
+use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
 use clippy_utils::eager_or_lazy::switch_to_eager_eval;
 use clippy_utils::macros::matching_root_macro_call;
 use clippy_utils::path_to_local_id;
 use clippy_utils::source::{snippet, str_literal_to_char_literal};
-use clippy_utils::visitors::{for_each_expr, Descend};
+use clippy_utils::visitors::{Descend, for_each_expr};
 use itertools::Itertools;
 use rustc_ast::{BinOpKind, LitKind};
 use rustc_errors::Applicability;
@@ -15,7 +15,7 @@ use rustc_hir::{Expr, ExprKind, PatKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty;
 use rustc_session::impl_lint_pass;
-use rustc_span::{sym, Span};
+use rustc_span::{Span, sym};
 
 declare_clippy_lint! {
     /// ### What it does
@@ -33,7 +33,7 @@ declare_clippy_lint! {
     /// ```no_run
     /// "Hello World!".trim_end_matches(['.', ',', '!', '?']);
     /// ```
-    #[clippy::version = "1.80.0"]
+    #[clippy::version = "1.81.0"]
     pub MANUAL_PATTERN_CHAR_COMPARISON,
     style,
     "manual char comparison in string patterns"
diff --git a/src/tools/clippy/clippy_lints/src/strings.rs b/src/tools/clippy/clippy_lints/src/strings.rs
index 6ca4ca000e9b..1fb82b66ab85 100644
--- a/src/tools/clippy/clippy_lints/src/strings.rs
+++ b/src/tools/clippy/clippy_lints/src/strings.rs
@@ -2,8 +2,8 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_the
 use clippy_utils::source::{snippet, snippet_with_applicability};
 use clippy_utils::ty::is_type_lang_item;
 use clippy_utils::{
-    get_expr_use_or_unification_node, get_parent_expr, is_lint_allowed, is_path_diagnostic_item, method_calls,
-    peel_blocks, SpanlessEq,
+    SpanlessEq, get_expr_use_or_unification_node, get_parent_expr, is_lint_allowed, is_path_diagnostic_item,
+    method_calls, peel_blocks,
 };
 use rustc_errors::Applicability;
 use rustc_hir::def_id::DefId;
diff --git a/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs b/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs
index 1c1de805db0c..be32dc0aab0b 100644
--- a/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs
+++ b/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs
@@ -1,4 +1,4 @@
-use clippy_utils::ast_utils::{eq_id, is_useless_with_eq_exprs, IdentIter};
+use clippy_utils::ast_utils::{IdentIter, eq_id, is_useless_with_eq_exprs};
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet_with_applicability;
 use core::ops::{Add, AddAssign};
@@ -7,9 +7,9 @@ use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::Applicability;
 use rustc_lint::{EarlyContext, EarlyLintPass};
 use rustc_session::declare_lint_pass;
+use rustc_span::Span;
 use rustc_span::source_map::Spanned;
 use rustc_span::symbol::Ident;
-use rustc_span::Span;
 
 declare_clippy_lint! {
     /// ### What it does
diff --git a/src/tools/clippy/clippy_lints/src/suspicious_trait_impl.rs b/src/tools/clippy/clippy_lints/src/suspicious_trait_impl.rs
index 744d6392e065..e9779d437d43 100644
--- a/src/tools/clippy/clippy_lints/src/suspicious_trait_impl.rs
+++ b/src/tools/clippy/clippy_lints/src/suspicious_trait_impl.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint;
 use clippy_utils::visitors::for_each_expr_without_closures;
-use clippy_utils::{binop_traits, trait_ref_of_method, BINOP_TRAITS, OP_ASSIGN_TRAITS};
+use clippy_utils::{BINOP_TRAITS, OP_ASSIGN_TRAITS, binop_traits, trait_ref_of_method};
 use core::ops::ControlFlow;
 use rustc_hir as hir;
 use rustc_lint::{LateContext, LateLintPass};
diff --git a/src/tools/clippy/clippy_lints/src/swap.rs b/src/tools/clippy/clippy_lints/src/swap.rs
index 197011cde3a1..e05fa4095b8e 100644
--- a/src/tools/clippy/clippy_lints/src/swap.rs
+++ b/src/tools/clippy/clippy_lints/src/swap.rs
@@ -6,7 +6,7 @@ use clippy_utils::ty::is_type_diagnostic_item;
 use clippy_utils::{can_mut_borrow_both, eq_expr_value, is_in_const_context, std_or_core};
 use itertools::Itertools;
 
-use rustc_hir::intravisit::{walk_expr, Visitor};
+use rustc_hir::intravisit::{Visitor, walk_expr};
 
 use crate::FxHashSet;
 use rustc_errors::Applicability;
@@ -17,7 +17,7 @@ use rustc_middle::ty;
 use rustc_session::declare_lint_pass;
 use rustc_span::source_map::Spanned;
 use rustc_span::symbol::Ident;
-use rustc_span::{sym, Span, SyntaxContext};
+use rustc_span::{Span, SyntaxContext, sym};
 
 declare_clippy_lint! {
     /// ### What it does
diff --git a/src/tools/clippy/clippy_lints/src/swap_ptr_to_ref.rs b/src/tools/clippy/clippy_lints/src/swap_ptr_to_ref.rs
index 20e9608a15dc..8c5cf93ab6e8 100644
--- a/src/tools/clippy/clippy_lints/src/swap_ptr_to_ref.rs
+++ b/src/tools/clippy/clippy_lints/src/swap_ptr_to_ref.rs
@@ -5,7 +5,7 @@ use rustc_errors::Applicability;
 use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, UnOp};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::declare_lint_pass;
-use rustc_span::{sym, Span, SyntaxContext};
+use rustc_span::{Span, SyntaxContext, sym};
 
 declare_clippy_lint! {
     /// ### What it does
diff --git a/src/tools/clippy/clippy_lints/src/tests_outside_test_module.rs b/src/tools/clippy/clippy_lints/src/tests_outside_test_module.rs
index 25d0a16e2ab4..3cd4fefffadf 100644
--- a/src/tools/clippy/clippy_lints/src/tests_outside_test_module.rs
+++ b/src/tools/clippy/clippy_lints/src/tests_outside_test_module.rs
@@ -4,8 +4,8 @@ use rustc_hir::intravisit::FnKind;
 use rustc_hir::{Body, FnDecl};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::declare_lint_pass;
-use rustc_span::def_id::LocalDefId;
 use rustc_span::Span;
+use rustc_span::def_id::LocalDefId;
 
 declare_clippy_lint! {
     /// ### What it does
diff --git a/src/tools/clippy/clippy_lints/src/to_digit_is_some.rs b/src/tools/clippy/clippy_lints/src/to_digit_is_some.rs
index dafe9e38818f..4f96a566b63c 100644
--- a/src/tools/clippy/clippy_lints/src/to_digit_is_some.rs
+++ b/src/tools/clippy/clippy_lints/src/to_digit_is_some.rs
@@ -56,11 +56,13 @@ impl<'tcx> LateLintPass<'tcx> for ToDigitIsSome {
                         && let hir::ExprKind::Path(to_digits_path) = &to_digits_call.kind
                         && let to_digits_call_res = cx.qpath_res(to_digits_path, to_digits_call.hir_id)
                         && let Some(to_digits_def_id) = to_digits_call_res.opt_def_id()
-                        && match_def_path(
-                            cx,
-                            to_digits_def_id,
-                            &["core", "char", "methods", "", "to_digit"],
-                        )
+                        && match_def_path(cx, to_digits_def_id, &[
+                            "core",
+                            "char",
+                            "methods",
+                            "",
+                            "to_digit",
+                        ])
                     {
                         Some((false, char_arg, radix_arg))
                     } else {
diff --git a/src/tools/clippy/clippy_lints/src/trait_bounds.rs b/src/tools/clippy/clippy_lints/src/trait_bounds.rs
index 2e87d36df312..00277593622a 100644
--- a/src/tools/clippy/clippy_lints/src/trait_bounds.rs
+++ b/src/tools/clippy/clippy_lints/src/trait_bounds.rs
@@ -1,8 +1,8 @@
-use clippy_config::msrvs::{self, Msrv};
 use clippy_config::Conf;
+use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg};
-use clippy_utils::source::{snippet, snippet_with_applicability, SpanRangeExt};
-use clippy_utils::{is_from_proc_macro, SpanlessEq, SpanlessHash};
+use clippy_utils::source::{SpanRangeExt, snippet, snippet_with_applicability};
+use clippy_utils::{SpanlessEq, SpanlessHash, is_from_proc_macro};
 use core::hash::{Hash, Hasher};
 use itertools::Itertools;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
diff --git a/src/tools/clippy/clippy_lints/src/transmute/mod.rs b/src/tools/clippy/clippy_lints/src/transmute/mod.rs
index da7906b11835..25fec9f688ca 100644
--- a/src/tools/clippy/clippy_lints/src/transmute/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/transmute/mod.rs
@@ -19,8 +19,8 @@ mod useless_transmute;
 mod utils;
 mod wrong_transmute;
 
-use clippy_config::msrvs::Msrv;
 use clippy_config::Conf;
+use clippy_config::msrvs::Msrv;
 use clippy_utils::is_in_const_context;
 use rustc_hir::{Expr, ExprKind, QPath};
 use rustc_lint::{LateContext, LateLintPass};
@@ -527,24 +527,44 @@ declare_clippy_lint! {
     /// Checks if transmute calls have all generics specified.
     ///
     /// ### Why is this bad?
-    /// If not set, some unexpected output type could be retrieved instead of the expected one,
-    /// potentially leading to invalid code.
+    /// If not, one or more unexpected types could be used during `transmute()`, potentially leading
+    /// to Undefined Behavior or other problems.
+    ///
+    /// This is particularly dangerous in case a seemingly innocent/unrelated change causes type
+    /// inference to result in a different type. For example, if `transmute()` is the tail
+    /// expression of an `if`-branch, and the `else`-branch type changes, the compiler may silently
+    /// infer a different type to be returned by `transmute()`. That is because the compiler is
+    /// free to change the inference of a type as long as that inference is technically correct,
+    /// regardless of the programmer's unknown expectation.
     ///
-    /// This is particularly dangerous in case a seemingly innocent/unrelated change can cause type
-    /// inference to start inferring a different type. E.g. the transmute is the tail expression of
-    /// an `if` branch, and a different branches type changes, causing the transmute to silently
-    /// have a different type, instead of a proper error.
+    /// Both type-parameters, the input- and the output-type, to any `transmute()` should
+    /// be given explicitly: Setting the input-type explicitly avoids confusion about what the
+    /// argument's type actually is. Setting the output-type explicitly avoids type-inference
+    /// to infer a technically correct yet unexpected type.
     ///
     /// ### Example
     /// ```no_run
     /// # unsafe {
+    /// // Avoid "naked" calls to `transmute()`!
     /// let x: i32 = std::mem::transmute([1u16, 2u16]);
+    ///
+    /// // `first_answers` is intended to transmute a slice of bool to a slice of u8.
+    /// // But the programmer forgot to index the first element of the outer slice,
+    /// // so we are actually transmuting from "pointers to slices" instead of
+    /// // transmuting from "a slice of bool", causing a nonsensical result.
+    /// let the_answers: &[&[bool]] = &[&[true, false, true]];
+    /// let first_answers: &[u8] = std::mem::transmute(the_answers);
     /// # }
     /// ```
     /// Use instead:
     /// ```no_run
     /// # unsafe {
     /// let x = std::mem::transmute::<[u16; 2], i32>([1u16, 2u16]);
+    ///
+    /// // The explicit type parameters on `transmute()` makes the intention clear,
+    /// // and cause a type-error if the actual types don't match our expectation.
+    /// let the_answers: &[&[bool]] = &[&[true, false, true]];
+    /// let first_answers: &[u8] = std::mem::transmute::<&[bool], &[u8]>(the_answers[0]);
     /// # }
     /// ```
     #[clippy::version = "1.79.0"]
diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs b/src/tools/clippy/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs
index ba8c7d6bfcb4..fca332dba401 100644
--- a/src/tools/clippy/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs
+++ b/src/tools/clippy/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs
@@ -6,8 +6,8 @@ use rustc_errors::Applicability;
 use rustc_hir::{Expr, Node};
 use rustc_hir_typeck::cast::check_cast;
 use rustc_lint::LateContext;
-use rustc_middle::ty::cast::CastKind;
 use rustc_middle::ty::Ty;
+use rustc_middle::ty::cast::CastKind;
 
 /// Checks for `transmutes_expressible_as_ptr_casts` lint.
 /// Returns `true` if it's triggered, otherwise returns `false`.
diff --git a/src/tools/clippy/clippy_lints/src/transmute/unsound_collection_transmute.rs b/src/tools/clippy/clippy_lints/src/transmute/unsound_collection_transmute.rs
index 35e938307660..f46e95b0b832 100644
--- a/src/tools/clippy/clippy_lints/src/transmute/unsound_collection_transmute.rs
+++ b/src/tools/clippy/clippy_lints/src/transmute/unsound_collection_transmute.rs
@@ -1,5 +1,5 @@
-use super::utils::is_layout_incompatible;
 use super::UNSOUND_COLLECTION_TRANSMUTE;
+use super::utils::is_layout_incompatible;
 use clippy_utils::diagnostics::span_lint;
 use rustc_hir::Expr;
 use rustc_lint::LateContext;
diff --git a/src/tools/clippy/clippy_lints/src/tuple_array_conversions.rs b/src/tools/clippy/clippy_lints/src/tuple_array_conversions.rs
index 1d0de9327546..3da8a449a7c5 100644
--- a/src/tools/clippy/clippy_lints/src/tuple_array_conversions.rs
+++ b/src/tools/clippy/clippy_lints/src/tuple_array_conversions.rs
@@ -1,5 +1,5 @@
-use clippy_config::msrvs::{self, Msrv};
 use clippy_config::Conf;
+use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::span_lint_and_help;
 use clippy_utils::visitors::for_each_local_use_after_expr;
 use clippy_utils::{is_from_proc_macro, path_to_local};
diff --git a/src/tools/clippy/clippy_lints/src/types/box_collection.rs b/src/tools/clippy/clippy_lints/src/types/box_collection.rs
index 9ac73394548c..24fe4e08a5b4 100644
--- a/src/tools/clippy/clippy_lints/src/types/box_collection.rs
+++ b/src/tools/clippy/clippy_lints/src/types/box_collection.rs
@@ -3,7 +3,7 @@ use clippy_utils::{path_def_id, qpath_generic_tys};
 use rustc_hir::def_id::DefId;
 use rustc_hir::{self as hir, QPath};
 use rustc_lint::LateContext;
-use rustc_span::{sym, Symbol};
+use rustc_span::{Symbol, sym};
 
 use super::BOX_COLLECTION;
 
diff --git a/src/tools/clippy/clippy_lints/src/types/mod.rs b/src/tools/clippy/clippy_lints/src/types/mod.rs
index 3a14927802b4..363aea8be72e 100644
--- a/src/tools/clippy/clippy_lints/src/types/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/types/mod.rs
@@ -18,8 +18,8 @@ use rustc_hir::{
 };
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::impl_lint_pass;
-use rustc_span::def_id::LocalDefId;
 use rustc_span::Span;
+use rustc_span::def_id::LocalDefId;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -261,8 +261,59 @@ declare_clippy_lint! {
     /// ### Example
     /// ```no_run
     /// # use std::rc::Rc;
-    /// struct Foo {
-    ///     inner: Rc>>>,
+    /// struct PointMatrixContainer {
+    ///     matrix: Rc>>>,
+    /// }
+    ///
+    /// fn main() {
+    ///     let point_matrix: Vec>> = vec![
+    ///         vec![
+    ///             Box::new((1, 2, 3, 4)),
+    ///             Box::new((5, 6, 7, 8)),
+    ///         ],
+    ///         vec![
+    ///             Box::new((9, 10, 11, 12)),
+    ///         ],
+    ///     ];
+    ///
+    ///     let shared_point_matrix: Rc>>> = Rc::new(point_matrix);
+    ///
+    ///     let container = PointMatrixContainer {
+    ///         matrix: shared_point_matrix,
+    ///     };
+    ///
+    ///     // ...
+    /// }
+    /// ```
+    /// Use instead:
+    /// ### Example
+    /// ```no_run
+    /// # use std::rc::Rc;
+    /// type PointMatrix = Vec>>;
+    /// type SharedPointMatrix = Rc;
+    ///
+    /// struct PointMatrixContainer {
+    ///     matrix: SharedPointMatrix,
+    /// }
+    ///
+    /// fn main() {
+    ///     let point_matrix: PointMatrix = vec![
+    ///         vec![
+    ///             Box::new((1, 2, 3, 4)),
+    ///             Box::new((5, 6, 7, 8)),
+    ///         ],
+    ///         vec![
+    ///             Box::new((9, 10, 11, 12)),
+    ///         ],
+    ///     ];
+    ///
+    ///     let shared_point_matrix: SharedPointMatrix = Rc::new(point_matrix);
+    ///
+    ///     let container = PointMatrixContainer {
+    ///         matrix: shared_point_matrix,
+    ///     };
+    ///
+    ///     // ...
     /// }
     /// ```
     #[clippy::version = "pre 1.29.0"]
@@ -335,30 +386,22 @@ impl<'tcx> LateLintPass<'tcx> for Types {
 
         let is_exported = cx.effective_visibilities.is_exported(def_id);
 
-        self.check_fn_decl(
-            cx,
-            decl,
-            CheckTyContext {
-                is_in_trait_impl,
-                is_exported,
-                in_body: matches!(fn_kind, FnKind::Closure),
-                ..CheckTyContext::default()
-            },
-        );
+        self.check_fn_decl(cx, decl, CheckTyContext {
+            is_in_trait_impl,
+            is_exported,
+            in_body: matches!(fn_kind, FnKind::Closure),
+            ..CheckTyContext::default()
+        });
     }
 
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
         let is_exported = cx.effective_visibilities.is_exported(item.owner_id.def_id);
 
         match item.kind {
-            ItemKind::Static(ty, _, _) | ItemKind::Const(ty, _, _) => self.check_ty(
-                cx,
-                ty,
-                CheckTyContext {
-                    is_exported,
-                    ..CheckTyContext::default()
-                },
-            ),
+            ItemKind::Static(ty, _, _) | ItemKind::Const(ty, _, _) => self.check_ty(cx, ty, CheckTyContext {
+                is_exported,
+                ..CheckTyContext::default()
+            }),
             // functions, enums, structs, impls and traits are covered
             _ => (),
         }
@@ -376,14 +419,10 @@ impl<'tcx> LateLintPass<'tcx> for Types {
                     false
                 };
 
-                self.check_ty(
-                    cx,
-                    ty,
-                    CheckTyContext {
-                        is_in_trait_impl,
-                        ..CheckTyContext::default()
-                    },
-                );
+                self.check_ty(cx, ty, CheckTyContext {
+                    is_in_trait_impl,
+                    ..CheckTyContext::default()
+                });
             },
             // Methods are covered by check_fn.
             // Type aliases are ignored because oftentimes it's impossible to
@@ -399,14 +438,10 @@ impl<'tcx> LateLintPass<'tcx> for Types {
 
         let is_exported = cx.effective_visibilities.is_exported(field.def_id);
 
-        self.check_ty(
-            cx,
-            field.ty,
-            CheckTyContext {
-                is_exported,
-                ..CheckTyContext::default()
-            },
-        );
+        self.check_ty(cx, field.ty, CheckTyContext {
+            is_exported,
+            ..CheckTyContext::default()
+        });
     }
 
     fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &TraitItem<'tcx>) {
@@ -434,14 +469,10 @@ impl<'tcx> LateLintPass<'tcx> for Types {
 
     fn check_local(&mut self, cx: &LateContext<'tcx>, local: &LetStmt<'tcx>) {
         if let Some(ty) = local.ty {
-            self.check_ty(
-                cx,
-                ty,
-                CheckTyContext {
-                    in_body: true,
-                    ..CheckTyContext::default()
-                },
-            );
+            self.check_ty(cx, ty, CheckTyContext {
+                in_body: true,
+                ..CheckTyContext::default()
+            });
         }
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/types/redundant_allocation.rs b/src/tools/clippy/clippy_lints/src/types/redundant_allocation.rs
index 0801eace4fcf..1a656088b174 100644
--- a/src/tools/clippy/clippy_lints/src/types/redundant_allocation.rs
+++ b/src/tools/clippy/clippy_lints/src/types/redundant_allocation.rs
@@ -9,7 +9,7 @@ use rustc_lint::LateContext;
 use rustc_middle::ty::TypeVisitableExt;
 use rustc_span::symbol::sym;
 
-use super::{utils, REDUNDANT_ALLOCATION};
+use super::{REDUNDANT_ALLOCATION, utils};
 
 pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, hir_ty: &hir::Ty<'tcx>, qpath: &QPath<'tcx>, def_id: DefId) -> bool {
     let mut applicability = Applicability::MaybeIncorrect;
diff --git a/src/tools/clippy/clippy_lints/src/types/type_complexity.rs b/src/tools/clippy/clippy_lints/src/types/type_complexity.rs
index 7fcfd5c8f350..0b64fddb447b 100644
--- a/src/tools/clippy/clippy_lints/src/types/type_complexity.rs
+++ b/src/tools/clippy/clippy_lints/src/types/type_complexity.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint;
 use rustc_hir as hir;
-use rustc_hir::intravisit::{walk_inf, walk_ty, Visitor};
+use rustc_hir::intravisit::{Visitor, walk_inf, walk_ty};
 use rustc_hir::{GenericParamKind, TyKind};
 use rustc_lint::LateContext;
 use rustc_target::spec::abi::Abi;
diff --git a/src/tools/clippy/clippy_lints/src/types/vec_box.rs b/src/tools/clippy/clippy_lints/src/types/vec_box.rs
index 29996a6f783e..230239335c65 100644
--- a/src/tools/clippy/clippy_lints/src/types/vec_box.rs
+++ b/src/tools/clippy/clippy_lints/src/types/vec_box.rs
@@ -6,8 +6,8 @@ use rustc_hir::def_id::DefId;
 use rustc_hir::{self as hir, GenericArg, LangItem, QPath, TyKind};
 use rustc_hir_analysis::lower_ty;
 use rustc_lint::LateContext;
-use rustc_middle::ty::layout::LayoutOf;
 use rustc_middle::ty::TypeVisitableExt;
+use rustc_middle::ty::layout::LayoutOf;
 use rustc_span::symbol::sym;
 
 use super::VEC_BOX;
diff --git a/src/tools/clippy/clippy_lints/src/unconditional_recursion.rs b/src/tools/clippy/clippy_lints/src/unconditional_recursion.rs
index 42100e1d755c..3fc08e8192d4 100644
--- a/src/tools/clippy/clippy_lints/src/unconditional_recursion.rs
+++ b/src/tools/clippy/clippy_lints/src/unconditional_recursion.rs
@@ -5,7 +5,7 @@ use rustc_data_structures::fx::FxHashMap;
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::{DefId, LocalDefId};
-use rustc_hir::intravisit::{walk_body, walk_expr, FnKind, Visitor};
+use rustc_hir::intravisit::{FnKind, Visitor, walk_body, walk_expr};
 use rustc_hir::{Body, Expr, ExprKind, FnDecl, HirId, Item, ItemKind, Node, QPath, TyKind};
 use rustc_hir_analysis::lower_ty;
 use rustc_lint::{LateContext, LateLintPass};
@@ -13,8 +13,8 @@ use rustc_middle::hir::map::Map;
 use rustc_middle::hir::nested_filter;
 use rustc_middle::ty::{self, AssocKind, Ty, TyCtxt};
 use rustc_session::impl_lint_pass;
-use rustc_span::symbol::{kw, Ident};
-use rustc_span::{sym, Span};
+use rustc_span::symbol::{Ident, kw};
+use rustc_span::{Span, sym};
 use rustc_trait_selection::error_reporting::traits::suggestions::ReturnsVisitor;
 use std::ops::ControlFlow;
 
@@ -257,13 +257,10 @@ fn is_default_method_on_current_ty<'tcx>(tcx: TyCtxt<'tcx>, qpath: QPath<'tcx>,
             }
             if matches!(
                 ty.kind,
-                TyKind::Path(QPath::Resolved(
-                    _,
-                    hir::Path {
-                        res: Res::SelfTyAlias { .. },
-                        ..
-                    },
-                ))
+                TyKind::Path(QPath::Resolved(_, hir::Path {
+                    res: Res::SelfTyAlias { .. },
+                    ..
+                },))
             ) {
                 return true;
             }
diff --git a/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs b/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs
index f51c5f74d993..44eb0a6c593d 100644
--- a/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs
+++ b/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs
@@ -4,12 +4,12 @@ use clippy_config::Conf;
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::is_lint_allowed;
 use clippy_utils::source::walk_span_to_context;
-use clippy_utils::visitors::{for_each_expr, Descend};
+use clippy_utils::visitors::{Descend, for_each_expr};
 use hir::HirId;
 use rustc_data_structures::sync::Lrc;
 use rustc_hir as hir;
 use rustc_hir::{Block, BlockCheckMode, ItemKind, Node, UnsafeSource};
-use rustc_lexer::{tokenize, TokenKind};
+use rustc_lexer::{TokenKind, tokenize};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
 use rustc_session::impl_lint_pass;
diff --git a/src/tools/clippy/clippy_lints/src/uninhabited_references.rs b/src/tools/clippy/clippy_lints/src/uninhabited_references.rs
index 88039372ebd2..cfa565cf8037 100644
--- a/src/tools/clippy/clippy_lints/src/uninhabited_references.rs
+++ b/src/tools/clippy/clippy_lints/src/uninhabited_references.rs
@@ -5,8 +5,8 @@ use rustc_hir_analysis::lower_ty;
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::lint::in_external_macro;
 use rustc_session::declare_lint_pass;
-use rustc_span::def_id::LocalDefId;
 use rustc_span::Span;
+use rustc_span::def_id::LocalDefId;
 
 declare_clippy_lint! {
     /// ### What it does
diff --git a/src/tools/clippy/clippy_lints/src/uninit_vec.rs b/src/tools/clippy/clippy_lints/src/uninit_vec.rs
index 9ffcfcc0f50c..93ed15777e01 100644
--- a/src/tools/clippy/clippy_lints/src/uninit_vec.rs
+++ b/src/tools/clippy/clippy_lints/src/uninit_vec.rs
@@ -1,13 +1,13 @@
 use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
-use clippy_utils::higher::{get_vec_init_kind, VecInitKind};
+use clippy_utils::higher::{VecInitKind, get_vec_init_kind};
 use clippy_utils::ty::{is_type_diagnostic_item, is_uninit_value_valid_for_ty};
-use clippy_utils::{is_integer_literal, is_lint_allowed, path_to_local_id, peel_hir_expr_while, SpanlessEq};
+use clippy_utils::{SpanlessEq, is_integer_literal, is_lint_allowed, path_to_local_id, peel_hir_expr_while};
 use rustc_hir::{Block, Expr, ExprKind, HirId, PatKind, PathSegment, Stmt, StmtKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::lint::in_external_macro;
 use rustc_middle::ty;
 use rustc_session::declare_lint_pass;
-use rustc_span::{sym, Span};
+use rustc_span::{Span, sym};
 
 // TODO: add `ReadBuf` (RFC 2930) in "How to fix" once it is available in std
 declare_clippy_lint! {
diff --git a/src/tools/clippy/clippy_lints/src/unit_return_expecting_ord.rs b/src/tools/clippy/clippy_lints/src/unit_return_expecting_ord.rs
index a8cc2f979633..87478a120dd0 100644
--- a/src/tools/clippy/clippy_lints/src/unit_return_expecting_ord.rs
+++ b/src/tools/clippy/clippy_lints/src/unit_return_expecting_ord.rs
@@ -5,7 +5,7 @@ use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty;
 use rustc_middle::ty::{ClauseKind, GenericPredicates, ProjectionPredicate, TraitPredicate};
 use rustc_session::declare_lint_pass;
-use rustc_span::{sym, BytePos, Span};
+use rustc_span::{BytePos, Span, sym};
 
 declare_clippy_lint! {
     /// ### What it does
diff --git a/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs b/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs
index 80b661a757c2..bf2d75ea1a96 100644
--- a/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs
+++ b/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs
@@ -4,7 +4,7 @@ use clippy_utils::visitors::{for_each_local_assignment, for_each_value_source, i
 use core::ops::ControlFlow;
 use rustc_errors::Applicability;
 use rustc_hir::def::{DefKind, Res};
-use rustc_hir::intravisit::{walk_body, Visitor};
+use rustc_hir::intravisit::{Visitor, walk_body};
 use rustc_hir::{Expr, ExprKind, HirId, HirIdSet, LetStmt, MatchSource, Node, PatKind, QPath, TyKind};
 use rustc_lint::{LateContext, LintContext};
 use rustc_middle::lint::{in_external_macro, is_from_async_await};
diff --git a/src/tools/clippy/clippy_lints/src/unit_types/unit_arg.rs b/src/tools/clippy/clippy_lints/src/unit_types/unit_arg.rs
index 978d49f09c3a..41b2ca5d268c 100644
--- a/src/tools/clippy/clippy_lints/src/unit_types/unit_arg.rs
+++ b/src/tools/clippy/clippy_lints/src/unit_types/unit_arg.rs
@@ -1,11 +1,11 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::is_from_proc_macro;
-use clippy_utils::source::{indent_of, reindent_multiline, SourceText, SpanRangeExt};
+use clippy_utils::source::{SourceText, SpanRangeExt, indent_of, reindent_multiline};
 use rustc_errors::Applicability;
 use rustc_hir::{Block, Expr, ExprKind, MatchSource, Node, StmtKind};
 use rustc_lint::LateContext;
 
-use super::{utils, UNIT_ARG};
+use super::{UNIT_ARG, utils};
 
 pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
     if expr.span.from_expansion() {
diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs b/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs
index 080efe983c2a..937e35dea96d 100644
--- a/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs
+++ b/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs
@@ -4,15 +4,15 @@ use clippy_utils::source::snippet;
 use clippy_utils::visitors::find_all_ret_expressions;
 use clippy_utils::{contains_return, is_res_lang_ctor, path_res, return_ty};
 use rustc_errors::Applicability;
-use rustc_hir::intravisit::FnKind;
 use rustc_hir::LangItem::{OptionSome, ResultOk};
+use rustc_hir::intravisit::FnKind;
 use rustc_hir::{Body, ExprKind, FnDecl, Impl, ItemKind, Node};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty;
 use rustc_session::impl_lint_pass;
+use rustc_span::Span;
 use rustc_span::def_id::LocalDefId;
 use rustc_span::symbol::sym;
-use rustc_span::Span;
 
 declare_clippy_lint! {
     /// ### What it does
diff --git a/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs b/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs
index 98f0be3135db..104be63bb157 100644
--- a/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs
+++ b/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs
@@ -1,14 +1,14 @@
 #![allow(clippy::wildcard_imports, clippy::enum_glob_use)]
 
-use clippy_config::msrvs::{self, Msrv};
 use clippy_config::Conf;
+use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::ast_utils::{eq_field_pat, eq_id, eq_maybe_qself, eq_pat, eq_path};
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::over;
+use rustc_ast::PatKind::*;
 use rustc_ast::mut_visit::*;
 use rustc_ast::ptr::P;
-use rustc_ast::PatKind::*;
-use rustc_ast::{self as ast, Mutability, Pat, PatKind, DUMMY_NODE_ID};
+use rustc_ast::{self as ast, DUMMY_NODE_ID, Mutability, Pat, PatKind};
 use rustc_ast_pretty::pprust;
 use rustc_errors::Applicability;
 use rustc_lint::{EarlyContext, EarlyLintPass};
@@ -16,7 +16,7 @@ use rustc_session::impl_lint_pass;
 use rustc_span::DUMMY_SP;
 use std::cell::Cell;
 use std::mem;
-use thin_vec::{thin_vec, ThinVec};
+use thin_vec::{ThinVec, thin_vec};
 
 declare_clippy_lint! {
     /// ### What it does
diff --git a/src/tools/clippy/clippy_lints/src/unsafe_removed_from_name.rs b/src/tools/clippy/clippy_lints/src/unsafe_removed_from_name.rs
index 309eaedac8d2..e70d2a2dafee 100644
--- a/src/tools/clippy/clippy_lints/src/unsafe_removed_from_name.rs
+++ b/src/tools/clippy/clippy_lints/src/unsafe_removed_from_name.rs
@@ -2,8 +2,8 @@ use clippy_utils::diagnostics::span_lint;
 use rustc_ast::ast::{Item, ItemKind, UseTree, UseTreeKind};
 use rustc_lint::{EarlyContext, EarlyLintPass};
 use rustc_session::declare_lint_pass;
-use rustc_span::symbol::Ident;
 use rustc_span::Span;
+use rustc_span::symbol::Ident;
 
 declare_clippy_lint! {
     /// ### What it does
diff --git a/src/tools/clippy/clippy_lints/src/unused_async.rs b/src/tools/clippy/clippy_lints/src/unused_async.rs
index 738fba54fa83..a1f08cf6623b 100644
--- a/src/tools/clippy/clippy_lints/src/unused_async.rs
+++ b/src/tools/clippy/clippy_lints/src/unused_async.rs
@@ -1,13 +1,13 @@
 use clippy_utils::diagnostics::span_lint_hir_and_then;
 use clippy_utils::is_def_id_trait_method;
 use rustc_hir::def::DefKind;
-use rustc_hir::intravisit::{walk_expr, walk_fn, FnKind, Visitor};
+use rustc_hir::intravisit::{FnKind, Visitor, walk_expr, walk_fn};
 use rustc_hir::{Body, Expr, ExprKind, FnDecl, Node, YieldSource};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::hir::nested_filter;
 use rustc_session::impl_lint_pass;
-use rustc_span::def_id::{LocalDefId, LocalDefIdSet};
 use rustc_span::Span;
+use rustc_span::def_id::{LocalDefId, LocalDefIdSet};
 
 declare_clippy_lint! {
     /// ### What it does
diff --git a/src/tools/clippy/clippy_lints/src/unused_io_amount.rs b/src/tools/clippy/clippy_lints/src/unused_io_amount.rs
index af7abd009d25..d2a21b11ef47 100644
--- a/src/tools/clippy/clippy_lints/src/unused_io_amount.rs
+++ b/src/tools/clippy/clippy_lints/src/unused_io_amount.rs
@@ -5,7 +5,7 @@ use hir::{ExprKind, HirId, PatKind};
 use rustc_hir as hir;
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::declare_lint_pass;
-use rustc_span::{sym, Span};
+use rustc_span::{Span, sym};
 
 declare_clippy_lint! {
     /// ### What it does
diff --git a/src/tools/clippy/clippy_lints/src/unused_peekable.rs b/src/tools/clippy/clippy_lints/src/unused_peekable.rs
index 86a811e17ca9..71aa57e0a14c 100644
--- a/src/tools/clippy/clippy_lints/src/unused_peekable.rs
+++ b/src/tools/clippy/clippy_lints/src/unused_peekable.rs
@@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_hir_and_then;
 use clippy_utils::ty::{is_type_diagnostic_item, peel_mid_ty_refs_is_mutable};
 use clippy_utils::{fn_def_id, is_trait_method, path_to_local_id, peel_ref_operators};
 use rustc_ast::Mutability;
-use rustc_hir::intravisit::{walk_expr, Visitor};
+use rustc_hir::intravisit::{Visitor, walk_expr};
 use rustc_hir::{Block, Expr, ExprKind, HirId, LetStmt, Node, PatKind, PathSegment, StmtKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::hir::nested_filter::OnlyBodies;
diff --git a/src/tools/clippy/clippy_lints/src/unused_trait_names.rs b/src/tools/clippy/clippy_lints/src/unused_trait_names.rs
new file mode 100644
index 000000000000..9fd6ebccd02f
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/unused_trait_names.rs
@@ -0,0 +1,94 @@
+use clippy_config::Conf;
+use clippy_config::msrvs::{self, Msrv};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::is_from_proc_macro;
+use clippy_utils::source::snippet_opt;
+use rustc_errors::Applicability;
+use rustc_hir::def::{DefKind, Res};
+use rustc_hir::{Item, ItemKind, UseKind};
+use rustc_lint::{LateContext, LateLintPass, LintContext as _};
+use rustc_middle::lint::in_external_macro;
+use rustc_middle::ty::Visibility;
+use rustc_session::impl_lint_pass;
+use rustc_span::symbol::kw;
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for `use Trait` where the Trait is only used for its methods and not referenced by a path directly.
+    ///
+    /// ### Why is this bad?
+    /// Traits imported that aren't used directly can be imported anonymously with `use Trait as _`.
+    /// It is more explicit, avoids polluting the current scope with unused names and can be useful to show which imports are required for traits.
+    ///
+    /// ### Example
+    /// ```no_run
+    /// use std::fmt::Write;
+    ///
+    /// fn main() {
+    ///     let mut s = String::new();
+    ///     let _ = write!(s, "hello, world!");
+    ///     println!("{s}");
+    /// }
+    /// ```
+    /// Use instead:
+    /// ```no_run
+    /// use std::fmt::Write as _;
+    ///
+    /// fn main() {
+    ///     let mut s = String::new();
+    ///     let _ = write!(s, "hello, world!");
+    ///     println!("{s}");
+    /// }
+    /// ```
+    #[clippy::version = "1.83.0"]
+    pub UNUSED_TRAIT_NAMES,
+    restriction,
+    "use items that import a trait but only use it anonymously"
+}
+
+pub struct UnusedTraitNames {
+    msrv: Msrv,
+}
+
+impl UnusedTraitNames {
+    pub fn new(conf: &'static Conf) -> Self {
+        Self {
+            msrv: conf.msrv.clone(),
+        }
+    }
+}
+
+impl_lint_pass!(UnusedTraitNames => [UNUSED_TRAIT_NAMES]);
+
+impl<'tcx> LateLintPass<'tcx> for UnusedTraitNames {
+    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
+        if self.msrv.meets(msrvs::UNDERSCORE_IMPORTS)
+            && !in_external_macro(cx.sess(), item.span)
+            && let ItemKind::Use(path, UseKind::Single) = item.kind
+            // Ignore imports that already use Underscore
+            && item.ident.name != kw::Underscore
+            // Only check traits
+            && let Some(Res::Def(DefKind::Trait, _)) = path.res.first()
+            && cx.tcx.maybe_unused_trait_imports(()).contains(&item.owner_id.def_id)
+            // Only check this import if it is visible to its module only (no pub, pub(crate), ...)
+            && let module = cx.tcx.parent_module_from_def_id(item.owner_id.def_id)
+            && cx.tcx.visibility(item.owner_id.def_id) == Visibility::Restricted(module.to_def_id())
+            && let Some(last_segment) = path.segments.last()
+            && let Some(snip) = snippet_opt(cx, last_segment.ident.span)
+            && !is_from_proc_macro(cx, &last_segment.ident)
+        {
+            let complete_span = last_segment.ident.span.to(item.ident.span);
+            span_lint_and_sugg(
+                cx,
+                UNUSED_TRAIT_NAMES,
+                complete_span,
+                "importing trait that is only used anonymously",
+                "use",
+                format!("{snip} as _"),
+                Applicability::MachineApplicable,
+            );
+        }
+    }
+
+    extract_msrv_attr!(LateContext);
+}
diff --git a/src/tools/clippy/clippy_lints/src/unused_unit.rs b/src/tools/clippy/clippy_lints/src/unused_unit.rs
index 65f431d338bd..d5309aade7aa 100644
--- a/src/tools/clippy/clippy_lints/src/unused_unit.rs
+++ b/src/tools/clippy/clippy_lints/src/unused_unit.rs
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::{position_before_rarrow, SpanRangeExt};
+use clippy_utils::source::{SpanRangeExt, position_before_rarrow};
 use rustc_ast::visit::FnKind;
-use rustc_ast::{ast, ClosureBinder};
+use rustc_ast::{ClosureBinder, ast};
 use rustc_errors::Applicability;
 use rustc_lint::{EarlyContext, EarlyLintPass};
 use rustc_session::declare_lint_pass;
diff --git a/src/tools/clippy/clippy_lints/src/unwrap.rs b/src/tools/clippy/clippy_lints/src/unwrap.rs
index 0b4ea0752b6b..596f0fd9c8b0 100644
--- a/src/tools/clippy/clippy_lints/src/unwrap.rs
+++ b/src/tools/clippy/clippy_lints/src/unwrap.rs
@@ -3,7 +3,7 @@ use clippy_utils::ty::is_type_diagnostic_item;
 use clippy_utils::usage::is_potentially_local_place;
 use clippy_utils::{higher, path_to_local};
 use rustc_errors::Applicability;
-use rustc_hir::intravisit::{walk_expr, walk_fn, FnKind, Visitor};
+use rustc_hir::intravisit::{FnKind, Visitor, walk_expr, walk_fn};
 use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, HirId, Node, PathSegment, UnOp};
 use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceWithHirId};
 use rustc_lint::{LateContext, LateLintPass};
@@ -13,7 +13,7 @@ use rustc_middle::mir::FakeReadCause;
 use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_session::declare_lint_pass;
 use rustc_span::def_id::LocalDefId;
-use rustc_span::{sym, Span};
+use rustc_span::{Span, sym};
 
 declare_clippy_lint! {
     /// ### What it does
diff --git a/src/tools/clippy/clippy_lints/src/unwrap_in_result.rs b/src/tools/clippy/clippy_lints/src/unwrap_in_result.rs
index f77badd97b7a..9b9a2ffbbc80 100644
--- a/src/tools/clippy/clippy_lints/src/unwrap_in_result.rs
+++ b/src/tools/clippy/clippy_lints/src/unwrap_in_result.rs
@@ -7,7 +7,7 @@ use rustc_hir as hir;
 use rustc_hir::ImplItemKind;
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::declare_lint_pass;
-use rustc_span::{sym, Span};
+use rustc_span::{Span, sym};
 
 declare_clippy_lint! {
     /// ### What it does
diff --git a/src/tools/clippy/clippy_lints/src/use_self.rs b/src/tools/clippy/clippy_lints/src/use_self.rs
index 5da48f4f7fbd..e340b419bd07 100644
--- a/src/tools/clippy/clippy_lints/src/use_self.rs
+++ b/src/tools/clippy/clippy_lints/src/use_self.rs
@@ -1,5 +1,5 @@
-use clippy_config::msrvs::{self, Msrv};
 use clippy_config::Conf;
+use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::is_from_proc_macro;
 use clippy_utils::ty::same_type_and_consts;
@@ -7,7 +7,7 @@ use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::Applicability;
 use rustc_hir::def::{CtorOf, DefKind, Res};
 use rustc_hir::def_id::LocalDefId;
-use rustc_hir::intravisit::{walk_inf, walk_ty, Visitor};
+use rustc_hir::intravisit::{Visitor, walk_inf, walk_ty};
 use rustc_hir::{
     self as hir, Expr, ExprKind, FnRetTy, FnSig, GenericArgsParentheses, GenericParam, GenericParamKind, HirId, Impl,
     ImplItemKind, Item, ItemKind, Pat, PatKind, Path, QPath, Ty, TyKind,
diff --git a/src/tools/clippy/clippy_lints/src/useless_conversion.rs b/src/tools/clippy/clippy_lints/src/useless_conversion.rs
index 2f7d54e73ed7..29a7949b3435 100644
--- a/src/tools/clippy/clippy_lints/src/useless_conversion.rs
+++ b/src/tools/clippy/clippy_lints/src/useless_conversion.rs
@@ -12,7 +12,7 @@ use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::traits::ObligationCause;
 use rustc_middle::ty::{self, EarlyBinder, GenericArg, GenericArgsRef, Ty, TypeVisitableExt};
 use rustc_session::impl_lint_pass;
-use rustc_span::{sym, Span};
+use rustc_span::{Span, sym};
 use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
 
 declare_clippy_lint! {
@@ -217,12 +217,12 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
                             },
                             ExprKind::MethodCall(.., args, _) => {
                                 cx.typeck_results().type_dependent_def_id(parent.hir_id).map(|did| {
-                                    return (
+                                    (
                                         did,
                                         args,
                                         cx.typeck_results().node_args(parent.hir_id),
                                         MethodOrFunction::Method,
-                                    );
+                                    )
                                 })
                             },
                             _ => None,
diff --git a/src/tools/clippy/clippy_lints/src/utils/author.rs b/src/tools/clippy/clippy_lints/src/utils/author.rs
index 0cce45290cfe..31f9d84f5e46 100644
--- a/src/tools/clippy/clippy_lints/src/utils/author.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/author.rs
@@ -1,6 +1,6 @@
 use clippy_utils::{get_attr, higher};
-use rustc_ast::ast::{LitFloatType, LitKind};
 use rustc_ast::LitIntType;
+use rustc_ast::ast::{LitFloatType, LitKind};
 use rustc_data_structures::fx::FxHashMap;
 use rustc_hir::{
     self as hir, ArrayLen, BindingMode, CaptureBy, Closure, ClosureKind, ConstArg, ConstArgKind, CoroutineKind,
diff --git a/src/tools/clippy/clippy_lints/src/utils/format_args_collector.rs b/src/tools/clippy/clippy_lints/src/utils/format_args_collector.rs
index f50ce6c99dea..8f314ce7a60c 100644
--- a/src/tools/clippy/clippy_lints/src/utils/format_args_collector.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/format_args_collector.rs
@@ -3,10 +3,10 @@ use clippy_utils::source::SpanRangeExt;
 use itertools::Itertools;
 use rustc_ast::{Crate, Expr, ExprKind, FormatArgs};
 use rustc_data_structures::fx::FxHashMap;
-use rustc_lexer::{tokenize, TokenKind};
+use rustc_lexer::{TokenKind, tokenize};
 use rustc_lint::{EarlyContext, EarlyLintPass};
 use rustc_session::impl_lint_pass;
-use rustc_span::{hygiene, Span};
+use rustc_span::{Span, hygiene};
 use std::iter::once;
 use std::mem;
 
diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/collapsible_calls.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/collapsible_calls.rs
index f75296826755..d8f101a8614d 100644
--- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/collapsible_calls.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/collapsible_calls.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet;
-use clippy_utils::{is_expr_path_def_path, is_lint_allowed, peel_blocks_with_stmt, SpanlessEq};
+use clippy_utils::{SpanlessEq, is_expr_path_def_path, is_lint_allowed, peel_blocks_with_stmt};
 use rustc_errors::Applicability;
 use rustc_hir::{Closure, Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/invalid_paths.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/invalid_paths.rs
index 0ffcb433481e..d444ad45087c 100644
--- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/invalid_paths.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/invalid_paths.rs
@@ -2,11 +2,11 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant};
 use clippy_utils::def_path_res;
 use clippy_utils::diagnostics::span_lint;
 use rustc_hir as hir;
-use rustc_hir::def::DefKind;
 use rustc_hir::Item;
+use rustc_hir::def::DefKind;
 use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty::fast_reject::SimplifiedType;
 use rustc_middle::ty::FloatTy;
+use rustc_middle::ty::fast_reject::SimplifiedType;
 use rustc_session::declare_lint_pass;
 use rustc_span::symbol::Symbol;
 
diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs
index 20526113d69e..7c45a5b2f097 100644
--- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs
@@ -12,7 +12,7 @@ use rustc_middle::hir::nested_filter;
 use rustc_session::impl_lint_pass;
 use rustc_span::source_map::Spanned;
 use rustc_span::symbol::Symbol;
-use rustc_span::{sym, Span};
+use rustc_span::{Span, sym};
 use {rustc_ast as ast, rustc_hir as hir};
 
 declare_clippy_lint! {
diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs
index 41183700f09e..76b0a52621be 100644
--- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs
@@ -8,12 +8,12 @@ use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::DefId;
 use rustc_hir::{Expr, ExprKind, LetStmt, Mutability, Node};
 use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::mir::interpret::{Allocation, GlobalAlloc};
 use rustc_middle::mir::ConstValue;
+use rustc_middle::mir::interpret::{Allocation, GlobalAlloc};
 use rustc_middle::ty::{self, Ty};
 use rustc_session::impl_lint_pass;
-use rustc_span::symbol::Symbol;
 use rustc_span::Span;
+use rustc_span::symbol::Symbol;
 
 use std::str;
 
diff --git a/src/tools/clippy/clippy_lints/src/vec.rs b/src/tools/clippy/clippy_lints/src/vec.rs
index d3e49bff422c..ce4f41e854d5 100644
--- a/src/tools/clippy/clippy_lints/src/vec.rs
+++ b/src/tools/clippy/clippy_lints/src/vec.rs
@@ -1,8 +1,8 @@
 use std::collections::BTreeMap;
 use std::ops::ControlFlow;
 
-use clippy_config::msrvs::{self, Msrv};
 use clippy_config::Conf;
+use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::consts::{ConstEvalCtxt, Constant};
 use clippy_utils::diagnostics::span_lint_hir_and_then;
 use clippy_utils::source::SpanRangeExt;
@@ -15,7 +15,7 @@ use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty;
 use rustc_middle::ty::layout::LayoutOf;
 use rustc_session::impl_lint_pass;
-use rustc_span::{sym, DesugaringKind, Span};
+use rustc_span::{DesugaringKind, Span, sym};
 
 #[expect(clippy::module_name_repetitions)]
 pub struct UselessVec {
diff --git a/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs b/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs
index a599415a2dd5..91dff5a95236 100644
--- a/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs
+++ b/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::higher::{get_vec_init_kind, VecInitKind};
+use clippy_utils::higher::{VecInitKind, get_vec_init_kind};
 use clippy_utils::source::snippet;
 use clippy_utils::visitors::for_each_local_use_after_expr;
 use clippy_utils::{get_parent_expr, path_to_local_id};
diff --git a/src/tools/clippy/clippy_lints/src/visibility.rs b/src/tools/clippy/clippy_lints/src/visibility.rs
index 7a85196cecaa..2e5fc5834e24 100644
--- a/src/tools/clippy/clippy_lints/src/visibility.rs
+++ b/src/tools/clippy/clippy_lints/src/visibility.rs
@@ -5,8 +5,8 @@ use rustc_errors::Applicability;
 use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
 use rustc_session::declare_lint_pass;
-use rustc_span::symbol::kw;
 use rustc_span::Span;
+use rustc_span::symbol::kw;
 
 declare_clippy_lint! {
     /// ### What it does
diff --git a/src/tools/clippy/clippy_lints/src/wildcard_imports.rs b/src/tools/clippy/clippy_lints/src/wildcard_imports.rs
index 7d9e58ad2f8d..405310512dff 100644
--- a/src/tools/clippy/clippy_lints/src/wildcard_imports.rs
+++ b/src/tools/clippy/clippy_lints/src/wildcard_imports.rs
@@ -10,7 +10,7 @@ use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::ty;
 use rustc_session::impl_lint_pass;
 use rustc_span::symbol::kw;
-use rustc_span::{sym, BytePos};
+use rustc_span::{BytePos, sym};
 
 declare_clippy_lint! {
     /// ### What it does
diff --git a/src/tools/clippy/clippy_lints/src/write.rs b/src/tools/clippy/clippy_lints/src/write.rs
index ec5a5896fb2b..1dd46ed5a891 100644
--- a/src/tools/clippy/clippy_lints/src/write.rs
+++ b/src/tools/clippy/clippy_lints/src/write.rs
@@ -1,8 +1,8 @@
 use clippy_config::Conf;
 use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
 use clippy_utils::is_in_test;
-use clippy_utils::macros::{format_arg_removal_span, root_macro_call_first_node, FormatArgsStorage, MacroCall};
-use clippy_utils::source::{expand_past_previous_comma, SpanRangeExt};
+use clippy_utils::macros::{FormatArgsStorage, MacroCall, format_arg_removal_span, root_macro_call_first_node};
+use clippy_utils::source::{SpanRangeExt, expand_past_previous_comma};
 use rustc_ast::token::LitKind;
 use rustc_ast::{
     FormatArgPosition, FormatArgPositionKind, FormatArgs, FormatArgsPiece, FormatOptions, FormatPlaceholder,
@@ -12,7 +12,7 @@ use rustc_errors::Applicability;
 use rustc_hir::{Expr, Impl, Item, ItemKind};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_session::impl_lint_pass;
-use rustc_span::{sym, BytePos, Span};
+use rustc_span::{BytePos, Span, sym};
 
 declare_clippy_lint! {
     /// ### What it does
diff --git a/src/tools/clippy/clippy_lints/src/zombie_processes.rs b/src/tools/clippy/clippy_lints/src/zombie_processes.rs
new file mode 100644
index 000000000000..ba2a80ee66b0
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/zombie_processes.rs
@@ -0,0 +1,334 @@
+use ControlFlow::{Break, Continue};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::{fn_def_id, get_enclosing_block, match_any_def_paths, match_def_path, path_to_local_id, paths};
+use rustc_ast::Mutability;
+use rustc_errors::Applicability;
+use rustc_hir::intravisit::{Visitor, walk_block, walk_expr, walk_local};
+use rustc_hir::{Expr, ExprKind, HirId, LetStmt, Node, PatKind, Stmt, StmtKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::declare_lint_pass;
+use rustc_span::sym;
+use std::ops::ControlFlow;
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Looks for code that spawns a process but never calls `wait()` on the child.
+    ///
+    /// ### Why is this bad?
+    /// As explained in the [standard library documentation](https://doc.rust-lang.org/stable/std/process/struct.Child.html#warning),
+    /// calling `wait()` is necessary on Unix platforms to properly release all OS resources associated with the process.
+    /// Not doing so will effectively leak process IDs and/or other limited global resources,
+    /// which can eventually lead to resource exhaustion, so it's recommended to call `wait()` in long-running applications.
+    /// Such processes are called "zombie processes".
+    ///
+    /// ### Example
+    /// ```rust
+    /// use std::process::Command;
+    ///
+    /// let _child = Command::new("ls").spawn().expect("failed to execute child");
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// use std::process::Command;
+    ///
+    /// let mut child = Command::new("ls").spawn().expect("failed to execute child");
+    /// child.wait().expect("failed to wait on child");
+    /// ```
+    #[clippy::version = "1.74.0"]
+    pub ZOMBIE_PROCESSES,
+    suspicious,
+    "not waiting on a spawned child process"
+}
+declare_lint_pass!(ZombieProcesses => [ZOMBIE_PROCESSES]);
+
+impl<'tcx> LateLintPass<'tcx> for ZombieProcesses {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
+        if let ExprKind::Call(..) | ExprKind::MethodCall(..) = expr.kind
+            && let Some(child_adt) = cx.typeck_results().expr_ty(expr).ty_adt_def()
+            && match_def_path(cx, child_adt.did(), &paths::CHILD)
+        {
+            match cx.tcx.parent_hir_node(expr.hir_id) {
+                Node::LetStmt(local)
+                    if let PatKind::Binding(_, local_id, ..) = local.pat.kind
+                        && let Some(enclosing_block) = get_enclosing_block(cx, expr.hir_id) =>
+                {
+                    let mut vis = WaitFinder::WalkUpTo(cx, local_id);
+
+                    // If it does have a `wait()` call, we're done. Don't lint.
+                    if let Break(BreakReason::WaitFound) = walk_block(&mut vis, enclosing_block) {
+                        return;
+                    }
+
+                    // Don't emit a suggestion since the binding is used later
+                    check(cx, expr, false);
+                },
+                Node::LetStmt(&LetStmt { pat, .. }) if let PatKind::Wild = pat.kind => {
+                    // `let _ = child;`, also dropped immediately without `wait()`ing
+                    check(cx, expr, true);
+                },
+                Node::Stmt(&Stmt {
+                    kind: StmtKind::Semi(_),
+                    ..
+                }) => {
+                    // Immediately dropped. E.g. `std::process::Command::new("echo").spawn().unwrap();`
+                    check(cx, expr, true);
+                },
+                _ => {},
+            }
+        }
+    }
+}
+
+enum BreakReason {
+    WaitFound,
+    EarlyReturn,
+}
+
+/// A visitor responsible for finding a `wait()` call on a local variable.
+///
+/// Conditional `wait()` calls are assumed to not call wait:
+/// ```ignore
+/// let mut c = Command::new("").spawn().unwrap();
+/// if true {
+///     c.wait();
+/// }
+/// ```
+///
+/// Note that this visitor does NOT explicitly look for `wait()` calls directly, but rather does the
+/// inverse -- checking if all uses of the local are either:
+/// - a field access (`child.{stderr,stdin,stdout}`)
+/// - calling `id` or `kill`
+/// - no use at all (e.g. `let _x = child;`)
+/// - taking a shared reference (`&`), `wait()` can't go through that
+///
+/// None of these are sufficient to prevent zombie processes.
+/// Doing it like this means more FNs, but FNs are better than FPs.
+///
+/// `return` expressions, conditional or not, short-circuit the visitor because
+/// if a `wait()` call hadn't been found at that point, it might never reach one at a later point:
+/// ```ignore
+/// let mut c = Command::new("").spawn().unwrap();
+/// if true {
+///     return; // Break(BreakReason::EarlyReturn)
+/// }
+/// c.wait(); // this might not be reachable
+/// ```
+enum WaitFinder<'a, 'tcx> {
+    WalkUpTo(&'a LateContext<'tcx>, HirId),
+    Found(&'a LateContext<'tcx>, HirId),
+}
+
+impl<'a, 'tcx> Visitor<'tcx> for WaitFinder<'a, 'tcx> {
+    type Result = ControlFlow;
+
+    fn visit_local(&mut self, l: &'tcx LetStmt<'tcx>) -> Self::Result {
+        if let Self::WalkUpTo(cx, local_id) = *self
+            && let PatKind::Binding(_, pat_id, ..) = l.pat.kind
+            && local_id == pat_id
+        {
+            *self = Self::Found(cx, local_id);
+        }
+
+        walk_local(self, l)
+    }
+
+    fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) -> Self::Result {
+        let Self::Found(cx, local_id) = *self else {
+            return walk_expr(self, ex);
+        };
+
+        if path_to_local_id(ex, local_id) {
+            match cx.tcx.parent_hir_node(ex.hir_id) {
+                Node::Stmt(Stmt {
+                    kind: StmtKind::Semi(_),
+                    ..
+                }) => {},
+                Node::Expr(expr) if let ExprKind::Field(..) = expr.kind => {},
+                Node::Expr(expr) if let ExprKind::AddrOf(_, Mutability::Not, _) = expr.kind => {},
+                Node::Expr(expr)
+                    if let Some(fn_did) = fn_def_id(cx, expr)
+                        && match_any_def_paths(cx, fn_did, &[&paths::CHILD_ID, &paths::CHILD_KILL]).is_some() => {},
+
+                // Conservatively assume that all other kinds of nodes call `.wait()` somehow.
+                _ => return Break(BreakReason::WaitFound),
+            }
+        } else {
+            match ex.kind {
+                ExprKind::Ret(..) => return Break(BreakReason::EarlyReturn),
+                ExprKind::If(cond, then, None) => {
+                    walk_expr(self, cond)?;
+
+                    // A `wait()` call in an if expression with no else is not enough:
+                    //
+                    // let c = spawn();
+                    // if true {
+                    //   c.wait();
+                    // }
+                    //
+                    // This might not call wait(). However, early returns are propagated,
+                    // because they might lead to a later wait() not being called.
+                    if let Break(BreakReason::EarlyReturn) = walk_expr(self, then) {
+                        return Break(BreakReason::EarlyReturn);
+                    }
+
+                    return Continue(());
+                },
+
+                ExprKind::If(cond, then, Some(else_)) => {
+                    walk_expr(self, cond)?;
+
+                    #[expect(clippy::unnested_or_patterns)]
+                    match (walk_expr(self, then), walk_expr(self, else_)) {
+                        (Continue(()), Continue(()))
+
+                        // `wait()` in one branch but nothing in the other does not count
+                        | (Continue(()), Break(BreakReason::WaitFound))
+                        | (Break(BreakReason::WaitFound), Continue(())) => {},
+
+                        // `wait()` in both branches is ok
+                        (Break(BreakReason::WaitFound), Break(BreakReason::WaitFound)) => {
+                            return Break(BreakReason::WaitFound);
+                        },
+
+                        // Propagate early returns in either branch
+                        (Break(BreakReason::EarlyReturn), _) | (_, Break(BreakReason::EarlyReturn)) => {
+                            return Break(BreakReason::EarlyReturn);
+                        },
+                    }
+
+                    return Continue(());
+                },
+                _ => {},
+            }
+        }
+
+        walk_expr(self, ex)
+    }
+}
+
+/// This function has shared logic between the different kinds of nodes that can trigger the lint.
+///
+/// In particular, `let  = ;` requires some custom additional logic
+/// such as checking that the binding is not used in certain ways, which isn't necessary for
+/// `let _ = ;`.
+///
+/// This checks if the program doesn't unconditionally exit after the spawn expression.
+fn check<'tcx>(cx: &LateContext<'tcx>, spawn_expr: &'tcx Expr<'tcx>, emit_suggestion: bool) {
+    let Some(block) = get_enclosing_block(cx, spawn_expr.hir_id) else {
+        return;
+    };
+
+    let mut vis = ExitPointFinder {
+        cx,
+        state: ExitPointState::WalkUpTo(spawn_expr.hir_id),
+    };
+    if let Break(ExitCallFound) = vis.visit_block(block) {
+        // Visitor found an unconditional `exit()` call, so don't lint.
+        return;
+    }
+
+    span_lint_and_then(
+        cx,
+        ZOMBIE_PROCESSES,
+        spawn_expr.span,
+        "spawned process is never `wait()`ed on",
+        |diag| {
+            if emit_suggestion {
+                diag.span_suggestion(
+                    spawn_expr.span.shrink_to_hi(),
+                    "try",
+                    ".wait()",
+                    Applicability::MaybeIncorrect,
+                );
+            } else {
+                diag.note("consider calling `.wait()`");
+            }
+
+            diag.note("not doing so might leave behind zombie processes")
+                .note("see https://doc.rust-lang.org/stable/std/process/struct.Child.html#warning");
+        },
+    );
+}
+
+/// Checks if the given expression exits the process.
+fn is_exit_expression(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
+    fn_def_id(cx, expr).is_some_and(|fn_did| {
+        cx.tcx.is_diagnostic_item(sym::process_exit, fn_did) || match_def_path(cx, fn_did, &paths::ABORT)
+    })
+}
+
+#[derive(Debug)]
+enum ExitPointState {
+    /// Still walking up to the expression that initiated the visitor.
+    WalkUpTo(HirId),
+    /// We're inside of a control flow construct (e.g. `if`, `match`, `loop`)
+    /// Within this, we shouldn't accept any `exit()` calls in here, but we can leave all of these
+    /// constructs later and still continue looking for an `exit()` call afterwards. Example:
+    /// ```ignore
+    /// Command::new("").spawn().unwrap();
+    ///
+    /// if true {                // depth=1
+    ///     if true {            // depth=2
+    ///         match () {       // depth=3
+    ///             () => loop { // depth=4
+    ///
+    ///                 std::process::exit();
+    ///                 ^^^^^^^^^^^^^^^^^^^^^ conditional exit call, ignored
+    ///
+    ///             }           // depth=3
+    ///         }               // depth=2
+    ///     }                   // depth=1
+    /// }                       // depth=0
+    ///
+    /// std::process::exit();
+    /// ^^^^^^^^^^^^^^^^^^^^^ this exit call is accepted because we're now unconditionally calling it
+    /// ```
+    /// We can only get into this state from `NoExit`.
+    InControlFlow { depth: u32 },
+    /// No exit call found yet, but looking for one.
+    NoExit,
+}
+
+fn expr_enters_control_flow(expr: &Expr<'_>) -> bool {
+    matches!(expr.kind, ExprKind::If(..) | ExprKind::Match(..) | ExprKind::Loop(..))
+}
+
+struct ExitPointFinder<'a, 'tcx> {
+    state: ExitPointState,
+    cx: &'a LateContext<'tcx>,
+}
+
+struct ExitCallFound;
+
+impl<'a, 'tcx> Visitor<'tcx> for ExitPointFinder<'a, 'tcx> {
+    type Result = ControlFlow;
+
+    fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) -> Self::Result {
+        match self.state {
+            ExitPointState::WalkUpTo(id) if expr.hir_id == id => {
+                self.state = ExitPointState::NoExit;
+                walk_expr(self, expr)
+            },
+            ExitPointState::NoExit if expr_enters_control_flow(expr) => {
+                self.state = ExitPointState::InControlFlow { depth: 1 };
+                walk_expr(self, expr)?;
+                if let ExitPointState::InControlFlow { .. } = self.state {
+                    self.state = ExitPointState::NoExit;
+                }
+                Continue(())
+            },
+            ExitPointState::NoExit if is_exit_expression(self.cx, expr) => Break(ExitCallFound),
+            ExitPointState::InControlFlow { ref mut depth } if expr_enters_control_flow(expr) => {
+                *depth += 1;
+                walk_expr(self, expr)?;
+                match self.state {
+                    ExitPointState::InControlFlow { depth: 1 } => self.state = ExitPointState::NoExit,
+                    ExitPointState::InControlFlow { ref mut depth } => *depth -= 1,
+                    _ => {},
+                }
+                Continue(())
+            },
+            _ => Continue(()),
+        }
+    }
+}
diff --git a/src/tools/clippy/clippy_utils/Cargo.toml b/src/tools/clippy/clippy_utils/Cargo.toml
index 629ce9d04d43..fe30b10c435e 100644
--- a/src/tools/clippy/clippy_utils/Cargo.toml
+++ b/src/tools/clippy/clippy_utils/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "clippy_utils"
-version = "0.1.82"
+version = "0.1.83"
 edition = "2021"
 publish = false
 
diff --git a/src/tools/clippy/clippy_utils/src/ast_utils.rs b/src/tools/clippy/clippy_utils/src/ast_utils.rs
index de8bbb619f8e..49323492e124 100644
--- a/src/tools/clippy/clippy_utils/src/ast_utils.rs
+++ b/src/tools/clippy/clippy_utils/src/ast_utils.rs
@@ -242,13 +242,16 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool {
 }
 
 fn eq_coroutine_kind(a: Option, b: Option) -> bool {
-    match (a, b) {
+    matches!(
+        (a, b),
         (Some(CoroutineKind::Async { .. }), Some(CoroutineKind::Async { .. }))
-        | (Some(CoroutineKind::Gen { .. }), Some(CoroutineKind::Gen { .. }))
-        | (Some(CoroutineKind::AsyncGen { .. }), Some(CoroutineKind::AsyncGen { .. }))
-        | (None, None) => true,
-        _ => false,
-    }
+            | (Some(CoroutineKind::Gen { .. }), Some(CoroutineKind::Gen { .. }))
+            | (
+                Some(CoroutineKind::AsyncGen { .. }),
+                Some(CoroutineKind::AsyncGen { .. })
+            )
+            | (None, None)
+    )
 }
 
 pub fn eq_field(l: &ExprField, r: &ExprField) -> bool {
diff --git a/src/tools/clippy/clippy_utils/src/ast_utils/ident_iter.rs b/src/tools/clippy/clippy_utils/src/ast_utils/ident_iter.rs
index eefcbabd835d..032cd3ed7399 100644
--- a/src/tools/clippy/clippy_utils/src/ast_utils/ident_iter.rs
+++ b/src/tools/clippy/clippy_utils/src/ast_utils/ident_iter.rs
@@ -1,5 +1,5 @@
 use core::iter::FusedIterator;
-use rustc_ast::visit::{walk_attribute, walk_expr, Visitor};
+use rustc_ast::visit::{Visitor, walk_attribute, walk_expr};
 use rustc_ast::{Attribute, Expr};
 use rustc_span::symbol::Ident;
 
diff --git a/src/tools/clippy/clippy_utils/src/attrs.rs b/src/tools/clippy/clippy_utils/src/attrs.rs
index 42c8b218d14e..edc9c6ccdff8 100644
--- a/src/tools/clippy/clippy_utils/src/attrs.rs
+++ b/src/tools/clippy/clippy_utils/src/attrs.rs
@@ -4,7 +4,7 @@ use rustc_lexer::TokenKind;
 use rustc_lint::LateContext;
 use rustc_middle::ty::{AdtDef, TyCtxt};
 use rustc_session::Session;
-use rustc_span::{sym, Span};
+use rustc_span::{Span, sym};
 use std::str::FromStr;
 
 use crate::source::SpanRangeExt;
@@ -183,15 +183,15 @@ pub fn span_contains_cfg(cx: &LateContext<'_>, s: Span) -> bool {
         let mut iter = tokenize_with_text(src);
 
         // Search for the token sequence [`#`, `[`, `cfg`]
-        while iter.any(|(t, _)| matches!(t, TokenKind::Pound)) {
-            let mut iter = iter.by_ref().skip_while(|(t, _)| {
+        while iter.any(|(t, ..)| matches!(t, TokenKind::Pound)) {
+            let mut iter = iter.by_ref().skip_while(|(t, ..)| {
                 matches!(
                     t,
                     TokenKind::Whitespace | TokenKind::LineComment { .. } | TokenKind::BlockComment { .. }
                 )
             });
-            if matches!(iter.next(), Some((TokenKind::OpenBracket, _)))
-                && matches!(iter.next(), Some((TokenKind::Ident, "cfg")))
+            if matches!(iter.next(), Some((TokenKind::OpenBracket, ..)))
+                && matches!(iter.next(), Some((TokenKind::Ident, "cfg", _)))
             {
                 return true;
             }
diff --git a/src/tools/clippy/clippy_utils/src/check_proc_macro.rs b/src/tools/clippy/clippy_utils/src/check_proc_macro.rs
index 2bd6837d973b..9143d292f670 100644
--- a/src/tools/clippy/clippy_utils/src/check_proc_macro.rs
+++ b/src/tools/clippy/clippy_utils/src/check_proc_macro.rs
@@ -12,9 +12,9 @@
 //! code was written, and check if the span contains that text. Note this will only work correctly
 //! if the span is not from a `macro_rules` based macro.
 
+use rustc_ast::AttrStyle;
 use rustc_ast::ast::{AttrKind, Attribute, IntTy, LitIntType, LitKind, StrStyle, TraitObjectSyntax, UintTy};
 use rustc_ast::token::CommentKind;
-use rustc_ast::AttrStyle;
 use rustc_hir::intravisit::FnKind;
 use rustc_hir::{
     Block, BlockCheckMode, Body, Closure, Destination, Expr, ExprKind, FieldDef, FnHeader, FnRetTy, HirId, Impl,
@@ -24,7 +24,7 @@ use rustc_hir::{
 use rustc_lint::{LateContext, LintContext};
 use rustc_middle::ty::TyCtxt;
 use rustc_session::Session;
-use rustc_span::symbol::{kw, Ident};
+use rustc_span::symbol::{Ident, kw};
 use rustc_span::{Span, Symbol};
 use rustc_target::spec::abi::Abi;
 
diff --git a/src/tools/clippy/clippy_utils/src/consts.rs b/src/tools/clippy/clippy_utils/src/consts.rs
index 760d5bc95f7f..bf47cf6d372d 100644
--- a/src/tools/clippy/clippy_utils/src/consts.rs
+++ b/src/tools/clippy/clippy_utils/src/consts.rs
@@ -1,24 +1,24 @@
 #![allow(clippy::float_cmp)]
 
 use crate::macros::HirNode;
-use crate::source::{walk_span_to_context, SpanRangeExt};
+use crate::source::{SpanRangeExt, walk_span_to_context};
 use crate::{clip, is_direct_expn_of, sext, unsext};
 
-use rustc_apfloat::ieee::{Half, Quad};
 use rustc_apfloat::Float;
+use rustc_apfloat::ieee::{Half, Quad};
 use rustc_ast::ast::{self, LitFloatType, LitKind};
 use rustc_data_structures::sync::Lrc;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::{BinOp, BinOpKind, Block, ConstBlock, Expr, ExprKind, HirId, Item, ItemKind, Node, QPath, UnOp};
 use rustc_lexer::tokenize;
 use rustc_lint::LateContext;
-use rustc_middle::mir::interpret::{alloc_range, Scalar};
 use rustc_middle::mir::ConstValue;
+use rustc_middle::mir::interpret::{Scalar, alloc_range};
 use rustc_middle::ty::{self, FloatTy, IntTy, ParamEnv, ScalarInt, Ty, TyCtxt, TypeckResults, UintTy};
 use rustc_middle::{bug, mir, span_bug};
 use rustc_span::def_id::DefId;
 use rustc_span::symbol::Ident;
-use rustc_span::{sym, SyntaxContext};
+use rustc_span::{SyntaxContext, sym};
 use rustc_target::abi::Size;
 use std::cell::Cell;
 use std::cmp::Ordering;
@@ -581,7 +581,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> {
     }
 
     fn constant_negate(&self, o: &Constant<'tcx>, ty: Ty<'_>) -> Option> {
-        use self::Constant::{Int, F32, F64};
+        use self::Constant::{F32, F64, Int};
         match *o {
             Int(value) => {
                 let ty::Int(ity) = *ty.kind() else { return None };
diff --git a/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs b/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs
index a6dd12a28c5b..9420d84b9591 100644
--- a/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs
+++ b/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs
@@ -14,12 +14,12 @@ use crate::ty::{all_predicates_of, is_copy};
 use crate::visitors::is_const_evaluatable;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::DefId;
-use rustc_hir::intravisit::{walk_expr, Visitor};
+use rustc_hir::intravisit::{Visitor, walk_expr};
 use rustc_hir::{BinOpKind, Block, Expr, ExprKind, QPath, UnOp};
 use rustc_lint::LateContext;
 use rustc_middle::ty;
 use rustc_middle::ty::adjustment::Adjust;
-use rustc_span::{sym, Symbol};
+use rustc_span::{Symbol, sym};
 use std::{cmp, ops};
 
 #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
diff --git a/src/tools/clippy/clippy_utils/src/higher.rs b/src/tools/clippy/clippy_utils/src/higher.rs
index e09cc9edf3ac..3175a9a1dd31 100644
--- a/src/tools/clippy/clippy_utils/src/higher.rs
+++ b/src/tools/clippy/clippy_utils/src/higher.rs
@@ -3,14 +3,14 @@
 #![deny(clippy::missing_docs_in_private_items)]
 
 use crate::consts::{ConstEvalCtxt, Constant};
-use crate::ty::is_type_diagnostic_item;
 use crate::is_expn_of;
+use crate::ty::is_type_diagnostic_item;
 
 use rustc_ast::ast;
 use rustc_hir as hir;
 use rustc_hir::{Arm, Block, Expr, ExprKind, HirId, LoopSource, MatchSource, Node, Pat, QPath};
 use rustc_lint::LateContext;
-use rustc_span::{sym, symbol, Span};
+use rustc_span::{Span, sym, symbol};
 
 /// The essential nodes of a desugared for loop as well as the entire span:
 /// `for pat in arg { body }` becomes `(pat, arg, body)`. Returns `(pat, arg, body, span)`.
diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs
index f61ef9ac1b05..76900379ac78 100644
--- a/src/tools/clippy/clippy_utils/src/hir_utils.rs
+++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs
@@ -1,20 +1,20 @@
 use crate::consts::ConstEvalCtxt;
 use crate::macros::macro_backtrace;
-use crate::source::{walk_span_to_context, SpanRange, SpanRangeExt};
+use crate::source::{SpanRange, SpanRangeExt, walk_span_to_context};
 use crate::tokenize_with_text;
 use rustc_ast::ast::InlineAsmTemplatePiece;
 use rustc_data_structures::fx::FxHasher;
-use rustc_hir::def::Res;
 use rustc_hir::MatchSource::TryDesugar;
+use rustc_hir::def::Res;
 use rustc_hir::{
     ArrayLen, AssocItemConstraint, BinOpKind, BindingMode, Block, BodyId, Closure, ConstArg, ConstArgKind, Expr,
     ExprField, ExprKind, FnRetTy, GenericArg, GenericArgs, HirId, HirIdMap, InlineAsmOperand, LetExpr, Lifetime,
     LifetimeName, Pat, PatField, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, Ty, TyKind,
 };
-use rustc_lexer::{tokenize, TokenKind};
+use rustc_lexer::{TokenKind, tokenize};
 use rustc_lint::LateContext;
 use rustc_middle::ty::TypeckResults;
-use rustc_span::{sym, BytePos, ExpnKind, MacroKind, Symbol, SyntaxContext};
+use rustc_span::{BytePos, ExpnKind, MacroKind, Symbol, SyntaxContext, sym};
 use std::hash::{Hash, Hasher};
 use std::ops::Range;
 
@@ -1194,8 +1194,8 @@ fn eq_span_tokens(
             && let Some(rsrc) = right.get_source_range(cx)
             && let Some(rsrc) = rsrc.as_str()
         {
-            let pred = |t: &(_, _)| pred(t.0);
-            let map = |(_, x)| x;
+            let pred = |&(token, ..): &(TokenKind, _, _)| pred(token);
+            let map = |(_, source, _)| source;
 
             let ltok = tokenize_with_text(lsrc).filter(pred).map(map);
             let rtok = tokenize_with_text(rsrc).filter(pred).map(map);
diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs
index 0d0d6219c5e0..6dbc3334157e 100644
--- a/src/tools/clippy/clippy_utils/src/lib.rs
+++ b/src/tools/clippy/clippy_utils/src/lib.rs
@@ -78,7 +78,7 @@ pub mod visitors;
 pub use self::attrs::*;
 pub use self::check_proc_macro::{is_from_proc_macro, is_span_if, is_span_match};
 pub use self::hir_utils::{
-    both, count_eq, eq_expr_value, hash_expr, hash_stmt, is_bool, over, HirEqInterExpr, SpanlessEq, SpanlessHash,
+    HirEqInterExpr, SpanlessEq, SpanlessHash, both, count_eq, eq_expr_value, hash_expr, hash_stmt, is_bool, over,
 };
 
 use core::mem;
@@ -94,20 +94,20 @@ use rustc_ast::ast::{self, LitKind, RangeLimits};
 use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::packed::Pu128;
 use rustc_data_structures::unhash::UnhashMap;
+use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk};
 use rustc_hir::def::{DefKind, Res};
-use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, LocalModDefId, LOCAL_CRATE};
+use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LOCAL_CRATE, LocalDefId, LocalModDefId};
 use rustc_hir::definitions::{DefPath, DefPathData};
 use rustc_hir::hir_id::{HirIdMap, HirIdSet};
-use rustc_hir::intravisit::{walk_expr, FnKind, Visitor};
-use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk};
+use rustc_hir::intravisit::{FnKind, Visitor, walk_expr};
 use rustc_hir::{
-    self as hir, def, Arm, ArrayLen, BindingMode, Block, BlockCheckMode, Body, ByRef, Closure, ConstArgKind,
-    ConstContext, Destination, Expr, ExprField, ExprKind, FnDecl, FnRetTy, GenericArgs, HirId, Impl, ImplItem,
-    ImplItemKind, ImplItemRef, Item, ItemKind, LangItem, LetStmt, MatchSource, Mutability, Node, OwnerId, OwnerNode,
-    Param, Pat, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitItemRef,
-    TraitRef, TyKind, UnOp,
+    self as hir, Arm, ArrayLen, BindingMode, Block, BlockCheckMode, Body, ByRef, Closure, ConstArgKind, ConstContext,
+    Destination, Expr, ExprField, ExprKind, FnDecl, FnRetTy, GenericArgs, HirId, Impl, ImplItem, ImplItemKind,
+    ImplItemRef, Item, ItemKind, LangItem, LetStmt, MatchSource, Mutability, Node, OwnerId, OwnerNode, Param, Pat,
+    PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitItemRef, TraitRef,
+    TyKind, UnOp, def,
 };
-use rustc_lexer::{tokenize, TokenKind};
+use rustc_lexer::{TokenKind, tokenize};
 use rustc_lint::{LateContext, Level, Lint, LintContext};
 use rustc_middle::hir::place::PlaceBase;
 use rustc_middle::mir::Const;
@@ -120,12 +120,12 @@ use rustc_middle::ty::{
 };
 use rustc_span::hygiene::{ExpnKind, MacroKind};
 use rustc_span::source_map::SourceMap;
-use rustc_span::symbol::{kw, Ident, Symbol};
-use rustc_span::{sym, Span};
+use rustc_span::symbol::{Ident, Symbol, kw};
+use rustc_span::{InnerSpan, Span, sym};
 use rustc_target::abi::Integer;
 use visitors::Visitable;
 
-use crate::consts::{mir_to_const, ConstEvalCtxt, Constant};
+use crate::consts::{ConstEvalCtxt, Constant, mir_to_const};
 use crate::higher::Range;
 use crate::ty::{adt_and_variant_of_res, can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type};
 use crate::visitors::for_each_expr_without_closures;
@@ -263,9 +263,13 @@ pub fn is_res_lang_ctor(cx: &LateContext<'_>, res: Res, lang_item: LangItem) ->
     }
 }
 
-
 /// Checks if `{ctor_call_id}(...)` is `{enum_item}::{variant_name}(...)`.
-pub fn is_enum_variant_ctor(cx: &LateContext<'_>, enum_item: Symbol, variant_name: Symbol, ctor_call_id: DefId) -> bool {
+pub fn is_enum_variant_ctor(
+    cx: &LateContext<'_>,
+    enum_item: Symbol,
+    variant_name: Symbol,
+    ctor_call_id: DefId,
+) -> bool {
     let Some(enum_def_id) = cx.tcx.get_diagnostic_item(enum_item) else {
         return false;
     };
@@ -589,14 +593,11 @@ fn find_primitive_impls<'tcx>(tcx: TyCtxt<'tcx>, name: &str) -> impl Iterator SimplifiedType::Float(FloatTy::F32),
         "f64" => SimplifiedType::Float(FloatTy::F64),
         _ => {
-            return Result::<&[_], rustc_errors::ErrorGuaranteed>::Ok(&[])
-                .into_iter()
-                .flatten()
-                .copied();
+            return [].iter().copied();
         },
     };
 
-    tcx.incoherent_impls(ty).into_iter().flatten().copied()
+    tcx.incoherent_impls(ty).into_iter().copied()
 }
 
 fn non_local_item_children_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: Symbol) -> Vec {
@@ -667,6 +668,17 @@ fn item_children_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: Symbol) -> Vec, name: Symbol) -> Vec {
+    tcx.crates(())
+        .iter()
+        .copied()
+        .filter(move |&num| tcx.crate_name(num) == name)
+        .map(CrateNum::as_def_id)
+        .map(|id| Res::Def(tcx.def_kind(id), id))
+        .collect()
+}
+
 /// Resolves a def path like `std::vec::Vec`.
 ///
 /// Can return multiple resolutions when there are multiple versions of the same crate, e.g.
@@ -677,15 +689,7 @@ fn item_children_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: Symbol) -> Vec, path: &[&str]) -> Vec {
-    fn find_crates(tcx: TyCtxt<'_>, name: Symbol) -> impl Iterator + '_ {
-        tcx.crates(())
-            .iter()
-            .copied()
-            .filter(move |&num| tcx.crate_name(num) == name)
-            .map(CrateNum::as_def_id)
-    }
-
-    let (base, mut path) = match *path {
+    let (base, path) = match *path {
         [primitive] => {
             return vec![PrimTy::from_name(Symbol::intern(primitive)).map_or(Res::Err, Res::PrimTy)];
         },
@@ -701,18 +705,25 @@ pub fn def_path_res(tcx: TyCtxt<'_>, path: &[&str]) -> Vec {
         None
     };
 
-    let starts = find_primitive_impls(tcx, base)
-        .chain(find_crates(tcx, base_sym))
+    let crates = find_primitive_impls(tcx, base)
         .chain(local_crate)
-        .map(|id| Res::Def(tcx.def_kind(id), id));
+        .map(|id| Res::Def(tcx.def_kind(id), id))
+        .chain(find_crates(tcx, base_sym))
+        .collect();
 
-    let mut resolutions: Vec = starts.collect();
+    def_path_res_with_base(tcx, crates, path)
+}
 
+/// Resolves a def path like `vec::Vec` with the base `std`.
+///
+/// This is lighter than [`def_path_res`], and should be called with [`find_crates`] looking up
+/// items from the same crate repeatedly, although should still be used sparingly.
+pub fn def_path_res_with_base(tcx: TyCtxt<'_>, mut base: Vec, mut path: &[&str]) -> Vec {
     while let [segment, rest @ ..] = path {
         path = rest;
         let segment = Symbol::intern(segment);
 
-        resolutions = resolutions
+        base = base
             .into_iter()
             .filter_map(|res| res.opt_def_id())
             .flat_map(|def_id| {
@@ -721,7 +732,6 @@ pub fn def_path_res(tcx: TyCtxt<'_>, path: &[&str]) -> Vec {
                 let inherent_impl_children = tcx
                     .inherent_impls(def_id)
                     .into_iter()
-                    .flatten()
                     .flat_map(|&impl_def_id| item_children_by_name(tcx, impl_def_id, segment));
 
                 let direct_children = item_children_by_name(tcx, def_id, segment);
@@ -731,7 +741,7 @@ pub fn def_path_res(tcx: TyCtxt<'_>, path: &[&str]) -> Vec {
             .collect();
     }
 
-    resolutions
+    base
 }
 
 /// Resolves a def path like `std::vec::Vec` to its [`DefId`]s, see [`def_path_res`].
@@ -2944,13 +2954,14 @@ pub fn expr_use_ctxt<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> ExprU
 }
 
 /// Tokenizes the input while keeping the text associated with each token.
-pub fn tokenize_with_text(s: &str) -> impl Iterator {
+pub fn tokenize_with_text(s: &str) -> impl Iterator {
     let mut pos = 0;
     tokenize(s).map(move |t| {
         let end = pos + t.len;
         let range = pos as usize..end as usize;
+        let inner = InnerSpan::new(range.start, range.end);
         pos = end;
-        (t.kind, s.get(range).unwrap_or_default())
+        (t.kind, s.get(range).unwrap_or_default(), inner)
     })
 }
 
@@ -2974,8 +2985,8 @@ pub fn span_contains_comment(sm: &SourceMap, span: Span) -> bool {
 pub fn span_extract_comment(sm: &SourceMap, span: Span) -> String {
     let snippet = sm.span_to_snippet(span).unwrap_or_default();
     let res = tokenize_with_text(&snippet)
-        .filter(|(t, _)| matches!(t, TokenKind::BlockComment { .. } | TokenKind::LineComment { .. }))
-        .map(|(_, s)| s)
+        .filter(|(t, ..)| matches!(t, TokenKind::BlockComment { .. } | TokenKind::LineComment { .. }))
+        .map(|(_, s, _)| s)
         .join("\n");
     res
 }
@@ -2995,7 +3006,7 @@ pub fn span_find_starting_semi(sm: &SourceMap, span: Span) -> Span {
 /// pat: Some(a)
 /// else_body: return None
 /// ```
-
+///
 /// And for this example:
 /// ```ignore
 /// let Some(FooBar { a, b }) = ex else { return None };
@@ -3005,7 +3016,7 @@ pub fn span_find_starting_semi(sm: &SourceMap, span: Span) -> Span {
 /// pat: Some(FooBar { a, b })
 /// else_body: return None
 /// ```
-
+///
 /// We output `Some(a)` in the first instance, and `Some(FooBar { a, b })` in the second, because
 /// the question mark operator is applicable here. Callers have to check whether we are in a
 /// constant or not.
diff --git a/src/tools/clippy/clippy_utils/src/macros.rs b/src/tools/clippy/clippy_utils/src/macros.rs
index 1d7479bff82d..9c4d19ac1f1d 100644
--- a/src/tools/clippy/clippy_utils/src/macros.rs
+++ b/src/tools/clippy/clippy_utils/src/macros.rs
@@ -1,6 +1,6 @@
 #![allow(clippy::similar_names)] // `expr` and `expn`
 
-use crate::visitors::{for_each_expr_without_closures, Descend};
+use crate::visitors::{Descend, for_each_expr_without_closures};
 
 use arrayvec::ArrayVec;
 use rustc_ast::{FormatArgs, FormatArgument, FormatPlaceholder};
@@ -10,7 +10,7 @@ use rustc_hir::{self as hir, Expr, ExprKind, HirId, Node, QPath};
 use rustc_lint::LateContext;
 use rustc_span::def_id::DefId;
 use rustc_span::hygiene::{self, MacroKind, SyntaxContext};
-use rustc_span::{sym, BytePos, ExpnData, ExpnId, ExpnKind, Span, SpanData, Symbol};
+use rustc_span::{BytePos, ExpnData, ExpnId, ExpnKind, Span, SpanData, Symbol, sym};
 use std::ops::ControlFlow;
 
 const FORMAT_MACRO_DIAG_ITEMS: &[Symbol] = &[
diff --git a/src/tools/clippy/clippy_utils/src/mir/mod.rs b/src/tools/clippy/clippy_utils/src/mir/mod.rs
index 654fb564848e..59bb5e35cda6 100644
--- a/src/tools/clippy/clippy_utils/src/mir/mod.rs
+++ b/src/tools/clippy/clippy_utils/src/mir/mod.rs
@@ -2,7 +2,7 @@ use rustc_hir::{Expr, HirId};
 use rustc_index::bit_set::BitSet;
 use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
 use rustc_middle::mir::{
-    traversal, BasicBlock, Body, InlineAsmOperand, Local, Location, Place, StatementKind, TerminatorKind, START_BLOCK,
+    BasicBlock, Body, InlineAsmOperand, Local, Location, Place, START_BLOCK, StatementKind, TerminatorKind, traversal,
 };
 use rustc_middle::ty::TyCtxt;
 
@@ -112,14 +112,10 @@ pub fn block_in_cycle(body: &Body<'_>, block: BasicBlock) -> bool {
 
 /// Convenience wrapper around `visit_local_usage`.
 pub fn used_exactly_once(mir: &Body<'_>, local: Local) -> Option {
-    visit_local_usage(
-        &[local],
-        mir,
-        Location {
-            block: START_BLOCK,
-            statement_index: 0,
-        },
-    )
+    visit_local_usage(&[local], mir, Location {
+        block: START_BLOCK,
+        statement_index: 0,
+    })
     .map(|mut vec| {
         let LocalUsage { local_use_locs, .. } = vec.remove(0);
         let mut locations = local_use_locs
diff --git a/src/tools/clippy/clippy_utils/src/paths.rs b/src/tools/clippy/clippy_utils/src/paths.rs
index a30edc48fff2..11a98b02f337 100644
--- a/src/tools/clippy/clippy_utils/src/paths.rs
+++ b/src/tools/clippy/clippy_utils/src/paths.rs
@@ -29,7 +29,11 @@ pub const SYM_MODULE: [&str; 3] = ["rustc_span", "symbol", "sym"];
 pub const SYNTAX_CONTEXT: [&str; 3] = ["rustc_span", "hygiene", "SyntaxContext"];
 
 // Paths in `core`/`alloc`/`std`. This should be avoided and cleaned up by adding diagnostic items.
-// ... none currently!
+pub const ABORT: [&str; 3] = ["std", "process", "abort"];
+pub const CHILD: [&str; 3] = ["std", "process", "Child"];
+pub const CHILD_ID: [&str; 4] = ["std", "process", "Child", "id"];
+pub const CHILD_KILL: [&str; 4] = ["std", "process", "Child", "kill"];
+pub const PANIC_ANY: [&str; 3] = ["std", "panic", "panic_any"];
 
 // Paths in clippy itself
 pub const MSRV: [&str; 3] = ["clippy_config", "msrvs", "Msrv"];
diff --git a/src/tools/clippy/clippy_utils/src/ptr.rs b/src/tools/clippy/clippy_utils/src/ptr.rs
index 991ea428dc33..273c1b0defab 100644
--- a/src/tools/clippy/clippy_utils/src/ptr.rs
+++ b/src/tools/clippy/clippy_utils/src/ptr.rs
@@ -1,5 +1,5 @@
 use crate::source::snippet;
-use crate::visitors::{for_each_expr_without_closures, Descend};
+use crate::visitors::{Descend, for_each_expr_without_closures};
 use crate::{path_to_local_id, strip_pat_refs};
 use core::ops::ControlFlow;
 use rustc_hir::{Body, BodyId, ExprKind, HirId, PatKind};
diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs
index d9befb3c157a..7a6107cfe539 100644
--- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs
+++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs
@@ -18,8 +18,8 @@ use rustc_middle::mir::{
 use rustc_middle::traits::{BuiltinImplSource, ImplSource, ObligationCause};
 use rustc_middle::ty::adjustment::PointerCoercion;
 use rustc_middle::ty::{self, GenericArgKind, TraitRef, Ty, TyCtxt};
-use rustc_span::symbol::sym;
 use rustc_span::Span;
+use rustc_span::symbol::sym;
 use rustc_trait_selection::traits::{ObligationCtxt, SelectionContext};
 use std::borrow::Cow;
 
@@ -123,7 +123,7 @@ fn check_rvalue<'tcx>(
             | CastKind::FloatToFloat
             | CastKind::FnPtrToPtr
             | CastKind::PtrToPtr
-            | CastKind::PointerCoercion(PointerCoercion::MutToConstPointer | PointerCoercion::ArrayToPointer),
+            | CastKind::PointerCoercion(PointerCoercion::MutToConstPointer | PointerCoercion::ArrayToPointer, _),
             operand,
             _,
         ) => check_operand(tcx, operand, span, body, msrv),
@@ -131,12 +131,12 @@ fn check_rvalue<'tcx>(
             CastKind::PointerCoercion(
                 PointerCoercion::UnsafeFnPointer
                 | PointerCoercion::ClosureFnPointer(_)
-                | PointerCoercion::ReifyFnPointer,
+                | PointerCoercion::ReifyFnPointer, _
             ),
             _,
             _,
         ) => Err((span, "function pointer casts are not allowed in const fn".into())),
-        Rvalue::Cast(CastKind::PointerCoercion(PointerCoercion::Unsize), op, cast_ty) => {
+        Rvalue::Cast(CastKind::PointerCoercion(PointerCoercion::Unsize, _), op, cast_ty) => {
             let Some(pointee_ty) = cast_ty.builtin_deref(true) else {
                 // We cannot allow this for now.
                 return Err((span, "unsizing casts are only allowed for references right now".into()));
@@ -154,7 +154,7 @@ fn check_rvalue<'tcx>(
         Rvalue::Cast(CastKind::PointerExposeProvenance, _, _) => {
             Err((span, "casting pointers to ints is unstable in const fn".into()))
         },
-        Rvalue::Cast(CastKind::DynStar, _, _) => {
+        Rvalue::Cast(CastKind::PointerCoercion(PointerCoercion::DynStar, _), _, _) => {
             // FIXME(dyn-star)
             unimplemented!()
         },
diff --git a/src/tools/clippy/clippy_utils/src/source.rs b/src/tools/clippy/clippy_utils/src/source.rs
index 482e1e0147b1..4ad7575e7201 100644
--- a/src/tools/clippy/clippy_utils/src/source.rs
+++ b/src/tools/clippy/clippy_utils/src/source.rs
@@ -9,10 +9,10 @@ use rustc_hir::{BlockCheckMode, Expr, ExprKind, UnsafeSource};
 use rustc_lint::{EarlyContext, LateContext};
 use rustc_middle::ty::TyCtxt;
 use rustc_session::Session;
-use rustc_span::source_map::{original_sp, SourceMap};
+use rustc_span::source_map::{SourceMap, original_sp};
 use rustc_span::{
-    hygiene, BytePos, FileNameDisplayPreference, Pos, SourceFile, SourceFileAndLine, Span, SpanData, SyntaxContext,
-    DUMMY_SP,
+    BytePos, DUMMY_SP, FileNameDisplayPreference, Pos, SourceFile, SourceFileAndLine, Span, SpanData, SyntaxContext,
+    hygiene,
 };
 use std::borrow::Cow;
 use std::fmt;
@@ -666,39 +666,6 @@ pub fn walk_span_to_context(span: Span, outer: SyntaxContext) -> Option {
     (outer_span.ctxt() == outer).then_some(outer_span)
 }
 
-/// Removes block comments from the given `Vec` of lines.
-///
-/// # Examples
-///
-/// ```rust,ignore
-/// without_block_comments(vec!["/*", "foo", "*/"]);
-/// // => vec![]
-///
-/// without_block_comments(vec!["bar", "/*", "foo", "*/"]);
-/// // => vec!["bar"]
-/// ```
-pub fn without_block_comments(lines: Vec<&str>) -> Vec<&str> {
-    let mut without = vec![];
-
-    let mut nest_level = 0;
-
-    for line in lines {
-        if line.contains("/*") {
-            nest_level += 1;
-            continue;
-        } else if line.contains("*/") {
-            nest_level -= 1;
-            continue;
-        }
-
-        if nest_level == 0 {
-            without.push(line);
-        }
-    }
-
-    without
-}
-
 /// Trims the whitespace from the start and the end of the span.
 pub fn trim_span(sm: &SourceMap, span: Span) -> Span {
     let data = span.data();
@@ -758,15 +725,12 @@ pub fn str_literal_to_char_literal(
             &snip[1..(snip.len() - 1)]
         };
 
-        let hint = format!(
-            "'{}'",
-            match ch {
-                "'" => "\\'",
-                r"\" => "\\\\",
-                "\\\"" => "\"", // no need to escape `"` in `'"'`
-                _ => ch,
-            }
-        );
+        let hint = format!("'{}'", match ch {
+            "'" => "\\'",
+            r"\" => "\\\\",
+            "\\\"" => "\"", // no need to escape `"` in `'"'`
+            _ => ch,
+        });
 
         Some(hint)
     } else {
@@ -776,7 +740,7 @@ pub fn str_literal_to_char_literal(
 
 #[cfg(test)]
 mod test {
-    use super::{reindent_multiline, without_block_comments};
+    use super::reindent_multiline;
 
     #[test]
     fn test_reindent_multiline_single_line() {
@@ -844,29 +808,4 @@ mod test {
         z
     }".into(), true, Some(8)));
     }
-
-    #[test]
-    fn test_without_block_comments_lines_without_block_comments() {
-        let result = without_block_comments(vec!["/*", "", "*/"]);
-        println!("result: {result:?}");
-        assert!(result.is_empty());
-
-        let result = without_block_comments(vec!["", "/*", "", "*/", "#[crate_type = \"lib\"]", "/*", "", "*/", ""]);
-        assert_eq!(result, vec!["", "#[crate_type = \"lib\"]", ""]);
-
-        let result = without_block_comments(vec!["/* rust", "", "*/"]);
-        assert!(result.is_empty());
-
-        let result = without_block_comments(vec!["/* one-line comment */"]);
-        assert!(result.is_empty());
-
-        let result = without_block_comments(vec!["/* nested", "/* multi-line", "comment", "*/", "test", "*/"]);
-        assert!(result.is_empty());
-
-        let result = without_block_comments(vec!["/* nested /* inline /* comment */ test */ */"]);
-        assert!(result.is_empty());
-
-        let result = without_block_comments(vec!["foo", "bar", "baz"]);
-        assert_eq!(result, vec!["foo", "bar", "baz"]);
-    }
 }
diff --git a/src/tools/clippy/clippy_utils/src/str_utils.rs b/src/tools/clippy/clippy_utils/src/str_utils.rs
index 421b25a77fe8..1588ee452dae 100644
--- a/src/tools/clippy/clippy_utils/src/str_utils.rs
+++ b/src/tools/clippy/clippy_utils/src/str_utils.rs
@@ -370,9 +370,11 @@ mod test {
         assert_eq!(camel_case_split("AbcDef"), vec!["Abc", "Def"]);
         assert_eq!(camel_case_split("Abc"), vec!["Abc"]);
         assert_eq!(camel_case_split("abcDef"), vec!["abc", "Def"]);
-        assert_eq!(
-            camel_case_split("\u{f6}\u{f6}AabABcd"),
-            vec!["\u{f6}\u{f6}", "Aab", "A", "Bcd"]
-        );
+        assert_eq!(camel_case_split("\u{f6}\u{f6}AabABcd"), vec![
+            "\u{f6}\u{f6}",
+            "Aab",
+            "A",
+            "Bcd"
+        ]);
     }
 }
diff --git a/src/tools/clippy/clippy_utils/src/sugg.rs b/src/tools/clippy/clippy_utils/src/sugg.rs
index 0f86d89c980d..3255c51d009c 100644
--- a/src/tools/clippy/clippy_utils/src/sugg.rs
+++ b/src/tools/clippy/clippy_utils/src/sugg.rs
@@ -180,8 +180,10 @@ impl<'a> Sugg<'a> {
     ) -> Self {
         use rustc_ast::ast::RangeLimits;
 
+        let mut snippet = |span: Span| snippet_with_context(cx, span, ctxt, default, app).0;
+
         match expr.kind {
-            _ if expr.span.ctxt() != ctxt => Sugg::NonParen(snippet_with_context(cx, expr.span, ctxt, default, app).0),
+            _ if expr.span.ctxt() != ctxt => Sugg::NonParen(snippet(expr.span)),
             ast::ExprKind::AddrOf(..)
             | ast::ExprKind::Closure { .. }
             | ast::ExprKind::If(..)
@@ -224,46 +226,38 @@ impl<'a> Sugg<'a> {
             | ast::ExprKind::While(..)
             | ast::ExprKind::Await(..)
             | ast::ExprKind::Err(_)
-            | ast::ExprKind::Dummy => Sugg::NonParen(snippet_with_context(cx, expr.span, ctxt, default, app).0),
+            | ast::ExprKind::Dummy => Sugg::NonParen(snippet(expr.span)),
             ast::ExprKind::Range(ref lhs, ref rhs, RangeLimits::HalfOpen) => Sugg::BinOp(
                 AssocOp::DotDot,
-                lhs.as_ref().map_or("".into(), |lhs| {
-                    snippet_with_context(cx, lhs.span, ctxt, default, app).0
-                }),
-                rhs.as_ref().map_or("".into(), |rhs| {
-                    snippet_with_context(cx, rhs.span, ctxt, default, app).0
-                }),
+                lhs.as_ref().map_or("".into(), |lhs| snippet(lhs.span)),
+                rhs.as_ref().map_or("".into(), |rhs| snippet(rhs.span)),
             ),
             ast::ExprKind::Range(ref lhs, ref rhs, RangeLimits::Closed) => Sugg::BinOp(
                 AssocOp::DotDotEq,
-                lhs.as_ref().map_or("".into(), |lhs| {
-                    snippet_with_context(cx, lhs.span, ctxt, default, app).0
-                }),
-                rhs.as_ref().map_or("".into(), |rhs| {
-                    snippet_with_context(cx, rhs.span, ctxt, default, app).0
-                }),
+                lhs.as_ref().map_or("".into(), |lhs| snippet(lhs.span)),
+                rhs.as_ref().map_or("".into(), |rhs| snippet(rhs.span)),
             ),
             ast::ExprKind::Assign(ref lhs, ref rhs, _) => Sugg::BinOp(
                 AssocOp::Assign,
-                snippet_with_context(cx, lhs.span, ctxt, default, app).0,
-                snippet_with_context(cx, rhs.span, ctxt, default, app).0,
+                snippet(lhs.span),
+                snippet(rhs.span),
             ),
             ast::ExprKind::AssignOp(op, ref lhs, ref rhs) => Sugg::BinOp(
                 astbinop2assignop(op),
-                snippet_with_context(cx, lhs.span, ctxt, default, app).0,
-                snippet_with_context(cx, rhs.span, ctxt, default, app).0,
+                snippet(lhs.span),
+                snippet(rhs.span),
             ),
             ast::ExprKind::Binary(op, ref lhs, ref rhs) => Sugg::BinOp(
                 AssocOp::from_ast_binop(op.node),
-                snippet_with_context(cx, lhs.span, ctxt, default, app).0,
-                snippet_with_context(cx, rhs.span, ctxt, default, app).0,
+                snippet(lhs.span),
+                snippet(rhs.span),
             ),
             ast::ExprKind::Cast(ref lhs, ref ty) |
             //FIXME(chenyukang), remove this after type ascription is removed from AST
             ast::ExprKind::Type(ref lhs, ref ty) => Sugg::BinOp(
                 AssocOp::As,
-                snippet_with_context(cx, lhs.span, ctxt, default, app).0,
-                snippet_with_context(cx, ty.span, ctxt, default, app).0,
+                snippet(lhs.span),
+                snippet(ty.span),
             ),
         }
     }
diff --git a/src/tools/clippy/clippy_utils/src/ty.rs b/src/tools/clippy/clippy_utils/src/ty.rs
index 585134209ca3..4e3a61a1aa5f 100644
--- a/src/tools/clippy/clippy_utils/src/ty.rs
+++ b/src/tools/clippy/clippy_utils/src/ty.rs
@@ -12,8 +12,8 @@ use rustc_hir::def_id::DefId;
 use rustc_hir::{Expr, FnDecl, LangItem, Safety, TyKind};
 use rustc_infer::infer::TyCtxtInferExt;
 use rustc_lint::LateContext;
-use rustc_middle::mir::interpret::Scalar;
 use rustc_middle::mir::ConstValue;
+use rustc_middle::mir::interpret::Scalar;
 use rustc_middle::traits::EvaluationResult;
 use rustc_middle::ty::layout::ValidityRequirement;
 use rustc_middle::ty::{
@@ -22,7 +22,7 @@ use rustc_middle::ty::{
     TypeVisitable, TypeVisitableExt, TypeVisitor, UintTy, Upcast, VariantDef, VariantDiscr,
 };
 use rustc_span::symbol::Ident;
-use rustc_span::{sym, Span, Symbol, DUMMY_SP};
+use rustc_span::{DUMMY_SP, Span, Symbol, sym};
 use rustc_target::abi::VariantIdx;
 use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
 use rustc_trait_selection::traits::query::normalize::QueryNormalizeExt;
@@ -606,10 +606,13 @@ fn is_uninit_value_valid_for_ty_fallback<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'t
         ty::Tuple(types) => types.iter().all(|ty| is_uninit_value_valid_for_ty(cx, ty)),
         // Unions are always fine right now.
         // This includes MaybeUninit, the main way people use uninitialized memory.
-        // For ADTs, we could look at all fields just like for tuples, but that's potentially
-        // exponential, so let's avoid doing that for now. Code doing that is sketchy enough to
-        // just use an `#[allow()]`.
-        ty::Adt(adt, _) => adt.is_union(),
+        ty::Adt(adt, _) if adt.is_union() => true,
+        // Types (e.g. `UnsafeCell>`) that recursively contain only types that can be uninit
+        // can themselves be uninit too.
+        // This purposefully ignores enums as they may have a discriminant that can't be uninit.
+        ty::Adt(adt, args) if adt.is_struct() => adt
+            .all_fields()
+            .all(|field| is_uninit_value_valid_for_ty(cx, field.ty(cx.tcx, args))),
         // For the rest, conservatively assume that they cannot be uninit.
         _ => false,
     }
@@ -1316,7 +1319,7 @@ pub fn deref_chain<'cx, 'tcx>(cx: &'cx LateContext<'tcx>, ty: Ty<'tcx>) -> impl
 /// If you need this, you should wrap this call in `clippy_utils::ty::deref_chain().any(...)`.
 pub fn get_adt_inherent_method<'a>(cx: &'a LateContext<'_>, ty: Ty<'_>, method_name: Symbol) -> Option<&'a AssocItem> {
     if let Some(ty_did) = ty.ty_adt_def().map(AdtDef::did) {
-        cx.tcx.inherent_impls(ty_did).into_iter().flatten().find_map(|&did| {
+        cx.tcx.inherent_impls(ty_did).into_iter().find_map(|&did| {
             cx.tcx
                 .associated_items(did)
                 .filter_by_name_unhygienic(method_name)
diff --git a/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs b/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs
index 875ddec259ee..e612e9c6cb60 100644
--- a/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs
+++ b/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs
@@ -14,14 +14,14 @@
 use crate::def_path_res;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::DefId;
-use rustc_hir::intravisit::{walk_qpath, walk_ty, Visitor};
+use rustc_hir::intravisit::{Visitor, walk_qpath, walk_ty};
 use rustc_hir::{self as hir, Expr, ExprKind, GenericArgs, HirId, Node, PathSegment, QPath, TyKind};
 use rustc_lint::LateContext;
 use rustc_middle::ty::{self, AdtDef, GenericArgKind, Ty};
 use rustc_span::{Span, Symbol};
 
 mod certainty;
-use certainty::{join, meet, Certainty, Meet};
+use certainty::{Certainty, Meet, join, meet};
 
 pub fn expr_type_is_certain(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
     expr_type_certainty(cx, expr).is_certain()
diff --git a/src/tools/clippy/clippy_utils/src/usage.rs b/src/tools/clippy/clippy_utils/src/usage.rs
index fbf3d95365ac..1230b60c3a6b 100644
--- a/src/tools/clippy/clippy_utils/src/usage.rs
+++ b/src/tools/clippy/clippy_utils/src/usage.rs
@@ -1,4 +1,4 @@
-use crate::visitors::{for_each_expr, for_each_expr_without_closures, Descend, Visitable};
+use crate::visitors::{Descend, Visitable, for_each_expr, for_each_expr_without_closures};
 use crate::{self as utils, get_enclosing_loop_or_multi_call_closure};
 use core::ops::ControlFlow;
 use hir::def::Res;
diff --git a/src/tools/clippy/clippy_utils/src/visitors.rs b/src/tools/clippy/clippy_utils/src/visitors.rs
index e5b6d3965e93..6d9a85a1181c 100644
--- a/src/tools/clippy/clippy_utils/src/visitors.rs
+++ b/src/tools/clippy/clippy_utils/src/visitors.rs
@@ -1,10 +1,10 @@
 use crate::ty::needs_ordered_drop;
 use crate::{get_enclosing_block, path_to_local_id};
 use core::ops::ControlFlow;
-use rustc_ast::visit::{try_visit, VisitorResult};
+use rustc_ast::visit::{VisitorResult, try_visit};
 use rustc_hir as hir;
 use rustc_hir::def::{CtorKind, DefKind, Res};
-use rustc_hir::intravisit::{self, walk_block, walk_expr, Visitor};
+use rustc_hir::intravisit::{self, Visitor, walk_block, walk_expr};
 use rustc_hir::{
     AnonConst, Arm, Block, BlockCheckMode, Body, BodyId, Expr, ExprKind, HirId, ItemId, ItemKind, LetExpr, Pat, QPath,
     Safety, Stmt, UnOp, UnsafeSource,
diff --git a/src/tools/clippy/declare_clippy_lint/Cargo.toml b/src/tools/clippy/declare_clippy_lint/Cargo.toml
index 31270241b0b2..67a1f7cc72c5 100644
--- a/src/tools/clippy/declare_clippy_lint/Cargo.toml
+++ b/src/tools/clippy/declare_clippy_lint/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "declare_clippy_lint"
-version = "0.1.82"
+version = "0.1.83"
 edition = "2021"
 publish = false
 
diff --git a/src/tools/clippy/declare_clippy_lint/src/lib.rs b/src/tools/clippy/declare_clippy_lint/src/lib.rs
index 6aa24329b065..fefc1a0a6c40 100644
--- a/src/tools/clippy/declare_clippy_lint/src/lib.rs
+++ b/src/tools/clippy/declare_clippy_lint/src/lib.rs
@@ -5,7 +5,7 @@
 use proc_macro::TokenStream;
 use quote::{format_ident, quote};
 use syn::parse::{Parse, ParseStream};
-use syn::{parse_macro_input, Attribute, Error, Expr, ExprLit, Ident, Lit, LitStr, Meta, Result, Token};
+use syn::{Attribute, Error, Expr, ExprLit, Ident, Lit, LitStr, Meta, Result, Token, parse_macro_input};
 
 fn parse_attr(path: [&'static str; LEN], attr: &Attribute) -> Option {
     if let Meta::NameValue(name_value) = &attr.meta {
@@ -140,15 +140,12 @@ pub fn declare_clippy_lint(input: TokenStream) -> TokenStream {
 
     let mut category = category.to_string();
 
-    let level = format_ident!(
-        "{}",
-        match category.as_str() {
-            "correctness" => "Deny",
-            "style" | "suspicious" | "complexity" | "perf" => "Warn",
-            "pedantic" | "restriction" | "cargo" | "nursery" | "internal" => "Allow",
-            _ => panic!("unknown category {category}"),
-        },
-    );
+    let level = format_ident!("{}", match category.as_str() {
+        "correctness" => "Deny",
+        "style" | "suspicious" | "complexity" | "perf" => "Warn",
+        "pedantic" | "restriction" | "cargo" | "nursery" | "internal" => "Allow",
+        _ => panic!("unknown category {category}"),
+    },);
 
     let info_name = format_ident!("{name}_INFO");
 
diff --git a/src/tools/clippy/lintcheck/src/driver.rs b/src/tools/clippy/lintcheck/src/driver.rs
index 2fda2b00f873..87ab14ba0cf8 100644
--- a/src/tools/clippy/lintcheck/src/driver.rs
+++ b/src/tools/clippy/lintcheck/src/driver.rs
@@ -1,4 +1,4 @@
-use crate::recursive::{deserialize_line, serialize_line, DriverInfo};
+use crate::recursive::{DriverInfo, deserialize_line, serialize_line};
 
 use std::io::{self, BufReader, Write};
 use std::net::TcpStream;
diff --git a/src/tools/clippy/lintcheck/src/recursive.rs b/src/tools/clippy/lintcheck/src/recursive.rs
index 6a662970ae84..57073f523648 100644
--- a/src/tools/clippy/lintcheck/src/recursive.rs
+++ b/src/tools/clippy/lintcheck/src/recursive.rs
@@ -3,8 +3,8 @@
 //! [`LintcheckServer`] to ask if it should be skipped, and if not sends the stderr of running
 //! clippy on the crate to the server
 
-use crate::input::RecursiveOptions;
 use crate::ClippyWarning;
+use crate::input::RecursiveOptions;
 
 use std::collections::HashSet;
 use std::io::{BufRead, BufReader, Read, Write};
diff --git a/src/tools/clippy/rust-toolchain b/src/tools/clippy/rust-toolchain
index 0be2e81810eb..b431599c224e 100644
--- a/src/tools/clippy/rust-toolchain
+++ b/src/tools/clippy/rust-toolchain
@@ -1,4 +1,4 @@
 [toolchain]
-channel = "nightly-2024-08-23"
+channel = "nightly-2024-09-22"
 components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]
 profile = "minimal"
diff --git a/src/tools/clippy/src/driver.rs b/src/tools/clippy/src/driver.rs
index 0ac3f35b4465..324f754e6155 100644
--- a/src/tools/clippy/src/driver.rs
+++ b/src/tools/clippy/src/driver.rs
@@ -15,9 +15,9 @@ extern crate rustc_session;
 extern crate rustc_span;
 
 use rustc_interface::interface;
+use rustc_session::EarlyDiagCtxt;
 use rustc_session::config::ErrorOutputType;
 use rustc_session::parse::ParseSess;
-use rustc_session::EarlyDiagCtxt;
 use rustc_span::symbol::Symbol;
 
 use std::env;
@@ -268,8 +268,6 @@ pub fn main() {
                 },
                 _ => Some(s.to_string()),
             })
-            // FIXME: remove this line in 1.79 to only keep `--cfg clippy`.
-            .chain(vec!["--cfg".into(), r#"feature="cargo-clippy""#.into()])
             .chain(vec!["--cfg".into(), "clippy".into()])
             .collect::>();
 
diff --git a/src/tools/clippy/tests/compile-test.rs b/src/tools/clippy/tests/compile-test.rs
index 9754254cdd0d..af2aa5192577 100644
--- a/src/tools/clippy/tests/compile-test.rs
+++ b/src/tools/clippy/tests/compile-test.rs
@@ -2,26 +2,25 @@
 #![warn(rust_2018_idioms, unused_lifetimes)]
 #![allow(unused_extern_crates)]
 
-use cargo_metadata::diagnostic::{Applicability, Diagnostic};
 use cargo_metadata::Message;
+use cargo_metadata::diagnostic::{Applicability, Diagnostic};
 use clippy_config::ClippyConfiguration;
+use clippy_lints::LintInfo;
 use clippy_lints::declared_lints::LINTS;
 use clippy_lints::deprecated_lints::{DEPRECATED, DEPRECATED_VERSION, RENAMED};
-use clippy_lints::LintInfo;
 use serde::{Deserialize, Serialize};
 use test_utils::IS_RUSTC_TEST_SUITE;
-use ui_test::custom_flags::rustfix::RustfixMode;
 use ui_test::custom_flags::Flag;
+use ui_test::custom_flags::rustfix::RustfixMode;
 use ui_test::spanned::Spanned;
-use ui_test::test_result::TestRun;
-use ui_test::{status_emitter, Args, CommandBuilder, Config, Match, OutputConflictHandling};
+use ui_test::{Args, CommandBuilder, Config, Match, OutputConflictHandling, status_emitter};
 
 use std::collections::{BTreeMap, HashMap};
 use std::env::{self, set_var, var_os};
 use std::ffi::{OsStr, OsString};
 use std::fmt::Write;
 use std::path::{Path, PathBuf};
-use std::sync::mpsc::{channel, Sender};
+use std::sync::mpsc::{Sender, channel};
 use std::{fs, iter, thread};
 
 // Test dependencies may need an `extern crate` here to ensure that they show up
@@ -469,15 +468,14 @@ fn applicability_ord(applicability: &Applicability) -> u8 {
 impl Flag for DiagnosticCollector {
     fn post_test_action(
         &self,
-        _config: &ui_test::per_test_config::TestConfig<'_>,
-        _cmd: &mut std::process::Command,
+        _config: &ui_test::per_test_config::TestConfig,
         output: &std::process::Output,
-        _build_manager: &ui_test::build_manager::BuildManager<'_>,
-    ) -> Result, ui_test::Errored> {
+        _build_manager: &ui_test::build_manager::BuildManager,
+    ) -> Result<(), ui_test::Errored> {
         if !output.stderr.is_empty() {
             self.sender.send(output.stderr.clone()).unwrap();
         }
-        Ok(Vec::new())
+        Ok(())
     }
 
     fn clone_inner(&self) -> Box {
diff --git a/src/tools/clippy/tests/config-metadata.rs b/src/tools/clippy/tests/config-metadata.rs
index 3e3711873baa..628dfc8f758a 100644
--- a/src/tools/clippy/tests/config-metadata.rs
+++ b/src/tools/clippy/tests/config-metadata.rs
@@ -1,6 +1,6 @@
 #![feature(rustc_private)]
 
-use clippy_config::{get_configuration_metadata, ClippyConfiguration};
+use clippy_config::{ClippyConfiguration, get_configuration_metadata};
 use itertools::Itertools;
 use regex::Regex;
 use std::borrow::Cow;
diff --git a/src/tools/clippy/tests/dogfood.rs b/src/tools/clippy/tests/dogfood.rs
index 8d10d5e7161a..858be389a9e6 100644
--- a/src/tools/clippy/tests/dogfood.rs
+++ b/src/tools/clippy/tests/dogfood.rs
@@ -6,11 +6,9 @@
 #![warn(rust_2018_idioms, unused_lifetimes)]
 
 use itertools::Itertools;
-use std::fs::File;
 use std::io::{self, IsTerminal};
 use std::path::PathBuf;
 use std::process::Command;
-use std::time::SystemTime;
 use test_utils::IS_RUSTC_TEST_SUITE;
 use ui_test::Args;
 
@@ -28,11 +26,7 @@ fn main() {
             println!("dogfood: test");
         }
     } else if !args.skip.iter().any(|arg| arg == "dogfood") {
-        if args.filters.iter().any(|arg| arg == "collect_metadata") {
-            collect_metadata();
-        } else {
-            dogfood();
-        }
+        dogfood();
     }
 }
 
@@ -61,47 +55,6 @@ fn dogfood() {
     );
 }
 
-fn collect_metadata() {
-    assert!(cfg!(feature = "internal"));
-
-    // Setup for validation
-    let metadata_output_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("util/gh-pages/lints.json");
-    let start_time = SystemTime::now();
-
-    // Run collection as is
-    std::env::set_var("ENABLE_METADATA_COLLECTION", "1");
-    assert!(run_clippy_for_package(
-        "clippy_lints",
-        &["-A", "unfulfilled_lint_expectations"]
-    ));
-
-    // Check if cargo caching got in the way
-    if let Ok(file) = File::open(metadata_output_path) {
-        if let Ok(metadata) = file.metadata() {
-            if let Ok(last_modification) = metadata.modified() {
-                if last_modification > start_time {
-                    // The output file has been modified. Most likely by a hungry
-                    // metadata collection monster. So We'll return.
-                    return;
-                }
-            }
-        }
-    }
-
-    // Force cargo to invalidate the caches
-    filetime::set_file_mtime(
-        PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("clippy_lints/src/lib.rs"),
-        filetime::FileTime::now(),
-    )
-    .unwrap();
-
-    // Running the collection again
-    assert!(run_clippy_for_package(
-        "clippy_lints",
-        &["-A", "unfulfilled_lint_expectations"]
-    ));
-}
-
 #[must_use]
 fn run_clippy_for_package(project: &str, args: &[&str]) -> bool {
     let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
diff --git a/src/tools/clippy/tests/ui-internal/unnecessary_def_path.fixed b/src/tools/clippy/tests/ui-internal/unnecessary_def_path.fixed
index 3908411da827..0a9948428341 100644
--- a/src/tools/clippy/tests/ui-internal/unnecessary_def_path.fixed
+++ b/src/tools/clippy/tests/ui-internal/unnecessary_def_path.fixed
@@ -13,7 +13,7 @@ extern crate rustc_span;
 use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item, match_type};
 #[allow(unused)]
 use clippy_utils::{
-    is_expr_path_def_path, is_path_diagnostic_item, is_res_diagnostic_ctor, is_res_lang_ctor, is_trait_method,
+    is_enum_variant_ctor, is_expr_path_def_path, is_path_diagnostic_item, is_res_lang_ctor, is_trait_method,
     match_def_path, match_trait_method, path_res,
 };
 
@@ -22,8 +22,8 @@ use rustc_hir::LangItem;
 #[allow(unused)]
 use rustc_span::sym;
 
-use rustc_hir::def_id::DefId;
 use rustc_hir::Expr;
+use rustc_hir::def_id::DefId;
 use rustc_lint::LateContext;
 use rustc_middle::ty::Ty;
 
diff --git a/src/tools/clippy/tests/ui-internal/unnecessary_def_path.rs b/src/tools/clippy/tests/ui-internal/unnecessary_def_path.rs
index 632e26215a4d..ba68de6c6d01 100644
--- a/src/tools/clippy/tests/ui-internal/unnecessary_def_path.rs
+++ b/src/tools/clippy/tests/ui-internal/unnecessary_def_path.rs
@@ -13,7 +13,7 @@ extern crate rustc_span;
 use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item, match_type};
 #[allow(unused)]
 use clippy_utils::{
-    is_expr_path_def_path, is_path_diagnostic_item, is_res_diagnostic_ctor, is_res_lang_ctor, is_trait_method,
+    is_enum_variant_ctor, is_expr_path_def_path, is_path_diagnostic_item, is_res_lang_ctor, is_trait_method,
     match_def_path, match_trait_method, path_res,
 };
 
@@ -22,8 +22,8 @@ use rustc_hir::LangItem;
 #[allow(unused)]
 use rustc_span::sym;
 
-use rustc_hir::def_id::DefId;
 use rustc_hir::Expr;
+use rustc_hir::def_id::DefId;
 use rustc_lint::LateContext;
 use rustc_middle::ty::Ty;
 
diff --git a/src/tools/clippy/tests/ui-toml/disallowed_names_append/disallowed_names.rs b/src/tools/clippy/tests/ui-toml/disallowed_names_append/disallowed_names.rs
index a2e2b46c4269..61ae8de8e335 100644
--- a/src/tools/clippy/tests/ui-toml/disallowed_names_append/disallowed_names.rs
+++ b/src/tools/clippy/tests/ui-toml/disallowed_names_append/disallowed_names.rs
@@ -1,4 +1,4 @@
-#[warn(clippy::disallowed_names)]
+#![warn(clippy::disallowed_names)]
 
 fn main() {
     // `foo` is part of the default configuration
diff --git a/src/tools/clippy/tests/ui-toml/disallowed_names_replace/disallowed_names.rs b/src/tools/clippy/tests/ui-toml/disallowed_names_replace/disallowed_names.rs
index a2e2b46c4269..61ae8de8e335 100644
--- a/src/tools/clippy/tests/ui-toml/disallowed_names_replace/disallowed_names.rs
+++ b/src/tools/clippy/tests/ui-toml/disallowed_names_replace/disallowed_names.rs
@@ -1,4 +1,4 @@
-#[warn(clippy::disallowed_names)]
+#![warn(clippy::disallowed_names)]
 
 fn main() {
     // `foo` is part of the default configuration
diff --git a/src/tools/clippy/tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.fixed b/src/tools/clippy/tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.fixed
index 5f4f007cf5c7..a6072111dc06 100644
--- a/src/tools/clippy/tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.fixed
+++ b/src/tools/clippy/tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.fixed
@@ -2,7 +2,7 @@
 
 use std::alloc as colla;
 use std::option::Option as Maybe;
-use std::process::{exit as goodbye, Child as Kid};
+use std::process::{Child as Kid, exit as goodbye};
 use std::thread::sleep as thread_sleep;
 #[rustfmt::skip]
 use std::{
diff --git a/src/tools/clippy/tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.rs b/src/tools/clippy/tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.rs
index f60058c86288..c2b61aab5b3c 100644
--- a/src/tools/clippy/tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.rs
+++ b/src/tools/clippy/tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.rs
@@ -2,7 +2,7 @@
 
 use std::alloc as colla;
 use std::option::Option as Maybe;
-use std::process::{exit as wrong_exit, Child as Kid};
+use std::process::{Child as Kid, exit as wrong_exit};
 use std::thread::sleep;
 #[rustfmt::skip]
 use std::{
diff --git a/src/tools/clippy/tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.stderr b/src/tools/clippy/tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.stderr
index f66938c83fbf..d3bb07ec47fd 100644
--- a/src/tools/clippy/tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.stderr
+++ b/src/tools/clippy/tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.stderr
@@ -1,8 +1,8 @@
 error: this import should be renamed
-  --> tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.rs:5:20
+  --> tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.rs:5:34
    |
-LL | use std::process::{exit as wrong_exit, Child as Kid};
-   |                    ^^^^^^^^^^^^^^^^^^ help: try: `exit as goodbye`
+LL | use std::process::{Child as Kid, exit as wrong_exit};
+   |                                  ^^^^^^^^^^^^^^^^^^ help: try: `exit as goodbye`
    |
    = note: `-D clippy::missing-enforced-import-renames` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::missing_enforced_import_renames)]`
diff --git a/src/tools/clippy/tests/ui-toml/panic/panic.rs b/src/tools/clippy/tests/ui-toml/panic/panic.rs
index 618a37ddfc55..b6264c950e45 100644
--- a/src/tools/clippy/tests/ui-toml/panic/panic.rs
+++ b/src/tools/clippy/tests/ui-toml/panic/panic.rs
@@ -1,5 +1,6 @@
 //@compile-flags: --test
 #![warn(clippy::panic)]
+use std::panic::panic_any;
 
 fn main() {
     enum Enam {
@@ -12,6 +13,10 @@ fn main() {
     }
 }
 
+fn issue_13292() {
+    panic_any("should lint")
+}
+
 #[test]
 fn lonely_test() {
     enum Enam {
diff --git a/src/tools/clippy/tests/ui-toml/panic/panic.stderr b/src/tools/clippy/tests/ui-toml/panic/panic.stderr
index bf7503e086c9..a034207d919f 100644
--- a/src/tools/clippy/tests/ui-toml/panic/panic.stderr
+++ b/src/tools/clippy/tests/ui-toml/panic/panic.stderr
@@ -1,5 +1,5 @@
 error: `panic` should not be present in production code
-  --> tests/ui-toml/panic/panic.rs:11:14
+  --> tests/ui-toml/panic/panic.rs:12:14
    |
 LL |         _ => panic!(""),
    |              ^^^^^^^^^^
@@ -7,5 +7,11 @@ LL |         _ => panic!(""),
    = note: `-D clippy::panic` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::panic)]`
 
-error: aborting due to 1 previous error
+error: `panic_any` should not be present in production code
+  --> tests/ui-toml/panic/panic.rs:17:5
+   |
+LL |     panic_any("should lint")
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
 
diff --git a/src/tools/clippy/tests/ui/allow_attributes.fixed b/src/tools/clippy/tests/ui/allow_attributes.fixed
index 49ee3ee17c70..058dbb77a320 100644
--- a/src/tools/clippy/tests/ui/allow_attributes.fixed
+++ b/src/tools/clippy/tests/ui/allow_attributes.fixed
@@ -58,3 +58,10 @@ fn msrv_1_80() {
     #[allow(unused)]
     let x = 1;
 }
+
+#[deny(clippy::allow_attributes)]
+fn deny_allow_attributes() -> Option {
+    let allow = None;
+    allow?;
+    Some(42)
+}
diff --git a/src/tools/clippy/tests/ui/allow_attributes.rs b/src/tools/clippy/tests/ui/allow_attributes.rs
index 854acf8348dc..6d94ce50e4c3 100644
--- a/src/tools/clippy/tests/ui/allow_attributes.rs
+++ b/src/tools/clippy/tests/ui/allow_attributes.rs
@@ -58,3 +58,10 @@ fn msrv_1_80() {
     #[allow(unused)]
     let x = 1;
 }
+
+#[deny(clippy::allow_attributes)]
+fn deny_allow_attributes() -> Option {
+    let allow = None;
+    allow?;
+    Some(42)
+}
diff --git a/src/tools/clippy/tests/ui/allow_attributes_without_reason.rs b/src/tools/clippy/tests/ui/allow_attributes_without_reason.rs
index 86f6b2c5742a..334e7ddd9d23 100644
--- a/src/tools/clippy/tests/ui/allow_attributes_without_reason.rs
+++ b/src/tools/clippy/tests/ui/allow_attributes_without_reason.rs
@@ -15,7 +15,6 @@ use proc_macros::{external, with_span};
 #[warn(deref_nullptr)]
 #[deny(deref_nullptr)]
 #[forbid(deref_nullptr)]
-
 fn main() {
     external! {
         #[allow(dead_code)]
diff --git a/src/tools/clippy/tests/ui/allow_attributes_without_reason.stderr b/src/tools/clippy/tests/ui/allow_attributes_without_reason.stderr
index 9bc3ca0f2afd..86d7845df041 100644
--- a/src/tools/clippy/tests/ui/allow_attributes_without_reason.stderr
+++ b/src/tools/clippy/tests/ui/allow_attributes_without_reason.stderr
@@ -36,7 +36,7 @@ LL | #[expect(dead_code)]
    = help: try adding a reason at the end with `, reason = ".."`
 
 error: `allow` attribute without specifying a reason
-  --> tests/ui/allow_attributes_without_reason.rs:47:5
+  --> tests/ui/allow_attributes_without_reason.rs:46:5
    |
 LL |     #[allow(unused)]
    |     ^^^^^^^^^^^^^^^^
@@ -44,7 +44,7 @@ LL |     #[allow(unused)]
    = help: try adding a reason at the end with `, reason = ".."`
 
 error: `allow` attribute without specifying a reason
-  --> tests/ui/allow_attributes_without_reason.rs:47:5
+  --> tests/ui/allow_attributes_without_reason.rs:46:5
    |
 LL |     #[allow(unused)]
    |     ^^^^^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/arithmetic_side_effects.rs b/src/tools/clippy/tests/ui/arithmetic_side_effects.rs
index 0838d064a5fa..3f2040730851 100644
--- a/src/tools/clippy/tests/ui/arithmetic_side_effects.rs
+++ b/src/tools/clippy/tests/ui/arithmetic_side_effects.rs
@@ -2,7 +2,6 @@
 
 #![feature(f128)]
 #![feature(f16)]
-
 #![allow(
     clippy::assign_op_pattern,
     clippy::erasing_op,
diff --git a/src/tools/clippy/tests/ui/arithmetic_side_effects.stderr b/src/tools/clippy/tests/ui/arithmetic_side_effects.stderr
index 78914667bf30..78b1aca4b8a4 100644
--- a/src/tools/clippy/tests/ui/arithmetic_side_effects.stderr
+++ b/src/tools/clippy/tests/ui/arithmetic_side_effects.stderr
@@ -1,5 +1,5 @@
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:167:13
+  --> tests/ui/arithmetic_side_effects.rs:166:13
    |
 LL |     let _ = 1f16 + 1f16;
    |             ^^^^^^^^^^^
@@ -8,733 +8,733 @@ LL |     let _ = 1f16 + 1f16;
    = help: to override `-D warnings` add `#[allow(clippy::arithmetic_side_effects)]`
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:170:13
+  --> tests/ui/arithmetic_side_effects.rs:169:13
    |
 LL |     let _ = 1f128 + 1f128;
    |             ^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:308:5
+  --> tests/ui/arithmetic_side_effects.rs:307:5
    |
 LL |     _n += 1;
    |     ^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:309:5
+  --> tests/ui/arithmetic_side_effects.rs:308:5
    |
 LL |     _n += &1;
    |     ^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:310:5
+  --> tests/ui/arithmetic_side_effects.rs:309:5
    |
 LL |     _n -= 1;
    |     ^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:311:5
+  --> tests/ui/arithmetic_side_effects.rs:310:5
    |
 LL |     _n -= &1;
    |     ^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:312:5
+  --> tests/ui/arithmetic_side_effects.rs:311:5
    |
 LL |     _n /= 0;
    |     ^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:313:5
+  --> tests/ui/arithmetic_side_effects.rs:312:5
    |
 LL |     _n /= &0;
    |     ^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:314:5
+  --> tests/ui/arithmetic_side_effects.rs:313:5
    |
 LL |     _n %= 0;
    |     ^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:315:5
+  --> tests/ui/arithmetic_side_effects.rs:314:5
    |
 LL |     _n %= &0;
    |     ^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:316:5
+  --> tests/ui/arithmetic_side_effects.rs:315:5
    |
 LL |     _n *= 2;
    |     ^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:317:5
+  --> tests/ui/arithmetic_side_effects.rs:316:5
    |
 LL |     _n *= &2;
    |     ^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:318:5
+  --> tests/ui/arithmetic_side_effects.rs:317:5
    |
 LL |     _n += -1;
    |     ^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:319:5
+  --> tests/ui/arithmetic_side_effects.rs:318:5
    |
 LL |     _n += &-1;
    |     ^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:320:5
+  --> tests/ui/arithmetic_side_effects.rs:319:5
    |
 LL |     _n -= -1;
    |     ^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:321:5
+  --> tests/ui/arithmetic_side_effects.rs:320:5
    |
 LL |     _n -= &-1;
    |     ^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:322:5
+  --> tests/ui/arithmetic_side_effects.rs:321:5
    |
 LL |     _n /= -0;
    |     ^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:323:5
+  --> tests/ui/arithmetic_side_effects.rs:322:5
    |
 LL |     _n /= &-0;
    |     ^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:324:5
+  --> tests/ui/arithmetic_side_effects.rs:323:5
    |
 LL |     _n %= -0;
    |     ^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:325:5
+  --> tests/ui/arithmetic_side_effects.rs:324:5
    |
 LL |     _n %= &-0;
    |     ^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:326:5
+  --> tests/ui/arithmetic_side_effects.rs:325:5
    |
 LL |     _n *= -2;
    |     ^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:327:5
+  --> tests/ui/arithmetic_side_effects.rs:326:5
    |
 LL |     _n *= &-2;
    |     ^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:328:5
+  --> tests/ui/arithmetic_side_effects.rs:327:5
    |
 LL |     _custom += Custom;
    |     ^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:329:5
+  --> tests/ui/arithmetic_side_effects.rs:328:5
    |
 LL |     _custom += &Custom;
    |     ^^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:330:5
+  --> tests/ui/arithmetic_side_effects.rs:329:5
    |
 LL |     _custom -= Custom;
    |     ^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:331:5
+  --> tests/ui/arithmetic_side_effects.rs:330:5
    |
 LL |     _custom -= &Custom;
    |     ^^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:332:5
+  --> tests/ui/arithmetic_side_effects.rs:331:5
    |
 LL |     _custom /= Custom;
    |     ^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:333:5
+  --> tests/ui/arithmetic_side_effects.rs:332:5
    |
 LL |     _custom /= &Custom;
    |     ^^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:334:5
+  --> tests/ui/arithmetic_side_effects.rs:333:5
    |
 LL |     _custom %= Custom;
    |     ^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:335:5
+  --> tests/ui/arithmetic_side_effects.rs:334:5
    |
 LL |     _custom %= &Custom;
    |     ^^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:336:5
+  --> tests/ui/arithmetic_side_effects.rs:335:5
    |
 LL |     _custom *= Custom;
    |     ^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:337:5
+  --> tests/ui/arithmetic_side_effects.rs:336:5
    |
 LL |     _custom *= &Custom;
    |     ^^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:338:5
+  --> tests/ui/arithmetic_side_effects.rs:337:5
    |
 LL |     _custom >>= Custom;
    |     ^^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:339:5
+  --> tests/ui/arithmetic_side_effects.rs:338:5
    |
 LL |     _custom >>= &Custom;
    |     ^^^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:340:5
+  --> tests/ui/arithmetic_side_effects.rs:339:5
    |
 LL |     _custom <<= Custom;
    |     ^^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:341:5
+  --> tests/ui/arithmetic_side_effects.rs:340:5
    |
 LL |     _custom <<= &Custom;
    |     ^^^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:342:5
+  --> tests/ui/arithmetic_side_effects.rs:341:5
    |
 LL |     _custom += -Custom;
    |     ^^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:343:5
+  --> tests/ui/arithmetic_side_effects.rs:342:5
    |
 LL |     _custom += &-Custom;
    |     ^^^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:344:5
+  --> tests/ui/arithmetic_side_effects.rs:343:5
    |
 LL |     _custom -= -Custom;
    |     ^^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:345:5
+  --> tests/ui/arithmetic_side_effects.rs:344:5
    |
 LL |     _custom -= &-Custom;
    |     ^^^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:346:5
+  --> tests/ui/arithmetic_side_effects.rs:345:5
    |
 LL |     _custom /= -Custom;
    |     ^^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:347:5
+  --> tests/ui/arithmetic_side_effects.rs:346:5
    |
 LL |     _custom /= &-Custom;
    |     ^^^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:348:5
+  --> tests/ui/arithmetic_side_effects.rs:347:5
    |
 LL |     _custom %= -Custom;
    |     ^^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:349:5
+  --> tests/ui/arithmetic_side_effects.rs:348:5
    |
 LL |     _custom %= &-Custom;
    |     ^^^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:350:5
+  --> tests/ui/arithmetic_side_effects.rs:349:5
    |
 LL |     _custom *= -Custom;
    |     ^^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:351:5
+  --> tests/ui/arithmetic_side_effects.rs:350:5
    |
 LL |     _custom *= &-Custom;
    |     ^^^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:352:5
+  --> tests/ui/arithmetic_side_effects.rs:351:5
    |
 LL |     _custom >>= -Custom;
    |     ^^^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:353:5
+  --> tests/ui/arithmetic_side_effects.rs:352:5
    |
 LL |     _custom >>= &-Custom;
    |     ^^^^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:354:5
+  --> tests/ui/arithmetic_side_effects.rs:353:5
    |
 LL |     _custom <<= -Custom;
    |     ^^^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:355:5
+  --> tests/ui/arithmetic_side_effects.rs:354:5
    |
 LL |     _custom <<= &-Custom;
    |     ^^^^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:358:10
+  --> tests/ui/arithmetic_side_effects.rs:357:10
    |
 LL |     _n = _n + 1;
    |          ^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:359:10
+  --> tests/ui/arithmetic_side_effects.rs:358:10
    |
 LL |     _n = _n + &1;
    |          ^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:360:10
+  --> tests/ui/arithmetic_side_effects.rs:359:10
    |
 LL |     _n = 1 + _n;
    |          ^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:361:10
+  --> tests/ui/arithmetic_side_effects.rs:360:10
    |
 LL |     _n = &1 + _n;
    |          ^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:362:10
+  --> tests/ui/arithmetic_side_effects.rs:361:10
    |
 LL |     _n = _n - 1;
    |          ^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:363:10
+  --> tests/ui/arithmetic_side_effects.rs:362:10
    |
 LL |     _n = _n - &1;
    |          ^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:364:10
+  --> tests/ui/arithmetic_side_effects.rs:363:10
    |
 LL |     _n = 1 - _n;
    |          ^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:365:10
+  --> tests/ui/arithmetic_side_effects.rs:364:10
    |
 LL |     _n = &1 - _n;
    |          ^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:366:10
+  --> tests/ui/arithmetic_side_effects.rs:365:10
    |
 LL |     _n = _n / 0;
    |          ^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:367:10
+  --> tests/ui/arithmetic_side_effects.rs:366:10
    |
 LL |     _n = _n / &0;
    |          ^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:368:10
+  --> tests/ui/arithmetic_side_effects.rs:367:10
    |
 LL |     _n = _n % 0;
    |          ^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:369:10
+  --> tests/ui/arithmetic_side_effects.rs:368:10
    |
 LL |     _n = _n % &0;
    |          ^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:370:10
+  --> tests/ui/arithmetic_side_effects.rs:369:10
    |
 LL |     _n = _n * 2;
    |          ^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:371:10
+  --> tests/ui/arithmetic_side_effects.rs:370:10
    |
 LL |     _n = _n * &2;
    |          ^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:372:10
+  --> tests/ui/arithmetic_side_effects.rs:371:10
    |
 LL |     _n = 2 * _n;
    |          ^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:373:10
+  --> tests/ui/arithmetic_side_effects.rs:372:10
    |
 LL |     _n = &2 * _n;
    |          ^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:374:10
+  --> tests/ui/arithmetic_side_effects.rs:373:10
    |
 LL |     _n = 23 + &85;
    |          ^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:375:10
+  --> tests/ui/arithmetic_side_effects.rs:374:10
    |
 LL |     _n = &23 + 85;
    |          ^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:376:10
+  --> tests/ui/arithmetic_side_effects.rs:375:10
    |
 LL |     _n = &23 + &85;
    |          ^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:377:15
+  --> tests/ui/arithmetic_side_effects.rs:376:15
    |
 LL |     _custom = _custom + _custom;
    |               ^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:378:15
+  --> tests/ui/arithmetic_side_effects.rs:377:15
    |
 LL |     _custom = _custom + &_custom;
    |               ^^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:379:15
+  --> tests/ui/arithmetic_side_effects.rs:378:15
    |
 LL |     _custom = Custom + _custom;
    |               ^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:380:15
+  --> tests/ui/arithmetic_side_effects.rs:379:15
    |
 LL |     _custom = &Custom + _custom;
    |               ^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:381:15
+  --> tests/ui/arithmetic_side_effects.rs:380:15
    |
 LL |     _custom = _custom - Custom;
    |               ^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:382:15
+  --> tests/ui/arithmetic_side_effects.rs:381:15
    |
 LL |     _custom = _custom - &Custom;
    |               ^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:383:15
+  --> tests/ui/arithmetic_side_effects.rs:382:15
    |
 LL |     _custom = Custom - _custom;
    |               ^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:384:15
+  --> tests/ui/arithmetic_side_effects.rs:383:15
    |
 LL |     _custom = &Custom - _custom;
    |               ^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:385:15
+  --> tests/ui/arithmetic_side_effects.rs:384:15
    |
 LL |     _custom = _custom / Custom;
    |               ^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:386:15
+  --> tests/ui/arithmetic_side_effects.rs:385:15
    |
 LL |     _custom = _custom / &Custom;
    |               ^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:387:15
+  --> tests/ui/arithmetic_side_effects.rs:386:15
    |
 LL |     _custom = _custom % Custom;
    |               ^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:388:15
+  --> tests/ui/arithmetic_side_effects.rs:387:15
    |
 LL |     _custom = _custom % &Custom;
    |               ^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:389:15
+  --> tests/ui/arithmetic_side_effects.rs:388:15
    |
 LL |     _custom = _custom * Custom;
    |               ^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:390:15
+  --> tests/ui/arithmetic_side_effects.rs:389:15
    |
 LL |     _custom = _custom * &Custom;
    |               ^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:391:15
+  --> tests/ui/arithmetic_side_effects.rs:390:15
    |
 LL |     _custom = Custom * _custom;
    |               ^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:392:15
+  --> tests/ui/arithmetic_side_effects.rs:391:15
    |
 LL |     _custom = &Custom * _custom;
    |               ^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:393:15
+  --> tests/ui/arithmetic_side_effects.rs:392:15
    |
 LL |     _custom = Custom + &Custom;
    |               ^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:394:15
+  --> tests/ui/arithmetic_side_effects.rs:393:15
    |
 LL |     _custom = &Custom + Custom;
    |               ^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:395:15
+  --> tests/ui/arithmetic_side_effects.rs:394:15
    |
 LL |     _custom = &Custom + &Custom;
    |               ^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:396:15
+  --> tests/ui/arithmetic_side_effects.rs:395:15
    |
 LL |     _custom = _custom >> _custom;
    |               ^^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:397:15
+  --> tests/ui/arithmetic_side_effects.rs:396:15
    |
 LL |     _custom = _custom >> &_custom;
    |               ^^^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:398:15
+  --> tests/ui/arithmetic_side_effects.rs:397:15
    |
 LL |     _custom = Custom << _custom;
    |               ^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:399:15
+  --> tests/ui/arithmetic_side_effects.rs:398:15
    |
 LL |     _custom = &Custom << _custom;
    |               ^^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:402:23
+  --> tests/ui/arithmetic_side_effects.rs:401:23
    |
 LL |     _n.saturating_div(0);
    |                       ^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:403:21
+  --> tests/ui/arithmetic_side_effects.rs:402:21
    |
 LL |     _n.wrapping_div(0);
    |                     ^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:404:21
+  --> tests/ui/arithmetic_side_effects.rs:403:21
    |
 LL |     _n.wrapping_rem(0);
    |                     ^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:405:28
+  --> tests/ui/arithmetic_side_effects.rs:404:28
    |
 LL |     _n.wrapping_rem_euclid(0);
    |                            ^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:407:23
+  --> tests/ui/arithmetic_side_effects.rs:406:23
    |
 LL |     _n.saturating_div(_n);
    |                       ^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:408:21
+  --> tests/ui/arithmetic_side_effects.rs:407:21
    |
 LL |     _n.wrapping_div(_n);
    |                     ^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:409:21
+  --> tests/ui/arithmetic_side_effects.rs:408:21
    |
 LL |     _n.wrapping_rem(_n);
    |                     ^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:410:28
+  --> tests/ui/arithmetic_side_effects.rs:409:28
    |
 LL |     _n.wrapping_rem_euclid(_n);
    |                            ^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:413:10
+  --> tests/ui/arithmetic_side_effects.rs:412:10
    |
 LL |     _n = -_n;
    |          ^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:414:10
+  --> tests/ui/arithmetic_side_effects.rs:413:10
    |
 LL |     _n = -&_n;
    |          ^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:415:15
+  --> tests/ui/arithmetic_side_effects.rs:414:15
    |
 LL |     _custom = -_custom;
    |               ^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:416:15
+  --> tests/ui/arithmetic_side_effects.rs:415:15
    |
 LL |     _custom = -&_custom;
    |               ^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:425:5
+  --> tests/ui/arithmetic_side_effects.rs:424:5
    |
 LL |     1 + i;
    |     ^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:426:5
+  --> tests/ui/arithmetic_side_effects.rs:425:5
    |
 LL |     i * 2;
    |     ^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:427:5
+  --> tests/ui/arithmetic_side_effects.rs:426:5
    |
 LL |     1 % i / 2;
    |     ^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:428:5
+  --> tests/ui/arithmetic_side_effects.rs:427:5
    |
 LL |     i - 2 + 2 - i;
    |     ^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:429:5
+  --> tests/ui/arithmetic_side_effects.rs:428:5
    |
 LL |     -i;
    |     ^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:440:5
+  --> tests/ui/arithmetic_side_effects.rs:439:5
    |
 LL |     i += 1;
    |     ^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:441:5
+  --> tests/ui/arithmetic_side_effects.rs:440:5
    |
 LL |     i -= 1;
    |     ^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:442:5
+  --> tests/ui/arithmetic_side_effects.rs:441:5
    |
 LL |     i *= 2;
    |     ^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:444:5
+  --> tests/ui/arithmetic_side_effects.rs:443:5
    |
 LL |     i /= 0;
    |     ^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:446:5
+  --> tests/ui/arithmetic_side_effects.rs:445:5
    |
 LL |     i /= var1;
    |     ^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:447:5
+  --> tests/ui/arithmetic_side_effects.rs:446:5
    |
 LL |     i /= var2;
    |     ^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:449:5
+  --> tests/ui/arithmetic_side_effects.rs:448:5
    |
 LL |     i %= 0;
    |     ^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:451:5
+  --> tests/ui/arithmetic_side_effects.rs:450:5
    |
 LL |     i %= var1;
    |     ^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:452:5
+  --> tests/ui/arithmetic_side_effects.rs:451:5
    |
 LL |     i %= var2;
    |     ^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:462:5
+  --> tests/ui/arithmetic_side_effects.rs:461:5
    |
 LL |     10 / a
    |     ^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:516:9
+  --> tests/ui/arithmetic_side_effects.rs:515:9
    |
 LL |         x / maybe_zero
    |         ^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:520:9
+  --> tests/ui/arithmetic_side_effects.rs:519:9
    |
 LL |         x % maybe_zero
    |         ^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:531:5
+  --> tests/ui/arithmetic_side_effects.rs:530:5
    |
 LL |     one.add_assign(1);
    |     ^^^^^^^^^^^^^^^^^
 
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:535:5
+  --> tests/ui/arithmetic_side_effects.rs:534:5
    |
 LL |     one.sub_assign(1);
    |     ^^^^^^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/asm_syntax_not_x86.rs b/src/tools/clippy/tests/ui/asm_syntax_not_x86.rs
index 33cea4806814..a7d29cc239e5 100644
--- a/src/tools/clippy/tests/ui/asm_syntax_not_x86.rs
+++ b/src/tools/clippy/tests/ui/asm_syntax_not_x86.rs
@@ -1,5 +1,4 @@
-//@ignore-target-i686
-//@ignore-target-x86
+//@ignore-target: i686 x86
 //@needs-asm-support
 
 #[warn(clippy::inline_asm_x86_intel_syntax)]
diff --git a/src/tools/clippy/tests/ui/asm_syntax_x86.rs b/src/tools/clippy/tests/ui/asm_syntax_x86.rs
index 835943b43894..5ceaceb7527b 100644
--- a/src/tools/clippy/tests/ui/asm_syntax_x86.rs
+++ b/src/tools/clippy/tests/ui/asm_syntax_x86.rs
@@ -1,6 +1,4 @@
-//@revisions: i686 x86_64
-//@[i686] only-target-i686
-//@[x86_64] only-target-x86_64
+//@only-target: i686 x86_64
 
 #[warn(clippy::inline_asm_x86_intel_syntax)]
 mod warn_intel {
diff --git a/src/tools/clippy/tests/ui/asm_syntax_x86.stderr b/src/tools/clippy/tests/ui/asm_syntax_x86.stderr
new file mode 100644
index 000000000000..1911ef66e239
--- /dev/null
+++ b/src/tools/clippy/tests/ui/asm_syntax_x86.stderr
@@ -0,0 +1,70 @@
+error: Intel x86 assembly syntax used
+  --> tests/ui/asm_syntax_x86.rs:8:9
+   |
+LL |         asm!("");
+   |         ^^^^^^^^
+   |
+   = help: use AT&T x86 assembly syntax
+   = note: `-D clippy::inline-asm-x86-intel-syntax` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::inline_asm_x86_intel_syntax)]`
+
+error: Intel x86 assembly syntax used
+  --> tests/ui/asm_syntax_x86.rs:10:9
+   |
+LL |         asm!("", options());
+   |         ^^^^^^^^^^^^^^^^^^^
+   |
+   = help: use AT&T x86 assembly syntax
+
+error: Intel x86 assembly syntax used
+  --> tests/ui/asm_syntax_x86.rs:12:9
+   |
+LL |         asm!("", options(nostack));
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: use AT&T x86 assembly syntax
+
+error: Intel x86 assembly syntax used
+  --> tests/ui/asm_syntax_x86.rs:18:5
+   |
+LL |     global_asm!("");
+   |     ^^^^^^^^^^^^^^^
+   |
+   = help: use AT&T x86 assembly syntax
+
+error: Intel x86 assembly syntax used
+  --> tests/ui/asm_syntax_x86.rs:20:5
+   |
+LL |     global_asm!("", options());
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: use AT&T x86 assembly syntax
+
+error: AT&T x86 assembly syntax used
+  --> tests/ui/asm_syntax_x86.rs:33:9
+   |
+LL |         asm!("", options(att_syntax));
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: use Intel x86 assembly syntax
+   = note: `-D clippy::inline-asm-x86-att-syntax` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::inline_asm_x86_att_syntax)]`
+
+error: AT&T x86 assembly syntax used
+  --> tests/ui/asm_syntax_x86.rs:35:9
+   |
+LL |         asm!("", options(nostack, att_syntax));
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: use Intel x86 assembly syntax
+
+error: AT&T x86 assembly syntax used
+  --> tests/ui/asm_syntax_x86.rs:41:5
+   |
+LL |     global_asm!("", options(att_syntax));
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: use Intel x86 assembly syntax
+
+error: aborting due to 8 previous errors
+
diff --git a/src/tools/clippy/tests/ui/auxiliary/external_item.rs b/src/tools/clippy/tests/ui/auxiliary/external_item.rs
new file mode 100644
index 000000000000..ca4bc369e449
--- /dev/null
+++ b/src/tools/clippy/tests/ui/auxiliary/external_item.rs
@@ -0,0 +1,7 @@
+pub struct _ExternalStruct {}
+
+impl _ExternalStruct {
+    pub fn _foo(self) {}
+}
+
+pub fn _exernal_foo() {}
diff --git a/src/tools/clippy/tests/ui/auxiliary/proc_macro_attr.rs b/src/tools/clippy/tests/ui/auxiliary/proc_macro_attr.rs
index f6fdebaf2527..e72d6b6ceadf 100644
--- a/src/tools/clippy/tests/ui/auxiliary/proc_macro_attr.rs
+++ b/src/tools/clippy/tests/ui/auxiliary/proc_macro_attr.rs
@@ -11,8 +11,8 @@ use quote::{quote, quote_spanned};
 use syn::spanned::Spanned;
 use syn::token::Star;
 use syn::{
-    parse_macro_input, parse_quote, FnArg, ImplItem, ItemFn, ItemImpl, ItemStruct, ItemTrait, Lifetime, Pat, PatIdent,
-    PatType, Signature, TraitItem, Type, Visibility,
+    FnArg, ImplItem, ItemFn, ItemImpl, ItemStruct, ItemTrait, Lifetime, Pat, PatIdent, PatType, Signature, TraitItem,
+    Type, Visibility, parse_macro_input, parse_quote,
 };
 
 #[proc_macro_attribute]
diff --git a/src/tools/clippy/tests/ui/auxiliary/proc_macro_derive.rs b/src/tools/clippy/tests/ui/auxiliary/proc_macro_derive.rs
index 4c3df4722690..bd90042c1da8 100644
--- a/src/tools/clippy/tests/ui/auxiliary/proc_macro_derive.rs
+++ b/src/tools/clippy/tests/ui/auxiliary/proc_macro_derive.rs
@@ -5,7 +5,7 @@
 
 extern crate proc_macro;
 
-use proc_macro::{quote, Delimiter, Group, Ident, Literal, Punct, Spacing, Span, TokenStream, TokenTree};
+use proc_macro::{Delimiter, Group, Ident, Literal, Punct, Spacing, Span, TokenStream, TokenTree, quote};
 
 #[proc_macro_derive(DeriveSomething)]
 pub fn derive(_: TokenStream) -> TokenStream {
diff --git a/src/tools/clippy/tests/ui/auxiliary/proc_macro_suspicious_else_formatting.rs b/src/tools/clippy/tests/ui/auxiliary/proc_macro_suspicious_else_formatting.rs
index 79e8eff3aa10..3d6f164d3583 100644
--- a/src/tools/clippy/tests/ui/auxiliary/proc_macro_suspicious_else_formatting.rs
+++ b/src/tools/clippy/tests/ui/auxiliary/proc_macro_suspicious_else_formatting.rs
@@ -1,5 +1,5 @@
 extern crate proc_macro;
-use proc_macro::{token_stream, Delimiter, Group, Ident, Span, TokenStream, TokenTree};
+use proc_macro::{Delimiter, Group, Ident, Span, TokenStream, TokenTree, token_stream};
 
 fn read_ident(iter: &mut token_stream::IntoIter) -> Ident {
     match iter.next() {
diff --git a/src/tools/clippy/tests/ui/auxiliary/proc_macros.rs b/src/tools/clippy/tests/ui/auxiliary/proc_macros.rs
index ed7412f7c407..1a2a4ec23114 100644
--- a/src/tools/clippy/tests/ui/auxiliary/proc_macros.rs
+++ b/src/tools/clippy/tests/ui/auxiliary/proc_macros.rs
@@ -5,9 +5,9 @@
 extern crate proc_macro;
 
 use core::mem;
-use proc_macro::token_stream::IntoIter;
 use proc_macro::Delimiter::{self, Brace, Parenthesis};
 use proc_macro::Spacing::{self, Alone, Joint};
+use proc_macro::token_stream::IntoIter;
 use proc_macro::{Group, Ident, Literal, Punct, Span, TokenStream, TokenTree as TT};
 use syn::spanned::Spanned;
 
diff --git a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/others.rs b/src/tools/clippy/tests/ui/borrow_interior_mutable_const/others.rs
index 452d1b198133..de220505c3e0 100644
--- a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/others.rs
+++ b/src/tools/clippy/tests/ui/borrow_interior_mutable_const/others.rs
@@ -5,8 +5,8 @@
 use std::borrow::Cow;
 use std::cell::{Cell, UnsafeCell};
 use std::fmt::Display;
-use std::sync::atomic::{AtomicUsize, Ordering};
 use std::sync::Once;
+use std::sync::atomic::{AtomicUsize, Ordering};
 
 const ATOMIC: AtomicUsize = AtomicUsize::new(5);
 const CELL: Cell = Cell::new(6);
diff --git a/src/tools/clippy/tests/ui/cast_alignment.rs b/src/tools/clippy/tests/ui/cast_alignment.rs
index 98ef5e36f948..72f5d4268cc1 100644
--- a/src/tools/clippy/tests/ui/cast_alignment.rs
+++ b/src/tools/clippy/tests/ui/cast_alignment.rs
@@ -2,16 +2,16 @@
 
 #![feature(rustc_private)]
 #![feature(core_intrinsics)]
-extern crate libc;
-
-#[warn(clippy::cast_ptr_alignment)]
-#[allow(
+#![warn(clippy::cast_ptr_alignment)]
+#![allow(
     clippy::no_effect,
     clippy::unnecessary_operation,
     clippy::cast_lossless,
     clippy::borrow_as_ptr
 )]
 
+extern crate libc;
+
 fn main() {
     /* These should be warned against */
 
diff --git a/src/tools/clippy/tests/ui/cmp_owned/without_suggestion.rs b/src/tools/clippy/tests/ui/cmp_owned/without_suggestion.rs
index ec45d635c172..913aab727471 100644
--- a/src/tools/clippy/tests/ui/cmp_owned/without_suggestion.rs
+++ b/src/tools/clippy/tests/ui/cmp_owned/without_suggestion.rs
@@ -1,5 +1,5 @@
-#[allow(clippy::unnecessary_operation)]
-#[allow(clippy::implicit_clone)]
+#![allow(clippy::unnecessary_operation)]
+#![allow(clippy::implicit_clone)]
 
 fn main() {
     let x = &Baz;
diff --git a/src/tools/clippy/tests/ui/collapsible_else_if.fixed b/src/tools/clippy/tests/ui/collapsible_else_if.fixed
index 3b410b2f17b8..c2d76146c641 100644
--- a/src/tools/clippy/tests/ui/collapsible_else_if.fixed
+++ b/src/tools/clippy/tests/ui/collapsible_else_if.fixed
@@ -1,9 +1,7 @@
 #![allow(clippy::assertions_on_constants, clippy::equatable_if_let, clippy::needless_if)]
+#![warn(clippy::collapsible_if, clippy::collapsible_else_if)]
 
 #[rustfmt::skip]
-#[warn(clippy::collapsible_if)]
-#[warn(clippy::collapsible_else_if)]
-
 fn main() {
     let x = "hello";
     let y = "world";
@@ -76,7 +74,6 @@ fn main() {
 }
 
 #[rustfmt::skip]
-#[allow(dead_code)]
 fn issue_7318() {
     if true { println!("I've been resolved!")
     }else if false {}
diff --git a/src/tools/clippy/tests/ui/collapsible_else_if.rs b/src/tools/clippy/tests/ui/collapsible_else_if.rs
index 772ef6f9fc60..3579e46cd447 100644
--- a/src/tools/clippy/tests/ui/collapsible_else_if.rs
+++ b/src/tools/clippy/tests/ui/collapsible_else_if.rs
@@ -1,9 +1,7 @@
 #![allow(clippy::assertions_on_constants, clippy::equatable_if_let, clippy::needless_if)]
+#![warn(clippy::collapsible_if, clippy::collapsible_else_if)]
 
 #[rustfmt::skip]
-#[warn(clippy::collapsible_if)]
-#[warn(clippy::collapsible_else_if)]
-
 fn main() {
     let x = "hello";
     let y = "world";
@@ -90,7 +88,6 @@ fn main() {
 }
 
 #[rustfmt::skip]
-#[allow(dead_code)]
 fn issue_7318() {
     if true { println!("I've been resolved!")
     }else{
diff --git a/src/tools/clippy/tests/ui/collapsible_else_if.stderr b/src/tools/clippy/tests/ui/collapsible_else_if.stderr
index dc19d90b4d13..395c2dcf68dc 100644
--- a/src/tools/clippy/tests/ui/collapsible_else_if.stderr
+++ b/src/tools/clippy/tests/ui/collapsible_else_if.stderr
@@ -1,5 +1,5 @@
 error: this `else { if .. }` block can be collapsed
-  --> tests/ui/collapsible_else_if.rs:13:12
+  --> tests/ui/collapsible_else_if.rs:11:12
    |
 LL |       } else {
    |  ____________^
@@ -19,7 +19,7 @@ LL +     }
    |
 
 error: this `else { if .. }` block can be collapsed
-  --> tests/ui/collapsible_else_if.rs:21:12
+  --> tests/ui/collapsible_else_if.rs:19:12
    |
 LL |       } else {
    |  ____________^
@@ -37,7 +37,7 @@ LL +     }
    |
 
 error: this `else { if .. }` block can be collapsed
-  --> tests/ui/collapsible_else_if.rs:29:12
+  --> tests/ui/collapsible_else_if.rs:27:12
    |
 LL |       } else {
    |  ____________^
@@ -60,7 +60,7 @@ LL +     }
    |
 
 error: this `else { if .. }` block can be collapsed
-  --> tests/ui/collapsible_else_if.rs:40:12
+  --> tests/ui/collapsible_else_if.rs:38:12
    |
 LL |       } else {
    |  ____________^
@@ -83,7 +83,7 @@ LL +     }
    |
 
 error: this `else { if .. }` block can be collapsed
-  --> tests/ui/collapsible_else_if.rs:51:12
+  --> tests/ui/collapsible_else_if.rs:49:12
    |
 LL |       } else {
    |  ____________^
@@ -106,7 +106,7 @@ LL +     }
    |
 
 error: this `else { if .. }` block can be collapsed
-  --> tests/ui/collapsible_else_if.rs:62:12
+  --> tests/ui/collapsible_else_if.rs:60:12
    |
 LL |       } else {
    |  ____________^
@@ -129,7 +129,7 @@ LL +     }
    |
 
 error: this `else { if .. }` block can be collapsed
-  --> tests/ui/collapsible_else_if.rs:73:12
+  --> tests/ui/collapsible_else_if.rs:71:12
    |
 LL |       } else {
    |  ____________^
@@ -152,7 +152,7 @@ LL +     }
    |
 
 error: this `else { if .. }` block can be collapsed
-  --> tests/ui/collapsible_else_if.rs:96:10
+  --> tests/ui/collapsible_else_if.rs:93:10
    |
 LL |       }else{
    |  __________^
diff --git a/src/tools/clippy/tests/ui/comparison_to_empty.fixed b/src/tools/clippy/tests/ui/comparison_to_empty.fixed
index e102b13a7618..a2a3dd9086d4 100644
--- a/src/tools/clippy/tests/ui/comparison_to_empty.fixed
+++ b/src/tools/clippy/tests/ui/comparison_to_empty.fixed
@@ -33,4 +33,12 @@ fn main() {
     if let [0] = &*s
         && s == [0]
     {}
+
+    // Also lint the `PartialEq` methods
+    let s = String::new();
+    let _ = s.is_empty();
+    let _ = !s.is_empty();
+    let v = vec![0];
+    let _ = v.is_empty();
+    let _ = !v.is_empty();
 }
diff --git a/src/tools/clippy/tests/ui/comparison_to_empty.rs b/src/tools/clippy/tests/ui/comparison_to_empty.rs
index 69a6c967d382..7c5689a4bbec 100644
--- a/src/tools/clippy/tests/ui/comparison_to_empty.rs
+++ b/src/tools/clippy/tests/ui/comparison_to_empty.rs
@@ -33,4 +33,12 @@ fn main() {
     if let [0] = &*s
         && s == [0]
     {}
+
+    // Also lint the `PartialEq` methods
+    let s = String::new();
+    let _ = s.eq("");
+    let _ = s.ne("");
+    let v = vec![0];
+    let _ = v.eq(&[]);
+    let _ = v.ne(&[]);
 }
diff --git a/src/tools/clippy/tests/ui/comparison_to_empty.stderr b/src/tools/clippy/tests/ui/comparison_to_empty.stderr
index 6b027459ed34..2ee0efc7dbb1 100644
--- a/src/tools/clippy/tests/ui/comparison_to_empty.stderr
+++ b/src/tools/clippy/tests/ui/comparison_to_empty.stderr
@@ -55,5 +55,29 @@ error: comparison to empty slice
 LL |         && s == []
    |            ^^^^^^^ help: using `is_empty` is clearer and more explicit: `s.is_empty()`
 
-error: aborting due to 9 previous errors
+error: comparison to empty slice
+  --> tests/ui/comparison_to_empty.rs:39:13
+   |
+LL |     let _ = s.eq("");
+   |             ^^^^^^^^ help: using `is_empty` is clearer and more explicit: `s.is_empty()`
+
+error: comparison to empty slice
+  --> tests/ui/comparison_to_empty.rs:40:13
+   |
+LL |     let _ = s.ne("");
+   |             ^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!s.is_empty()`
+
+error: comparison to empty slice
+  --> tests/ui/comparison_to_empty.rs:42:13
+   |
+LL |     let _ = v.eq(&[]);
+   |             ^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `v.is_empty()`
+
+error: comparison to empty slice
+  --> tests/ui/comparison_to_empty.rs:43:13
+   |
+LL |     let _ = v.ne(&[]);
+   |             ^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!v.is_empty()`
+
+error: aborting due to 13 previous errors
 
diff --git a/src/tools/clippy/tests/ui/crashes/associated-constant-ice.rs b/src/tools/clippy/tests/ui/crashes/associated-constant-ice.rs
index 948deba3ea6e..fec16671eeb3 100644
--- a/src/tools/clippy/tests/ui/crashes/associated-constant-ice.rs
+++ b/src/tools/clippy/tests/ui/crashes/associated-constant-ice.rs
@@ -1,4 +1,4 @@
-/// Test for https://github.com/rust-lang/rust-clippy/issues/1698
+// Test for https://github.com/rust-lang/rust-clippy/issues/1698
 
 pub trait Trait {
     const CONSTANT: u8;
diff --git a/src/tools/clippy/tests/ui/crashes/cc_seme.rs b/src/tools/clippy/tests/ui/crashes/cc_seme.rs
index 98588be9cf82..98897d6d7aaf 100644
--- a/src/tools/clippy/tests/ui/crashes/cc_seme.rs
+++ b/src/tools/clippy/tests/ui/crashes/cc_seme.rs
@@ -1,6 +1,4 @@
-#[allow(dead_code)]
-
-/// Test for https://github.com/rust-lang/rust-clippy/issues/478
+// Test for https://github.com/rust-lang/rust-clippy/issues/478
 
 enum Baz {
     One,
diff --git a/src/tools/clippy/tests/ui/crashes/ice-11230.rs b/src/tools/clippy/tests/ui/crashes/ice-11230.rs
index 5761882273e0..94044e9435ed 100644
--- a/src/tools/clippy/tests/ui/crashes/ice-11230.rs
+++ b/src/tools/clippy/tests/ui/crashes/ice-11230.rs
@@ -1,4 +1,4 @@
-/// Test for https://github.com/rust-lang/rust-clippy/issues/11230
+// Test for https://github.com/rust-lang/rust-clippy/issues/11230
 
 fn main() {
     const A: &[for<'a> fn(&'a ())] = &[];
diff --git a/src/tools/clippy/tests/ui/crashes/ice-1588.rs b/src/tools/clippy/tests/ui/crashes/ice-1588.rs
index b0a3d11bce46..9ec093721c17 100644
--- a/src/tools/clippy/tests/ui/crashes/ice-1588.rs
+++ b/src/tools/clippy/tests/ui/crashes/ice-1588.rs
@@ -1,6 +1,6 @@
 #![allow(clippy::all)]
 
-/// Test for https://github.com/rust-lang/rust-clippy/issues/1588
+// Test for https://github.com/rust-lang/rust-clippy/issues/1588
 
 fn main() {
     match 1 {
diff --git a/src/tools/clippy/tests/ui/crashes/ice-1969.rs b/src/tools/clippy/tests/ui/crashes/ice-1969.rs
index 96a8fe6c24d5..eb901c767294 100644
--- a/src/tools/clippy/tests/ui/crashes/ice-1969.rs
+++ b/src/tools/clippy/tests/ui/crashes/ice-1969.rs
@@ -1,6 +1,6 @@
 #![allow(clippy::all)]
 
-/// Test for https://github.com/rust-lang/rust-clippy/issues/1969
+// Test for https://github.com/rust-lang/rust-clippy/issues/1969
 
 fn main() {}
 
diff --git a/src/tools/clippy/tests/ui/crashes/ice-2499.rs b/src/tools/clippy/tests/ui/crashes/ice-2499.rs
index 45b3b1869dde..732f331ad145 100644
--- a/src/tools/clippy/tests/ui/crashes/ice-2499.rs
+++ b/src/tools/clippy/tests/ui/crashes/ice-2499.rs
@@ -1,8 +1,8 @@
 #![allow(dead_code, clippy::char_lit_as_u8, clippy::needless_bool)]
 
-/// Should not trigger an ICE in `SpanlessHash` / `consts::constant`
-///
-/// Issue: https://github.com/rust-lang/rust-clippy/issues/2499
+// Should not trigger an ICE in `SpanlessHash` / `consts::constant`
+//
+// Issue: https://github.com/rust-lang/rust-clippy/issues/2499
 
 fn f(s: &[u8]) -> bool {
     let t = s[0] as char;
diff --git a/src/tools/clippy/tests/ui/crashes/ice-2594.rs b/src/tools/clippy/tests/ui/crashes/ice-2594.rs
index 3f3986b6fc69..dddf860bd178 100644
--- a/src/tools/clippy/tests/ui/crashes/ice-2594.rs
+++ b/src/tools/clippy/tests/ui/crashes/ice-2594.rs
@@ -3,7 +3,6 @@
 /// Should not trigger an ICE in `SpanlessHash` / `consts::constant`
 ///
 /// Issue: https://github.com/rust-lang/rust-clippy/issues/2594
-
 fn spanless_hash_ice() {
     let txt = "something";
     let empty_header: [u8; 1] = [1; 1];
diff --git a/src/tools/clippy/tests/ui/crashes/ice-2727.rs b/src/tools/clippy/tests/ui/crashes/ice-2727.rs
index 56024abc8f58..59fb9b04b86d 100644
--- a/src/tools/clippy/tests/ui/crashes/ice-2727.rs
+++ b/src/tools/clippy/tests/ui/crashes/ice-2727.rs
@@ -1,4 +1,4 @@
-/// Test for https://github.com/rust-lang/rust-clippy/issues/2727
+// Test for https://github.com/rust-lang/rust-clippy/issues/2727
 
 pub fn f(new: fn()) {
     new();
diff --git a/src/tools/clippy/tests/ui/crashes/ice-2760.rs b/src/tools/clippy/tests/ui/crashes/ice-2760.rs
index 61ef24804986..5f7d91abf995 100644
--- a/src/tools/clippy/tests/ui/crashes/ice-2760.rs
+++ b/src/tools/clippy/tests/ui/crashes/ice-2760.rs
@@ -5,10 +5,10 @@
     dead_code
 )]
 
-/// This should not compile-fail with:
-///
-///      error[E0277]: the trait bound `T: Foo` is not satisfied
-// See rust-lang/rust-clippy#2760.
+// This should not compile-fail with:
+//
+//      error[E0277]: the trait bound `T: Foo` is not satisfied
+// See https://github.com/rust-lang/rust-clippy/issues/2760
 
 trait Foo {
     type Bar;
diff --git a/src/tools/clippy/tests/ui/crashes/ice-2862.rs b/src/tools/clippy/tests/ui/crashes/ice-2862.rs
index 8326e3663b05..2573b571f55c 100644
--- a/src/tools/clippy/tests/ui/crashes/ice-2862.rs
+++ b/src/tools/clippy/tests/ui/crashes/ice-2862.rs
@@ -1,4 +1,4 @@
-/// Test for https://github.com/rust-lang/rust-clippy/issues/2862
+// Test for https://github.com/rust-lang/rust-clippy/issues/2862
 
 pub trait FooMap {
     fn map B>(&self, f: F) -> B;
diff --git a/src/tools/clippy/tests/ui/crashes/ice-2865.rs b/src/tools/clippy/tests/ui/crashes/ice-2865.rs
index c62981396016..28363707acca 100644
--- a/src/tools/clippy/tests/ui/crashes/ice-2865.rs
+++ b/src/tools/clippy/tests/ui/crashes/ice-2865.rs
@@ -1,6 +1,6 @@
 #![allow(dead_code, clippy::extra_unused_lifetimes)]
 
-/// Test for https://github.com/rust-lang/rust-clippy/issues/2865
+// Test for https://github.com/rust-lang/rust-clippy/issues/2865
 
 struct Ice {
     size: String,
diff --git a/src/tools/clippy/tests/ui/crashes/ice-3151.rs b/src/tools/clippy/tests/ui/crashes/ice-3151.rs
index 268ba86fc7aa..f88a26cb4859 100644
--- a/src/tools/clippy/tests/ui/crashes/ice-3151.rs
+++ b/src/tools/clippy/tests/ui/crashes/ice-3151.rs
@@ -1,4 +1,4 @@
-/// Test for https://github.com/rust-lang/rust-clippy/issues/3151
+// Test for https://github.com/rust-lang/rust-clippy/issues/3151
 
 #[derive(Clone)]
 pub struct HashMap {
diff --git a/src/tools/clippy/tests/ui/crashes/ice-3462.rs b/src/tools/clippy/tests/ui/crashes/ice-3462.rs
index 21cd9d337cdd..ccd617e305da 100644
--- a/src/tools/clippy/tests/ui/crashes/ice-3462.rs
+++ b/src/tools/clippy/tests/ui/crashes/ice-3462.rs
@@ -2,7 +2,7 @@
 #![allow(clippy::disallowed_names, clippy::equatable_if_let, clippy::needless_if)]
 #![allow(unused)]
 
-/// Test for https://github.com/rust-lang/rust-clippy/issues/3462
+// Test for https://github.com/rust-lang/rust-clippy/issues/3462
 
 enum Foo {
     Bar,
diff --git a/src/tools/clippy/tests/ui/crashes/ice-3747.rs b/src/tools/clippy/tests/ui/crashes/ice-3747.rs
index cdf018cbc88d..44b1d7ed1b2e 100644
--- a/src/tools/clippy/tests/ui/crashes/ice-3747.rs
+++ b/src/tools/clippy/tests/ui/crashes/ice-3747.rs
@@ -1,4 +1,4 @@
-/// Test for https://github.com/rust-lang/rust-clippy/issues/3747
+// Test for https://github.com/rust-lang/rust-clippy/issues/3747
 
 macro_rules! a {
     ( $pub:tt $($attr:tt)* ) => {
diff --git a/src/tools/clippy/tests/ui/crashes/ice-3969.rs b/src/tools/clippy/tests/ui/crashes/ice-3969.rs
index d5676cbd91d1..ac09ce08753a 100644
--- a/src/tools/clippy/tests/ui/crashes/ice-3969.rs
+++ b/src/tools/clippy/tests/ui/crashes/ice-3969.rs
@@ -1,7 +1,7 @@
 // https://github.com/rust-lang/rust-clippy/issues/3969
 // used to crash: error: internal compiler error:
 // src/librustc_traits/normalize_erasing_regions.rs:43: could not fully normalize `::Item test from rustc ./ui/trivial-bounds/trivial-bounds-inconsistent.rs
+// std::iter::Iterator>::Item` test from rustc ./ui/trivial-bounds/trivial-bounds-inconsistent.rs
 
 // Check that tautalogically false bounds are accepted, and are used
 // in type inference.
@@ -18,7 +18,7 @@ struct Dst {
 struct TwoStrs(str, str)
 where
     str: Sized;
-//~^ ERROR: trait bound str: std::marker::Sized does not depend on any type or lifetim
+//~^ ERROR: trait bound str: std::marker::Sized does not depend on any type or lifetime
 //~| NOTE: `-D trivial-bounds` implied by `-D warnings`
 
 fn unsized_local()
diff --git a/src/tools/clippy/tests/ui/crashes/ice-6251.rs b/src/tools/clippy/tests/ui/crashes/ice-6251.rs
index a81137a64655..73e919b6dd2e 100644
--- a/src/tools/clippy/tests/ui/crashes/ice-6251.rs
+++ b/src/tools/clippy/tests/ui/crashes/ice-6251.rs
@@ -1,5 +1,5 @@
 // originally from glacier/fixed/77329.rs
-// assertion failed: `(left == right) ; different DefIds
+// assertion failed: `(left == right)` ; different DefIds
 //@no-rustfix
 fn bug() -> impl Iterator {
     std::iter::empty()
diff --git a/src/tools/clippy/tests/ui/crashes/ice-700.rs b/src/tools/clippy/tests/ui/crashes/ice-700.rs
index 0cbceedbd6bd..5e004b94330e 100644
--- a/src/tools/clippy/tests/ui/crashes/ice-700.rs
+++ b/src/tools/clippy/tests/ui/crashes/ice-700.rs
@@ -1,6 +1,6 @@
 #![deny(clippy::all)]
 
-/// Test for https://github.com/rust-lang/rust-clippy/issues/700
+// Test for https://github.com/rust-lang/rust-clippy/issues/700
 
 fn core() {}
 
diff --git a/src/tools/clippy/tests/ui/crashes/ice-7410.rs b/src/tools/clippy/tests/ui/crashes/ice-7410.rs
index a2683b3ce340..ccf6d7ff94f2 100644
--- a/src/tools/clippy/tests/ui/crashes/ice-7410.rs
+++ b/src/tools/clippy/tests/ui/crashes/ice-7410.rs
@@ -1,6 +1,5 @@
 //@compile-flags: -Clink-arg=-nostartfiles
-//@ignore-target-apple
-//@ignore-target-windows
+//@ignore-target: apple windows
 
 #![feature(lang_items, start, libc)]
 #![no_std]
diff --git a/src/tools/clippy/tests/ui/crashes/ice_exact_size.rs b/src/tools/clippy/tests/ui/crashes/ice_exact_size.rs
index 30e4b11ec0bd..c0671eaff145 100644
--- a/src/tools/clippy/tests/ui/crashes/ice_exact_size.rs
+++ b/src/tools/clippy/tests/ui/crashes/ice_exact_size.rs
@@ -1,6 +1,6 @@
 #![deny(clippy::all)]
 
-/// Test for https://github.com/rust-lang/rust-clippy/issues/1336
+// Test for https://github.com/rust-lang/rust-clippy/issues/1336
 
 #[allow(dead_code)]
 struct Foo;
diff --git a/src/tools/clippy/tests/ui/crashes/if_same_then_else.rs b/src/tools/clippy/tests/ui/crashes/if_same_then_else.rs
index 2f913292995e..a900fe5e6bc6 100644
--- a/src/tools/clippy/tests/ui/crashes/if_same_then_else.rs
+++ b/src/tools/clippy/tests/ui/crashes/if_same_then_else.rs
@@ -1,7 +1,7 @@
 #![allow(clippy::comparison_chain)]
 #![deny(clippy::if_same_then_else)]
 
-/// Test for https://github.com/rust-lang/rust-clippy/issues/2426
+// Test for https://github.com/rust-lang/rust-clippy/issues/2426
 
 fn main() {}
 
diff --git a/src/tools/clippy/tests/ui/crashes/inherent_impl.rs b/src/tools/clippy/tests/ui/crashes/inherent_impl.rs
index aeb27b5ba8c2..800a5a383f62 100644
--- a/src/tools/clippy/tests/ui/crashes/inherent_impl.rs
+++ b/src/tools/clippy/tests/ui/crashes/inherent_impl.rs
@@ -1,6 +1,6 @@
 #![deny(clippy::multiple_inherent_impl)]
 
-/// Test for https://github.com/rust-lang/rust-clippy/issues/4578
+// Test for https://github.com/rust-lang/rust-clippy/issues/4578
 
 macro_rules! impl_foo {
     ($struct:ident) => {
diff --git a/src/tools/clippy/tests/ui/crashes/issue-825.rs b/src/tools/clippy/tests/ui/crashes/issue-825.rs
index 05696e3d7d56..e8b455a0ec67 100644
--- a/src/tools/clippy/tests/ui/crashes/issue-825.rs
+++ b/src/tools/clippy/tests/ui/crashes/issue-825.rs
@@ -1,6 +1,6 @@
 #![allow(warnings)]
 
-/// Test for https://github.com/rust-lang/rust-clippy/issues/825
+// Test for https://github.com/rust-lang/rust-clippy/issues/825
 
 // this should compile in a reasonable amount of time
 fn rust_type_id(name: &str) {
diff --git a/src/tools/clippy/tests/ui/crashes/match_same_arms_const.rs b/src/tools/clippy/tests/ui/crashes/match_same_arms_const.rs
index 94c939665e61..626179c00155 100644
--- a/src/tools/clippy/tests/ui/crashes/match_same_arms_const.rs
+++ b/src/tools/clippy/tests/ui/crashes/match_same_arms_const.rs
@@ -1,6 +1,6 @@
 #![deny(clippy::match_same_arms)]
 
-/// Test for https://github.com/rust-lang/rust-clippy/issues/2427
+// Test for https://github.com/rust-lang/rust-clippy/issues/2427
 
 const PRICE_OF_SWEETS: u32 = 5;
 const PRICE_OF_KINDNESS: u32 = 0;
diff --git a/src/tools/clippy/tests/ui/crashes/needless_pass_by_value-w-late-bound.stderr b/src/tools/clippy/tests/ui/crashes/needless_pass_by_value-w-late-bound.stderr
index 90076d4338a4..284795700069 100644
--- a/src/tools/clippy/tests/ui/crashes/needless_pass_by_value-w-late-bound.stderr
+++ b/src/tools/clippy/tests/ui/crashes/needless_pass_by_value-w-late-bound.stderr
@@ -4,7 +4,7 @@ error: this argument is passed by value, but not consumed in the function body
 LL | fn test(x: Foo<'_>) {}
    |            ^^^^^^^ help: consider taking a reference instead: `&Foo<'_>`
    |
-help: consider marking this type as `Copy`
+help: or consider marking this type as `Copy`
   --> tests/ui/crashes/needless_pass_by_value-w-late-bound.rs:5:1
    |
 LL | struct Foo<'a>(&'a [(); 100]);
diff --git a/src/tools/clippy/tests/ui/crashes/returns.rs b/src/tools/clippy/tests/ui/crashes/returns.rs
index 8021ed4607dd..91cdb5306c89 100644
--- a/src/tools/clippy/tests/ui/crashes/returns.rs
+++ b/src/tools/clippy/tests/ui/crashes/returns.rs
@@ -1,4 +1,4 @@
-/// Test for https://github.com/rust-lang/rust-clippy/issues/1346
+// Test for https://github.com/rust-lang/rust-clippy/issues/1346
 
 #[deny(warnings)]
 fn cfg_return() -> i32 {
diff --git a/src/tools/clippy/tests/ui/crate_level_checks/entrypoint_recursion.rs b/src/tools/clippy/tests/ui/crate_level_checks/entrypoint_recursion.rs
index aa76688d8010..5d853d97bc35 100644
--- a/src/tools/clippy/tests/ui/crate_level_checks/entrypoint_recursion.rs
+++ b/src/tools/clippy/tests/ui/crate_level_checks/entrypoint_recursion.rs
@@ -1,4 +1,4 @@
-//@ignore-target-apple
+//@ignore-target: apple
 
 #![feature(rustc_attrs)]
 
diff --git a/src/tools/clippy/tests/ui/crate_level_checks/no_std_main_recursion.rs b/src/tools/clippy/tests/ui/crate_level_checks/no_std_main_recursion.rs
index 32eba9695920..9e5b2a489034 100644
--- a/src/tools/clippy/tests/ui/crate_level_checks/no_std_main_recursion.rs
+++ b/src/tools/clippy/tests/ui/crate_level_checks/no_std_main_recursion.rs
@@ -1,5 +1,5 @@
 //@compile-flags: -Clink-arg=-nostartfiles
-//@ignore-target-apple
+//@ignore-target: apple
 
 #![feature(lang_items, start, libc)]
 #![no_std]
diff --git a/src/tools/clippy/tests/ui/declare_interior_mutable_const/others.rs b/src/tools/clippy/tests/ui/declare_interior_mutable_const/others.rs
index 9dafad8b784b..0dccf18c4930 100644
--- a/src/tools/clippy/tests/ui/declare_interior_mutable_const/others.rs
+++ b/src/tools/clippy/tests/ui/declare_interior_mutable_const/others.rs
@@ -4,8 +4,8 @@ use std::borrow::Cow;
 use std::cell::Cell;
 use std::fmt::Display;
 use std::ptr;
-use std::sync::atomic::AtomicUsize;
 use std::sync::Once;
+use std::sync::atomic::AtomicUsize;
 
 const ATOMIC: AtomicUsize = AtomicUsize::new(5); //~ ERROR: interior mutable
 const CELL: Cell = Cell::new(6); //~ ERROR: interior mutable
diff --git a/src/tools/clippy/tests/ui/def_id_nocore.rs b/src/tools/clippy/tests/ui/def_id_nocore.rs
index 190d636ebf34..c9650312db87 100644
--- a/src/tools/clippy/tests/ui/def_id_nocore.rs
+++ b/src/tools/clippy/tests/ui/def_id_nocore.rs
@@ -1,4 +1,4 @@
-//@ignore-target-apple
+//@ignore-target: apple
 
 #![feature(no_core, lang_items, start)]
 #![no_core]
diff --git a/src/tools/clippy/tests/ui/diverging_sub_expression.rs b/src/tools/clippy/tests/ui/diverging_sub_expression.rs
index e0acf050949a..1abba60fd34a 100644
--- a/src/tools/clippy/tests/ui/diverging_sub_expression.rs
+++ b/src/tools/clippy/tests/ui/diverging_sub_expression.rs
@@ -67,3 +67,9 @@ fn foobar() {
         };
     }
 }
+
+#[allow(unused)]
+fn ignore_todo() {
+    let x: u32 = todo!();
+    println!("{x}");
+}
diff --git a/src/tools/clippy/tests/ui/doc/doc_lazy_blank_line.fixed b/src/tools/clippy/tests/ui/doc/doc_lazy_blank_line.fixed
deleted file mode 100644
index 1aaa26afe7f2..000000000000
--- a/src/tools/clippy/tests/ui/doc/doc_lazy_blank_line.fixed
+++ /dev/null
@@ -1,47 +0,0 @@
-// https://github.com/rust-lang/rust-clippy/issues/12917
-#![warn(clippy::doc_lazy_continuation)]
-
-/// This is a constant.
-///
-/// The meaning of which should not be explained.
-pub const A: i32 = 42;
-
-/// This is another constant, no longer used.
-///
-/// This block of documentation has a long
-/// explanation and derivation to explain
-/// why it is what it is, and how it's used.
-///
-/// It is left here for historical reasons, and
-/// for reference.
-///
-/// Reasons it's great:
-///  - First reason
-///  - Second reason
-///
-//pub const B: i32 = 1337;
-
-/// This is yet another constant.
-///
-/// This has a similar fate as `B`.
-///
-/// Reasons it's useful:
-///  1. First reason
-///  2. Second reason
-///
-//pub const C: i32 = 8008;
-
-/// This is still in use.
-pub const D: i32 = 20;
-
-/// > blockquote code path
-///
-
-/// bottom text
-pub const E: i32 = 20;
-
-/// > blockquote code path
-///
-#[repr(C)]
-/// bottom text
-pub struct Foo(i32);
diff --git a/src/tools/clippy/tests/ui/doc/doc_lazy_blank_line.rs b/src/tools/clippy/tests/ui/doc/doc_lazy_blank_line.rs
deleted file mode 100644
index e1ab8fc83892..000000000000
--- a/src/tools/clippy/tests/ui/doc/doc_lazy_blank_line.rs
+++ /dev/null
@@ -1,43 +0,0 @@
-// https://github.com/rust-lang/rust-clippy/issues/12917
-#![warn(clippy::doc_lazy_continuation)]
-
-/// This is a constant.
-///
-/// The meaning of which should not be explained.
-pub const A: i32 = 42;
-
-/// This is another constant, no longer used.
-///
-/// This block of documentation has a long
-/// explanation and derivation to explain
-/// why it is what it is, and how it's used.
-///
-/// It is left here for historical reasons, and
-/// for reference.
-///
-/// Reasons it's great:
-///  - First reason
-///  - Second reason
-//pub const B: i32 = 1337;
-
-/// This is yet another constant.
-///
-/// This has a similar fate as `B`.
-///
-/// Reasons it's useful:
-///  1. First reason
-///  2. Second reason
-//pub const C: i32 = 8008;
-
-/// This is still in use.
-pub const D: i32 = 20;
-
-/// > blockquote code path
-
-/// bottom text
-pub const E: i32 = 20;
-
-/// > blockquote code path
-#[repr(C)]
-/// bottom text
-pub struct Foo(i32);
diff --git a/src/tools/clippy/tests/ui/doc/doc_lazy_blank_line.stderr b/src/tools/clippy/tests/ui/doc/doc_lazy_blank_line.stderr
deleted file mode 100644
index 854906a74741..000000000000
--- a/src/tools/clippy/tests/ui/doc/doc_lazy_blank_line.stderr
+++ /dev/null
@@ -1,56 +0,0 @@
-error: doc list item without indentation
-  --> tests/ui/doc/doc_lazy_blank_line.rs:23:5
-   |
-LL | /// This is yet another constant.
-   |     ^
-   |
-   = help: if this is intended to be part of the list, indent 3 spaces
-   = note: `-D clippy::doc-lazy-continuation` implied by `-D warnings`
-   = help: to override `-D warnings` add `#[allow(clippy::doc_lazy_continuation)]`
-help: if this should be its own paragraph, add a blank doc comment line
-   |
-LL ~ ///  - Second reason
-LL + ///
-   |
-
-error: doc list item without indentation
-  --> tests/ui/doc/doc_lazy_blank_line.rs:32:5
-   |
-LL | /// This is still in use.
-   |     ^
-   |
-   = help: if this is intended to be part of the list, indent 4 spaces
-help: if this should be its own paragraph, add a blank doc comment line
-   |
-LL ~ ///  2. Second reason
-LL + ///
-   |
-
-error: doc quote line without `>` marker
-  --> tests/ui/doc/doc_lazy_blank_line.rs:37:5
-   |
-LL | /// bottom text
-   |     ^
-   |
-   = help: if this not intended to be a quote at all, escape it with `\>`
-help: if this should be its own paragraph, add a blank doc comment line
-   |
-LL ~ /// > blockquote code path
-LL + ///
-   |
-
-error: doc quote line without `>` marker
-  --> tests/ui/doc/doc_lazy_blank_line.rs:42:5
-   |
-LL | /// bottom text
-   |     ^
-   |
-   = help: if this not intended to be a quote at all, escape it with `\>`
-help: if this should be its own paragraph, add a blank doc comment line
-   |
-LL ~ /// > blockquote code path
-LL + ///
-   |
-
-error: aborting due to 4 previous errors
-
diff --git a/src/tools/clippy/tests/ui/doc/doc_lazy_list.fixed b/src/tools/clippy/tests/ui/doc/doc_lazy_list.fixed
index ea59ae4c01c9..da537518a2b5 100644
--- a/src/tools/clippy/tests/ui/doc/doc_lazy_list.fixed
+++ b/src/tools/clippy/tests/ui/doc/doc_lazy_list.fixed
@@ -7,9 +7,8 @@ fn one() {}
 
 /// 1. first line
 ///    lazy list continuations don't make warnings with this lint
-///
 //~^ ERROR: doc list item without indentation
-/// because they don't have the
+///    because they don't have the
 //~^ ERROR: doc list item without indentation
 fn two() {}
 
@@ -20,9 +19,8 @@ fn three() {}
 
 ///   - first line
 ///     lazy list continuations don't make warnings with this lint
-///
 //~^ ERROR: doc list item without indentation
-/// because they don't have the
+///     because they don't have the
 //~^ ERROR: doc list item without indentation
 fn four() {}
 
@@ -33,9 +31,8 @@ fn five() {}
 
 ///   - - first line
 ///       this will warn on the lazy continuation
-///
 //~^ ERROR: doc list item without indentation
-///     and so should this
+///       and so should this
 //~^ ERROR: doc list item without indentation
 fn six() {}
 
diff --git a/src/tools/clippy/tests/ui/doc/doc_lazy_list.stderr b/src/tools/clippy/tests/ui/doc/doc_lazy_list.stderr
index 52aa74df8948..b38f43b7555f 100644
--- a/src/tools/clippy/tests/ui/doc/doc_lazy_list.stderr
+++ b/src/tools/clippy/tests/ui/doc/doc_lazy_list.stderr
@@ -30,12 +30,11 @@ error: doc list item without indentation
 LL | /// because they don't have the
    |     ^
    |
-   = help: if this is intended to be part of the list, indent 3 spaces
-help: if this should be its own paragraph, add a blank doc comment line
-   |
-LL ~ /// lazy list continuations don't make warnings with this lint
-LL + ///
+   = help: if this is supposed to be its own paragraph, add a blank line
+help: indent this line
    |
+LL | ///    because they don't have the
+   |     +++
 
 error: doc list item without indentation
   --> tests/ui/doc/doc_lazy_list.rs:16:5
@@ -67,12 +66,11 @@ error: doc list item without indentation
 LL | /// because they don't have the
    |     ^
    |
-   = help: if this is intended to be part of the list, indent 4 spaces
-help: if this should be its own paragraph, add a blank doc comment line
-   |
-LL ~ /// lazy list continuations don't make warnings with this lint
-LL + ///
+   = help: if this is supposed to be its own paragraph, add a blank line
+help: indent this line
    |
+LL | ///     because they don't have the
+   |     ++++
 
 error: doc list item without indentation
   --> tests/ui/doc/doc_lazy_list.rs:28:5
@@ -104,12 +102,11 @@ error: doc list item without indentation
 LL | ///     and so should this
    |     ^^^^
    |
-   = help: if this is intended to be part of the list, indent 2 spaces
-help: if this should be its own paragraph, add a blank doc comment line
-   |
-LL ~ /// this will warn on the lazy continuation
-LL + ///
+   = help: if this is supposed to be its own paragraph, add a blank line
+help: indent this line
    |
+LL | ///       and so should this
+   |         ++
 
 error: doc list item without indentation
   --> tests/ui/doc/doc_lazy_list.rs:56:5
diff --git a/src/tools/clippy/tests/ui/duplicate_underscore_argument.rs b/src/tools/clippy/tests/ui/duplicate_underscore_argument.rs
index 118f6e4a34c0..a725538436cb 100644
--- a/src/tools/clippy/tests/ui/duplicate_underscore_argument.rs
+++ b/src/tools/clippy/tests/ui/duplicate_underscore_argument.rs
@@ -1,5 +1,4 @@
 #![warn(clippy::duplicate_underscore_argument)]
-#[allow(dead_code, unused)]
 
 fn join_the_dark_side(darth: i32, _darth: i32) {}
 //~^ ERROR: `darth` already exists, having another argument having almost the same name ma
diff --git a/src/tools/clippy/tests/ui/duplicate_underscore_argument.stderr b/src/tools/clippy/tests/ui/duplicate_underscore_argument.stderr
index 40a24b823d11..74979b157882 100644
--- a/src/tools/clippy/tests/ui/duplicate_underscore_argument.stderr
+++ b/src/tools/clippy/tests/ui/duplicate_underscore_argument.stderr
@@ -1,5 +1,5 @@
 error: `darth` already exists, having another argument having almost the same name makes code comprehension and documentation more difficult
-  --> tests/ui/duplicate_underscore_argument.rs:4:23
+  --> tests/ui/duplicate_underscore_argument.rs:3:23
    |
 LL | fn join_the_dark_side(darth: i32, _darth: i32) {}
    |                       ^^^^^
diff --git a/src/tools/clippy/tests/ui/duplicated_attributes.rs b/src/tools/clippy/tests/ui/duplicated_attributes.rs
index 97cf4a69682d..874f5d22075c 100644
--- a/src/tools/clippy/tests/ui/duplicated_attributes.rs
+++ b/src/tools/clippy/tests/ui/duplicated_attributes.rs
@@ -27,4 +27,8 @@ trait Abc {}
 #[proc_macro_attr::duplicated_attr()] // Should not warn!
 fn babar() {}
 
+#[allow(missing_docs, reason = "library for internal use only")]
+#[allow(exported_private_dependencies, reason = "library for internal use only")]
+fn duplicate_reason() {}
+
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/empty_line_after/doc_comments.1.fixed b/src/tools/clippy/tests/ui/empty_line_after/doc_comments.1.fixed
new file mode 100644
index 000000000000..fd6a94b6a80c
--- /dev/null
+++ b/src/tools/clippy/tests/ui/empty_line_after/doc_comments.1.fixed
@@ -0,0 +1,135 @@
+#![warn(clippy::empty_line_after_outer_attr, clippy::empty_line_after_doc_comments)]
+
+//~vvv empty_line_after_doc_comments
+/// Meant to be an
+/// inner doc comment
+/// for the crate
+fn first_in_crate() {}
+
+mod m {
+    //~vvv empty_line_after_doc_comments
+    /// Meant to be an
+    /// inner doc comment
+    /// for the module
+    fn first_in_module() {}
+}
+
+mod some_mod {
+    //! This doc comment should *NOT* produce a warning
+
+    mod some_inner_mod {
+        fn some_noop() {}
+    }
+
+    //~v empty_line_after_doc_comments
+    /// # Indented
+    /// Blank line
+    fn indented() {}
+}
+
+//~v empty_line_after_doc_comments
+/// This should produce a warning
+fn with_doc_and_newline() {}
+
+// This should *NOT* produce a warning
+#[crate_type = "lib"]
+/// some comment
+fn with_no_newline_and_comment() {}
+
+//~v empty_line_after_doc_comments
+/// This doc comment should produce a warning
+/** This is also a doc comment and is part of the warning
+ */
+#[allow(non_camel_case_types)]
+#[allow(missing_docs)]
+#[allow(dead_code)]
+fn three_attributes() {}
+
+mod misattributed {
+    //~v empty_line_after_doc_comments
+    /// docs for `old_code`
+    // fn old_code() {}
+    fn new_code() {}
+
+    //~vv empty_line_after_doc_comments
+    /// Docs
+    /// for OldA
+    // struct OldA;
+    /// Docs
+    /// for OldB
+    // struct OldB;
+    /// Docs
+    /// for Multiple
+    #[allow(dead_code)]
+    struct Multiple;
+}
+
+mod block_comments {
+    //~v empty_line_after_doc_comments
+    /**
+     * Meant to be inner doc comment
+     */
+    fn first_in_module() {}
+
+    //~v empty_line_after_doc_comments
+    /**
+     * Docs for `old_code`
+     */
+    /* fn old_code() {} */
+    /**
+     * Docs for `new_code`
+     */
+    fn new_code() {}
+
+    //~v empty_line_after_doc_comments
+    /// Docs for `old_code2`
+    /* fn old_code2() {} */
+    /// Docs for `new_code2`
+    fn new_code2() {}
+}
+
+// This should *NOT* produce a warning
+#[doc = "
+Returns the escaped value of the textual representation of
+
+"]
+pub fn function() -> bool {
+    true
+}
+
+// This should *NOT* produce a warning
+#[derive(Clone, Copy)]
+pub enum FooFighter {
+    Bar1,
+
+    Bar2,
+
+    Bar3,
+
+    Bar4,
+}
+
+/// Should not lint
+// some line comment
+/// gaps without an empty line
+struct LineComment;
+
+/// This should *NOT* produce a warning because the empty line is inside a block comment
+/*
+
+*/
+pub struct EmptyInBlockComment;
+
+/// This should *NOT* produce a warning
+/* test */
+pub struct BlockComment;
+
+/// Ignore the empty line inside a cfg_attr'd out attribute
+#[cfg_attr(any(), multiline(
+    foo = 1
+
+    bar = 2
+))]
+fn empty_line_in_cfg_attr() {}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/empty_line_after/doc_comments.2.fixed b/src/tools/clippy/tests/ui/empty_line_after/doc_comments.2.fixed
new file mode 100644
index 000000000000..7a57dcd92332
--- /dev/null
+++ b/src/tools/clippy/tests/ui/empty_line_after/doc_comments.2.fixed
@@ -0,0 +1,144 @@
+#![warn(clippy::empty_line_after_outer_attr, clippy::empty_line_after_doc_comments)]
+
+//~vvv empty_line_after_doc_comments
+//! Meant to be an
+//! inner doc comment
+//! for the crate
+
+fn first_in_crate() {}
+
+mod m {
+    //~vvv empty_line_after_doc_comments
+    //! Meant to be an
+    //! inner doc comment
+    //! for the module
+
+    fn first_in_module() {}
+}
+
+mod some_mod {
+    //! This doc comment should *NOT* produce a warning
+
+    mod some_inner_mod {
+        fn some_noop() {}
+    }
+
+    //~v empty_line_after_doc_comments
+    /// # Indented
+    ///
+    /// Blank line
+    fn indented() {}
+}
+
+//~v empty_line_after_doc_comments
+/// This should produce a warning
+fn with_doc_and_newline() {}
+
+// This should *NOT* produce a warning
+#[crate_type = "lib"]
+/// some comment
+fn with_no_newline_and_comment() {}
+
+//~v empty_line_after_doc_comments
+/// This doc comment should produce a warning
+/** This is also a doc comment and is part of the warning
+ */
+#[allow(non_camel_case_types)]
+#[allow(missing_docs)]
+#[allow(dead_code)]
+fn three_attributes() {}
+
+mod misattributed {
+    //~v empty_line_after_doc_comments
+    // /// docs for `old_code`
+    // fn old_code() {}
+
+    fn new_code() {}
+
+    //~vv empty_line_after_doc_comments
+    // /// Docs
+    // /// for OldA
+    // struct OldA;
+
+    // /// Docs
+    // /// for OldB
+    // struct OldB;
+
+    /// Docs
+    /// for Multiple
+    #[allow(dead_code)]
+    struct Multiple;
+}
+
+mod block_comments {
+    //~v empty_line_after_doc_comments
+    /*!
+     * Meant to be inner doc comment
+     */
+
+    fn first_in_module() {}
+
+    //~v empty_line_after_doc_comments
+    /*
+     * Docs for `old_code`
+     */
+    /* fn old_code() {} */
+
+    /**
+     * Docs for `new_code`
+     */
+    fn new_code() {}
+
+    //~v empty_line_after_doc_comments
+    // /// Docs for `old_code2`
+    /* fn old_code2() {} */
+
+    /// Docs for `new_code2`
+    fn new_code2() {}
+}
+
+// This should *NOT* produce a warning
+#[doc = "
+Returns the escaped value of the textual representation of
+
+"]
+pub fn function() -> bool {
+    true
+}
+
+// This should *NOT* produce a warning
+#[derive(Clone, Copy)]
+pub enum FooFighter {
+    Bar1,
+
+    Bar2,
+
+    Bar3,
+
+    Bar4,
+}
+
+/// Should not lint
+// some line comment
+/// gaps without an empty line
+struct LineComment;
+
+/// This should *NOT* produce a warning because the empty line is inside a block comment
+/*
+
+*/
+pub struct EmptyInBlockComment;
+
+/// This should *NOT* produce a warning
+/* test */
+pub struct BlockComment;
+
+/// Ignore the empty line inside a cfg_attr'd out attribute
+#[cfg_attr(any(), multiline(
+    foo = 1
+
+    bar = 2
+))]
+fn empty_line_in_cfg_attr() {}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/empty_line_after/doc_comments.rs b/src/tools/clippy/tests/ui/empty_line_after/doc_comments.rs
new file mode 100644
index 000000000000..1da761a5c3d5
--- /dev/null
+++ b/src/tools/clippy/tests/ui/empty_line_after/doc_comments.rs
@@ -0,0 +1,147 @@
+#![warn(clippy::empty_line_after_outer_attr, clippy::empty_line_after_doc_comments)]
+
+//~vvv empty_line_after_doc_comments
+/// Meant to be an
+/// inner doc comment
+/// for the crate
+
+fn first_in_crate() {}
+
+mod m {
+    //~vvv empty_line_after_doc_comments
+    /// Meant to be an
+    /// inner doc comment
+    /// for the module
+
+    fn first_in_module() {}
+}
+
+mod some_mod {
+    //! This doc comment should *NOT* produce a warning
+
+    mod some_inner_mod {
+        fn some_noop() {}
+    }
+
+    //~v empty_line_after_doc_comments
+    /// # Indented
+
+    /// Blank line
+    fn indented() {}
+}
+
+//~v empty_line_after_doc_comments
+/// This should produce a warning
+
+fn with_doc_and_newline() {}
+
+// This should *NOT* produce a warning
+#[crate_type = "lib"]
+/// some comment
+fn with_no_newline_and_comment() {}
+
+//~v empty_line_after_doc_comments
+/// This doc comment should produce a warning
+
+/** This is also a doc comment and is part of the warning
+ */
+
+#[allow(non_camel_case_types)]
+#[allow(missing_docs)]
+#[allow(dead_code)]
+fn three_attributes() {}
+
+mod misattributed {
+    //~v empty_line_after_doc_comments
+    /// docs for `old_code`
+    // fn old_code() {}
+
+    fn new_code() {}
+
+    //~vv empty_line_after_doc_comments
+    /// Docs
+    /// for OldA
+    // struct OldA;
+
+    /// Docs
+    /// for OldB
+    // struct OldB;
+
+    /// Docs
+    /// for Multiple
+    #[allow(dead_code)]
+    struct Multiple;
+}
+
+mod block_comments {
+    //~v empty_line_after_doc_comments
+    /**
+     * Meant to be inner doc comment
+     */
+
+    fn first_in_module() {}
+
+    //~v empty_line_after_doc_comments
+    /**
+     * Docs for `old_code`
+     */
+    /* fn old_code() {} */
+
+    /**
+     * Docs for `new_code`
+     */
+    fn new_code() {}
+
+    //~v empty_line_after_doc_comments
+    /// Docs for `old_code2`
+    /* fn old_code2() {} */
+
+    /// Docs for `new_code2`
+    fn new_code2() {}
+}
+
+// This should *NOT* produce a warning
+#[doc = "
+Returns the escaped value of the textual representation of
+
+"]
+pub fn function() -> bool {
+    true
+}
+
+// This should *NOT* produce a warning
+#[derive(Clone, Copy)]
+pub enum FooFighter {
+    Bar1,
+
+    Bar2,
+
+    Bar3,
+
+    Bar4,
+}
+
+/// Should not lint
+// some line comment
+/// gaps without an empty line
+struct LineComment;
+
+/// This should *NOT* produce a warning because the empty line is inside a block comment
+/*
+
+*/
+pub struct EmptyInBlockComment;
+
+/// This should *NOT* produce a warning
+/* test */
+pub struct BlockComment;
+
+/// Ignore the empty line inside a cfg_attr'd out attribute
+#[cfg_attr(any(), multiline(
+    foo = 1
+
+    bar = 2
+))]
+fn empty_line_in_cfg_attr() {}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/empty_line_after/doc_comments.stderr b/src/tools/clippy/tests/ui/empty_line_after/doc_comments.stderr
new file mode 100644
index 000000000000..c238b4c9a17f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/empty_line_after/doc_comments.stderr
@@ -0,0 +1,176 @@
+error: empty line after doc comment
+  --> tests/ui/empty_line_after/doc_comments.rs:6:1
+   |
+LL | / /// for the crate
+LL | |
+   | |_
+LL |   fn first_in_crate() {}
+   |   ------------------- the comment documents this function
+   |
+   = note: `-D clippy::empty-line-after-doc-comments` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::empty_line_after_doc_comments)]`
+   = help: if the empty line is unintentional remove it
+help: if the comment should document the crate use an inner doc comment
+   |
+LL ~ //! Meant to be an
+LL ~ //! inner doc comment
+LL ~ //! for the crate
+   |
+
+error: empty line after doc comment
+  --> tests/ui/empty_line_after/doc_comments.rs:14:5
+   |
+LL | /     /// for the module
+LL | |
+   | |_
+LL |       fn first_in_module() {}
+   |       -------------------- the comment documents this function
+   |
+   = help: if the empty line is unintentional remove it
+help: if the comment should document the parent module use an inner doc comment
+   |
+LL ~     //! Meant to be an
+LL ~     //! inner doc comment
+LL ~     //! for the module
+   |
+
+error: empty line after doc comment
+  --> tests/ui/empty_line_after/doc_comments.rs:27:5
+   |
+LL | /     /// # Indented
+LL | |
+   | |_
+LL |       /// Blank line
+LL |       fn indented() {}
+   |       ------------- the comment documents this function
+   |
+   = help: if the empty line is unintentional remove it
+help: if the documentation should include the empty line include it in the comment
+   |
+LL |     ///
+   |
+
+error: empty line after doc comment
+  --> tests/ui/empty_line_after/doc_comments.rs:34:1
+   |
+LL | / /// This should produce a warning
+LL | |
+   | |_
+LL |   fn with_doc_and_newline() {}
+   |   ------------------------- the comment documents this function
+   |
+   = help: if the empty line is unintentional remove it
+
+error: empty lines after doc comment
+  --> tests/ui/empty_line_after/doc_comments.rs:44:1
+   |
+LL | / /// This doc comment should produce a warning
+LL | |
+LL | | /** This is also a doc comment and is part of the warning
+LL | |  */
+LL | |
+   | |_
+...
+LL |   fn three_attributes() {}
+   |   --------------------- the comment documents this function
+   |
+   = help: if the empty lines are unintentional remove them
+
+error: empty line after doc comment
+  --> tests/ui/empty_line_after/doc_comments.rs:56:5
+   |
+LL | /     /// docs for `old_code`
+LL | |     // fn old_code() {}
+LL | |
+   | |_
+LL |       fn new_code() {}
+   |       ------------- the comment documents this function
+   |
+   = help: if the empty line is unintentional remove it
+help: if the doc comment should not document `new_code` comment it out
+   |
+LL |     // /// docs for `old_code`
+   |     ++
+
+error: empty lines after doc comment
+  --> tests/ui/empty_line_after/doc_comments.rs:63:5
+   |
+LL | /     /// for OldA
+LL | |     // struct OldA;
+LL | |
+LL | |     /// Docs
+LL | |     /// for OldB
+LL | |     // struct OldB;
+LL | |
+   | |_
+...
+LL |       struct Multiple;
+   |       --------------- the comment documents this struct
+   |
+   = help: if the empty lines are unintentional remove them
+help: if the doc comment should not document `Multiple` comment it out
+   |
+LL ~     // /// Docs
+LL ~     // /// for OldA
+LL |     // struct OldA;
+LL |
+LL ~     // /// Docs
+LL ~     // /// for OldB
+   |
+
+error: empty line after doc comment
+  --> tests/ui/empty_line_after/doc_comments.rs:78:5
+   |
+LL | /     /**
+LL | |      * Meant to be inner doc comment
+LL | |      */
+LL | |
+   | |_
+LL |       fn first_in_module() {}
+   |       -------------------- the comment documents this function
+   |
+   = help: if the empty line is unintentional remove it
+help: if the comment should document the parent module use an inner doc comment
+   |
+LL |     /*!
+   |       ~
+
+error: empty line after doc comment
+  --> tests/ui/empty_line_after/doc_comments.rs:85:5
+   |
+LL | /     /**
+LL | |      * Docs for `old_code`
+LL | |      */
+LL | |     /* fn old_code() {} */
+LL | |
+   | |_
+...
+LL |       fn new_code() {}
+   |       ------------- the comment documents this function
+   |
+   = help: if the empty line is unintentional remove it
+help: if the doc comment should not document `new_code` comment it out
+   |
+LL -     /**
+LL +     /*
+   |
+
+error: empty line after doc comment
+  --> tests/ui/empty_line_after/doc_comments.rs:96:5
+   |
+LL | /     /// Docs for `old_code2`
+LL | |     /* fn old_code2() {} */
+LL | |
+   | |_
+LL |       /// Docs for `new_code2`
+LL |       fn new_code2() {}
+   |       -------------- the comment documents this function
+   |
+   = help: if the empty line is unintentional remove it
+help: if the doc comment should not document `new_code2` comment it out
+   |
+LL |     // /// Docs for `old_code2`
+   |     ++
+
+error: aborting due to 10 previous errors
+
diff --git a/src/tools/clippy/tests/ui/empty_line_after/outer_attribute.1.fixed b/src/tools/clippy/tests/ui/empty_line_after/outer_attribute.1.fixed
new file mode 100644
index 000000000000..36d80a2c95bf
--- /dev/null
+++ b/src/tools/clippy/tests/ui/empty_line_after/outer_attribute.1.fixed
@@ -0,0 +1,108 @@
+//@aux-build:../auxiliary/proc_macro_attr.rs
+#![warn(clippy::empty_line_after_outer_attr, clippy::empty_line_after_doc_comments)]
+
+//~v empty_line_after_outer_attr
+#[crate_type = "lib"]
+fn first_in_crate() {}
+
+#[macro_use]
+extern crate proc_macro_attr;
+
+//~v empty_line_after_outer_attr
+#[inline]
+/// some comment
+fn with_one_newline_and_comment() {}
+
+#[inline]
+/// some comment
+fn with_no_newline_and_comment() {}
+
+//~v empty_line_after_outer_attr
+#[inline]
+fn with_one_newline() {}
+
+#[rustfmt::skip]
+mod two_lines {
+    //~v empty_line_after_outer_attr
+    #[crate_type = "lib"]
+    fn with_two_newlines() {}
+}
+
+//~v empty_line_after_outer_attr
+#[doc = "doc attributes should be considered attributes"]
+enum Baz {
+    One,
+    Two,
+}
+
+//~v empty_line_after_outer_attr
+#[repr(C)]
+struct Foo {
+    one: isize,
+    two: isize,
+}
+
+//~v empty_line_after_outer_attr
+#[allow(dead_code)]
+mod foo {}
+
+//~v empty_line_after_outer_attr
+#[inline]
+// Still lint cases where the empty line does not immediately follow the attribute
+fn comment_before_empty_line() {}
+
+//~v empty_line_after_outer_attr
+#[allow(unused)]
+// This comment is isolated
+pub fn isolated_comment() {}
+
+#[doc = "
+Returns the escaped value of the textual representation of
+
+"]
+pub fn function() -> bool {
+    true
+}
+
+#[derive(Clone, Copy)]
+pub enum FooFighter {
+    Bar1,
+
+    Bar2,
+
+    Bar3,
+
+    Bar4,
+}
+
+#[crate_type = "lib"]
+/*
+
+*/
+pub struct EmptyLineInBlockComment;
+
+#[crate_type = "lib"]
+/* test */
+pub struct BlockComment;
+
+// See https://github.com/rust-lang/rust-clippy/issues/5567
+#[rustfmt::skip]
+#[fake_async_trait]
+pub trait Bazz {
+    fn foo() -> Vec {
+        let _i = "";
+
+
+
+        vec![]
+    }
+}
+
+#[derive(Clone, Copy)]
+#[dummy(string = "first line
+
+second line
+")]
+pub struct Args;
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/empty_line_after/outer_attribute.2.fixed b/src/tools/clippy/tests/ui/empty_line_after/outer_attribute.2.fixed
new file mode 100644
index 000000000000..0e8e4129e858
--- /dev/null
+++ b/src/tools/clippy/tests/ui/empty_line_after/outer_attribute.2.fixed
@@ -0,0 +1,111 @@
+//@aux-build:../auxiliary/proc_macro_attr.rs
+#![warn(clippy::empty_line_after_outer_attr, clippy::empty_line_after_doc_comments)]
+
+//~v empty_line_after_outer_attr
+#![crate_type = "lib"]
+
+fn first_in_crate() {}
+
+#[macro_use]
+extern crate proc_macro_attr;
+
+//~v empty_line_after_outer_attr
+#[inline]
+/// some comment
+fn with_one_newline_and_comment() {}
+
+#[inline]
+/// some comment
+fn with_no_newline_and_comment() {}
+
+//~v empty_line_after_outer_attr
+#[inline]
+fn with_one_newline() {}
+
+#[rustfmt::skip]
+mod two_lines {
+    //~v empty_line_after_outer_attr
+    #![crate_type = "lib"]
+
+
+    fn with_two_newlines() {}
+}
+
+//~v empty_line_after_outer_attr
+#[doc = "doc attributes should be considered attributes"]
+enum Baz {
+    One,
+    Two,
+}
+
+//~v empty_line_after_outer_attr
+#[repr(C)]
+struct Foo {
+    one: isize,
+    two: isize,
+}
+
+//~v empty_line_after_outer_attr
+#[allow(dead_code)]
+mod foo {}
+
+//~v empty_line_after_outer_attr
+#[inline]
+// Still lint cases where the empty line does not immediately follow the attribute
+fn comment_before_empty_line() {}
+
+//~v empty_line_after_outer_attr
+#[allow(unused)]
+// This comment is isolated
+pub fn isolated_comment() {}
+
+#[doc = "
+Returns the escaped value of the textual representation of
+
+"]
+pub fn function() -> bool {
+    true
+}
+
+#[derive(Clone, Copy)]
+pub enum FooFighter {
+    Bar1,
+
+    Bar2,
+
+    Bar3,
+
+    Bar4,
+}
+
+#[crate_type = "lib"]
+/*
+
+*/
+pub struct EmptyLineInBlockComment;
+
+#[crate_type = "lib"]
+/* test */
+pub struct BlockComment;
+
+// See https://github.com/rust-lang/rust-clippy/issues/5567
+#[rustfmt::skip]
+#[fake_async_trait]
+pub trait Bazz {
+    fn foo() -> Vec {
+        let _i = "";
+
+
+
+        vec![]
+    }
+}
+
+#[derive(Clone, Copy)]
+#[dummy(string = "first line
+
+second line
+")]
+pub struct Args;
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/empty_line_after/outer_attribute.rs b/src/tools/clippy/tests/ui/empty_line_after/outer_attribute.rs
new file mode 100644
index 000000000000..1295088ac00e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/empty_line_after/outer_attribute.rs
@@ -0,0 +1,119 @@
+//@aux-build:../auxiliary/proc_macro_attr.rs
+#![warn(clippy::empty_line_after_outer_attr, clippy::empty_line_after_doc_comments)]
+
+//~v empty_line_after_outer_attr
+#[crate_type = "lib"]
+
+fn first_in_crate() {}
+
+#[macro_use]
+extern crate proc_macro_attr;
+
+//~v empty_line_after_outer_attr
+#[inline]
+
+/// some comment
+fn with_one_newline_and_comment() {}
+
+#[inline]
+/// some comment
+fn with_no_newline_and_comment() {}
+
+//~v empty_line_after_outer_attr
+#[inline]
+
+fn with_one_newline() {}
+
+#[rustfmt::skip]
+mod two_lines {
+    //~v empty_line_after_outer_attr
+    #[crate_type = "lib"]
+
+
+    fn with_two_newlines() {}
+}
+
+//~v empty_line_after_outer_attr
+#[doc = "doc attributes should be considered attributes"]
+
+enum Baz {
+    One,
+    Two,
+}
+
+//~v empty_line_after_outer_attr
+#[repr(C)]
+
+struct Foo {
+    one: isize,
+    two: isize,
+}
+
+//~v empty_line_after_outer_attr
+#[allow(dead_code)]
+
+mod foo {}
+
+//~v empty_line_after_outer_attr
+#[inline]
+// Still lint cases where the empty line does not immediately follow the attribute
+
+fn comment_before_empty_line() {}
+
+//~v empty_line_after_outer_attr
+#[allow(unused)]
+
+// This comment is isolated
+
+pub fn isolated_comment() {}
+
+#[doc = "
+Returns the escaped value of the textual representation of
+
+"]
+pub fn function() -> bool {
+    true
+}
+
+#[derive(Clone, Copy)]
+pub enum FooFighter {
+    Bar1,
+
+    Bar2,
+
+    Bar3,
+
+    Bar4,
+}
+
+#[crate_type = "lib"]
+/*
+
+*/
+pub struct EmptyLineInBlockComment;
+
+#[crate_type = "lib"]
+/* test */
+pub struct BlockComment;
+
+// See https://github.com/rust-lang/rust-clippy/issues/5567
+#[rustfmt::skip]
+#[fake_async_trait]
+pub trait Bazz {
+    fn foo() -> Vec {
+        let _i = "";
+
+
+
+        vec![]
+    }
+}
+
+#[derive(Clone, Copy)]
+#[dummy(string = "first line
+
+second line
+")]
+pub struct Args;
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/empty_line_after/outer_attribute.stderr b/src/tools/clippy/tests/ui/empty_line_after/outer_attribute.stderr
new file mode 100644
index 000000000000..958b40424a92
--- /dev/null
+++ b/src/tools/clippy/tests/ui/empty_line_after/outer_attribute.stderr
@@ -0,0 +1,116 @@
+error: empty line after outer attribute
+  --> tests/ui/empty_line_after/outer_attribute.rs:5:1
+   |
+LL | / #[crate_type = "lib"]
+LL | |
+   | |_
+LL |   fn first_in_crate() {}
+   |   ------------------- the attribute applies to this function
+   |
+   = note: `-D clippy::empty-line-after-outer-attr` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::empty_line_after_outer_attr)]`
+   = help: if the empty line is unintentional remove it
+help: if the attribute should apply to the crate use an inner attribute
+   |
+LL | #![crate_type = "lib"]
+   |  +
+
+error: empty line after outer attribute
+  --> tests/ui/empty_line_after/outer_attribute.rs:13:1
+   |
+LL | / #[inline]
+LL | |
+   | |_
+LL |   /// some comment
+LL |   fn with_one_newline_and_comment() {}
+   |   --------------------------------- the attribute applies to this function
+   |
+   = help: if the empty line is unintentional remove it
+
+error: empty line after outer attribute
+  --> tests/ui/empty_line_after/outer_attribute.rs:23:1
+   |
+LL | / #[inline]
+LL | |
+   | |_
+LL |   fn with_one_newline() {}
+   |   --------------------- the attribute applies to this function
+   |
+   = help: if the empty line is unintentional remove it
+
+error: empty lines after outer attribute
+  --> tests/ui/empty_line_after/outer_attribute.rs:30:5
+   |
+LL | /     #[crate_type = "lib"]
+LL | |
+LL | |
+   | |_
+LL |       fn with_two_newlines() {}
+   |       ---------------------- the attribute applies to this function
+   |
+   = help: if the empty lines are unintentional remove them
+help: if the attribute should apply to the parent module use an inner attribute
+   |
+LL |     #![crate_type = "lib"]
+   |      +
+
+error: empty line after outer attribute
+  --> tests/ui/empty_line_after/outer_attribute.rs:37:1
+   |
+LL | / #[doc = "doc attributes should be considered attributes"]
+LL | |
+   | |_
+LL |   enum Baz {
+   |   -------- the attribute applies to this enum
+   |
+   = help: if the empty line is unintentional remove it
+
+error: empty line after outer attribute
+  --> tests/ui/empty_line_after/outer_attribute.rs:45:1
+   |
+LL | / #[repr(C)]
+LL | |
+   | |_
+LL |   struct Foo {
+   |   ---------- the attribute applies to this struct
+   |
+   = help: if the empty line is unintentional remove it
+
+error: empty line after outer attribute
+  --> tests/ui/empty_line_after/outer_attribute.rs:53:1
+   |
+LL | / #[allow(dead_code)]
+LL | |
+   | |_
+LL |   mod foo {}
+   |   ------- the attribute applies to this module
+   |
+   = help: if the empty line is unintentional remove it
+
+error: empty line after outer attribute
+  --> tests/ui/empty_line_after/outer_attribute.rs:58:1
+   |
+LL | / #[inline]
+LL | | // Still lint cases where the empty line does not immediately follow the attribute
+LL | |
+   | |_
+LL |   fn comment_before_empty_line() {}
+   |   ------------------------------ the attribute applies to this function
+   |
+   = help: if the empty line is unintentional remove it
+
+error: empty lines after outer attribute
+  --> tests/ui/empty_line_after/outer_attribute.rs:64:1
+   |
+LL | / #[allow(unused)]
+LL | |
+LL | | // This comment is isolated
+LL | |
+   | |_
+LL |   pub fn isolated_comment() {}
+   |   ------------------------- the attribute applies to this function
+   |
+   = help: if the empty lines are unintentional remove them
+
+error: aborting due to 9 previous errors
+
diff --git a/src/tools/clippy/tests/ui/empty_line_after_doc_comments.rs b/src/tools/clippy/tests/ui/empty_line_after_doc_comments.rs
deleted file mode 100644
index dd78491749c7..000000000000
--- a/src/tools/clippy/tests/ui/empty_line_after_doc_comments.rs
+++ /dev/null
@@ -1,132 +0,0 @@
-//@aux-build:proc_macro_attr.rs
-#![warn(clippy::empty_line_after_doc_comments)]
-#![allow(clippy::assertions_on_constants, clippy::duplicated_attributes)]
-#![feature(custom_inner_attributes)]
-#![rustfmt::skip]
-
-#[macro_use]
-extern crate proc_macro_attr;
-
-mod some_mod {
-    //! This doc comment should *NOT* produce a warning
-
-    mod some_inner_mod {
-        fn some_noop() {}
-    }
-}
-
-/// This should produce a warning
-
-fn with_doc_and_newline() { assert!(true)}
-
-// This should *NOT* produce a warning
-#[crate_type = "lib"]
-
-/// some comment
-fn with_one_newline_and_comment() { assert!(true) }
-
-// This should *NOT* produce a warning
-#[crate_type = "lib"]
-/// some comment
-fn with_no_newline_and_comment() { assert!(true) }
-
-
-// This should *NOT* produce a warning
-#[crate_type = "lib"]
-
-fn with_one_newline() { assert!(true) }
-
-// This should *NOT* produce a warning
-#[crate_type = "lib"]
-
-
-fn with_two_newlines() { assert!(true) }
-
-
-// This should *NOT* produce a warning
-#[crate_type = "lib"]
-
-enum Baz {
-    One,
-    Two
-}
-
-// This should *NOT* produce a warning
-#[crate_type = "lib"]
-
-struct Foo {
-    one: isize,
-    two: isize
-}
-
-// This should *NOT* produce a warning
-#[crate_type = "lib"]
-
-mod foo {
-}
-
-/// This doc comment should produce a warning
-
-/** This is also a doc comment and should produce a warning
- */
-
-// This should *NOT* produce a warning
-#[allow(non_camel_case_types)]
-#[allow(missing_docs)]
-#[allow(missing_docs)]
-fn three_attributes() { assert!(true) }
-
-// This should *NOT* produce a warning
-#[doc = "
-Returns the escaped value of the textual representation of
-
-"]
-pub fn function() -> bool {
-    true
-}
-
-// This should *NOT* produce a warning
-#[derive(Clone, Copy)]
-pub enum FooFighter {
-    Bar1,
-
-    Bar2,
-
-    Bar3,
-
-    Bar4
-}
-
-// This should *NOT* produce a warning because the empty line is inside a block comment
-#[crate_type = "lib"]
-/*
-
-*/
-pub struct S;
-
-// This should *NOT* produce a warning
-#[crate_type = "lib"]
-/* test */
-pub struct T;
-
-// This should *NOT* produce a warning
-// See https://github.com/rust-lang/rust-clippy/issues/5567
-#[fake_async_trait]
-pub trait Bazz {
-    fn foo() -> Vec {
-        let _i = "";
-
-
-
-        vec![]
-    }
-}
-
-#[derive(Clone, Copy)]
-#[dummy(string = "first line
-
-second line
-")]
-pub struct Args;
-
-fn main() {}
diff --git a/src/tools/clippy/tests/ui/empty_line_after_doc_comments.stderr b/src/tools/clippy/tests/ui/empty_line_after_doc_comments.stderr
deleted file mode 100644
index 889ccf6ba19d..000000000000
--- a/src/tools/clippy/tests/ui/empty_line_after_doc_comments.stderr
+++ /dev/null
@@ -1,37 +0,0 @@
-error: found an empty line after a doc comment. Perhaps you need to use `//!` to make a comment on a module, remove the empty line, or make a regular comment with `//`?
-  --> tests/ui/empty_line_after_doc_comments.rs:18:1
-   |
-LL | / /// This should produce a warning
-LL | |
-LL | | fn with_doc_and_newline() { assert!(true)}
-   | |_
-   |
-   = note: `-D clippy::empty-line-after-doc-comments` implied by `-D warnings`
-   = help: to override `-D warnings` add `#[allow(clippy::empty_line_after_doc_comments)]`
-
-error: found an empty line after a doc comment. Perhaps you need to use `//!` to make a comment on a module, remove the empty line, or make a regular comment with `//`?
-  --> tests/ui/empty_line_after_doc_comments.rs:68:1
-   |
-LL | / /// This doc comment should produce a warning
-LL | |
-LL | | /** This is also a doc comment and should produce a warning
-LL | |  */
-...  |
-LL | | #[allow(missing_docs)]
-LL | | fn three_attributes() { assert!(true) }
-   | |_
-
-error: found an empty line after a doc comment. Perhaps you need to use `//!` to make a comment on a module, remove the empty line, or make a regular comment with `//`?
-  --> tests/ui/empty_line_after_doc_comments.rs:70:1
-   |
-LL | / /** This is also a doc comment and should produce a warning
-LL | |  */
-LL | |
-LL | | // This should *NOT* produce a warning
-...  |
-LL | | #[allow(missing_docs)]
-LL | | fn three_attributes() { assert!(true) }
-   | |_
-
-error: aborting due to 3 previous errors
-
diff --git a/src/tools/clippy/tests/ui/empty_line_after_outer_attribute.rs b/src/tools/clippy/tests/ui/empty_line_after_outer_attribute.rs
deleted file mode 100644
index f147cf2cd5d1..000000000000
--- a/src/tools/clippy/tests/ui/empty_line_after_outer_attribute.rs
+++ /dev/null
@@ -1,120 +0,0 @@
-//@aux-build:proc_macro_attr.rs
-#![warn(clippy::empty_line_after_outer_attr)]
-#![allow(clippy::assertions_on_constants, clippy::duplicated_attributes)]
-#![feature(custom_inner_attributes)]
-#![rustfmt::skip]
-
-#[macro_use]
-extern crate proc_macro_attr;
-
-// This should produce a warning
-#[crate_type = "lib"]
-
-/// some comment
-fn with_one_newline_and_comment() { assert!(true) }
-
-// This should not produce a warning
-#[crate_type = "lib"]
-/// some comment
-fn with_no_newline_and_comment() { assert!(true) }
-
-
-// This should produce a warning
-#[crate_type = "lib"]
-
-fn with_one_newline() { assert!(true) }
-
-// This should produce a warning, too
-#[crate_type = "lib"]
-
-
-fn with_two_newlines() { assert!(true) }
-
-
-// This should produce a warning
-#[crate_type = "lib"]
-
-enum Baz {
-    One,
-    Two
-}
-
-// This should produce a warning
-#[crate_type = "lib"]
-
-struct Foo {
-    one: isize,
-    two: isize
-}
-
-// This should produce a warning
-#[crate_type = "lib"]
-
-mod foo {
-}
-
-/// This doc comment should not produce a warning
-
-/** This is also a doc comment and should not produce a warning
- */
-
-// This should not produce a warning
-#[allow(non_camel_case_types)]
-#[allow(missing_docs)]
-#[allow(missing_docs)]
-fn three_attributes() { assert!(true) }
-
-// This should not produce a warning
-#[doc = "
-Returns the escaped value of the textual representation of
-
-"]
-pub fn function() -> bool {
-    true
-}
-
-// This should not produce a warning
-#[derive(Clone, Copy)]
-pub enum FooFighter {
-    Bar1,
-
-    Bar2,
-
-    Bar3,
-
-    Bar4
-}
-
-// This should not produce a warning because the empty line is inside a block comment
-#[crate_type = "lib"]
-/*
-
-*/
-pub struct S;
-
-// This should not produce a warning
-#[crate_type = "lib"]
-/* test */
-pub struct T;
-
-// This should not produce a warning
-// See https://github.com/rust-lang/rust-clippy/issues/5567
-#[fake_async_trait]
-pub trait Bazz {
-    fn foo() -> Vec {
-        let _i = "";
-
-
-
-        vec![]
-    }
-}
-
-#[derive(Clone, Copy)]
-#[dummy(string = "first line
-
-second line
-")]
-pub struct Args;
-
-fn main() {}
diff --git a/src/tools/clippy/tests/ui/empty_line_after_outer_attribute.stderr b/src/tools/clippy/tests/ui/empty_line_after_outer_attribute.stderr
deleted file mode 100644
index b43e6e30da22..000000000000
--- a/src/tools/clippy/tests/ui/empty_line_after_outer_attribute.stderr
+++ /dev/null
@@ -1,54 +0,0 @@
-error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
-  --> tests/ui/empty_line_after_outer_attribute.rs:11:1
-   |
-LL | / #[crate_type = "lib"]
-LL | |
-LL | | /// some comment
-LL | | fn with_one_newline_and_comment() { assert!(true) }
-   | |_
-   |
-   = note: `-D clippy::empty-line-after-outer-attr` implied by `-D warnings`
-   = help: to override `-D warnings` add `#[allow(clippy::empty_line_after_outer_attr)]`
-
-error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
-  --> tests/ui/empty_line_after_outer_attribute.rs:23:1
-   |
-LL | / #[crate_type = "lib"]
-LL | |
-LL | | fn with_one_newline() { assert!(true) }
-   | |_
-
-error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
-  --> tests/ui/empty_line_after_outer_attribute.rs:28:1
-   |
-LL | / #[crate_type = "lib"]
-...  |
-LL | | fn with_two_newlines() { assert!(true) }
-   | |_
-
-error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
-  --> tests/ui/empty_line_after_outer_attribute.rs:35:1
-   |
-LL | / #[crate_type = "lib"]
-LL | |
-LL | | enum Baz {
-   | |_
-
-error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
-  --> tests/ui/empty_line_after_outer_attribute.rs:43:1
-   |
-LL | / #[crate_type = "lib"]
-LL | |
-LL | | struct Foo {
-   | |_
-
-error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
-  --> tests/ui/empty_line_after_outer_attribute.rs:51:1
-   |
-LL | / #[crate_type = "lib"]
-LL | |
-LL | | mod foo {
-   | |_
-
-error: aborting due to 6 previous errors
-
diff --git a/src/tools/clippy/tests/ui/empty_loop_no_std.rs b/src/tools/clippy/tests/ui/empty_loop_no_std.rs
index 5fe32351ed45..1bb895bda75d 100644
--- a/src/tools/clippy/tests/ui/empty_loop_no_std.rs
+++ b/src/tools/clippy/tests/ui/empty_loop_no_std.rs
@@ -1,5 +1,5 @@
 //@compile-flags: -Clink-arg=-nostartfiles
-//@ignore-target-apple
+//@ignore-target: apple
 
 #![warn(clippy::empty_loop)]
 #![feature(lang_items, start, libc)]
diff --git a/src/tools/clippy/tests/ui/enum_clike_unportable_variant.rs b/src/tools/clippy/tests/ui/enum_clike_unportable_variant.rs
index c50404c5047b..37849179d6a0 100644
--- a/src/tools/clippy/tests/ui/enum_clike_unportable_variant.rs
+++ b/src/tools/clippy/tests/ui/enum_clike_unportable_variant.rs
@@ -1,4 +1,4 @@
-//@ignore-32bit
+//@ignore-bitwidth: 32
 
 #![warn(clippy::enum_clike_unportable_variant)]
 #![allow(unused, non_upper_case_globals)]
diff --git a/src/tools/clippy/tests/ui/exit1.rs b/src/tools/clippy/tests/ui/exit1.rs
index a89f6dd4ca0e..36b3c42fd995 100644
--- a/src/tools/clippy/tests/ui/exit1.rs
+++ b/src/tools/clippy/tests/ui/exit1.rs
@@ -1,4 +1,4 @@
-#[warn(clippy::exit)]
+#![warn(clippy::exit)]
 
 fn not_main() {
     if true {
diff --git a/src/tools/clippy/tests/ui/exit2.rs b/src/tools/clippy/tests/ui/exit2.rs
index d5ff93fb9ccb..9bbb7b073a4b 100644
--- a/src/tools/clippy/tests/ui/exit2.rs
+++ b/src/tools/clippy/tests/ui/exit2.rs
@@ -1,4 +1,4 @@
-#[warn(clippy::exit)]
+#![warn(clippy::exit)]
 
 fn also_not_main() {
     std::process::exit(3);
diff --git a/src/tools/clippy/tests/ui/exit3.rs b/src/tools/clippy/tests/ui/exit3.rs
index 9dc0e1015a4f..cab908aafd33 100644
--- a/src/tools/clippy/tests/ui/exit3.rs
+++ b/src/tools/clippy/tests/ui/exit3.rs
@@ -1,4 +1,4 @@
-#[warn(clippy::exit)]
+#![warn(clippy::exit)]
 
 fn main() {
     if true {
diff --git a/src/tools/clippy/tests/ui/expect_fun_call.fixed b/src/tools/clippy/tests/ui/expect_fun_call.fixed
index 6ac3c43ad298..8f800c71941a 100644
--- a/src/tools/clippy/tests/ui/expect_fun_call.fixed
+++ b/src/tools/clippy/tests/ui/expect_fun_call.fixed
@@ -5,8 +5,6 @@
     clippy::unnecessary_literal_unwrap
 )]
 
-/// Checks implementation of the `EXPECT_FUN_CALL` lint
-
 macro_rules! one {
     () => {
         1
diff --git a/src/tools/clippy/tests/ui/expect_fun_call.rs b/src/tools/clippy/tests/ui/expect_fun_call.rs
index 22ea2db504fa..b5cfafb2993e 100644
--- a/src/tools/clippy/tests/ui/expect_fun_call.rs
+++ b/src/tools/clippy/tests/ui/expect_fun_call.rs
@@ -5,8 +5,6 @@
     clippy::unnecessary_literal_unwrap
 )]
 
-/// Checks implementation of the `EXPECT_FUN_CALL` lint
-
 macro_rules! one {
     () => {
         1
diff --git a/src/tools/clippy/tests/ui/expect_fun_call.stderr b/src/tools/clippy/tests/ui/expect_fun_call.stderr
index b41904d04fa4..bae853ac5c19 100644
--- a/src/tools/clippy/tests/ui/expect_fun_call.stderr
+++ b/src/tools/clippy/tests/ui/expect_fun_call.stderr
@@ -1,5 +1,5 @@
 error: use of `expect` followed by a function call
-  --> tests/ui/expect_fun_call.rs:37:26
+  --> tests/ui/expect_fun_call.rs:35:26
    |
 LL |     with_none_and_format.expect(&format!("Error {}: fake error", error_code));
    |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| panic!("Error {}: fake error", error_code))`
@@ -8,85 +8,85 @@ LL |     with_none_and_format.expect(&format!("Error {}: fake error", error_code
    = help: to override `-D warnings` add `#[allow(clippy::expect_fun_call)]`
 
 error: use of `expect` followed by a function call
-  --> tests/ui/expect_fun_call.rs:40:26
+  --> tests/ui/expect_fun_call.rs:38:26
    |
 LL |     with_none_and_as_str.expect(format!("Error {}: fake error", error_code).as_str());
    |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| panic!("Error {}: fake error", error_code))`
 
 error: use of `expect` followed by a function call
-  --> tests/ui/expect_fun_call.rs:43:37
+  --> tests/ui/expect_fun_call.rs:41:37
    |
 LL |     with_none_and_format_with_macro.expect(format!("Error {}: fake error", one!()).as_str());
    |                                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| panic!("Error {}: fake error", one!()))`
 
 error: use of `expect` followed by a function call
-  --> tests/ui/expect_fun_call.rs:53:25
+  --> tests/ui/expect_fun_call.rs:51:25
    |
 LL |     with_err_and_format.expect(&format!("Error {}: fake error", error_code));
    |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|_| panic!("Error {}: fake error", error_code))`
 
 error: use of `expect` followed by a function call
-  --> tests/ui/expect_fun_call.rs:56:25
+  --> tests/ui/expect_fun_call.rs:54:25
    |
 LL |     with_err_and_as_str.expect(format!("Error {}: fake error", error_code).as_str());
    |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|_| panic!("Error {}: fake error", error_code))`
 
 error: use of `expect` followed by a function call
-  --> tests/ui/expect_fun_call.rs:68:17
+  --> tests/ui/expect_fun_call.rs:66:17
    |
 LL |     Some("foo").expect(format!("{} {}", 1, 2).as_ref());
    |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| panic!("{} {}", 1, 2))`
 
 error: use of `expect` followed by a function call
-  --> tests/ui/expect_fun_call.rs:89:21
+  --> tests/ui/expect_fun_call.rs:87:21
    |
 LL |         Some("foo").expect(&get_string());
    |                     ^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| { panic!("{}", get_string()) })`
 
 error: use of `expect` followed by a function call
-  --> tests/ui/expect_fun_call.rs:90:21
+  --> tests/ui/expect_fun_call.rs:88:21
    |
 LL |         Some("foo").expect(get_string().as_ref());
    |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| { panic!("{}", get_string()) })`
 
 error: use of `expect` followed by a function call
-  --> tests/ui/expect_fun_call.rs:91:21
+  --> tests/ui/expect_fun_call.rs:89:21
    |
 LL |         Some("foo").expect(get_string().as_str());
    |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| { panic!("{}", get_string()) })`
 
 error: use of `expect` followed by a function call
-  --> tests/ui/expect_fun_call.rs:93:21
+  --> tests/ui/expect_fun_call.rs:91:21
    |
 LL |         Some("foo").expect(get_static_str());
    |                     ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| { panic!("{}", get_static_str()) })`
 
 error: use of `expect` followed by a function call
-  --> tests/ui/expect_fun_call.rs:94:21
+  --> tests/ui/expect_fun_call.rs:92:21
    |
 LL |         Some("foo").expect(get_non_static_str(&0));
    |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| { panic!("{}", get_non_static_str(&0).to_string()) })`
 
 error: use of `expect` followed by a function call
-  --> tests/ui/expect_fun_call.rs:98:16
+  --> tests/ui/expect_fun_call.rs:96:16
    |
 LL |     Some(true).expect(&format!("key {}, {}", 1, 2));
    |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| panic!("key {}, {}", 1, 2))`
 
 error: use of `expect` followed by a function call
-  --> tests/ui/expect_fun_call.rs:104:17
+  --> tests/ui/expect_fun_call.rs:102:17
    |
 LL |         opt_ref.expect(&format!("{:?}", opt_ref));
    |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| panic!("{:?}", opt_ref))`
 
 error: use of `expect` followed by a function call
-  --> tests/ui/expect_fun_call.rs:108:20
+  --> tests/ui/expect_fun_call.rs:106:20
    |
 LL |     format_capture.expect(&format!("{error_code}"));
    |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| panic!("{error_code}"))`
 
 error: use of `expect` followed by a function call
-  --> tests/ui/expect_fun_call.rs:111:30
+  --> tests/ui/expect_fun_call.rs:109:30
    |
 LL |     format_capture_and_value.expect(&format!("{error_code}, {}", 1));
    |                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| panic!("{error_code}, {}", 1))`
diff --git a/src/tools/clippy/tests/ui/field_reassign_with_default.rs b/src/tools/clippy/tests/ui/field_reassign_with_default.rs
index 620cffb4f04d..2a432751952d 100644
--- a/src/tools/clippy/tests/ui/field_reassign_with_default.rs
+++ b/src/tools/clippy/tests/ui/field_reassign_with_default.rs
@@ -192,8 +192,8 @@ struct WrapperMulti {
 }
 
 mod issue6312 {
-    use std::sync::atomic::AtomicBool;
     use std::sync::Arc;
+    use std::sync::atomic::AtomicBool;
 
     // do not lint: type implements `Drop` but not all fields are `Copy`
     #[derive(Clone, Default)]
diff --git a/src/tools/clippy/tests/ui/floating_point_arithmetic_nostd.rs b/src/tools/clippy/tests/ui/floating_point_arithmetic_nostd.rs
index 47c113d61c04..8ea75fae89b6 100644
--- a/src/tools/clippy/tests/ui/floating_point_arithmetic_nostd.rs
+++ b/src/tools/clippy/tests/ui/floating_point_arithmetic_nostd.rs
@@ -4,7 +4,7 @@
 #![no_std]
 
 // The following should not lint, as the suggested methods `{f16,f32,f64,f128}.mul_add()`
-// and ``{f16,f32,f64,f128}::abs()` are not available in no_std
+// and `{f16,f32,f64,f128}::abs()` are not available in no_std
 
 pub fn mul_add() {
     let a: f64 = 1234.567;
diff --git a/src/tools/clippy/tests/ui/format_args.fixed b/src/tools/clippy/tests/ui/format_args.fixed
index 4d812f6bf1dc..20d1e3d90504 100644
--- a/src/tools/clippy/tests/ui/format_args.fixed
+++ b/src/tools/clippy/tests/ui/format_args.fixed
@@ -8,7 +8,7 @@
     clippy::uninlined_format_args
 )]
 
-use std::io::{stdout, Write};
+use std::io::{Write, stdout};
 use std::ops::Deref;
 use std::panic::Location;
 
diff --git a/src/tools/clippy/tests/ui/format_args.rs b/src/tools/clippy/tests/ui/format_args.rs
index d242623feb62..18ab223db78a 100644
--- a/src/tools/clippy/tests/ui/format_args.rs
+++ b/src/tools/clippy/tests/ui/format_args.rs
@@ -8,7 +8,7 @@
     clippy::uninlined_format_args
 )]
 
-use std::io::{stdout, Write};
+use std::io::{Write, stdout};
 use std::ops::Deref;
 use std::panic::Location;
 
diff --git a/src/tools/clippy/tests/ui/format_args_unfixable.rs b/src/tools/clippy/tests/ui/format_args_unfixable.rs
index b7492e38b256..f04715f4f018 100644
--- a/src/tools/clippy/tests/ui/format_args_unfixable.rs
+++ b/src/tools/clippy/tests/ui/format_args_unfixable.rs
@@ -2,7 +2,7 @@
 #![allow(unused)]
 #![allow(clippy::assertions_on_constants, clippy::eq_op, clippy::uninlined_format_args)]
 
-use std::io::{stdout, Error, ErrorKind, Write};
+use std::io::{Error, ErrorKind, Write, stdout};
 use std::ops::Deref;
 use std::panic::Location;
 
diff --git a/src/tools/clippy/tests/ui/if_then_some_else_none.fixed b/src/tools/clippy/tests/ui/if_then_some_else_none.fixed
index ad13372a68b7..1f47dddcbc40 100644
--- a/src/tools/clippy/tests/ui/if_then_some_else_none.fixed
+++ b/src/tools/clippy/tests/ui/if_then_some_else_none.fixed
@@ -113,6 +113,10 @@ fn issue11394(b: bool, v: Result<(), ()>) -> Result<(), ()> {
     Ok(())
 }
 
+fn issue13407(s: &str) -> Option {
+    (s == "1").then(|| true)
+}
+
 const fn issue12103(x: u32) -> Option {
     // Should not issue an error in `const` context
     if x > 42 { Some(150) } else { None }
diff --git a/src/tools/clippy/tests/ui/if_then_some_else_none.rs b/src/tools/clippy/tests/ui/if_then_some_else_none.rs
index 73edbb7da2a8..499f008fb876 100644
--- a/src/tools/clippy/tests/ui/if_then_some_else_none.rs
+++ b/src/tools/clippy/tests/ui/if_then_some_else_none.rs
@@ -131,6 +131,10 @@ fn issue11394(b: bool, v: Result<(), ()>) -> Result<(), ()> {
     Ok(())
 }
 
+fn issue13407(s: &str) -> Option {
+    if s == "1" { Some(true) } else { None }
+}
+
 const fn issue12103(x: u32) -> Option {
     // Should not issue an error in `const` context
     if x > 42 { Some(150) } else { None }
diff --git a/src/tools/clippy/tests/ui/if_then_some_else_none.stderr b/src/tools/clippy/tests/ui/if_then_some_else_none.stderr
index aed01e026cbe..e7bc66b3ee88 100644
--- a/src/tools/clippy/tests/ui/if_then_some_else_none.stderr
+++ b/src/tools/clippy/tests/ui/if_then_some_else_none.stderr
@@ -52,5 +52,11 @@ LL | |         None
 LL | |     };
    | |_____^ help: try: `foo().then(||  { println!("true!"); 150 })`
 
-error: aborting due to 5 previous errors
+error: this could be simplified with `bool::then`
+  --> tests/ui/if_then_some_else_none.rs:135:5
+   |
+LL |     if s == "1" { Some(true) } else { None }
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(s == "1").then(|| true)`
+
+error: aborting due to 6 previous errors
 
diff --git a/src/tools/clippy/tests/ui/ignored_unit_patterns.fixed b/src/tools/clippy/tests/ui/ignored_unit_patterns.fixed
index 118f0b488952..fde404373098 100644
--- a/src/tools/clippy/tests/ui/ignored_unit_patterns.fixed
+++ b/src/tools/clippy/tests/ui/ignored_unit_patterns.fixed
@@ -21,15 +21,12 @@ fn main() {
     let _ = foo().map_err(|()| todo!());
     //~^ ERROR: matching over `()` is more explicit
 
-    println!(
-        "{:?}",
-        match foo() {
-            Ok(()) => {},
-            //~^ ERROR: matching over `()` is more explicit
-            Err(()) => {},
-            //~^ ERROR: matching over `()` is more explicit
-        }
-    );
+    println!("{:?}", match foo() {
+        Ok(()) => {},
+        //~^ ERROR: matching over `()` is more explicit
+        Err(()) => {},
+        //~^ ERROR: matching over `()` is more explicit
+    });
 }
 
 // ignored_unit_patterns in derive macro should be ok
diff --git a/src/tools/clippy/tests/ui/ignored_unit_patterns.rs b/src/tools/clippy/tests/ui/ignored_unit_patterns.rs
index 92feb9e6c281..528844d76e05 100644
--- a/src/tools/clippy/tests/ui/ignored_unit_patterns.rs
+++ b/src/tools/clippy/tests/ui/ignored_unit_patterns.rs
@@ -21,15 +21,12 @@ fn main() {
     let _ = foo().map_err(|_| todo!());
     //~^ ERROR: matching over `()` is more explicit
 
-    println!(
-        "{:?}",
-        match foo() {
-            Ok(_) => {},
-            //~^ ERROR: matching over `()` is more explicit
-            Err(_) => {},
-            //~^ ERROR: matching over `()` is more explicit
-        }
-    );
+    println!("{:?}", match foo() {
+        Ok(_) => {},
+        //~^ ERROR: matching over `()` is more explicit
+        Err(_) => {},
+        //~^ ERROR: matching over `()` is more explicit
+    });
 }
 
 // ignored_unit_patterns in derive macro should be ok
diff --git a/src/tools/clippy/tests/ui/ignored_unit_patterns.stderr b/src/tools/clippy/tests/ui/ignored_unit_patterns.stderr
index 00a254e39192..54ff4454d6bd 100644
--- a/src/tools/clippy/tests/ui/ignored_unit_patterns.stderr
+++ b/src/tools/clippy/tests/ui/ignored_unit_patterns.stderr
@@ -26,31 +26,31 @@ LL |     let _ = foo().map_err(|_| todo!());
    |                            ^ help: use `()` instead of `_`: `()`
 
 error: matching over `()` is more explicit
-  --> tests/ui/ignored_unit_patterns.rs:27:16
+  --> tests/ui/ignored_unit_patterns.rs:25:12
    |
-LL |             Ok(_) => {},
-   |                ^ help: use `()` instead of `_`: `()`
+LL |         Ok(_) => {},
+   |            ^ help: use `()` instead of `_`: `()`
 
 error: matching over `()` is more explicit
-  --> tests/ui/ignored_unit_patterns.rs:29:17
+  --> tests/ui/ignored_unit_patterns.rs:27:13
    |
-LL |             Err(_) => {},
-   |                 ^ help: use `()` instead of `_`: `()`
+LL |         Err(_) => {},
+   |             ^ help: use `()` instead of `_`: `()`
 
 error: matching over `()` is more explicit
-  --> tests/ui/ignored_unit_patterns.rs:41:9
+  --> tests/ui/ignored_unit_patterns.rs:38:9
    |
 LL |     let _ = foo().unwrap();
    |         ^ help: use `()` instead of `_`: `()`
 
 error: matching over `()` is more explicit
-  --> tests/ui/ignored_unit_patterns.rs:50:13
+  --> tests/ui/ignored_unit_patterns.rs:47:13
    |
 LL |         (1, _) => unimplemented!(),
    |             ^ help: use `()` instead of `_`: `()`
 
 error: matching over `()` is more explicit
-  --> tests/ui/ignored_unit_patterns.rs:57:13
+  --> tests/ui/ignored_unit_patterns.rs:54:13
    |
 LL |     for (x, _) in v {
    |             ^ help: use `()` instead of `_`: `()`
diff --git a/src/tools/clippy/tests/ui/implicit_saturating_sub.fixed b/src/tools/clippy/tests/ui/implicit_saturating_sub.fixed
index 27f679797dd9..81cc14949144 100644
--- a/src/tools/clippy/tests/ui/implicit_saturating_sub.fixed
+++ b/src/tools/clippy/tests/ui/implicit_saturating_sub.fixed
@@ -184,18 +184,18 @@ fn main() {
     let mut m = Mock;
     let mut u_32 = 3000;
     let a = 200;
-    let mut _b = 8;
+    let mut b = 8;
 
     if m != 0 {
         m -= 1;
     }
 
     if a > 0 {
-        _b -= 1;
+        b -= 1;
     }
 
     if 0 > a {
-        _b -= 1;
+        b -= 1;
     }
 
     if u_32 > 0 {
@@ -214,4 +214,11 @@ fn main() {
     } else if u_32 > 0 {
         u_32 -= 1;
     }
+
+    let result = if a < b {
+        println!("we shouldn't remove this");
+        0
+    } else {
+        a - b
+    };
 }
diff --git a/src/tools/clippy/tests/ui/implicit_saturating_sub.rs b/src/tools/clippy/tests/ui/implicit_saturating_sub.rs
index 5d7b95d2c652..f73396ebd27e 100644
--- a/src/tools/clippy/tests/ui/implicit_saturating_sub.rs
+++ b/src/tools/clippy/tests/ui/implicit_saturating_sub.rs
@@ -230,18 +230,18 @@ fn main() {
     let mut m = Mock;
     let mut u_32 = 3000;
     let a = 200;
-    let mut _b = 8;
+    let mut b = 8;
 
     if m != 0 {
         m -= 1;
     }
 
     if a > 0 {
-        _b -= 1;
+        b -= 1;
     }
 
     if 0 > a {
-        _b -= 1;
+        b -= 1;
     }
 
     if u_32 > 0 {
@@ -260,4 +260,11 @@ fn main() {
     } else if u_32 > 0 {
         u_32 -= 1;
     }
+
+    let result = if a < b {
+        println!("we shouldn't remove this");
+        0
+    } else {
+        a - b
+    };
 }
diff --git a/src/tools/clippy/tests/ui/incompatible_msrv.rs b/src/tools/clippy/tests/ui/incompatible_msrv.rs
index 8ef1f554bc35..c23df223d508 100644
--- a/src/tools/clippy/tests/ui/incompatible_msrv.rs
+++ b/src/tools/clippy/tests/ui/incompatible_msrv.rs
@@ -2,8 +2,8 @@
 #![feature(custom_inner_attributes)]
 #![clippy::msrv = "1.3.0"]
 
-use std::collections::hash_map::Entry;
 use std::collections::HashMap;
+use std::collections::hash_map::Entry;
 use std::future::Future;
 use std::thread::sleep;
 use std::time::Duration;
diff --git a/src/tools/clippy/tests/ui/legacy_numeric_constants.fixed b/src/tools/clippy/tests/ui/legacy_numeric_constants.fixed
index a6ef8f8c119a..3a2294ef4c59 100644
--- a/src/tools/clippy/tests/ui/legacy_numeric_constants.fixed
+++ b/src/tools/clippy/tests/ui/legacy_numeric_constants.fixed
@@ -22,8 +22,8 @@ macro_rules! b {
     };
 }
 
-use std::u32::MAX;
 use std::u8::MIN;
+use std::u32::MAX;
 use std::{f64, u32};
 
 #[warn(clippy::legacy_numeric_constants)]
@@ -99,8 +99,8 @@ fn allow() {
     ::std::primitive::u8::MIN;
     ::std::u8::MIN;
     ::std::primitive::u8::min_value();
-    use std::u64;
     use std::u8::MIN;
+    use std::u64;
 }
 
 #[warn(clippy::legacy_numeric_constants)]
diff --git a/src/tools/clippy/tests/ui/legacy_numeric_constants.rs b/src/tools/clippy/tests/ui/legacy_numeric_constants.rs
index cd633545372c..6cb3e694ea14 100644
--- a/src/tools/clippy/tests/ui/legacy_numeric_constants.rs
+++ b/src/tools/clippy/tests/ui/legacy_numeric_constants.rs
@@ -22,8 +22,8 @@ macro_rules! b {
     };
 }
 
-use std::u32::MAX;
 use std::u8::MIN;
+use std::u32::MAX;
 use std::{f64, u32};
 
 #[warn(clippy::legacy_numeric_constants)]
@@ -99,8 +99,8 @@ fn allow() {
     ::std::primitive::u8::MIN;
     ::std::u8::MIN;
     ::std::primitive::u8::min_value();
-    use std::u64;
     use std::u8::MIN;
+    use std::u64;
 }
 
 #[warn(clippy::legacy_numeric_constants)]
diff --git a/src/tools/clippy/tests/ui/macro_use_imports.fixed b/src/tools/clippy/tests/ui/macro_use_imports.fixed
index 38ed5a957e73..28844fab7479 100644
--- a/src/tools/clippy/tests/ui/macro_use_imports.fixed
+++ b/src/tools/clippy/tests/ui/macro_use_imports.fixed
@@ -2,7 +2,7 @@
 //@aux-build:macro_use_helper.rs
 //@aux-build:proc_macro_derive.rs
 
-//@ignore-32bit
+//@ignore-bitwidth: 32
 
 #![allow(unused_imports, unreachable_code, unused_variables, dead_code, unused_attributes)]
 #![allow(clippy::single_component_path_imports)]
diff --git a/src/tools/clippy/tests/ui/macro_use_imports.rs b/src/tools/clippy/tests/ui/macro_use_imports.rs
index ae6cc16ed276..5381f2959898 100644
--- a/src/tools/clippy/tests/ui/macro_use_imports.rs
+++ b/src/tools/clippy/tests/ui/macro_use_imports.rs
@@ -2,7 +2,7 @@
 //@aux-build:macro_use_helper.rs
 //@aux-build:proc_macro_derive.rs
 
-//@ignore-32bit
+//@ignore-bitwidth: 32
 
 #![allow(unused_imports, unreachable_code, unused_variables, dead_code, unused_attributes)]
 #![allow(clippy::single_component_path_imports)]
diff --git a/src/tools/clippy/tests/ui/macro_use_imports_expect.rs b/src/tools/clippy/tests/ui/macro_use_imports_expect.rs
index df6d5b9fbabf..60cce1d24a28 100644
--- a/src/tools/clippy/tests/ui/macro_use_imports_expect.rs
+++ b/src/tools/clippy/tests/ui/macro_use_imports_expect.rs
@@ -1,7 +1,7 @@
 //@aux-build:macro_rules.rs
 //@aux-build:macro_use_helper.rs
 //@aux-build:proc_macro_derive.rs
-//@ignore-32bit
+//@ignore-bitwidth: 32
 
 #![allow(unused_imports, unreachable_code, unused_variables, dead_code, unused_attributes)]
 #![allow(clippy::single_component_path_imports)]
diff --git a/src/tools/clippy/tests/ui/manual_arithmetic_check-2.rs b/src/tools/clippy/tests/ui/manual_arithmetic_check-2.rs
new file mode 100644
index 000000000000..e97e3bdfef76
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_arithmetic_check-2.rs
@@ -0,0 +1,35 @@
+//@no-rustfix
+#![warn(clippy::implicit_saturating_sub)]
+#![allow(arithmetic_overflow)]
+
+fn main() {
+    let a = 12u32;
+    let b = 13u32;
+
+    let result = if a > b { b - a } else { 0 };
+    //~^ ERROR: inverted arithmetic check before subtraction
+    let result = if b < a { b - a } else { 0 };
+    //~^ ERROR: inverted arithmetic check before subtraction
+
+    let result = if a > b { 0 } else { a - b };
+    //~^ ERROR: inverted arithmetic check before subtraction
+    let result = if a >= b { 0 } else { a - b };
+    //~^ ERROR: inverted arithmetic check before subtraction
+    let result = if b < a { 0 } else { a - b };
+    //~^ ERROR: inverted arithmetic check before subtraction
+    let result = if b <= a { 0 } else { a - b };
+    //~^ ERROR: inverted arithmetic check before subtraction
+
+    let af = 12f32;
+    let bf = 13f32;
+    // Should not lint!
+    let result = if bf < af { 0. } else { af - bf };
+
+    // Should not lint!
+    let result = if a < b {
+        println!("we shouldn't remove this");
+        0
+    } else {
+        a - b
+    };
+}
diff --git a/src/tools/clippy/tests/ui/manual_arithmetic_check-2.stderr b/src/tools/clippy/tests/ui/manual_arithmetic_check-2.stderr
new file mode 100644
index 000000000000..4121aa7464f0
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_arithmetic_check-2.stderr
@@ -0,0 +1,75 @@
+error: inverted arithmetic check before subtraction
+  --> tests/ui/manual_arithmetic_check-2.rs:9:23
+   |
+LL |     let result = if a > b { b - a } else { 0 };
+   |                       ^     ----- help: try replacing it with: `a - b`
+   |
+note: this subtraction underflows when `b < a`
+  --> tests/ui/manual_arithmetic_check-2.rs:9:29
+   |
+LL |     let result = if a > b { b - a } else { 0 };
+   |                             ^^^^^
+   = note: `#[deny(clippy::inverted_saturating_sub)]` on by default
+
+error: inverted arithmetic check before subtraction
+  --> tests/ui/manual_arithmetic_check-2.rs:11:23
+   |
+LL |     let result = if b < a { b - a } else { 0 };
+   |                       ^     ----- help: try replacing it with: `a - b`
+   |
+note: this subtraction underflows when `b < a`
+  --> tests/ui/manual_arithmetic_check-2.rs:11:29
+   |
+LL |     let result = if b < a { b - a } else { 0 };
+   |                             ^^^^^
+
+error: inverted arithmetic check before subtraction
+  --> tests/ui/manual_arithmetic_check-2.rs:14:23
+   |
+LL |     let result = if a > b { 0 } else { a - b };
+   |                       ^                ----- help: try replacing it with: `b - a`
+   |
+note: this subtraction underflows when `a < b`
+  --> tests/ui/manual_arithmetic_check-2.rs:14:40
+   |
+LL |     let result = if a > b { 0 } else { a - b };
+   |                                        ^^^^^
+
+error: inverted arithmetic check before subtraction
+  --> tests/ui/manual_arithmetic_check-2.rs:16:23
+   |
+LL |     let result = if a >= b { 0 } else { a - b };
+   |                       ^^                ----- help: try replacing it with: `b - a`
+   |
+note: this subtraction underflows when `a < b`
+  --> tests/ui/manual_arithmetic_check-2.rs:16:41
+   |
+LL |     let result = if a >= b { 0 } else { a - b };
+   |                                         ^^^^^
+
+error: inverted arithmetic check before subtraction
+  --> tests/ui/manual_arithmetic_check-2.rs:18:23
+   |
+LL |     let result = if b < a { 0 } else { a - b };
+   |                       ^                ----- help: try replacing it with: `b - a`
+   |
+note: this subtraction underflows when `a < b`
+  --> tests/ui/manual_arithmetic_check-2.rs:18:40
+   |
+LL |     let result = if b < a { 0 } else { a - b };
+   |                                        ^^^^^
+
+error: inverted arithmetic check before subtraction
+  --> tests/ui/manual_arithmetic_check-2.rs:20:23
+   |
+LL |     let result = if b <= a { 0 } else { a - b };
+   |                       ^^                ----- help: try replacing it with: `b - a`
+   |
+note: this subtraction underflows when `a < b`
+  --> tests/ui/manual_arithmetic_check-2.rs:20:41
+   |
+LL |     let result = if b <= a { 0 } else { a - b };
+   |                                         ^^^^^
+
+error: aborting due to 6 previous errors
+
diff --git a/src/tools/clippy/tests/ui/manual_arithmetic_check.fixed b/src/tools/clippy/tests/ui/manual_arithmetic_check.fixed
new file mode 100644
index 000000000000..29ecbb9ad2ad
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_arithmetic_check.fixed
@@ -0,0 +1,24 @@
+#![warn(clippy::implicit_saturating_sub, clippy::inverted_saturating_sub)]
+#![allow(clippy::if_same_then_else)]
+
+fn main() {
+    let a = 12u32;
+    let b = 13u32;
+    let c = 8u32;
+
+    let result = a.saturating_sub(b);
+    //~^ ERROR: manual arithmetic check found
+    let result = a.saturating_sub(b);
+    //~^ ERROR: manual arithmetic check found
+
+    let result = a.saturating_sub(b);
+    //~^ ERROR: manual arithmetic check found
+    let result = a.saturating_sub(b);
+    //~^ ERROR: manual arithmetic check found
+
+    // Should not warn!
+    let result = if a > b { a - b } else { a - c };
+
+    // Just to check it won't break clippy.
+    let result = if b > a { 0 } else { 0 };
+}
diff --git a/src/tools/clippy/tests/ui/manual_arithmetic_check.rs b/src/tools/clippy/tests/ui/manual_arithmetic_check.rs
new file mode 100644
index 000000000000..69554c6b61ca
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_arithmetic_check.rs
@@ -0,0 +1,24 @@
+#![warn(clippy::implicit_saturating_sub, clippy::inverted_saturating_sub)]
+#![allow(clippy::if_same_then_else)]
+
+fn main() {
+    let a = 12u32;
+    let b = 13u32;
+    let c = 8u32;
+
+    let result = if a > b { a - b } else { 0 };
+    //~^ ERROR: manual arithmetic check found
+    let result = if b < a { a - b } else { 0 };
+    //~^ ERROR: manual arithmetic check found
+
+    let result = if a < b { 0 } else { a - b };
+    //~^ ERROR: manual arithmetic check found
+    let result = if b > a { 0 } else { a - b };
+    //~^ ERROR: manual arithmetic check found
+
+    // Should not warn!
+    let result = if a > b { a - b } else { a - c };
+
+    // Just to check it won't break clippy.
+    let result = if b > a { 0 } else { 0 };
+}
diff --git a/src/tools/clippy/tests/ui/manual_arithmetic_check.stderr b/src/tools/clippy/tests/ui/manual_arithmetic_check.stderr
new file mode 100644
index 000000000000..b0cf73cd9151
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_arithmetic_check.stderr
@@ -0,0 +1,29 @@
+error: manual arithmetic check found
+  --> tests/ui/manual_arithmetic_check.rs:9:18
+   |
+LL |     let result = if a > b { a - b } else { 0 };
+   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `a.saturating_sub(b)`
+   |
+   = note: `-D clippy::implicit-saturating-sub` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::implicit_saturating_sub)]`
+
+error: manual arithmetic check found
+  --> tests/ui/manual_arithmetic_check.rs:11:18
+   |
+LL |     let result = if b < a { a - b } else { 0 };
+   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `a.saturating_sub(b)`
+
+error: manual arithmetic check found
+  --> tests/ui/manual_arithmetic_check.rs:14:18
+   |
+LL |     let result = if a < b { 0 } else { a - b };
+   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `a.saturating_sub(b)`
+
+error: manual arithmetic check found
+  --> tests/ui/manual_arithmetic_check.rs:16:18
+   |
+LL |     let result = if b > a { 0 } else { a - b };
+   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `a.saturating_sub(b)`
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/tests/ui/manual_div_ceil.fixed b/src/tools/clippy/tests/ui/manual_div_ceil.fixed
new file mode 100644
index 000000000000..e7801f7376aa
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_div_ceil.fixed
@@ -0,0 +1,30 @@
+#![warn(clippy::manual_div_ceil)]
+
+fn main() {
+    let x = 7_u32;
+    let y = 4_u32;
+    let z = 11_u32;
+
+    // Lint
+    let _ = x.div_ceil(y); //~ ERROR: manually reimplementing `div_ceil`
+    let _ = x.div_ceil(y); //~ ERROR: manually reimplementing `div_ceil`
+    let _ = x.div_ceil(y); //~ ERROR: manually reimplementing `div_ceil`
+
+    let _ = 7_u32.div_ceil(4); //~ ERROR: manually reimplementing `div_ceil`
+    let _ = (7_i32 as u32).div_ceil(4); //~ ERROR: manually reimplementing `div_ceil`
+
+    // No lint
+    let _ = (x + (y - 2)) / y;
+    let _ = (x + (y + 1)) / y;
+
+    let _ = (x + (y - 1)) / z;
+
+    let x_i = 7_i32;
+    let y_i = 4_i32;
+    let z_i = 11_i32;
+
+    // No lint because `int_roundings` feature is not enabled.
+    let _ = (z as i32 + (y_i - 1)) / y_i;
+    let _ = (7_u32 as i32 + (y_i - 1)) / y_i;
+    let _ = (7_u32 as i32 + (4 - 1)) / 4;
+}
diff --git a/src/tools/clippy/tests/ui/manual_div_ceil.rs b/src/tools/clippy/tests/ui/manual_div_ceil.rs
new file mode 100644
index 000000000000..2de74c7eaa88
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_div_ceil.rs
@@ -0,0 +1,30 @@
+#![warn(clippy::manual_div_ceil)]
+
+fn main() {
+    let x = 7_u32;
+    let y = 4_u32;
+    let z = 11_u32;
+
+    // Lint
+    let _ = (x + (y - 1)) / y; //~ ERROR: manually reimplementing `div_ceil`
+    let _ = ((y - 1) + x) / y; //~ ERROR: manually reimplementing `div_ceil`
+    let _ = (x + y - 1) / y; //~ ERROR: manually reimplementing `div_ceil`
+
+    let _ = (7_u32 + (4 - 1)) / 4; //~ ERROR: manually reimplementing `div_ceil`
+    let _ = (7_i32 as u32 + (4 - 1)) / 4; //~ ERROR: manually reimplementing `div_ceil`
+
+    // No lint
+    let _ = (x + (y - 2)) / y;
+    let _ = (x + (y + 1)) / y;
+
+    let _ = (x + (y - 1)) / z;
+
+    let x_i = 7_i32;
+    let y_i = 4_i32;
+    let z_i = 11_i32;
+
+    // No lint because `int_roundings` feature is not enabled.
+    let _ = (z as i32 + (y_i - 1)) / y_i;
+    let _ = (7_u32 as i32 + (y_i - 1)) / y_i;
+    let _ = (7_u32 as i32 + (4 - 1)) / 4;
+}
diff --git a/src/tools/clippy/tests/ui/manual_div_ceil.stderr b/src/tools/clippy/tests/ui/manual_div_ceil.stderr
new file mode 100644
index 000000000000..dc652dff405f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_div_ceil.stderr
@@ -0,0 +1,35 @@
+error: manually reimplementing `div_ceil`
+  --> tests/ui/manual_div_ceil.rs:9:13
+   |
+LL |     let _ = (x + (y - 1)) / y;
+   |             ^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(y)`
+   |
+   = note: `-D clippy::manual-div-ceil` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::manual_div_ceil)]`
+
+error: manually reimplementing `div_ceil`
+  --> tests/ui/manual_div_ceil.rs:10:13
+   |
+LL |     let _ = ((y - 1) + x) / y;
+   |             ^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(y)`
+
+error: manually reimplementing `div_ceil`
+  --> tests/ui/manual_div_ceil.rs:11:13
+   |
+LL |     let _ = (x + y - 1) / y;
+   |             ^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(y)`
+
+error: manually reimplementing `div_ceil`
+  --> tests/ui/manual_div_ceil.rs:13:13
+   |
+LL |     let _ = (7_u32 + (4 - 1)) / 4;
+   |             ^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `7_u32.div_ceil(4)`
+
+error: manually reimplementing `div_ceil`
+  --> tests/ui/manual_div_ceil.rs:14:13
+   |
+LL |     let _ = (7_i32 as u32 + (4 - 1)) / 4;
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `(7_i32 as u32).div_ceil(4)`
+
+error: aborting due to 5 previous errors
+
diff --git a/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.fixed b/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.fixed
new file mode 100644
index 000000000000..a1d678c66898
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.fixed
@@ -0,0 +1,25 @@
+#![warn(clippy::manual_div_ceil)]
+#![feature(int_roundings)]
+
+fn main() {
+    let x = 7_i32;
+    let y = 4_i32;
+    let z = 3_i32;
+    let z_u: u32 = 11;
+
+    // Lint.
+    let _ = x.div_ceil(y);
+    let _ = x.div_ceil(y);
+    let _ = x.div_ceil(y);
+
+    let _ = 7_i32.div_ceil(4);
+    let _ = (7_i32 as u32).div_ceil(4);
+    let _ = (7_u32 as i32).div_ceil(4);
+    let _ = z_u.div_ceil(4);
+
+    // No lint.
+    let _ = (x + (y - 2)) / y;
+    let _ = (x + (y + 1)) / y;
+
+    let _ = (x + (y - 1)) / z;
+}
diff --git a/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.rs b/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.rs
new file mode 100644
index 000000000000..58cb1dbe34d1
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.rs
@@ -0,0 +1,25 @@
+#![warn(clippy::manual_div_ceil)]
+#![feature(int_roundings)]
+
+fn main() {
+    let x = 7_i32;
+    let y = 4_i32;
+    let z = 3_i32;
+    let z_u: u32 = 11;
+
+    // Lint.
+    let _ = (x + (y - 1)) / y;
+    let _ = ((y - 1) + x) / y;
+    let _ = (x + y - 1) / y;
+
+    let _ = (7_i32 + (4 - 1)) / 4;
+    let _ = (7_i32 as u32 + (4 - 1)) / 4;
+    let _ = (7_u32 as i32 + (4 - 1)) / 4;
+    let _ = (z_u + (4 - 1)) / 4;
+
+    // No lint.
+    let _ = (x + (y - 2)) / y;
+    let _ = (x + (y + 1)) / y;
+
+    let _ = (x + (y - 1)) / z;
+}
diff --git a/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.stderr b/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.stderr
new file mode 100644
index 000000000000..361ef9bd9f42
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.stderr
@@ -0,0 +1,47 @@
+error: manually reimplementing `div_ceil`
+  --> tests/ui/manual_div_ceil_with_feature.rs:11:13
+   |
+LL |     let _ = (x + (y - 1)) / y;
+   |             ^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(y)`
+   |
+   = note: `-D clippy::manual-div-ceil` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::manual_div_ceil)]`
+
+error: manually reimplementing `div_ceil`
+  --> tests/ui/manual_div_ceil_with_feature.rs:12:13
+   |
+LL |     let _ = ((y - 1) + x) / y;
+   |             ^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(y)`
+
+error: manually reimplementing `div_ceil`
+  --> tests/ui/manual_div_ceil_with_feature.rs:13:13
+   |
+LL |     let _ = (x + y - 1) / y;
+   |             ^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(y)`
+
+error: manually reimplementing `div_ceil`
+  --> tests/ui/manual_div_ceil_with_feature.rs:15:13
+   |
+LL |     let _ = (7_i32 + (4 - 1)) / 4;
+   |             ^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `7_i32.div_ceil(4)`
+
+error: manually reimplementing `div_ceil`
+  --> tests/ui/manual_div_ceil_with_feature.rs:16:13
+   |
+LL |     let _ = (7_i32 as u32 + (4 - 1)) / 4;
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `(7_i32 as u32).div_ceil(4)`
+
+error: manually reimplementing `div_ceil`
+  --> tests/ui/manual_div_ceil_with_feature.rs:17:13
+   |
+LL |     let _ = (7_u32 as i32 + (4 - 1)) / 4;
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `(7_u32 as i32).div_ceil(4)`
+
+error: manually reimplementing `div_ceil`
+  --> tests/ui/manual_div_ceil_with_feature.rs:18:13
+   |
+LL |     let _ = (z_u + (4 - 1)) / 4;
+   |             ^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `z_u.div_ceil(4)`
+
+error: aborting due to 7 previous errors
+
diff --git a/src/tools/clippy/tests/ui/manual_is_power_of_two.fixed b/src/tools/clippy/tests/ui/manual_is_power_of_two.fixed
new file mode 100644
index 000000000000..dda5fe0ec3e7
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_is_power_of_two.fixed
@@ -0,0 +1,20 @@
+#![warn(clippy::manual_is_power_of_two)]
+
+fn main() {
+    let a = 16_u64;
+
+    let _ = a.is_power_of_two();
+    let _ = a.is_power_of_two();
+
+    // Test different orders of expression
+    let _ = a.is_power_of_two();
+    let _ = a.is_power_of_two();
+    let _ = a.is_power_of_two();
+    let _ = a.is_power_of_two();
+
+    let b = 4_i64;
+
+    // is_power_of_two only works for unsigned integers
+    let _ = b.count_ones() == 1;
+    let _ = b & (b - 1) == 0;
+}
diff --git a/src/tools/clippy/tests/ui/manual_is_power_of_two.rs b/src/tools/clippy/tests/ui/manual_is_power_of_two.rs
new file mode 100644
index 000000000000..a1d3a95c4102
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_is_power_of_two.rs
@@ -0,0 +1,20 @@
+#![warn(clippy::manual_is_power_of_two)]
+
+fn main() {
+    let a = 16_u64;
+
+    let _ = a.count_ones() == 1;
+    let _ = a & (a - 1) == 0;
+
+    // Test different orders of expression
+    let _ = 1 == a.count_ones();
+    let _ = (a - 1) & a == 0;
+    let _ = 0 == a & (a - 1);
+    let _ = 0 == (a - 1) & a;
+
+    let b = 4_i64;
+
+    // is_power_of_two only works for unsigned integers
+    let _ = b.count_ones() == 1;
+    let _ = b & (b - 1) == 0;
+}
diff --git a/src/tools/clippy/tests/ui/manual_is_power_of_two.stderr b/src/tools/clippy/tests/ui/manual_is_power_of_two.stderr
new file mode 100644
index 000000000000..3cfc6297abf2
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_is_power_of_two.stderr
@@ -0,0 +1,41 @@
+error: manually reimplementing `is_power_of_two`
+  --> tests/ui/manual_is_power_of_two.rs:6:13
+   |
+LL |     let _ = a.count_ones() == 1;
+   |             ^^^^^^^^^^^^^^^^^^^ help: consider using `.is_power_of_two()`: `a.is_power_of_two()`
+   |
+   = note: `-D clippy::manual-is-power-of-two` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::manual_is_power_of_two)]`
+
+error: manually reimplementing `is_power_of_two`
+  --> tests/ui/manual_is_power_of_two.rs:7:13
+   |
+LL |     let _ = a & (a - 1) == 0;
+   |             ^^^^^^^^^^^^^^^^ help: consider using `.is_power_of_two()`: `a.is_power_of_two()`
+
+error: manually reimplementing `is_power_of_two`
+  --> tests/ui/manual_is_power_of_two.rs:10:13
+   |
+LL |     let _ = 1 == a.count_ones();
+   |             ^^^^^^^^^^^^^^^^^^^ help: consider using `.is_power_of_two()`: `a.is_power_of_two()`
+
+error: manually reimplementing `is_power_of_two`
+  --> tests/ui/manual_is_power_of_two.rs:11:13
+   |
+LL |     let _ = (a - 1) & a == 0;
+   |             ^^^^^^^^^^^^^^^^ help: consider using `.is_power_of_two()`: `a.is_power_of_two()`
+
+error: manually reimplementing `is_power_of_two`
+  --> tests/ui/manual_is_power_of_two.rs:12:13
+   |
+LL |     let _ = 0 == a & (a - 1);
+   |             ^^^^^^^^^^^^^^^^ help: consider using `.is_power_of_two()`: `a.is_power_of_two()`
+
+error: manually reimplementing `is_power_of_two`
+  --> tests/ui/manual_is_power_of_two.rs:13:13
+   |
+LL |     let _ = 0 == (a - 1) & a;
+   |             ^^^^^^^^^^^^^^^^ help: consider using `.is_power_of_two()`: `a.is_power_of_two()`
+
+error: aborting due to 6 previous errors
+
diff --git a/src/tools/clippy/tests/ui/manual_non_exhaustive_enum.rs b/src/tools/clippy/tests/ui/manual_non_exhaustive_enum.rs
index 31c3cc801372..ffe2bb924673 100644
--- a/src/tools/clippy/tests/ui/manual_non_exhaustive_enum.rs
+++ b/src/tools/clippy/tests/ui/manual_non_exhaustive_enum.rs
@@ -1,8 +1,8 @@
 #![warn(clippy::manual_non_exhaustive)]
 #![allow(unused)]
 //@no-rustfix
-enum E {
-    //~^ ERROR: this seems like a manual implementation of the non-exhaustive pattern
+pub enum E {
+    //~^ manual_non_exhaustive
     A,
     B,
     #[doc(hidden)]
@@ -11,7 +11,7 @@ enum E {
 
 // if the user explicitly marks as nonexhaustive we shouldn't warn them
 #[non_exhaustive]
-enum Ep {
+pub enum Ep {
     A,
     B,
     #[doc(hidden)]
@@ -19,14 +19,14 @@ enum Ep {
 }
 
 // marker variant does not have doc hidden attribute, should be ignored
-enum NoDocHidden {
+pub enum NoDocHidden {
     A,
     B,
     _C,
 }
 
 // name of variant with doc hidden does not start with underscore
-enum NoUnderscore {
+pub enum NoUnderscore {
     A,
     B,
     #[doc(hidden)]
@@ -34,7 +34,7 @@ enum NoUnderscore {
 }
 
 // variant with doc hidden is not unit, should be ignored
-enum NotUnit {
+pub enum NotUnit {
     A,
     B,
     #[doc(hidden)]
@@ -42,13 +42,13 @@ enum NotUnit {
 }
 
 // variant with doc hidden is the only one, should be ignored
-enum OnlyMarker {
+pub enum OnlyMarker {
     #[doc(hidden)]
     _A,
 }
 
 // variant with multiple markers, should be ignored
-enum MultipleMarkers {
+pub enum MultipleMarkers {
     A,
     #[doc(hidden)]
     _B,
@@ -58,13 +58,13 @@ enum MultipleMarkers {
 
 // already non_exhaustive and no markers, should be ignored
 #[non_exhaustive]
-enum NonExhaustive {
+pub enum NonExhaustive {
     A,
     B,
 }
 
 // marked is used, don't lint
-enum UsedHidden {
+pub enum UsedHidden {
     #[doc(hidden)]
     _A,
     B,
@@ -77,11 +77,9 @@ fn foo(x: &mut UsedHidden) {
 }
 
 #[expect(clippy::manual_non_exhaustive)]
-enum ExpectLint {
+pub enum ExpectLint {
     A,
     B,
     #[doc(hidden)]
     _C,
 }
-
-fn main() {}
diff --git a/src/tools/clippy/tests/ui/manual_non_exhaustive_enum.stderr b/src/tools/clippy/tests/ui/manual_non_exhaustive_enum.stderr
index dc669568dd2d..0a9ac157f850 100644
--- a/src/tools/clippy/tests/ui/manual_non_exhaustive_enum.stderr
+++ b/src/tools/clippy/tests/ui/manual_non_exhaustive_enum.stderr
@@ -1,11 +1,7 @@
 error: this seems like a manual implementation of the non-exhaustive pattern
   --> tests/ui/manual_non_exhaustive_enum.rs:4:1
    |
-LL |   enum E {
-   |   ^-----
-   |   |
-   |  _help: add the attribute: `#[non_exhaustive] enum E`
-   | |
+LL | / pub enum E {
 LL | |
 LL | |     A,
 LL | |     B,
@@ -21,15 +17,16 @@ LL |     _C,
    |     ^^
    = note: `-D clippy::manual-non-exhaustive` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::manual_non_exhaustive)]`
+help: use the `#[non_exhaustive]` attribute instead
+   |
+LL + #[non_exhaustive]
+LL | pub enum E {
+   |
 
 error: this seems like a manual implementation of the non-exhaustive pattern
   --> tests/ui/manual_non_exhaustive_enum.rs:29:1
    |
-LL |   enum NoUnderscore {
-   |   ^----------------
-   |   |
-   |  _help: add the attribute: `#[non_exhaustive] enum NoUnderscore`
-   | |
+LL | / pub enum NoUnderscore {
 LL | |     A,
 LL | |     B,
 LL | |     #[doc(hidden)]
@@ -42,6 +39,11 @@ help: remove this variant
    |
 LL |     C,
    |     ^
+help: use the `#[non_exhaustive]` attribute instead
+   |
+LL + #[non_exhaustive]
+LL | pub enum NoUnderscore {
+   |
 
 error: aborting due to 2 previous errors
 
diff --git a/src/tools/clippy/tests/ui/manual_non_exhaustive_struct.rs b/src/tools/clippy/tests/ui/manual_non_exhaustive_struct.rs
index 4b2803ccc4a7..31dd50281fa1 100644
--- a/src/tools/clippy/tests/ui/manual_non_exhaustive_struct.rs
+++ b/src/tools/clippy/tests/ui/manual_non_exhaustive_struct.rs
@@ -1,9 +1,9 @@
 #![warn(clippy::manual_non_exhaustive)]
 #![allow(unused)]
 //@no-rustfix
-mod structs {
-    struct S {
-        //~^ ERROR: this seems like a manual implementation of the non-exhaustive pattern
+pub mod structs {
+    pub struct S {
+        //~^ manual_non_exhaustive
         pub a: i32,
         pub b: i32,
         _c: (),
@@ -11,68 +11,76 @@ mod structs {
 
     // user forgot to remove the private field
     #[non_exhaustive]
-    struct Sp {
-        //~^ ERROR: this seems like a manual implementation of the non-exhaustive pattern
+    pub struct Sp {
+        //~^ manual_non_exhaustive
         pub a: i32,
         pub b: i32,
         _c: (),
     }
 
     // some other fields are private, should be ignored
-    struct PrivateFields {
+    pub struct PrivateFields {
         a: i32,
         pub b: i32,
         _c: (),
     }
 
-    // private field name does not start with underscore, should be ignored
-    struct NoUnderscore {
+    pub struct NoUnderscore {
+        //~^ manual_non_exhaustive
         pub a: i32,
         pub b: i32,
         c: (),
     }
 
     // private field is not unit type, should be ignored
-    struct NotUnit {
+    pub struct NotUnit {
         pub a: i32,
         pub b: i32,
         _c: i32,
     }
 
     // private field is the only field, should be ignored
-    struct OnlyMarker {
+    pub struct OnlyMarker {
         _a: (),
     }
 
     // already non exhaustive and no private fields, should be ignored
     #[non_exhaustive]
-    struct NonExhaustive {
+    pub struct NonExhaustive {
         pub a: i32,
         pub b: i32,
     }
 }
 
-mod tuple_structs {
-    struct T(pub i32, pub i32, ());
-    //~^ ERROR: this seems like a manual implementation of the non-exhaustive pattern
+pub mod tuple_structs {
+    pub struct T(pub i32, pub i32, ());
+    //~^ manual_non_exhaustive
 
     // user forgot to remove the private field
     #[non_exhaustive]
-    struct Tp(pub i32, pub i32, ());
-    //~^ ERROR: this seems like a manual implementation of the non-exhaustive pattern
+    pub struct Tp(pub i32, pub i32, ());
+    //~^ manual_non_exhaustive
 
     // some other fields are private, should be ignored
-    struct PrivateFields(pub i32, i32, ());
+    pub struct PrivateFields(pub i32, i32, ());
 
     // private field is not unit type, should be ignored
-    struct NotUnit(pub i32, pub i32, i32);
+    pub struct NotUnit(pub i32, pub i32, i32);
 
     // private field is the only field, should be ignored
-    struct OnlyMarker(());
+    pub struct OnlyMarker(());
 
     // already non exhaustive and no private fields, should be ignored
     #[non_exhaustive]
-    struct NonExhaustive(pub i32, pub i32);
+    pub struct NonExhaustive(pub i32, pub i32);
 }
 
-fn main() {}
+mod private {
+    // Don't lint structs that are not actually public as `#[non_exhaustive]` only applies to
+    // external crates. The manual pattern can still be used to get module local non exhaustiveness
+    pub struct NotPublic {
+        pub a: i32,
+        pub b: i32,
+        _c: (),
+    }
+}
diff --git a/src/tools/clippy/tests/ui/manual_non_exhaustive_struct.stderr b/src/tools/clippy/tests/ui/manual_non_exhaustive_struct.stderr
index 1cab812988a3..81de1e4f2719 100644
--- a/src/tools/clippy/tests/ui/manual_non_exhaustive_struct.stderr
+++ b/src/tools/clippy/tests/ui/manual_non_exhaustive_struct.stderr
@@ -1,11 +1,7 @@
 error: this seems like a manual implementation of the non-exhaustive pattern
   --> tests/ui/manual_non_exhaustive_struct.rs:5:5
    |
-LL |       struct S {
-   |       ^-------
-   |       |
-   |  _____help: add the attribute: `#[non_exhaustive] struct S`
-   | |
+LL | /     pub struct S {
 LL | |
 LL | |         pub a: i32,
 LL | |         pub b: i32,
@@ -20,11 +16,16 @@ LL |         _c: (),
    |         ^^^^^^
    = note: `-D clippy::manual-non-exhaustive` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::manual_non_exhaustive)]`
+help: use the `#[non_exhaustive]` attribute instead
+   |
+LL ~     #[non_exhaustive]
+LL ~     pub struct S {
+   |
 
 error: this seems like a manual implementation of the non-exhaustive pattern
   --> tests/ui/manual_non_exhaustive_struct.rs:14:5
    |
-LL | /     struct Sp {
+LL | /     pub struct Sp {
 LL | |
 LL | |         pub a: i32,
 LL | |         pub b: i32,
@@ -32,6 +33,11 @@ LL | |         _c: (),
 LL | |     }
    | |_____^
    |
+note: the struct is already non-exhaustive
+  --> tests/ui/manual_non_exhaustive_struct.rs:13:5
+   |
+LL |     #[non_exhaustive]
+   |     ^^^^^^^^^^^^^^^^^
 help: remove this field
   --> tests/ui/manual_non_exhaustive_struct.rs:18:9
    |
@@ -39,13 +45,10 @@ LL |         _c: (),
    |         ^^^^^^
 
 error: this seems like a manual implementation of the non-exhaustive pattern
-  --> tests/ui/manual_non_exhaustive_struct.rs:29:5
+  --> tests/ui/manual_non_exhaustive_struct.rs:28:5
    |
-LL |       struct NoUnderscore {
-   |       ^------------------
-   |       |
-   |  _____help: add the attribute: `#[non_exhaustive] struct NoUnderscore`
-   | |
+LL | /     pub struct NoUnderscore {
+LL | |
 LL | |         pub a: i32,
 LL | |         pub b: i32,
 LL | |         c: (),
@@ -57,32 +60,45 @@ help: remove this field
    |
 LL |         c: (),
    |         ^^^^^
+help: use the `#[non_exhaustive]` attribute instead
+   |
+LL ~     #[non_exhaustive]
+LL ~     pub struct NoUnderscore {
+   |
 
 error: this seems like a manual implementation of the non-exhaustive pattern
   --> tests/ui/manual_non_exhaustive_struct.rs:56:5
    |
-LL |     struct T(pub i32, pub i32, ());
-   |     --------^^^^^^^^^^^^^^^^^^^^^^^
-   |     |
-   |     help: add the attribute: `#[non_exhaustive] struct T`
+LL |     pub struct T(pub i32, pub i32, ());
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
 help: remove this field
-  --> tests/ui/manual_non_exhaustive_struct.rs:56:32
+  --> tests/ui/manual_non_exhaustive_struct.rs:56:36
+   |
+LL |     pub struct T(pub i32, pub i32, ());
+   |                                    ^^
+help: use the `#[non_exhaustive]` attribute instead
+   |
+LL ~     #[non_exhaustive]
+LL ~     pub struct T(pub i32, pub i32, ());
    |
-LL |     struct T(pub i32, pub i32, ());
-   |                                ^^
 
 error: this seems like a manual implementation of the non-exhaustive pattern
   --> tests/ui/manual_non_exhaustive_struct.rs:61:5
    |
-LL |     struct Tp(pub i32, pub i32, ());
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |     pub struct Tp(pub i32, pub i32, ());
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: the struct is already non-exhaustive
+  --> tests/ui/manual_non_exhaustive_struct.rs:60:5
    |
+LL |     #[non_exhaustive]
+   |     ^^^^^^^^^^^^^^^^^
 help: remove this field
-  --> tests/ui/manual_non_exhaustive_struct.rs:61:33
+  --> tests/ui/manual_non_exhaustive_struct.rs:61:37
    |
-LL |     struct Tp(pub i32, pub i32, ());
-   |                                 ^^
+LL |     pub struct Tp(pub i32, pub i32, ());
+   |                                     ^^
 
 error: aborting due to 5 previous errors
 
diff --git a/src/tools/clippy/tests/ui/manual_range_patterns.fixed b/src/tools/clippy/tests/ui/manual_range_patterns.fixed
index f1b99637afdb..60467bf9e884 100644
--- a/src/tools/clippy/tests/ui/manual_range_patterns.fixed
+++ b/src/tools/clippy/tests/ui/manual_range_patterns.fixed
@@ -45,4 +45,12 @@ fn main() {
         };
     }
     mac!(f);
+
+    #[rustfmt::skip]
+    let _ = match f {
+        | 2..=15 => 4,
+        | 241..=254 => 5,
+        | 255 => 6,
+        | _ => 7,
+    };
 }
diff --git a/src/tools/clippy/tests/ui/manual_range_patterns.rs b/src/tools/clippy/tests/ui/manual_range_patterns.rs
index 869ffbe80b97..9cd803334499 100644
--- a/src/tools/clippy/tests/ui/manual_range_patterns.rs
+++ b/src/tools/clippy/tests/ui/manual_range_patterns.rs
@@ -45,4 +45,12 @@ fn main() {
         };
     }
     mac!(f);
+
+    #[rustfmt::skip]
+    let _ = match f {
+        | 2..=15 => 4,
+        | 241..=254 => 5,
+        | 255 => 6,
+        | _ => 7,
+    };
 }
diff --git a/src/tools/clippy/tests/ui/manual_saturating_arithmetic.fixed b/src/tools/clippy/tests/ui/manual_saturating_arithmetic.fixed
index 41a32df32ee4..528a65790c4c 100644
--- a/src/tools/clippy/tests/ui/manual_saturating_arithmetic.fixed
+++ b/src/tools/clippy/tests/ui/manual_saturating_arithmetic.fixed
@@ -1,6 +1,6 @@
 #![allow(clippy::legacy_numeric_constants, unused_imports)]
 
-use std::{i128, i32, u128, u32};
+use std::{i32, i128, u32, u128};
 
 fn main() {
     let _ = 1u32.saturating_add(1);
diff --git a/src/tools/clippy/tests/ui/manual_saturating_arithmetic.rs b/src/tools/clippy/tests/ui/manual_saturating_arithmetic.rs
index 3a6b32d80690..55f3720dfcc0 100644
--- a/src/tools/clippy/tests/ui/manual_saturating_arithmetic.rs
+++ b/src/tools/clippy/tests/ui/manual_saturating_arithmetic.rs
@@ -1,6 +1,6 @@
 #![allow(clippy::legacy_numeric_constants, unused_imports)]
 
-use std::{i128, i32, u128, u32};
+use std::{i32, i128, u32, u128};
 
 fn main() {
     let _ = 1u32.checked_add(1).unwrap_or(u32::max_value());
diff --git a/src/tools/clippy/tests/ui/match_overlapping_arm.rs b/src/tools/clippy/tests/ui/match_overlapping_arm.rs
index 4457ae73da2f..a056fdeaa5d0 100644
--- a/src/tools/clippy/tests/ui/match_overlapping_arm.rs
+++ b/src/tools/clippy/tests/ui/match_overlapping_arm.rs
@@ -2,8 +2,6 @@
 #![allow(clippy::redundant_pattern_matching)]
 #![allow(clippy::if_same_then_else, clippy::equatable_if_let, clippy::needless_if)]
 
-/// Tests for match_overlapping_arm
-
 fn overlapping() {
     const FOO: u64 = 2;
 
diff --git a/src/tools/clippy/tests/ui/match_overlapping_arm.stderr b/src/tools/clippy/tests/ui/match_overlapping_arm.stderr
index 65092ffbb555..a60a09a07990 100644
--- a/src/tools/clippy/tests/ui/match_overlapping_arm.stderr
+++ b/src/tools/clippy/tests/ui/match_overlapping_arm.stderr
@@ -1,11 +1,11 @@
 error: some ranges overlap
-  --> tests/ui/match_overlapping_arm.rs:11:9
+  --> tests/ui/match_overlapping_arm.rs:9:9
    |
 LL |         0..=10 => println!("0..=10"),
    |         ^^^^^^
    |
 note: overlaps with this
-  --> tests/ui/match_overlapping_arm.rs:13:9
+  --> tests/ui/match_overlapping_arm.rs:11:9
    |
 LL |         0..=11 => println!("0..=11"),
    |         ^^^^^^
@@ -13,85 +13,85 @@ LL |         0..=11 => println!("0..=11"),
    = help: to override `-D warnings` add `#[allow(clippy::match_overlapping_arm)]`
 
 error: some ranges overlap
-  --> tests/ui/match_overlapping_arm.rs:18:9
+  --> tests/ui/match_overlapping_arm.rs:16:9
    |
 LL |         0..=5 => println!("0..=5"),
    |         ^^^^^
    |
 note: overlaps with this
-  --> tests/ui/match_overlapping_arm.rs:21:9
+  --> tests/ui/match_overlapping_arm.rs:19:9
    |
 LL |         FOO..=11 => println!("FOO..=11"),
    |         ^^^^^^^^
 
 error: some ranges overlap
-  --> tests/ui/match_overlapping_arm.rs:56:9
+  --> tests/ui/match_overlapping_arm.rs:54:9
    |
 LL |         0..11 => println!("0..11"),
    |         ^^^^^
    |
 note: overlaps with this
-  --> tests/ui/match_overlapping_arm.rs:58:9
+  --> tests/ui/match_overlapping_arm.rs:56:9
    |
 LL |         0..=11 => println!("0..=11"),
    |         ^^^^^^
 
 error: some ranges overlap
-  --> tests/ui/match_overlapping_arm.rs:82:9
+  --> tests/ui/match_overlapping_arm.rs:80:9
    |
 LL |         0..=10 => println!("0..=10"),
    |         ^^^^^^
    |
 note: overlaps with this
-  --> tests/ui/match_overlapping_arm.rs:81:9
+  --> tests/ui/match_overlapping_arm.rs:79:9
    |
 LL |         5..14 => println!("5..14"),
    |         ^^^^^
 
 error: some ranges overlap
-  --> tests/ui/match_overlapping_arm.rs:88:9
+  --> tests/ui/match_overlapping_arm.rs:86:9
    |
 LL |         0..7 => println!("0..7"),
    |         ^^^^
    |
 note: overlaps with this
-  --> tests/ui/match_overlapping_arm.rs:90:9
+  --> tests/ui/match_overlapping_arm.rs:88:9
    |
 LL |         0..=10 => println!("0..=10"),
    |         ^^^^^^
 
 error: some ranges overlap
-  --> tests/ui/match_overlapping_arm.rs:101:9
+  --> tests/ui/match_overlapping_arm.rs:99:9
    |
 LL |         ..=23 => println!("..=23"),
    |         ^^^^^
    |
 note: overlaps with this
-  --> tests/ui/match_overlapping_arm.rs:103:9
+  --> tests/ui/match_overlapping_arm.rs:101:9
    |
 LL |         ..26 => println!("..26"),
    |         ^^^^
 
 error: some ranges overlap
-  --> tests/ui/match_overlapping_arm.rs:111:9
+  --> tests/ui/match_overlapping_arm.rs:109:9
    |
 LL |         21..=30 => (),
    |         ^^^^^^^
    |
 note: overlaps with this
-  --> tests/ui/match_overlapping_arm.rs:113:9
+  --> tests/ui/match_overlapping_arm.rs:111:9
    |
 LL |         21..=40 => (),
    |         ^^^^^^^
 
 error: some ranges overlap
-  --> tests/ui/match_overlapping_arm.rs:126:9
+  --> tests/ui/match_overlapping_arm.rs:124:9
    |
 LL |         0..=0x0000_0000_0000_00ff => (),
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^
    |
 note: overlaps with this
-  --> tests/ui/match_overlapping_arm.rs:128:9
+  --> tests/ui/match_overlapping_arm.rs:126:9
    |
 LL |         0..=0x0000_0000_0000_ffff => (),
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/mixed_attributes_style.rs b/src/tools/clippy/tests/ui/mixed_attributes_style.rs
index 1a646c265223..93ab9392cf8b 100644
--- a/src/tools/clippy/tests/ui/mixed_attributes_style.rs
+++ b/src/tools/clippy/tests/ui/mixed_attributes_style.rs
@@ -82,7 +82,8 @@ mod issue_12530 {
             #![allow(dead_code)]
         }
     }
-    /// Nested mod //~ ERROR: item has both inner and outer attributes
+    /// Nested mod
+    //~^ ERROR: item has both inner and outer attributes
     #[allow(unused)]
     mod nest_mod_2 {
         #![allow(unused)]
diff --git a/src/tools/clippy/tests/ui/mixed_attributes_style.stderr b/src/tools/clippy/tests/ui/mixed_attributes_style.stderr
index a1d3fc430f6c..abdc2df23cf1 100644
--- a/src/tools/clippy/tests/ui/mixed_attributes_style.stderr
+++ b/src/tools/clippy/tests/ui/mixed_attributes_style.stderr
@@ -46,13 +46,14 @@ error: item has both inner and outer attributes
   --> tests/ui/mixed_attributes_style.rs:85:5
    |
 LL | /     /// Nested mod
+LL | |
 LL | |     #[allow(unused)]
 LL | |     mod nest_mod_2 {
 LL | |         #![allow(unused)]
    | |_________________________^
 
 error: item has both inner and outer attributes
-  --> tests/ui/mixed_attributes_style.rs:90:9
+  --> tests/ui/mixed_attributes_style.rs:91:9
    |
 LL | /         #[allow(dead_code)]
 LL | |         mod inner_mod {
diff --git a/src/tools/clippy/tests/ui/must_use_candidates.fixed b/src/tools/clippy/tests/ui/must_use_candidates.fixed
index adb266ffbc8d..2459af606884 100644
--- a/src/tools/clippy/tests/ui/must_use_candidates.fixed
+++ b/src/tools/clippy/tests/ui/must_use_candidates.fixed
@@ -7,8 +7,8 @@
 )]
 #![warn(clippy::must_use_candidate)]
 use std::rc::Rc;
-use std::sync::atomic::{AtomicBool, Ordering};
 use std::sync::Arc;
+use std::sync::atomic::{AtomicBool, Ordering};
 
 pub struct MyAtomic(AtomicBool);
 pub struct MyPure;
diff --git a/src/tools/clippy/tests/ui/must_use_candidates.rs b/src/tools/clippy/tests/ui/must_use_candidates.rs
index 49bb16af788f..5f9b2449552a 100644
--- a/src/tools/clippy/tests/ui/must_use_candidates.rs
+++ b/src/tools/clippy/tests/ui/must_use_candidates.rs
@@ -7,8 +7,8 @@
 )]
 #![warn(clippy::must_use_candidate)]
 use std::rc::Rc;
-use std::sync::atomic::{AtomicBool, Ordering};
 use std::sync::Arc;
+use std::sync::atomic::{AtomicBool, Ordering};
 
 pub struct MyAtomic(AtomicBool);
 pub struct MyPure;
diff --git a/src/tools/clippy/tests/ui/mut_key.rs b/src/tools/clippy/tests/ui/mut_key.rs
index 81d8732b3b21..43ff705c4775 100644
--- a/src/tools/clippy/tests/ui/mut_key.rs
+++ b/src/tools/clippy/tests/ui/mut_key.rs
@@ -2,9 +2,9 @@ use std::cell::Cell;
 use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
 use std::hash::{Hash, Hasher};
 use std::rc::Rc;
+use std::sync::Arc;
 use std::sync::atomic::AtomicUsize;
 use std::sync::atomic::Ordering::Relaxed;
-use std::sync::Arc;
 
 struct Key(AtomicUsize);
 
diff --git a/src/tools/clippy/tests/ui/needless_pass_by_value.rs b/src/tools/clippy/tests/ui/needless_pass_by_value.rs
index 14cba5a7eb52..9408b8c948fe 100644
--- a/src/tools/clippy/tests/ui/needless_pass_by_value.rs
+++ b/src/tools/clippy/tests/ui/needless_pass_by_value.rs
@@ -84,7 +84,7 @@ trait Serialize {}
 impl<'a, T> Serialize for &'a T where T: Serialize {}
 impl Serialize for i32 {}
 
-fn test_blanket_ref(_foo: T, _serializable: S) {}
+fn test_blanket_ref(vals: T, serializable: S) {}
 //~^ ERROR: this argument is passed by value, but not consumed in the function body
 
 fn issue_2114(s: String, t: String, u: Vec, v: Vec) {
@@ -116,7 +116,7 @@ impl S {
     ) {
     }
 
-    fn baz(&self, _u: U, _s: Self) {}
+    fn baz(&self, uu: U, ss: Self) {}
     //~^ ERROR: this argument is passed by value, but not consumed in the function body
     //~| ERROR: this argument is passed by value, but not consumed in the function body
 }
@@ -162,13 +162,13 @@ fn test_destructure_copy(x: CopyWrapper, y: CopyWrapper, z: CopyWrapper) {
 // The following 3 lines should not cause an ICE. See #2831
 trait Bar<'a, A> {}
 impl<'b, T> Bar<'b, T> for T {}
-fn some_fun<'b, S: Bar<'b, ()>>(_item: S) {}
+fn some_fun<'b, S: Bar<'b, ()>>(items: S) {}
 //~^ ERROR: this argument is passed by value, but not consumed in the function body
 
 // Also this should not cause an ICE. See #2831
 trait Club<'a, A> {}
 impl Club<'static, T> for T {}
-fn more_fun(_item: impl Club<'static, i32>) {}
+fn more_fun(items: impl Club<'static, i32>) {}
 //~^ ERROR: this argument is passed by value, but not consumed in the function body
 
 fn is_sync(_: T)
@@ -177,6 +177,10 @@ where
 {
 }
 
+struct Obj(String);
+
+fn prefix_test(_unused_with_prefix: Obj) {}
+
 fn main() {
     // This should not cause an ICE either
     // https://github.com/rust-lang/rust-clippy/issues/3144
diff --git a/src/tools/clippy/tests/ui/needless_pass_by_value.stderr b/src/tools/clippy/tests/ui/needless_pass_by_value.stderr
index 827a200ba681..46ef8f3e8da4 100644
--- a/src/tools/clippy/tests/ui/needless_pass_by_value.stderr
+++ b/src/tools/clippy/tests/ui/needless_pass_by_value.stderr
@@ -46,7 +46,7 @@ LL | fn test_destructure(x: Wrapper, y: Wrapper, z: Wrapper) {
 error: this argument is passed by value, but not consumed in the function body
   --> tests/ui/needless_pass_by_value.rs:87:49
    |
-LL | fn test_blanket_ref(_foo: T, _serializable: S) {}
+LL | fn test_blanket_ref(vals: T, serializable: S) {}
    |                                                 ^ help: consider taking a reference instead: `&T`
 
 error: this argument is passed by value, but not consumed in the function body
@@ -106,13 +106,13 @@ LL |         t: String,
 error: this argument is passed by value, but not consumed in the function body
   --> tests/ui/needless_pass_by_value.rs:119:23
    |
-LL |     fn baz(&self, _u: U, _s: Self) {}
+LL |     fn baz(&self, uu: U, ss: Self) {}
    |                       ^ help: consider taking a reference instead: `&U`
 
 error: this argument is passed by value, but not consumed in the function body
   --> tests/ui/needless_pass_by_value.rs:119:30
    |
-LL |     fn baz(&self, _u: U, _s: Self) {}
+LL |     fn baz(&self, uu: U, ss: Self) {}
    |                              ^^^^ help: consider taking a reference instead: `&Self`
 
 error: this argument is passed by value, but not consumed in the function body
@@ -121,7 +121,7 @@ error: this argument is passed by value, but not consumed in the function body
 LL | fn bar_copy(x: u32, y: CopyWrapper) {
    |                        ^^^^^^^^^^^ help: consider taking a reference instead: `&CopyWrapper`
    |
-help: consider marking this type as `Copy`
+help: or consider marking this type as `Copy`
   --> tests/ui/needless_pass_by_value.rs:141:1
    |
 LL | struct CopyWrapper(u32);
@@ -133,7 +133,7 @@ error: this argument is passed by value, but not consumed in the function body
 LL | fn test_destructure_copy(x: CopyWrapper, y: CopyWrapper, z: CopyWrapper) {
    |                             ^^^^^^^^^^^ help: consider taking a reference instead: `&CopyWrapper`
    |
-help: consider marking this type as `Copy`
+help: or consider marking this type as `Copy`
   --> tests/ui/needless_pass_by_value.rs:141:1
    |
 LL | struct CopyWrapper(u32);
@@ -145,7 +145,7 @@ error: this argument is passed by value, but not consumed in the function body
 LL | fn test_destructure_copy(x: CopyWrapper, y: CopyWrapper, z: CopyWrapper) {
    |                                             ^^^^^^^^^^^ help: consider taking a reference instead: `&CopyWrapper`
    |
-help: consider marking this type as `Copy`
+help: or consider marking this type as `Copy`
   --> tests/ui/needless_pass_by_value.rs:141:1
    |
 LL | struct CopyWrapper(u32);
@@ -157,7 +157,7 @@ error: this argument is passed by value, but not consumed in the function body
 LL | fn test_destructure_copy(x: CopyWrapper, y: CopyWrapper, z: CopyWrapper) {
    |                                                             ^^^^^^^^^^^ help: consider taking a reference instead: `&CopyWrapper`
    |
-help: consider marking this type as `Copy`
+help: or consider marking this type as `Copy`
   --> tests/ui/needless_pass_by_value.rs:141:1
    |
 LL | struct CopyWrapper(u32);
@@ -166,13 +166,13 @@ LL | struct CopyWrapper(u32);
 error: this argument is passed by value, but not consumed in the function body
   --> tests/ui/needless_pass_by_value.rs:165:40
    |
-LL | fn some_fun<'b, S: Bar<'b, ()>>(_item: S) {}
+LL | fn some_fun<'b, S: Bar<'b, ()>>(items: S) {}
    |                                        ^ help: consider taking a reference instead: `&S`
 
 error: this argument is passed by value, but not consumed in the function body
   --> tests/ui/needless_pass_by_value.rs:171:20
    |
-LL | fn more_fun(_item: impl Club<'static, i32>) {}
+LL | fn more_fun(items: impl Club<'static, i32>) {}
    |                    ^^^^^^^^^^^^^^^^^^^^^^^ help: consider taking a reference instead: `&impl Club<'static, i32>`
 
 error: aborting due to 22 previous errors
diff --git a/src/tools/clippy/tests/ui/needless_return.fixed b/src/tools/clippy/tests/ui/needless_return.fixed
index fc4129e1db84..c5c570690b45 100644
--- a/src/tools/clippy/tests/ui/needless_return.fixed
+++ b/src/tools/clippy/tests/ui/needless_return.fixed
@@ -355,4 +355,8 @@ fn conjunctive_blocks() -> String {
     ({ "a".to_string() } + "b" + { "c" })
 }
 
+fn issue12907() -> String {
+    "".split("").next().unwrap().to_string()
+}
+
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/needless_return.rs b/src/tools/clippy/tests/ui/needless_return.rs
index 61c7a02008f0..738611391dfd 100644
--- a/src/tools/clippy/tests/ui/needless_return.rs
+++ b/src/tools/clippy/tests/ui/needless_return.rs
@@ -365,4 +365,8 @@ fn conjunctive_blocks() -> String {
     return { "a".to_string() } + "b" + { "c" };
 }
 
+fn issue12907() -> String {
+    return "".split("").next().unwrap().to_string();
+}
+
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/needless_return.stderr b/src/tools/clippy/tests/ui/needless_return.stderr
index ea9c230eafd2..da0fa220d8c4 100644
--- a/src/tools/clippy/tests/ui/needless_return.stderr
+++ b/src/tools/clippy/tests/ui/needless_return.stderr
@@ -665,5 +665,17 @@ LL -     return { "a".to_string() } + "b" + { "c" };
 LL +     ({ "a".to_string() } + "b" + { "c" })
    |
 
-error: aborting due to 53 previous errors
+error: unneeded `return` statement
+  --> tests/ui/needless_return.rs:369:5
+   |
+LL |     return "".split("").next().unwrap().to_string();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: remove `return`
+   |
+LL -     return "".split("").next().unwrap().to_string();
+LL +     "".split("").next().unwrap().to_string()
+   |
+
+error: aborting due to 54 previous errors
 
diff --git a/src/tools/clippy/tests/ui/non_octal_unix_permissions.fixed b/src/tools/clippy/tests/ui/non_octal_unix_permissions.fixed
index 237f5f5b97a4..f68d5e30d27e 100644
--- a/src/tools/clippy/tests/ui/non_octal_unix_permissions.fixed
+++ b/src/tools/clippy/tests/ui/non_octal_unix_permissions.fixed
@@ -1,4 +1,4 @@
-//@ignore-target-windows
+//@ignore-target: windows
 
 #![warn(clippy::non_octal_unix_permissions)]
 use std::fs::{DirBuilder, File, OpenOptions, Permissions};
diff --git a/src/tools/clippy/tests/ui/non_octal_unix_permissions.rs b/src/tools/clippy/tests/ui/non_octal_unix_permissions.rs
index c8da5dbcec28..647c3769d2c3 100644
--- a/src/tools/clippy/tests/ui/non_octal_unix_permissions.rs
+++ b/src/tools/clippy/tests/ui/non_octal_unix_permissions.rs
@@ -1,4 +1,4 @@
-//@ignore-target-windows
+//@ignore-target: windows
 
 #![warn(clippy::non_octal_unix_permissions)]
 use std::fs::{DirBuilder, File, OpenOptions, Permissions};
diff --git a/src/tools/clippy/tests/ui/non_zero_suggestions.fixed b/src/tools/clippy/tests/ui/non_zero_suggestions.fixed
new file mode 100644
index 000000000000..e67c992fdde0
--- /dev/null
+++ b/src/tools/clippy/tests/ui/non_zero_suggestions.fixed
@@ -0,0 +1,65 @@
+#![warn(clippy::non_zero_suggestions)]
+use std::num::{NonZeroI8, NonZeroI16, NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroUsize};
+
+fn main() {
+    /// Positive test cases (lint should trigger)
+    // U32 -> U64
+    let x: u64 = 100;
+    let y = NonZeroU32::new(10).unwrap();
+    let r1 = x / NonZeroU64::from(y);
+    //~^ ERROR: consider using `NonZeroU64::from()` for more efficient and type-safe conversion
+
+    let r2 = x % NonZeroU64::from(y);
+    //~^ ERROR: consider using `NonZeroU64::from()` for more efficient and type-safe conversion
+
+    // U16 -> U32
+    let a: u32 = 50;
+    let b = NonZeroU16::new(5).unwrap();
+    let r3 = a / NonZeroU32::from(b);
+    //~^ ERROR: consider using `NonZeroU32::from()` for more efficient and type-safe conversion
+
+    let x = NonZeroU64::from(NonZeroU32::new(5).unwrap());
+    //~^ ERROR: consider using `NonZeroU64::from()` for more efficient and type-safe conversion
+
+    /// Negative test cases (lint should not trigger)
+    // Left hand side expressions should not be triggered
+    let c: u32 = 50;
+    let d = NonZeroU16::new(5).unwrap();
+    let r4 = u32::from(b.get()) / a;
+
+    // Should not trigger for any other operand other than `/` and `%`
+    let r5 = a + u32::from(b.get());
+    let r6 = a - u32::from(b.get());
+
+    // Same size types
+    let e: u32 = 200;
+    let f = NonZeroU32::new(20).unwrap();
+    let r7 = e / f.get();
+
+    // Smaller to larger, but not NonZero
+    let g: u64 = 1000;
+    let h: u32 = 50;
+    let r8 = g / u64::from(h);
+
+    // Using From correctly
+    let k: u64 = 300;
+    let l = NonZeroU32::new(15).unwrap();
+    let r9 = k / NonZeroU64::from(l);
+}
+
+// Additional function to test the lint in a different context
+fn divide_numbers(x: u64, y: NonZeroU32) -> u64 {
+    x / NonZeroU64::from(y)
+    //~^ ERROR: consider using `NonZeroU64::from()` for more efficient and type-safe conversion
+}
+
+struct Calculator {
+    value: u64,
+}
+
+impl Calculator {
+    fn divide(&self, divisor: NonZeroU32) -> u64 {
+        self.value / NonZeroU64::from(divisor)
+        //~^ ERROR: consider using `NonZeroU64::from()` for more efficient and type-safe conversion
+    }
+}
diff --git a/src/tools/clippy/tests/ui/non_zero_suggestions.rs b/src/tools/clippy/tests/ui/non_zero_suggestions.rs
new file mode 100644
index 000000000000..de82371a8f29
--- /dev/null
+++ b/src/tools/clippy/tests/ui/non_zero_suggestions.rs
@@ -0,0 +1,65 @@
+#![warn(clippy::non_zero_suggestions)]
+use std::num::{NonZeroI8, NonZeroI16, NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroUsize};
+
+fn main() {
+    /// Positive test cases (lint should trigger)
+    // U32 -> U64
+    let x: u64 = 100;
+    let y = NonZeroU32::new(10).unwrap();
+    let r1 = x / u64::from(y.get());
+    //~^ ERROR: consider using `NonZeroU64::from()` for more efficient and type-safe conversion
+
+    let r2 = x % u64::from(y.get());
+    //~^ ERROR: consider using `NonZeroU64::from()` for more efficient and type-safe conversion
+
+    // U16 -> U32
+    let a: u32 = 50;
+    let b = NonZeroU16::new(5).unwrap();
+    let r3 = a / u32::from(b.get());
+    //~^ ERROR: consider using `NonZeroU32::from()` for more efficient and type-safe conversion
+
+    let x = u64::from(NonZeroU32::new(5).unwrap().get());
+    //~^ ERROR: consider using `NonZeroU64::from()` for more efficient and type-safe conversion
+
+    /// Negative test cases (lint should not trigger)
+    // Left hand side expressions should not be triggered
+    let c: u32 = 50;
+    let d = NonZeroU16::new(5).unwrap();
+    let r4 = u32::from(b.get()) / a;
+
+    // Should not trigger for any other operand other than `/` and `%`
+    let r5 = a + u32::from(b.get());
+    let r6 = a - u32::from(b.get());
+
+    // Same size types
+    let e: u32 = 200;
+    let f = NonZeroU32::new(20).unwrap();
+    let r7 = e / f.get();
+
+    // Smaller to larger, but not NonZero
+    let g: u64 = 1000;
+    let h: u32 = 50;
+    let r8 = g / u64::from(h);
+
+    // Using From correctly
+    let k: u64 = 300;
+    let l = NonZeroU32::new(15).unwrap();
+    let r9 = k / NonZeroU64::from(l);
+}
+
+// Additional function to test the lint in a different context
+fn divide_numbers(x: u64, y: NonZeroU32) -> u64 {
+    x / u64::from(y.get())
+    //~^ ERROR: consider using `NonZeroU64::from()` for more efficient and type-safe conversion
+}
+
+struct Calculator {
+    value: u64,
+}
+
+impl Calculator {
+    fn divide(&self, divisor: NonZeroU32) -> u64 {
+        self.value / u64::from(divisor.get())
+        //~^ ERROR: consider using `NonZeroU64::from()` for more efficient and type-safe conversion
+    }
+}
diff --git a/src/tools/clippy/tests/ui/non_zero_suggestions.stderr b/src/tools/clippy/tests/ui/non_zero_suggestions.stderr
new file mode 100644
index 000000000000..7a57f7983be7
--- /dev/null
+++ b/src/tools/clippy/tests/ui/non_zero_suggestions.stderr
@@ -0,0 +1,41 @@
+error: consider using `NonZeroU64::from()` for more efficient and type-safe conversion
+  --> tests/ui/non_zero_suggestions.rs:9:18
+   |
+LL |     let r1 = x / u64::from(y.get());
+   |                  ^^^^^^^^^^^^^^^^^^ help: replace with: `NonZeroU64::from(y)`
+   |
+   = note: `-D clippy::non-zero-suggestions` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::non_zero_suggestions)]`
+
+error: consider using `NonZeroU64::from()` for more efficient and type-safe conversion
+  --> tests/ui/non_zero_suggestions.rs:12:18
+   |
+LL |     let r2 = x % u64::from(y.get());
+   |                  ^^^^^^^^^^^^^^^^^^ help: replace with: `NonZeroU64::from(y)`
+
+error: consider using `NonZeroU32::from()` for more efficient and type-safe conversion
+  --> tests/ui/non_zero_suggestions.rs:18:18
+   |
+LL |     let r3 = a / u32::from(b.get());
+   |                  ^^^^^^^^^^^^^^^^^^ help: replace with: `NonZeroU32::from(b)`
+
+error: consider using `NonZeroU64::from()` for more efficient and type-safe conversion
+  --> tests/ui/non_zero_suggestions.rs:21:13
+   |
+LL |     let x = u64::from(NonZeroU32::new(5).unwrap().get());
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `NonZeroU64::from(NonZeroU32::new(5).unwrap())`
+
+error: consider using `NonZeroU64::from()` for more efficient and type-safe conversion
+  --> tests/ui/non_zero_suggestions.rs:52:9
+   |
+LL |     x / u64::from(y.get())
+   |         ^^^^^^^^^^^^^^^^^^ help: replace with: `NonZeroU64::from(y)`
+
+error: consider using `NonZeroU64::from()` for more efficient and type-safe conversion
+  --> tests/ui/non_zero_suggestions.rs:62:22
+   |
+LL |         self.value / u64::from(divisor.get())
+   |                      ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `NonZeroU64::from(divisor)`
+
+error: aborting due to 6 previous errors
+
diff --git a/src/tools/clippy/tests/ui/non_zero_suggestions_unfixable.rs b/src/tools/clippy/tests/ui/non_zero_suggestions_unfixable.rs
new file mode 100644
index 000000000000..193c710e589f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/non_zero_suggestions_unfixable.rs
@@ -0,0 +1,23 @@
+#![warn(clippy::non_zero_suggestions)]
+//@no-rustfix
+use std::num::{NonZeroI8, NonZeroI16, NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroUsize};
+
+fn main() {
+    let x: u64 = u64::from(NonZeroU32::new(5).unwrap().get());
+    //~^ ERROR: consider using `NonZeroU64::from()` for more efficient and type-safe conversion
+
+    let n = NonZeroU32::new(20).unwrap();
+    let y = u64::from(n.get());
+    //~^ ERROR: consider using `NonZeroU64::from()` for more efficient and type-safe conversion
+    some_fn_that_only_takes_u64(y);
+
+    let m = NonZeroU32::try_from(1).unwrap();
+    let _z: NonZeroU64 = m.into();
+}
+
+fn return_non_zero(x: u64, y: NonZeroU32) -> u64 {
+    u64::from(y.get())
+    //~^ ERROR: consider using `NonZeroU64::from()` for more efficient and type-safe conversion
+}
+
+fn some_fn_that_only_takes_u64(_: u64) {}
diff --git a/src/tools/clippy/tests/ui/non_zero_suggestions_unfixable.stderr b/src/tools/clippy/tests/ui/non_zero_suggestions_unfixable.stderr
new file mode 100644
index 000000000000..787179f2a2d6
--- /dev/null
+++ b/src/tools/clippy/tests/ui/non_zero_suggestions_unfixable.stderr
@@ -0,0 +1,23 @@
+error: consider using `NonZeroU64::from()` for more efficient and type-safe conversion
+  --> tests/ui/non_zero_suggestions_unfixable.rs:6:18
+   |
+LL |     let x: u64 = u64::from(NonZeroU32::new(5).unwrap().get());
+   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `NonZeroU64::from(NonZeroU32::new(5).unwrap())`
+   |
+   = note: `-D clippy::non-zero-suggestions` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::non_zero_suggestions)]`
+
+error: consider using `NonZeroU64::from()` for more efficient and type-safe conversion
+  --> tests/ui/non_zero_suggestions_unfixable.rs:10:13
+   |
+LL |     let y = u64::from(n.get());
+   |             ^^^^^^^^^^^^^^^^^^ help: replace with: `NonZeroU64::from(n)`
+
+error: consider using `NonZeroU64::from()` for more efficient and type-safe conversion
+  --> tests/ui/non_zero_suggestions_unfixable.rs:19:5
+   |
+LL |     u64::from(y.get())
+   |     ^^^^^^^^^^^^^^^^^^ help: replace with: `NonZeroU64::from(y)`
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/panic_in_result_fn.rs b/src/tools/clippy/tests/ui/panic_in_result_fn.rs
index aaaf9a109acb..e2375aa996f7 100644
--- a/src/tools/clippy/tests/ui/panic_in_result_fn.rs
+++ b/src/tools/clippy/tests/ui/panic_in_result_fn.rs
@@ -71,6 +71,15 @@ fn function_result_with_custom_todo() -> Result // should not emit
     Ok(true)
 }
 
+fn issue_13381() -> Result<(), String> {
+    const {
+        if N == 0 {
+            panic!();
+        }
+    }
+    Ok(())
+}
+
 fn main() -> Result<(), String> {
     todo!("finish main method");
     Ok(())
diff --git a/src/tools/clippy/tests/ui/pointers_in_nomem_asm_block.rs b/src/tools/clippy/tests/ui/pointers_in_nomem_asm_block.rs
new file mode 100644
index 000000000000..b5abcbb3474c
--- /dev/null
+++ b/src/tools/clippy/tests/ui/pointers_in_nomem_asm_block.rs
@@ -0,0 +1,33 @@
+//@ needs-asm-support
+#![warn(clippy::pointers_in_nomem_asm_block)]
+#![crate_type = "lib"]
+#![no_std]
+
+use core::arch::asm;
+
+unsafe fn nomem_bad(p: &i32) {
+    asm!(
+        "asdf {p1}, {p2}, {p3}",
+        p1 = in(reg) p,
+        //~^ ERROR: passing pointers to nomem asm block
+        p2 = in(reg) p as *const _ as usize,
+        p3 = in(reg) p,
+        options(nomem, nostack, preserves_flags)
+    );
+}
+
+unsafe fn nomem_good(p: &i32) {
+    asm!("asdf {p}", p = in(reg) p, options(readonly, nostack, preserves_flags));
+    let p = p as *const i32 as usize;
+    asm!("asdf {p}", p = in(reg) p, options(nomem, nostack, preserves_flags));
+}
+
+unsafe fn nomem_bad2(p: &mut i32) {
+    asm!("asdf {p}", p = in(reg) p, options(nomem, nostack, preserves_flags));
+    //~^ ERROR: passing pointers to nomem asm block
+}
+
+unsafe fn nomem_fn(p: extern "C" fn()) {
+    asm!("call {p}", p = in(reg) p, options(nomem));
+    //~^ ERROR: passing pointers to nomem asm block
+}
diff --git a/src/tools/clippy/tests/ui/pointers_in_nomem_asm_block.stderr b/src/tools/clippy/tests/ui/pointers_in_nomem_asm_block.stderr
new file mode 100644
index 000000000000..cabeb37344f2
--- /dev/null
+++ b/src/tools/clippy/tests/ui/pointers_in_nomem_asm_block.stderr
@@ -0,0 +1,34 @@
+error: passing pointers to nomem asm block
+  --> tests/ui/pointers_in_nomem_asm_block.rs:11:9
+   |
+LL |         p1 = in(reg) p,
+   |         ^^^^^^^^^^^^^^
+...
+LL |         p3 = in(reg) p,
+   |         ^^^^^^^^^^^^^^
+   |
+   = note: `nomem` means that no memory write or read happens inside the asm! block
+   = note: if this is intentional and no pointers are read or written to, consider allowing the lint
+   = note: `-D clippy::pointers-in-nomem-asm-block` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::pointers_in_nomem_asm_block)]`
+
+error: passing pointers to nomem asm block
+  --> tests/ui/pointers_in_nomem_asm_block.rs:26:22
+   |
+LL |     asm!("asdf {p}", p = in(reg) p, options(nomem, nostack, preserves_flags));
+   |                      ^^^^^^^^^^^^^
+   |
+   = note: `nomem` means that no memory write or read happens inside the asm! block
+   = note: if this is intentional and no pointers are read or written to, consider allowing the lint
+
+error: passing pointers to nomem asm block
+  --> tests/ui/pointers_in_nomem_asm_block.rs:31:22
+   |
+LL |     asm!("call {p}", p = in(reg) p, options(nomem));
+   |                      ^^^^^^^^^^^^^
+   |
+   = note: `nomem` means that no memory write or read happens inside the asm! block
+   = note: if this is intentional and no pointers are read or written to, consider allowing the lint
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/ptr_arg.rs b/src/tools/clippy/tests/ui/ptr_arg.rs
index e6ef62681212..e8b42d3b913b 100644
--- a/src/tools/clippy/tests/ui/ptr_arg.rs
+++ b/src/tools/clippy/tests/ui/ptr_arg.rs
@@ -309,3 +309,25 @@ mod issue_11181 {
         extern "C" fn allowed(_v: &Vec) {}
     }
 }
+
+mod issue_13308 {
+    use std::ops::Deref;
+
+    fn repro(source: &str, destination: &mut String) {
+        source.clone_into(destination);
+    }
+    fn repro2(source: &str, destination: &mut String) {
+        ToOwned::clone_into(source, destination);
+    }
+
+    fn h1(_: &::Target) {}
+    fn h2(_: T, _: &T::Target) {}
+
+    // Other cases that are still ok to lint and ideally shouldn't regress
+    fn good(v1: &String, v2: &String) {
+        //~^ ERROR: writing `&String` instead of `&str`
+        //~^^ ERROR: writing `&String` instead of `&str`
+        h1(v1);
+        h2(String::new(), v2);
+    }
+}
diff --git a/src/tools/clippy/tests/ui/ptr_arg.stderr b/src/tools/clippy/tests/ui/ptr_arg.stderr
index 4246453e64ca..1a6b3aa1f8d4 100644
--- a/src/tools/clippy/tests/ui/ptr_arg.stderr
+++ b/src/tools/clippy/tests/ui/ptr_arg.stderr
@@ -221,5 +221,17 @@ error: using a reference to `Cow` is not recommended
 LL |     fn cow_bad_ret_ty_2<'a, 'b>(input: &'a Cow<'a, str>) -> &'b str {
    |                                        ^^^^^^^^^^^^^^^^ help: change this to: `&str`
 
-error: aborting due to 25 previous errors
+error: writing `&String` instead of `&str` involves a new object where a slice will do
+  --> tests/ui/ptr_arg.rs:327:17
+   |
+LL |     fn good(v1: &String, v2: &String) {
+   |                 ^^^^^^^ help: change this to: `&str`
+
+error: writing `&String` instead of `&str` involves a new object where a slice will do
+  --> tests/ui/ptr_arg.rs:327:30
+   |
+LL |     fn good(v1: &String, v2: &String) {
+   |                              ^^^^^^^ help: change this to: `&str`
+
+error: aborting due to 27 previous errors
 
diff --git a/src/tools/clippy/tests/ui/ptr_cast_constness.fixed b/src/tools/clippy/tests/ui/ptr_cast_constness.fixed
index 21ac42196e1b..9a5272c7adc3 100644
--- a/src/tools/clippy/tests/ui/ptr_cast_constness.fixed
+++ b/src/tools/clippy/tests/ui/ptr_cast_constness.fixed
@@ -68,3 +68,20 @@ fn _msrv_1_65() {
     let _ = ptr.cast_mut();
     let _ = mut_ptr.cast_const();
 }
+
+#[inline_macros]
+fn null_pointers() {
+    use std::ptr;
+    let _ = std::ptr::null_mut::();
+    let _ = std::ptr::null::();
+    let _ = std::ptr::null_mut::();
+    let _ = std::ptr::null::();
+
+    // Make sure the lint is triggered inside a macro
+    let _ = inline!(std::ptr::null_mut::());
+    let _ = inline!(std::ptr::null_mut::());
+
+    // Do not lint inside macros from external crates
+    let _ = external!(ptr::null::() as *mut u32);
+    let _ = external!(ptr::null::().cast_mut());
+}
diff --git a/src/tools/clippy/tests/ui/ptr_cast_constness.rs b/src/tools/clippy/tests/ui/ptr_cast_constness.rs
index 5ce590b2b7e4..43ab5f5ba7cc 100644
--- a/src/tools/clippy/tests/ui/ptr_cast_constness.rs
+++ b/src/tools/clippy/tests/ui/ptr_cast_constness.rs
@@ -68,3 +68,20 @@ fn _msrv_1_65() {
     let _ = ptr as *mut u32;
     let _ = mut_ptr as *const u32;
 }
+
+#[inline_macros]
+fn null_pointers() {
+    use std::ptr;
+    let _ = ptr::null::() as *mut String;
+    let _ = ptr::null_mut::() as *const u32;
+    let _ = ptr::null::().cast_mut();
+    let _ = ptr::null_mut::().cast_const();
+
+    // Make sure the lint is triggered inside a macro
+    let _ = inline!(ptr::null::() as *mut u32);
+    let _ = inline!(ptr::null::().cast_mut());
+
+    // Do not lint inside macros from external crates
+    let _ = external!(ptr::null::() as *mut u32);
+    let _ = external!(ptr::null::().cast_mut());
+}
diff --git a/src/tools/clippy/tests/ui/ptr_cast_constness.stderr b/src/tools/clippy/tests/ui/ptr_cast_constness.stderr
index 2c52ebd3464d..a693793a4ae9 100644
--- a/src/tools/clippy/tests/ui/ptr_cast_constness.stderr
+++ b/src/tools/clippy/tests/ui/ptr_cast_constness.stderr
@@ -43,5 +43,45 @@ error: `as` casting between raw pointers while changing only its constness
 LL |     let _ = mut_ptr as *const u32;
    |             ^^^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast_const`, a safer alternative: `mut_ptr.cast_const()`
 
-error: aborting due to 7 previous errors
+error: `as` casting to make a const null pointer into a mutable null pointer
+  --> tests/ui/ptr_cast_constness.rs:75:13
+   |
+LL |     let _ = ptr::null::() as *mut String;
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `null_mut()` directly instead: `std::ptr::null_mut::()`
+
+error: `as` casting to make a mutable null pointer into a const null pointer
+  --> tests/ui/ptr_cast_constness.rs:76:13
+   |
+LL |     let _ = ptr::null_mut::() as *const u32;
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `null()` directly instead: `std::ptr::null::()`
+
+error: changing constness of a null pointer
+  --> tests/ui/ptr_cast_constness.rs:77:13
+   |
+LL |     let _ = ptr::null::().cast_mut();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `null_mut()` directly instead: `std::ptr::null_mut::()`
+
+error: changing constness of a null pointer
+  --> tests/ui/ptr_cast_constness.rs:78:13
+   |
+LL |     let _ = ptr::null_mut::().cast_const();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `null()` directly instead: `std::ptr::null::()`
+
+error: `as` casting to make a const null pointer into a mutable null pointer
+  --> tests/ui/ptr_cast_constness.rs:81:21
+   |
+LL |     let _ = inline!(ptr::null::() as *mut u32);
+   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `null_mut()` directly instead: `std::ptr::null_mut::()`
+   |
+   = note: this error originates in the macro `__inline_mac_fn_null_pointers` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: changing constness of a null pointer
+  --> tests/ui/ptr_cast_constness.rs:82:21
+   |
+LL |     let _ = inline!(ptr::null::().cast_mut());
+   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `null_mut()` directly instead: `std::ptr::null_mut::()`
+   |
+   = note: this error originates in the macro `__inline_mac_fn_null_pointers` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 13 previous errors
 
diff --git a/src/tools/clippy/tests/ui/result_large_err.rs b/src/tools/clippy/tests/ui/result_large_err.rs
index b25348bf9961..9c39f023da2d 100644
--- a/src/tools/clippy/tests/ui/result_large_err.rs
+++ b/src/tools/clippy/tests/ui/result_large_err.rs
@@ -1,4 +1,4 @@
-//@ignore-32bit
+//@ignore-bitwidth: 32
 
 #![warn(clippy::result_large_err)]
 #![allow(clippy::large_enum_variant)]
diff --git a/src/tools/clippy/tests/ui/single_call_fn.rs b/src/tools/clippy/tests/ui/single_call_fn.rs
index 55e7508a9573..a0597664da55 100644
--- a/src/tools/clippy/tests/ui/single_call_fn.rs
+++ b/src/tools/clippy/tests/ui/single_call_fn.rs
@@ -1,4 +1,4 @@
-//@ignore-32bit
+//@ignore-bitwidth: 32
 //@aux-build:proc_macros.rs
 #![allow(clippy::redundant_closure_call, unused)]
 #![warn(clippy::single_call_fn)]
diff --git a/src/tools/clippy/tests/ui/single_match.fixed b/src/tools/clippy/tests/ui/single_match.fixed
index 5249c4408899..4016b2699d6b 100644
--- a/src/tools/clippy/tests/ui/single_match.fixed
+++ b/src/tools/clippy/tests/ui/single_match.fixed
@@ -39,8 +39,8 @@ enum Foo {
     Bar,
     Baz(u8),
 }
-use std::borrow::Cow;
 use Foo::*;
+use std::borrow::Cow;
 
 fn single_match_know_enum() {
     let x = Some(1u8);
@@ -296,3 +296,27 @@ fn issue11365() {
 
     if let Some(A | B) = &Some(A) { println!() }
 }
+
+#[derive(Eq, PartialEq)]
+pub struct Data([u8; 4]);
+
+const DATA: Data = Data([1, 2, 3, 4]);
+const CONST_I32: i32 = 1;
+
+fn irrefutable_match() {
+    println!();
+
+    println!();
+
+    let i = 0;
+    {
+        let a = 1;
+        let b = 2;
+    }
+
+    
+
+    
+
+    println!()
+}
diff --git a/src/tools/clippy/tests/ui/single_match.rs b/src/tools/clippy/tests/ui/single_match.rs
index 882098a56e78..75edaa606053 100644
--- a/src/tools/clippy/tests/ui/single_match.rs
+++ b/src/tools/clippy/tests/ui/single_match.rs
@@ -51,8 +51,8 @@ enum Foo {
     Bar,
     Baz(u8),
 }
-use std::borrow::Cow;
 use Foo::*;
+use std::borrow::Cow;
 
 fn single_match_know_enum() {
     let x = Some(1u8);
@@ -360,3 +360,45 @@ fn issue11365() {
         None | Some(_) => {},
     }
 }
+
+#[derive(Eq, PartialEq)]
+pub struct Data([u8; 4]);
+
+const DATA: Data = Data([1, 2, 3, 4]);
+const CONST_I32: i32 = 1;
+
+fn irrefutable_match() {
+    match DATA {
+        DATA => println!(),
+        _ => {},
+    }
+
+    match CONST_I32 {
+        CONST_I32 => println!(),
+        _ => {},
+    }
+
+    let i = 0;
+    match i {
+        i => {
+            let a = 1;
+            let b = 2;
+        },
+        _ => {},
+    }
+
+    match i {
+        i => {},
+        _ => {},
+    }
+
+    match i {
+        i => (),
+        _ => (),
+    }
+
+    match CONST_I32 {
+        CONST_I32 => println!(),
+        _ => {},
+    }
+}
diff --git a/src/tools/clippy/tests/ui/single_match.stderr b/src/tools/clippy/tests/ui/single_match.stderr
index ceb2a193bf7b..9240b09c50a9 100644
--- a/src/tools/clippy/tests/ui/single_match.stderr
+++ b/src/tools/clippy/tests/ui/single_match.stderr
@@ -216,5 +216,70 @@ LL | |         None | Some(_) => {},
 LL | |     }
    | |_____^ help: try: `if let Some(A | B) = &Some(A) { println!() }`
 
-error: aborting due to 20 previous errors
+error: this pattern is irrefutable, `match` is useless
+  --> tests/ui/single_match.rs:371:5
+   |
+LL | /     match DATA {
+LL | |         DATA => println!(),
+LL | |         _ => {},
+LL | |     }
+   | |_____^ help: try: `println!();`
+
+error: this pattern is irrefutable, `match` is useless
+  --> tests/ui/single_match.rs:376:5
+   |
+LL | /     match CONST_I32 {
+LL | |         CONST_I32 => println!(),
+LL | |         _ => {},
+LL | |     }
+   | |_____^ help: try: `println!();`
+
+error: this pattern is irrefutable, `match` is useless
+  --> tests/ui/single_match.rs:382:5
+   |
+LL | /     match i {
+LL | |         i => {
+LL | |             let a = 1;
+LL | |             let b = 2;
+LL | |         },
+LL | |         _ => {},
+LL | |     }
+   | |_____^
+   |
+help: try
+   |
+LL ~     {
+LL +         let a = 1;
+LL +         let b = 2;
+LL +     }
+   |
+
+error: this pattern is irrefutable, `match` is useless
+  --> tests/ui/single_match.rs:390:5
+   |
+LL | /     match i {
+LL | |         i => {},
+LL | |         _ => {},
+LL | |     }
+   | |_____^ help: `match` expression can be removed
+
+error: this pattern is irrefutable, `match` is useless
+  --> tests/ui/single_match.rs:395:5
+   |
+LL | /     match i {
+LL | |         i => (),
+LL | |         _ => (),
+LL | |     }
+   | |_____^ help: `match` expression can be removed
+
+error: this pattern is irrefutable, `match` is useless
+  --> tests/ui/single_match.rs:400:5
+   |
+LL | /     match CONST_I32 {
+LL | |         CONST_I32 => println!(),
+LL | |         _ => {},
+LL | |     }
+   | |_____^ help: try: `println!()`
+
+error: aborting due to 26 previous errors
 
diff --git a/src/tools/clippy/tests/ui/single_match_else.fixed b/src/tools/clippy/tests/ui/single_match_else.fixed
index 163be16ad8be..c2ca746976bd 100644
--- a/src/tools/clippy/tests/ui/single_match_else.fixed
+++ b/src/tools/clippy/tests/ui/single_match_else.fixed
@@ -171,3 +171,7 @@ fn issue_10808(bar: Option) {
         },
     }
 }
+
+fn irrefutable_match() -> Option<&'static ExprNode> {
+    Some(&NODE)
+}
diff --git a/src/tools/clippy/tests/ui/single_match_else.rs b/src/tools/clippy/tests/ui/single_match_else.rs
index 3f1fd2b31832..2d9e877ee0fe 100644
--- a/src/tools/clippy/tests/ui/single_match_else.rs
+++ b/src/tools/clippy/tests/ui/single_match_else.rs
@@ -199,3 +199,13 @@ fn issue_10808(bar: Option) {
         },
     }
 }
+
+fn irrefutable_match() -> Option<&'static ExprNode> {
+    match ExprNode::Butterflies {
+        ExprNode::Butterflies => Some(&NODE),
+        _ => {
+            let x = 5;
+            None
+        },
+    }
+}
diff --git a/src/tools/clippy/tests/ui/single_match_else.stderr b/src/tools/clippy/tests/ui/single_match_else.stderr
index 61c348260d05..a2801751a430 100644
--- a/src/tools/clippy/tests/ui/single_match_else.stderr
+++ b/src/tools/clippy/tests/ui/single_match_else.stderr
@@ -197,5 +197,17 @@ LL +         println!("None");
 LL +     }
    |
 
-error: aborting due to 9 previous errors
+error: this pattern is irrefutable, `match` is useless
+  --> tests/ui/single_match_else.rs:204:5
+   |
+LL | /     match ExprNode::Butterflies {
+LL | |         ExprNode::Butterflies => Some(&NODE),
+LL | |         _ => {
+LL | |             let x = 5;
+LL | |             None
+LL | |         },
+LL | |     }
+   | |_____^ help: try: `Some(&NODE)`
+
+error: aborting due to 10 previous errors
 
diff --git a/src/tools/clippy/tests/ui/string_slice.rs b/src/tools/clippy/tests/ui/string_slice.rs
index 1d1911aaa1d9..dc519493a4dd 100644
--- a/src/tools/clippy/tests/ui/string_slice.rs
+++ b/src/tools/clippy/tests/ui/string_slice.rs
@@ -1,7 +1,7 @@
-use std::borrow::Cow;
+#![warn(clippy::string_slice)]
+#![allow(clippy::no_effect)]
 
-#[warn(clippy::string_slice)]
-#[allow(clippy::no_effect)]
+use std::borrow::Cow;
 
 fn main() {
     &"Ölkanne"[1..];
diff --git a/src/tools/clippy/tests/ui/suspicious_command_arg_space.fixed b/src/tools/clippy/tests/ui/suspicious_command_arg_space.fixed
index 5d7b1e0c17f2..704d6ea1bb83 100644
--- a/src/tools/clippy/tests/ui/suspicious_command_arg_space.fixed
+++ b/src/tools/clippy/tests/ui/suspicious_command_arg_space.fixed
@@ -1,3 +1,4 @@
+#![allow(clippy::zombie_processes)]
 fn main() {
     // Things it should warn about:
     std::process::Command::new("echo").args(["-n", "hello"]).spawn().unwrap();
diff --git a/src/tools/clippy/tests/ui/suspicious_command_arg_space.rs b/src/tools/clippy/tests/ui/suspicious_command_arg_space.rs
index 8abd9803a0c6..2a2a7557381c 100644
--- a/src/tools/clippy/tests/ui/suspicious_command_arg_space.rs
+++ b/src/tools/clippy/tests/ui/suspicious_command_arg_space.rs
@@ -1,3 +1,4 @@
+#![allow(clippy::zombie_processes)]
 fn main() {
     // Things it should warn about:
     std::process::Command::new("echo").arg("-n hello").spawn().unwrap();
diff --git a/src/tools/clippy/tests/ui/suspicious_command_arg_space.stderr b/src/tools/clippy/tests/ui/suspicious_command_arg_space.stderr
index d2517b66b566..6fd07d07d7be 100644
--- a/src/tools/clippy/tests/ui/suspicious_command_arg_space.stderr
+++ b/src/tools/clippy/tests/ui/suspicious_command_arg_space.stderr
@@ -1,5 +1,5 @@
 error: single argument that looks like it should be multiple arguments
-  --> tests/ui/suspicious_command_arg_space.rs:3:44
+  --> tests/ui/suspicious_command_arg_space.rs:4:44
    |
 LL |     std::process::Command::new("echo").arg("-n hello").spawn().unwrap();
    |                                            ^^^^^^^^^^
@@ -12,7 +12,7 @@ LL |     std::process::Command::new("echo").args(["-n", "hello"]).spawn().unwrap
    |                                        ~~~~ ~~~~~~~~~~~~~~~
 
 error: single argument that looks like it should be multiple arguments
-  --> tests/ui/suspicious_command_arg_space.rs:6:43
+  --> tests/ui/suspicious_command_arg_space.rs:7:43
    |
 LL |     std::process::Command::new("cat").arg("--number file").spawn().unwrap();
    |                                           ^^^^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/suspicious_to_owned.rs b/src/tools/clippy/tests/ui/suspicious_to_owned.rs
index f32b07d45f63..794c2e7174af 100644
--- a/src/tools/clippy/tests/ui/suspicious_to_owned.rs
+++ b/src/tools/clippy/tests/ui/suspicious_to_owned.rs
@@ -3,7 +3,7 @@
 #![warn(clippy::implicit_clone)]
 #![allow(clippy::redundant_clone)]
 use std::borrow::Cow;
-use std::ffi::{c_char, CStr};
+use std::ffi::{CStr, c_char};
 
 fn main() {
     let moo = "Moooo";
diff --git a/src/tools/clippy/tests/ui/tabs_in_doc_comments.fixed b/src/tools/clippy/tests/ui/tabs_in_doc_comments.fixed
index 26cc5c27e88c..3536c1746df3 100644
--- a/src/tools/clippy/tests/ui/tabs_in_doc_comments.fixed
+++ b/src/tools/clippy/tests/ui/tabs_in_doc_comments.fixed
@@ -1,5 +1,4 @@
 #![warn(clippy::tabs_in_doc_comments)]
-#[allow(dead_code)]
 
 ///
 /// Struct to hold two strings:
diff --git a/src/tools/clippy/tests/ui/tabs_in_doc_comments.rs b/src/tools/clippy/tests/ui/tabs_in_doc_comments.rs
index 14b06966ecc1..033a685066e1 100644
--- a/src/tools/clippy/tests/ui/tabs_in_doc_comments.rs
+++ b/src/tools/clippy/tests/ui/tabs_in_doc_comments.rs
@@ -1,5 +1,4 @@
 #![warn(clippy::tabs_in_doc_comments)]
-#[allow(dead_code)]
 
 ///
 /// Struct to hold two strings:
diff --git a/src/tools/clippy/tests/ui/tabs_in_doc_comments.stderr b/src/tools/clippy/tests/ui/tabs_in_doc_comments.stderr
index aef6c3914526..f8d30b728e58 100644
--- a/src/tools/clippy/tests/ui/tabs_in_doc_comments.stderr
+++ b/src/tools/clippy/tests/ui/tabs_in_doc_comments.stderr
@@ -1,5 +1,5 @@
 error: using tabs in doc comments is not recommended
-  --> tests/ui/tabs_in_doc_comments.rs:6:5
+  --> tests/ui/tabs_in_doc_comments.rs:5:5
    |
 LL | ///     - first        one
    |     ^^^^ help: consider using four spaces per tab
@@ -8,43 +8,43 @@ LL | ///     - first        one
    = help: to override `-D warnings` add `#[allow(clippy::tabs_in_doc_comments)]`
 
 error: using tabs in doc comments is not recommended
-  --> tests/ui/tabs_in_doc_comments.rs:6:13
+  --> tests/ui/tabs_in_doc_comments.rs:5:13
    |
 LL | ///     - first        one
    |                ^^^^^^^^ help: consider using four spaces per tab
 
 error: using tabs in doc comments is not recommended
-  --> tests/ui/tabs_in_doc_comments.rs:7:5
+  --> tests/ui/tabs_in_doc_comments.rs:6:5
    |
 LL | ///     - second    one
    |     ^^^^ help: consider using four spaces per tab
 
 error: using tabs in doc comments is not recommended
-  --> tests/ui/tabs_in_doc_comments.rs:7:14
+  --> tests/ui/tabs_in_doc_comments.rs:6:14
    |
 LL | ///     - second    one
    |                 ^^^^ help: consider using four spaces per tab
 
 error: using tabs in doc comments is not recommended
-  --> tests/ui/tabs_in_doc_comments.rs:10:9
+  --> tests/ui/tabs_in_doc_comments.rs:9:9
    |
 LL |     ///     - First String:
    |         ^^^^ help: consider using four spaces per tab
 
 error: using tabs in doc comments is not recommended
-  --> tests/ui/tabs_in_doc_comments.rs:11:9
+  --> tests/ui/tabs_in_doc_comments.rs:10:9
    |
 LL |     ///         - needs to be inside here
    |         ^^^^^^^^ help: consider using four spaces per tab
 
 error: using tabs in doc comments is not recommended
-  --> tests/ui/tabs_in_doc_comments.rs:14:9
+  --> tests/ui/tabs_in_doc_comments.rs:13:9
    |
 LL |     ///     - Second String:
    |         ^^^^ help: consider using four spaces per tab
 
 error: using tabs in doc comments is not recommended
-  --> tests/ui/tabs_in_doc_comments.rs:15:9
+  --> tests/ui/tabs_in_doc_comments.rs:14:9
    |
 LL |     ///         - needs to be inside here
    |         ^^^^^^^^ help: consider using four spaces per tab
diff --git a/src/tools/clippy/tests/ui/too_long_first_doc_paragraph.rs b/src/tools/clippy/tests/ui/too_long_first_doc_paragraph.rs
index 1042249c5b7b..7d0a37cde46d 100644
--- a/src/tools/clippy/tests/ui/too_long_first_doc_paragraph.rs
+++ b/src/tools/clippy/tests/ui/too_long_first_doc_paragraph.rs
@@ -2,6 +2,16 @@
 
 #![warn(clippy::too_long_first_doc_paragraph)]
 
+pub mod foo {
+
+    // in foo.rs
+    //! A very short summary.
+    //! A much longer explanation that goes into a lot more detail about
+    //! how the thing works, possibly with doclinks and so one,
+    //! and probably spanning a many rows. Blablabla, it needs to be over
+    //! 200 characters so I needed to write something longeeeeeeer.
+}
+
 /// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc turpis nunc, lacinia
 /// a dolor in, pellentesque aliquet enim. Cras nec maximus sem. Mauris arcu libero,
 /// gravida non lacinia at, rhoncus eu lacus.
diff --git a/src/tools/clippy/tests/ui/too_long_first_doc_paragraph.stderr b/src/tools/clippy/tests/ui/too_long_first_doc_paragraph.stderr
index 7f48e5cf884e..39926647f543 100644
--- a/src/tools/clippy/tests/ui/too_long_first_doc_paragraph.stderr
+++ b/src/tools/clippy/tests/ui/too_long_first_doc_paragraph.stderr
@@ -1,16 +1,32 @@
 error: first doc comment paragraph is too long
-  --> tests/ui/too_long_first_doc_paragraph.rs:5:1
+  --> tests/ui/too_long_first_doc_paragraph.rs:8:5
+   |
+LL | /     //! A very short summary.
+LL | |     //! A much longer explanation that goes into a lot more detail about
+LL | |     //! how the thing works, possibly with doclinks and so one,
+LL | |     //! and probably spanning a many rows. Blablabla, it needs to be over
+LL | |     //! 200 characters so I needed to write something longeeeeeeer.
+   | |____^
+   |
+   = note: `-D clippy::too-long-first-doc-paragraph` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::too_long_first_doc_paragraph)]`
+help: add an empty line
+   |
+LL ~     //! A very short summary.
+LL +     //!
+LL ~     //! A much longer explanation that goes into a lot more detail about
+   |
+
+error: first doc comment paragraph is too long
+  --> tests/ui/too_long_first_doc_paragraph.rs:15:1
    |
 LL | / /// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc turpis nunc, lacinia
 LL | | /// a dolor in, pellentesque aliquet enim. Cras nec maximus sem. Mauris arcu libero,
 LL | | /// gravida non lacinia at, rhoncus eu lacus.
    | |_
-   |
-   = note: `-D clippy::too-long-first-doc-paragraph` implied by `-D warnings`
-   = help: to override `-D warnings` add `#[allow(clippy::too_long_first_doc_paragraph)]`
 
 error: first doc comment paragraph is too long
-  --> tests/ui/too_long_first_doc_paragraph.rs:26:1
+  --> tests/ui/too_long_first_doc_paragraph.rs:36:1
    |
 LL | / /// Lorem
 LL | | /// ipsum dolor sit amet, consectetur adipiscing elit. Nunc turpis nunc, lacinia
@@ -18,5 +34,5 @@ LL | | /// a dolor in, pellentesque aliquet enim. Cras nec maximus sem. Mauris a
 LL | | /// gravida non lacinia at, rhoncus eu lacus.
    | |_
 
-error: aborting due to 2 previous errors
+error: aborting due to 3 previous errors
 
diff --git a/src/tools/clippy/tests/ui/transmute_32bit.rs b/src/tools/clippy/tests/ui/transmute_32bit.rs
index 8e1316ca39d4..e162bbb2d929 100644
--- a/src/tools/clippy/tests/ui/transmute_32bit.rs
+++ b/src/tools/clippy/tests/ui/transmute_32bit.rs
@@ -1,4 +1,4 @@
-//@ignore-64bit
+//@ignore-bitwidth: 64
 
 #[warn(clippy::wrong_transmute)]
 fn main() {
diff --git a/src/tools/clippy/tests/ui/transmute_64bit.rs b/src/tools/clippy/tests/ui/transmute_64bit.rs
index 767cc503a39d..fd0ad74bcfa0 100644
--- a/src/tools/clippy/tests/ui/transmute_64bit.rs
+++ b/src/tools/clippy/tests/ui/transmute_64bit.rs
@@ -1,4 +1,4 @@
-//@ignore-32bit
+//@ignore-bitwidth: 32
 
 #[warn(clippy::wrong_transmute)]
 fn main() {
diff --git a/src/tools/clippy/tests/ui/transmute_collection.rs b/src/tools/clippy/tests/ui/transmute_collection.rs
index e30b34a5d7d6..6748b66e0eb6 100644
--- a/src/tools/clippy/tests/ui/transmute_collection.rs
+++ b/src/tools/clippy/tests/ui/transmute_collection.rs
@@ -2,7 +2,7 @@
 #![allow(clippy::missing_transmute_annotations)]
 
 use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, VecDeque};
-use std::mem::{transmute, MaybeUninit};
+use std::mem::{MaybeUninit, transmute};
 
 fn main() {
     unsafe {
diff --git a/src/tools/clippy/tests/ui/transmute_undefined_repr.rs b/src/tools/clippy/tests/ui/transmute_undefined_repr.rs
index dd4bac7f1ed7..5b16d71f1142 100644
--- a/src/tools/clippy/tests/ui/transmute_undefined_repr.rs
+++ b/src/tools/clippy/tests/ui/transmute_undefined_repr.rs
@@ -8,7 +8,7 @@
 
 use core::any::TypeId;
 use core::ffi::c_void;
-use core::mem::{size_of, transmute, MaybeUninit};
+use core::mem::{MaybeUninit, size_of, transmute};
 use core::ptr::NonNull;
 
 fn value() -> T {
diff --git a/src/tools/clippy/tests/ui/uninit_vec.rs b/src/tools/clippy/tests/ui/uninit_vec.rs
index 0cc77a8775d5..464f88140bdb 100644
--- a/src/tools/clippy/tests/ui/uninit_vec.rs
+++ b/src/tools/clippy/tests/ui/uninit_vec.rs
@@ -150,4 +150,36 @@ fn main() {
             vec.set_len(10);
         }
     }
+
+    fn nested_union() {
+        let mut vec: Vec>> = Vec::with_capacity(1);
+        unsafe {
+            vec.set_len(1);
+        }
+    }
+
+    struct Recursive(*const Recursive, MaybeUninit);
+    fn recursive_union() {
+        // Make sure we don't stack overflow on recursive types.
+        // The pointer acts as the base case because it can't be uninit regardless of its pointee.
+
+        let mut vec: Vec> = Vec::with_capacity(1);
+        //~^ uninit_vec
+        unsafe {
+            vec.set_len(1);
+        }
+    }
+
+    #[repr(u8)]
+    enum Enum {
+        Variant(T),
+    }
+    fn union_in_enum() {
+        // Enums can have a discriminant that can't be uninit, so this should still warn
+        let mut vec: Vec> = Vec::with_capacity(1);
+        //~^ uninit_vec
+        unsafe {
+            vec.set_len(1);
+        }
+    }
 }
diff --git a/src/tools/clippy/tests/ui/uninit_vec.stderr b/src/tools/clippy/tests/ui/uninit_vec.stderr
index e8b77d653f08..e7c81cf792f2 100644
--- a/src/tools/clippy/tests/ui/uninit_vec.stderr
+++ b/src/tools/clippy/tests/ui/uninit_vec.stderr
@@ -125,5 +125,27 @@ LL |             vec.set_len(10);
    |
    = help: initialize the buffer or wrap the content in `MaybeUninit`
 
-error: aborting due to 12 previous errors
+error: calling `set_len()` immediately after reserving a buffer creates uninitialized values
+  --> tests/ui/uninit_vec.rs:166:9
+   |
+LL |         let mut vec: Vec> = Vec::with_capacity(1);
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL |             vec.set_len(1);
+   |             ^^^^^^^^^^^^^^
+   |
+   = 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:179:9
+   |
+LL |         let mut vec: Vec> = Vec::with_capacity(1);
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL |             vec.set_len(1);
+   |             ^^^^^^^^^^^^^^
+   |
+   = help: initialize the buffer or wrap the content in `MaybeUninit`
+
+error: aborting due to 14 previous errors
 
diff --git a/src/tools/clippy/tests/ui/unnecessary_first_then_check.fixed b/src/tools/clippy/tests/ui/unnecessary_first_then_check.fixed
new file mode 100644
index 000000000000..7202e1bbd179
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unnecessary_first_then_check.fixed
@@ -0,0 +1,22 @@
+#![warn(clippy::unnecessary_first_then_check)]
+#![allow(clippy::useless_vec, clippy::const_is_empty)]
+
+fn main() {
+    let s = [1, 2, 3];
+    let _: bool = !s.is_empty();
+    let _: bool = s.is_empty();
+
+    let v = vec![1, 2, 3];
+    let _: bool = !v.is_empty();
+
+    let n = [[1, 2, 3], [4, 5, 6]];
+    let _: bool = !n[0].is_empty();
+    let _: bool = n[0].is_empty();
+
+    struct Foo {
+        bar: &'static [i32],
+    }
+    let f = [Foo { bar: &[] }];
+    let _: bool = !f[0].bar.is_empty();
+    let _: bool = f[0].bar.is_empty();
+}
diff --git a/src/tools/clippy/tests/ui/unnecessary_first_then_check.rs b/src/tools/clippy/tests/ui/unnecessary_first_then_check.rs
new file mode 100644
index 000000000000..762b95999288
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unnecessary_first_then_check.rs
@@ -0,0 +1,22 @@
+#![warn(clippy::unnecessary_first_then_check)]
+#![allow(clippy::useless_vec, clippy::const_is_empty)]
+
+fn main() {
+    let s = [1, 2, 3];
+    let _: bool = s.first().is_some();
+    let _: bool = s.first().is_none();
+
+    let v = vec![1, 2, 3];
+    let _: bool = v.first().is_some();
+
+    let n = [[1, 2, 3], [4, 5, 6]];
+    let _: bool = n[0].first().is_some();
+    let _: bool = n[0].first().is_none();
+
+    struct Foo {
+        bar: &'static [i32],
+    }
+    let f = [Foo { bar: &[] }];
+    let _: bool = f[0].bar.first().is_some();
+    let _: bool = f[0].bar.first().is_none();
+}
diff --git a/src/tools/clippy/tests/ui/unnecessary_first_then_check.stderr b/src/tools/clippy/tests/ui/unnecessary_first_then_check.stderr
new file mode 100644
index 000000000000..bbaf7e68edab
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unnecessary_first_then_check.stderr
@@ -0,0 +1,47 @@
+error: unnecessary use of `first().is_some()` to check if slice is not empty
+  --> tests/ui/unnecessary_first_then_check.rs:6:19
+   |
+LL |     let _: bool = s.first().is_some();
+   |                   ^^^^^^^^^^^^^^^^^^^ help: replace this with: `!s.is_empty()`
+   |
+   = note: `-D clippy::unnecessary-first-then-check` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::unnecessary_first_then_check)]`
+
+error: unnecessary use of `first().is_none()` to check if slice is empty
+  --> tests/ui/unnecessary_first_then_check.rs:7:21
+   |
+LL |     let _: bool = s.first().is_none();
+   |                     ^^^^^^^^^^^^^^^^^ help: replace this with: `is_empty()`
+
+error: unnecessary use of `first().is_some()` to check if slice is not empty
+  --> tests/ui/unnecessary_first_then_check.rs:10:19
+   |
+LL |     let _: bool = v.first().is_some();
+   |                   ^^^^^^^^^^^^^^^^^^^ help: replace this with: `!v.is_empty()`
+
+error: unnecessary use of `first().is_some()` to check if slice is not empty
+  --> tests/ui/unnecessary_first_then_check.rs:13:19
+   |
+LL |     let _: bool = n[0].first().is_some();
+   |                   ^^^^^^^^^^^^^^^^^^^^^^ help: replace this with: `!n[0].is_empty()`
+
+error: unnecessary use of `first().is_none()` to check if slice is empty
+  --> tests/ui/unnecessary_first_then_check.rs:14:24
+   |
+LL |     let _: bool = n[0].first().is_none();
+   |                        ^^^^^^^^^^^^^^^^^ help: replace this with: `is_empty()`
+
+error: unnecessary use of `first().is_some()` to check if slice is not empty
+  --> tests/ui/unnecessary_first_then_check.rs:20:19
+   |
+LL |     let _: bool = f[0].bar.first().is_some();
+   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace this with: `!f[0].bar.is_empty()`
+
+error: unnecessary use of `first().is_none()` to check if slice is empty
+  --> tests/ui/unnecessary_first_then_check.rs:21:28
+   |
+LL |     let _: bool = f[0].bar.first().is_none();
+   |                            ^^^^^^^^^^^^^^^^^ help: replace this with: `is_empty()`
+
+error: aborting due to 7 previous errors
+
diff --git a/src/tools/clippy/tests/ui/unnecessary_min_or_max.fixed b/src/tools/clippy/tests/ui/unnecessary_min_or_max.fixed
index 392f6dd1fee4..1f3e131516ce 100644
--- a/src/tools/clippy/tests/ui/unnecessary_min_or_max.fixed
+++ b/src/tools/clippy/tests/ui/unnecessary_min_or_max.fixed
@@ -65,3 +65,32 @@ fn random_u32() -> u32 {
     // random number generator
     0
 }
+
+struct Issue13191 {
+    min: u16,
+    max: u16,
+}
+
+impl Issue13191 {
+    fn new() -> Self {
+        Self { min: 0, max: 0 }
+    }
+
+    fn min(mut self, value: u16) -> Self {
+        self.min = value;
+        self
+    }
+
+    fn max(mut self, value: u16) -> Self {
+        self.max = value;
+        self
+    }
+}
+
+fn issue_13191() {
+    // should not fixed
+    Issue13191::new().min(0);
+
+    // should not fixed
+    Issue13191::new().max(0);
+}
diff --git a/src/tools/clippy/tests/ui/unnecessary_min_or_max.rs b/src/tools/clippy/tests/ui/unnecessary_min_or_max.rs
index b03755e6d23d..58356b9d49e7 100644
--- a/src/tools/clippy/tests/ui/unnecessary_min_or_max.rs
+++ b/src/tools/clippy/tests/ui/unnecessary_min_or_max.rs
@@ -65,3 +65,32 @@ fn random_u32() -> u32 {
     // random number generator
     0
 }
+
+struct Issue13191 {
+    min: u16,
+    max: u16,
+}
+
+impl Issue13191 {
+    fn new() -> Self {
+        Self { min: 0, max: 0 }
+    }
+
+    fn min(mut self, value: u16) -> Self {
+        self.min = value;
+        self
+    }
+
+    fn max(mut self, value: u16) -> Self {
+        self.max = value;
+        self
+    }
+}
+
+fn issue_13191() {
+    // should not fixed
+    Issue13191::new().min(0);
+
+    // should not fixed
+    Issue13191::new().max(0);
+}
diff --git a/src/tools/clippy/tests/ui/unsafe_removed_from_name.rs b/src/tools/clippy/tests/ui/unsafe_removed_from_name.rs
index e0e0ded140fc..e9e6c8312f5e 100644
--- a/src/tools/clippy/tests/ui/unsafe_removed_from_name.rs
+++ b/src/tools/clippy/tests/ui/unsafe_removed_from_name.rs
@@ -7,7 +7,7 @@ use std::cell::UnsafeCell as TotallySafeCell;
 //~| NOTE: `-D clippy::unsafe-removed-from-name` implied by `-D warnings`
 
 use std::cell::UnsafeCell as TotallySafeCellAgain;
-//~^ ERROR: removed `unsafe` from the name of `UnsafeCell` in use as `TotallySafeCellAgain
+//~^ ERROR: removed `unsafe` from the name of `UnsafeCell` in use as `TotallySafeCellAgain`
 
 // Shouldn't error
 use std::cell::RefCell as ProbablyNotUnsafe;
diff --git a/src/tools/clippy/tests/ui/unused_trait_names.fixed b/src/tools/clippy/tests/ui/unused_trait_names.fixed
new file mode 100644
index 000000000000..7dfd0db65aa1
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unused_trait_names.fixed
@@ -0,0 +1,286 @@
+//@aux-build:proc_macros.rs
+
+#![allow(unused)]
+#![warn(clippy::unused_trait_names)]
+#![feature(decl_macro)]
+
+extern crate proc_macros;
+
+fn main() {}
+
+fn bad() {
+    use std::any::Any as _;
+
+    println!("{:?}", "foo".type_id());
+}
+
+fn good() {
+    use std::any::Any as _;
+
+    println!("{:?}", "foo".type_id());
+}
+
+fn used_good() {
+    use std::any::Any;
+
+    println!("{:?}", Any::type_id("foo"));
+    println!("{:?}", "foo".type_id());
+}
+
+fn multi_bad() {
+    use std::any::{self, Any as _, TypeId};
+
+    println!("{:?}", "foo".type_id());
+}
+
+fn multi_good() {
+    use std::any::{self, Any as _, TypeId};
+
+    println!("{:?}", "foo".type_id());
+}
+
+fn renamed_bad() {
+    use std::any::Any as _;
+
+    println!("{:?}", "foo".type_id());
+}
+
+fn multi_renamed_bad() {
+    use std::any::{Any as _, TypeId as MyTypeId};
+
+    println!("{:?}", "foo".type_id());
+}
+
+mod pub_good {
+    pub use std::any::Any;
+
+    fn foo() {
+        println!("{:?}", "foo".type_id());
+    }
+}
+
+mod used_mod_good {
+    use std::any::Any;
+
+    fn foo() {
+        println!("{:?}", Any::type_id("foo"));
+    }
+}
+
+mod mod_import_bad {
+    fn mod_import_bad() {
+        use std::any::Any as _;
+
+        println!("{:?}", "foo".type_id());
+    }
+}
+
+mod nested_mod_used_good1 {
+    use std::any::Any;
+
+    mod foo {
+        fn foo() {
+            super::Any::type_id("foo");
+        }
+    }
+}
+
+mod nested_mod_used_good2 {
+    use std::any::Any;
+
+    mod foo {
+        use super::Any;
+
+        fn foo() {
+            Any::type_id("foo");
+        }
+    }
+}
+
+mod nested_mod_used_good3 {
+    use std::any::Any;
+
+    mod foo {
+        use crate::nested_mod_used_good3::Any;
+
+        fn foo() {
+            println!("{:?}", Any::type_id("foo"));
+        }
+    }
+}
+
+mod nested_mod_used_bad {
+    use std::any::Any as _;
+
+    fn bar() {
+        println!("{:?}", "foo".type_id());
+    }
+
+    mod foo {
+        use std::any::Any;
+
+        fn foo() {
+            println!("{:?}", Any::type_id("foo"));
+        }
+    }
+}
+
+// More complex example where `use std::any::Any;` should be anonymised but `use std::any::Any as
+// MyAny;` should not as it is used by a sub module. Even though if you removed `use std::any::Any;`
+// the code would still compile.
+mod nested_mod_used_bad1 {
+    use std::any::Any as _;
+
+    use std::any::Any as MyAny;
+
+    fn baz() {
+        println!("{:?}", "baz".type_id());
+    }
+
+    mod foo {
+        use crate::nested_mod_used_bad1::MyAny;
+
+        fn foo() {
+            println!("{:?}", MyAny::type_id("foo"));
+        }
+    }
+}
+
+// Example of nested import with an unused import to try and trick it
+mod nested_mod_used_good5 {
+    use std::any::Any;
+
+    mod foo {
+        use std::any::Any;
+
+        fn baz() {
+            println!("{:?}", "baz".type_id());
+        }
+
+        mod bar {
+            use crate::nested_mod_used_good5::foo::Any;
+
+            fn foo() {
+                println!("{:?}", Any::type_id("foo"));
+            }
+        }
+    }
+}
+
+mod simple_trait {
+    pub trait MyTrait {
+        fn do_things(&self);
+    }
+
+    pub struct MyStruct;
+
+    impl MyTrait for MyStruct {
+        fn do_things(&self) {}
+    }
+}
+
+// Underscore imports were stabilized in 1.33
+#[clippy::msrv = "1.32"]
+fn msrv_1_32() {
+    use simple_trait::{MyStruct, MyTrait};
+    MyStruct.do_things();
+}
+
+#[clippy::msrv = "1.33"]
+fn msrv_1_33() {
+    use simple_trait::{MyStruct, MyTrait as _};
+    MyStruct.do_things();
+}
+
+mod lint_inside_macro_expansion_bad {
+    macro_rules! foo {
+        () => {
+            use std::any::Any as _;
+            fn bar() {
+                "bar".type_id();
+            }
+        };
+    }
+
+    foo!();
+}
+
+mod macro_and_trait_same_name {
+    pub macro Foo() {}
+    pub trait Foo {
+        fn bar(&self);
+    }
+    impl Foo for () {
+        fn bar(&self) {}
+    }
+}
+
+fn call_macro_and_trait_good() {
+    // importing trait and macro but only using macro by path won't allow us to change this to
+    // `use macro_and_trait_same_name::Foo as _;`
+    use macro_and_trait_same_name::Foo;
+    Foo!();
+    ().bar();
+}
+
+proc_macros::external!(
+    fn ignore_inside_external_proc_macro() {
+        use std::any::Any;
+        "foo".type_id();
+    }
+);
+
+proc_macros::with_span!(
+    span
+
+    fn ignore_inside_with_span_proc_macro() {
+        use std::any::Any;
+        "foo".type_id();
+    }
+);
+
+// This should warn the import is unused but should not trigger unused_trait_names
+#[warn(unused)]
+mod unused_import {
+    
+}
+
+#[allow(clippy::unused_trait_names)]
+fn allow_lint_fn() {
+    use std::any::Any;
+
+    "foo".type_id();
+}
+
+#[allow(clippy::unused_trait_names)]
+mod allow_lint_mod {
+    use std::any::Any;
+
+    fn foo() {
+        "foo".type_id();
+    }
+}
+
+mod allow_lint_import {
+    #[allow(clippy::unused_trait_names)]
+    use std::any::Any;
+
+    fn foo() {
+        "foo".type_id();
+    }
+}
+
+// Limitation: Suggests `use std::any::Any as _::{self};` which looks weird
+// fn use_trait_self_good() {
+//     use std::any::Any::{self};
+//     "foo".type_id();
+// }
+
+// Limitation: Suggests `use std::any::{Any as _, Any as _};`
+// mod repeated_renamed {
+//     use std::any::{Any, Any as MyAny};
+
+//     fn foo() {
+//         "foo".type_id();
+//     }
+// }
diff --git a/src/tools/clippy/tests/ui/unused_trait_names.rs b/src/tools/clippy/tests/ui/unused_trait_names.rs
new file mode 100644
index 000000000000..ce44eedbc79c
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unused_trait_names.rs
@@ -0,0 +1,286 @@
+//@aux-build:proc_macros.rs
+
+#![allow(unused)]
+#![warn(clippy::unused_trait_names)]
+#![feature(decl_macro)]
+
+extern crate proc_macros;
+
+fn main() {}
+
+fn bad() {
+    use std::any::Any;
+
+    println!("{:?}", "foo".type_id());
+}
+
+fn good() {
+    use std::any::Any as _;
+
+    println!("{:?}", "foo".type_id());
+}
+
+fn used_good() {
+    use std::any::Any;
+
+    println!("{:?}", Any::type_id("foo"));
+    println!("{:?}", "foo".type_id());
+}
+
+fn multi_bad() {
+    use std::any::{self, Any, TypeId};
+
+    println!("{:?}", "foo".type_id());
+}
+
+fn multi_good() {
+    use std::any::{self, Any as _, TypeId};
+
+    println!("{:?}", "foo".type_id());
+}
+
+fn renamed_bad() {
+    use std::any::Any as MyAny;
+
+    println!("{:?}", "foo".type_id());
+}
+
+fn multi_renamed_bad() {
+    use std::any::{Any as MyAny, TypeId as MyTypeId};
+
+    println!("{:?}", "foo".type_id());
+}
+
+mod pub_good {
+    pub use std::any::Any;
+
+    fn foo() {
+        println!("{:?}", "foo".type_id());
+    }
+}
+
+mod used_mod_good {
+    use std::any::Any;
+
+    fn foo() {
+        println!("{:?}", Any::type_id("foo"));
+    }
+}
+
+mod mod_import_bad {
+    fn mod_import_bad() {
+        use std::any::Any;
+
+        println!("{:?}", "foo".type_id());
+    }
+}
+
+mod nested_mod_used_good1 {
+    use std::any::Any;
+
+    mod foo {
+        fn foo() {
+            super::Any::type_id("foo");
+        }
+    }
+}
+
+mod nested_mod_used_good2 {
+    use std::any::Any;
+
+    mod foo {
+        use super::Any;
+
+        fn foo() {
+            Any::type_id("foo");
+        }
+    }
+}
+
+mod nested_mod_used_good3 {
+    use std::any::Any;
+
+    mod foo {
+        use crate::nested_mod_used_good3::Any;
+
+        fn foo() {
+            println!("{:?}", Any::type_id("foo"));
+        }
+    }
+}
+
+mod nested_mod_used_bad {
+    use std::any::Any;
+
+    fn bar() {
+        println!("{:?}", "foo".type_id());
+    }
+
+    mod foo {
+        use std::any::Any;
+
+        fn foo() {
+            println!("{:?}", Any::type_id("foo"));
+        }
+    }
+}
+
+// More complex example where `use std::any::Any;` should be anonymised but `use std::any::Any as
+// MyAny;` should not as it is used by a sub module. Even though if you removed `use std::any::Any;`
+// the code would still compile.
+mod nested_mod_used_bad1 {
+    use std::any::Any;
+
+    use std::any::Any as MyAny;
+
+    fn baz() {
+        println!("{:?}", "baz".type_id());
+    }
+
+    mod foo {
+        use crate::nested_mod_used_bad1::MyAny;
+
+        fn foo() {
+            println!("{:?}", MyAny::type_id("foo"));
+        }
+    }
+}
+
+// Example of nested import with an unused import to try and trick it
+mod nested_mod_used_good5 {
+    use std::any::Any;
+
+    mod foo {
+        use std::any::Any;
+
+        fn baz() {
+            println!("{:?}", "baz".type_id());
+        }
+
+        mod bar {
+            use crate::nested_mod_used_good5::foo::Any;
+
+            fn foo() {
+                println!("{:?}", Any::type_id("foo"));
+            }
+        }
+    }
+}
+
+mod simple_trait {
+    pub trait MyTrait {
+        fn do_things(&self);
+    }
+
+    pub struct MyStruct;
+
+    impl MyTrait for MyStruct {
+        fn do_things(&self) {}
+    }
+}
+
+// Underscore imports were stabilized in 1.33
+#[clippy::msrv = "1.32"]
+fn msrv_1_32() {
+    use simple_trait::{MyStruct, MyTrait};
+    MyStruct.do_things();
+}
+
+#[clippy::msrv = "1.33"]
+fn msrv_1_33() {
+    use simple_trait::{MyStruct, MyTrait};
+    MyStruct.do_things();
+}
+
+mod lint_inside_macro_expansion_bad {
+    macro_rules! foo {
+        () => {
+            use std::any::Any;
+            fn bar() {
+                "bar".type_id();
+            }
+        };
+    }
+
+    foo!();
+}
+
+mod macro_and_trait_same_name {
+    pub macro Foo() {}
+    pub trait Foo {
+        fn bar(&self);
+    }
+    impl Foo for () {
+        fn bar(&self) {}
+    }
+}
+
+fn call_macro_and_trait_good() {
+    // importing trait and macro but only using macro by path won't allow us to change this to
+    // `use macro_and_trait_same_name::Foo as _;`
+    use macro_and_trait_same_name::Foo;
+    Foo!();
+    ().bar();
+}
+
+proc_macros::external!(
+    fn ignore_inside_external_proc_macro() {
+        use std::any::Any;
+        "foo".type_id();
+    }
+);
+
+proc_macros::with_span!(
+    span
+
+    fn ignore_inside_with_span_proc_macro() {
+        use std::any::Any;
+        "foo".type_id();
+    }
+);
+
+// This should warn the import is unused but should not trigger unused_trait_names
+#[warn(unused)]
+mod unused_import {
+    use std::any::Any;
+}
+
+#[allow(clippy::unused_trait_names)]
+fn allow_lint_fn() {
+    use std::any::Any;
+
+    "foo".type_id();
+}
+
+#[allow(clippy::unused_trait_names)]
+mod allow_lint_mod {
+    use std::any::Any;
+
+    fn foo() {
+        "foo".type_id();
+    }
+}
+
+mod allow_lint_import {
+    #[allow(clippy::unused_trait_names)]
+    use std::any::Any;
+
+    fn foo() {
+        "foo".type_id();
+    }
+}
+
+// Limitation: Suggests `use std::any::Any as _::{self};` which looks weird
+// fn use_trait_self_good() {
+//     use std::any::Any::{self};
+//     "foo".type_id();
+// }
+
+// Limitation: Suggests `use std::any::{Any as _, Any as _};`
+// mod repeated_renamed {
+//     use std::any::{Any, Any as MyAny};
+
+//     fn foo() {
+//         "foo".type_id();
+//     }
+// }
diff --git a/src/tools/clippy/tests/ui/unused_trait_names.stderr b/src/tools/clippy/tests/ui/unused_trait_names.stderr
new file mode 100644
index 000000000000..f59d8f58a170
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unused_trait_names.stderr
@@ -0,0 +1,73 @@
+error: unused import: `std::any::Any`
+  --> tests/ui/unused_trait_names.rs:245:9
+   |
+LL |     use std::any::Any;
+   |         ^^^^^^^^^^^^^
+   |
+   = note: `-D unused-imports` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(unused_imports)]`
+
+error: importing trait that is only used anonymously
+  --> tests/ui/unused_trait_names.rs:12:19
+   |
+LL |     use std::any::Any;
+   |                   ^^^ help: use: `Any as _`
+   |
+   = note: `-D clippy::unused-trait-names` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::unused_trait_names)]`
+
+error: importing trait that is only used anonymously
+  --> tests/ui/unused_trait_names.rs:31:26
+   |
+LL |     use std::any::{self, Any, TypeId};
+   |                          ^^^ help: use: `Any as _`
+
+error: importing trait that is only used anonymously
+  --> tests/ui/unused_trait_names.rs:43:19
+   |
+LL |     use std::any::Any as MyAny;
+   |                   ^^^^^^^^^^^^ help: use: `Any as _`
+
+error: importing trait that is only used anonymously
+  --> tests/ui/unused_trait_names.rs:49:20
+   |
+LL |     use std::any::{Any as MyAny, TypeId as MyTypeId};
+   |                    ^^^^^^^^^^^^ help: use: `Any as _`
+
+error: importing trait that is only used anonymously
+  --> tests/ui/unused_trait_names.rs:72:23
+   |
+LL |         use std::any::Any;
+   |                       ^^^ help: use: `Any as _`
+
+error: importing trait that is only used anonymously
+  --> tests/ui/unused_trait_names.rs:113:19
+   |
+LL |     use std::any::Any;
+   |                   ^^^ help: use: `Any as _`
+
+error: importing trait that is only used anonymously
+  --> tests/ui/unused_trait_names.rs:132:19
+   |
+LL |     use std::any::Any;
+   |                   ^^^ help: use: `Any as _`
+
+error: importing trait that is only used anonymously
+  --> tests/ui/unused_trait_names.rs:191:34
+   |
+LL |     use simple_trait::{MyStruct, MyTrait};
+   |                                  ^^^^^^^ help: use: `MyTrait as _`
+
+error: importing trait that is only used anonymously
+  --> tests/ui/unused_trait_names.rs:198:27
+   |
+LL |             use std::any::Any;
+   |                           ^^^ help: use: `Any as _`
+...
+LL |     foo!();
+   |     ------ in this macro invocation
+   |
+   = note: this error originates in the macro `foo` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 10 previous errors
+
diff --git a/src/tools/clippy/tests/ui/used_underscore_binding.stderr b/src/tools/clippy/tests/ui/used_underscore_binding.stderr
index 556e1792b3e6..f9e8013d3ad5 100644
--- a/src/tools/clippy/tests/ui/used_underscore_binding.stderr
+++ b/src/tools/clippy/tests/ui/used_underscore_binding.stderr
@@ -1,10 +1,10 @@
-error: used binding `_foo` which is prefixed with an underscore. A leading underscore signals that a binding will not be used
+error: used underscore-prefixed binding
   --> tests/ui/used_underscore_binding.rs:23:5
    |
 LL |     _foo + 1
    |     ^^^^
    |
-note: `_foo` is defined here
+note: binding is defined here
   --> tests/ui/used_underscore_binding.rs:22:22
    |
 LL | fn prefix_underscore(_foo: u32) -> u32 {
@@ -12,61 +12,61 @@ LL | fn prefix_underscore(_foo: u32) -> u32 {
    = note: `-D clippy::used-underscore-binding` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::used_underscore_binding)]`
 
-error: used binding `_foo` which is prefixed with an underscore. A leading underscore signals that a binding will not be used
+error: used underscore-prefixed binding
   --> tests/ui/used_underscore_binding.rs:28:20
    |
 LL |     println!("{}", _foo);
    |                    ^^^^
    |
-note: `_foo` is defined here
+note: binding is defined here
   --> tests/ui/used_underscore_binding.rs:27:24
    |
 LL | fn in_macro_or_desugar(_foo: u32) {
    |                        ^^^^
 
-error: used binding `_foo` which is prefixed with an underscore. A leading underscore signals that a binding will not be used
+error: used underscore-prefixed binding
   --> tests/ui/used_underscore_binding.rs:29:16
    |
 LL |     assert_eq!(_foo, _foo);
    |                ^^^^
    |
-note: `_foo` is defined here
+note: binding is defined here
   --> tests/ui/used_underscore_binding.rs:27:24
    |
 LL | fn in_macro_or_desugar(_foo: u32) {
    |                        ^^^^
 
-error: used binding `_foo` which is prefixed with an underscore. A leading underscore signals that a binding will not be used
+error: used underscore-prefixed binding
   --> tests/ui/used_underscore_binding.rs:29:22
    |
 LL |     assert_eq!(_foo, _foo);
    |                      ^^^^
    |
-note: `_foo` is defined here
+note: binding is defined here
   --> tests/ui/used_underscore_binding.rs:27:24
    |
 LL | fn in_macro_or_desugar(_foo: u32) {
    |                        ^^^^
 
-error: used binding `_underscore_field` which is prefixed with an underscore. A leading underscore signals that a binding will not be used
+error: used underscore-prefixed binding
   --> tests/ui/used_underscore_binding.rs:42:5
    |
 LL |     s._underscore_field += 1;
    |     ^^^^^^^^^^^^^^^^^^^
    |
-note: `_underscore_field` is defined here
+note: binding is defined here
   --> tests/ui/used_underscore_binding.rs:36:5
    |
 LL |     _underscore_field: u32,
    |     ^^^^^^^^^^^^^^^^^^^^^^
 
-error: used binding `_i` which is prefixed with an underscore. A leading underscore signals that a binding will not be used
+error: used underscore-prefixed binding
   --> tests/ui/used_underscore_binding.rs:103:16
    |
 LL |         uses_i(_i);
    |                ^^
    |
-note: `_i` is defined here
+note: binding is defined here
   --> tests/ui/used_underscore_binding.rs:102:13
    |
 LL |         let _i = 5;
diff --git a/src/tools/clippy/tests/ui/used_underscore_items.rs b/src/tools/clippy/tests/ui/used_underscore_items.rs
new file mode 100644
index 000000000000..223016a5c966
--- /dev/null
+++ b/src/tools/clippy/tests/ui/used_underscore_items.rs
@@ -0,0 +1,63 @@
+//@aux-build:external_item.rs
+#![allow(unused)]
+#![warn(clippy::used_underscore_items)]
+
+extern crate external_item;
+
+// should not lint macro
+macro_rules! macro_wrap_func {
+    () => {
+        fn _marco_foo() {}
+    };
+}
+
+macro_wrap_func!();
+
+struct _FooStruct {}
+
+impl _FooStruct {
+    fn _method_call(self) {}
+}
+
+fn _foo1() {}
+
+fn _foo2() -> i32 {
+    0
+}
+
+mod a {
+    pub mod b {
+        pub mod c {
+            pub fn _foo3() {}
+
+            pub struct _FooStruct2 {}
+
+            impl _FooStruct2 {
+                pub fn _method_call(self) {}
+            }
+        }
+    }
+}
+
+fn main() {
+    _foo1();
+    let _ = _foo2();
+    a::b::c::_foo3();
+    let _ = &_FooStruct {};
+    let _ = _FooStruct {};
+
+    let foo_struct = _FooStruct {};
+    foo_struct._method_call();
+
+    let foo_struct2 = a::b::c::_FooStruct2 {};
+    foo_struct2._method_call();
+}
+
+// should not lint exteranl crate.
+// user cannot control how others name their items
+fn external_item_call() {
+    let foo_struct3 = external_item::_ExternalStruct {};
+    foo_struct3._foo();
+
+    external_item::_exernal_foo();
+}
diff --git a/src/tools/clippy/tests/ui/used_underscore_items.stderr b/src/tools/clippy/tests/ui/used_underscore_items.stderr
new file mode 100644
index 000000000000..93ac3a6fec6b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/used_underscore_items.stderr
@@ -0,0 +1,112 @@
+error: used underscore-prefixed item
+  --> tests/ui/used_underscore_items.rs:43:5
+   |
+LL |     _foo1();
+   |     ^^^^^^^
+   |
+note: item is defined here
+  --> tests/ui/used_underscore_items.rs:22:1
+   |
+LL | fn _foo1() {}
+   | ^^^^^^^^^^
+   = note: `-D clippy::used-underscore-items` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::used_underscore_items)]`
+
+error: used underscore-prefixed item
+  --> tests/ui/used_underscore_items.rs:44:13
+   |
+LL |     let _ = _foo2();
+   |             ^^^^^^^
+   |
+note: item is defined here
+  --> tests/ui/used_underscore_items.rs:24:1
+   |
+LL | fn _foo2() -> i32 {
+   | ^^^^^^^^^^^^^^^^^
+
+error: used underscore-prefixed item
+  --> tests/ui/used_underscore_items.rs:45:5
+   |
+LL |     a::b::c::_foo3();
+   |     ^^^^^^^^^^^^^^^^
+   |
+note: item is defined here
+  --> tests/ui/used_underscore_items.rs:31:13
+   |
+LL |             pub fn _foo3() {}
+   |             ^^^^^^^^^^^^^^
+
+error: used underscore-prefixed item
+  --> tests/ui/used_underscore_items.rs:46:14
+   |
+LL |     let _ = &_FooStruct {};
+   |              ^^^^^^^^^^^^^
+   |
+note: item is defined here
+  --> tests/ui/used_underscore_items.rs:16:1
+   |
+LL | struct _FooStruct {}
+   | ^^^^^^^^^^^^^^^^^
+
+error: used underscore-prefixed item
+  --> tests/ui/used_underscore_items.rs:47:13
+   |
+LL |     let _ = _FooStruct {};
+   |             ^^^^^^^^^^^^^
+   |
+note: item is defined here
+  --> tests/ui/used_underscore_items.rs:16:1
+   |
+LL | struct _FooStruct {}
+   | ^^^^^^^^^^^^^^^^^
+
+error: used underscore-prefixed item
+  --> tests/ui/used_underscore_items.rs:49:22
+   |
+LL |     let foo_struct = _FooStruct {};
+   |                      ^^^^^^^^^^^^^
+   |
+note: item is defined here
+  --> tests/ui/used_underscore_items.rs:16:1
+   |
+LL | struct _FooStruct {}
+   | ^^^^^^^^^^^^^^^^^
+
+error: used underscore-prefixed item
+  --> tests/ui/used_underscore_items.rs:50:5
+   |
+LL |     foo_struct._method_call();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: item is defined here
+  --> tests/ui/used_underscore_items.rs:19:5
+   |
+LL |     fn _method_call(self) {}
+   |     ^^^^^^^^^^^^^^^^^^^^^
+
+error: used underscore-prefixed item
+  --> tests/ui/used_underscore_items.rs:52:23
+   |
+LL |     let foo_struct2 = a::b::c::_FooStruct2 {};
+   |                       ^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: item is defined here
+  --> tests/ui/used_underscore_items.rs:33:13
+   |
+LL |             pub struct _FooStruct2 {}
+   |             ^^^^^^^^^^^^^^^^^^^^^^
+
+error: used underscore-prefixed item
+  --> tests/ui/used_underscore_items.rs:53:5
+   |
+LL |     foo_struct2._method_call();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: item is defined here
+  --> tests/ui/used_underscore_items.rs:36:17
+   |
+LL |                 pub fn _method_call(self) {}
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 9 previous errors
+
diff --git a/src/tools/clippy/tests/ui/zombie_processes.rs b/src/tools/clippy/tests/ui/zombie_processes.rs
new file mode 100644
index 000000000000..a2abc7fc3a17
--- /dev/null
+++ b/src/tools/clippy/tests/ui/zombie_processes.rs
@@ -0,0 +1,138 @@
+#![warn(clippy::zombie_processes)]
+#![allow(clippy::if_same_then_else, clippy::ifs_same_cond)]
+
+use std::process::{Child, Command};
+
+fn main() {
+    {
+        // Check that #[expect] works
+        #[expect(clippy::zombie_processes)]
+        let mut x = Command::new("").spawn().unwrap();
+    }
+
+    {
+        let mut x = Command::new("").spawn().unwrap();
+        //~^ ERROR: spawned process is never `wait()`ed on
+        x.kill();
+        x.id();
+    }
+    {
+        let mut x = Command::new("").spawn().unwrap();
+        x.wait().unwrap(); // OK
+    }
+    {
+        let x = Command::new("").spawn().unwrap();
+        x.wait_with_output().unwrap(); // OK
+    }
+    {
+        let mut x = Command::new("").spawn().unwrap();
+        x.try_wait().unwrap(); // OK
+    }
+    {
+        let mut x = Command::new("").spawn().unwrap();
+        let mut r = &mut x;
+        r.wait().unwrap(); // OK, not calling `.wait()` directly on `x` but through `r` -> `x`
+    }
+    {
+        let mut x = Command::new("").spawn().unwrap();
+        process_child(x); // OK, other function might call `.wait()` so assume it does
+    }
+    {
+        let mut x = Command::new("").spawn().unwrap();
+        //~^ ERROR: spawned process is never `wait()`ed on
+        let v = &x;
+        // (allow shared refs is fine because one cannot call `.wait()` through that)
+    }
+
+    // https://github.com/rust-lang/rust-clippy/pull/11476#issuecomment-1718456033
+    // Unconditionally exiting the process in various ways (should not lint)
+    {
+        let mut x = Command::new("").spawn().unwrap();
+        std::process::exit(0);
+    }
+    {
+        let mut x = Command::new("").spawn().unwrap();
+        std::process::abort(); // same as above, but abort instead of exit
+    }
+    {
+        let mut x = Command::new("").spawn().unwrap();
+        if true { /* nothing */ }
+        std::process::abort(); // still unconditionally exits
+    }
+
+    // Conditionally exiting
+    // It should assume that it might not exit and still lint
+    {
+        let mut x = Command::new("").spawn().unwrap();
+        //~^ ERROR: spawned process is never `wait()`ed on
+        if true {
+            std::process::exit(0);
+        }
+    }
+    {
+        let mut x = Command::new("").spawn().unwrap();
+        //~^ ERROR: spawned process is never `wait()`ed on
+        if true {
+            while false {}
+            // Calling `exit()` after leaving a while loop should still be linted.
+            std::process::exit(0);
+        }
+    }
+
+    {
+        let mut x = { Command::new("").spawn().unwrap() };
+        x.wait().unwrap();
+    }
+
+    {
+        struct S {
+            c: Child,
+        }
+
+        let mut s = S {
+            c: Command::new("").spawn().unwrap(),
+        };
+        s.c.wait().unwrap();
+    }
+
+    {
+        let mut x = Command::new("").spawn().unwrap();
+        //~^ ERROR: spawned process is never `wait()`ed on
+        if true {
+            return;
+        }
+        x.wait().unwrap();
+    }
+
+    {
+        let mut x = Command::new("").spawn().unwrap();
+        //~^ ERROR: spawned process is never `wait()`ed on
+        if true {
+            x.wait().unwrap();
+        }
+    }
+
+    {
+        let mut x = Command::new("").spawn().unwrap();
+        if true {
+            x.wait().unwrap();
+        } else if true {
+            x.wait().unwrap();
+        } else {
+            x.wait().unwrap();
+        }
+    }
+
+    {
+        let mut x = Command::new("").spawn().unwrap();
+        if true {
+            x.wait().unwrap();
+            return;
+        }
+        x.wait().unwrap();
+    }
+}
+
+fn process_child(c: Child) {
+    todo!()
+}
diff --git a/src/tools/clippy/tests/ui/zombie_processes.stderr b/src/tools/clippy/tests/ui/zombie_processes.stderr
new file mode 100644
index 000000000000..eec821a4c8f1
--- /dev/null
+++ b/src/tools/clippy/tests/ui/zombie_processes.stderr
@@ -0,0 +1,64 @@
+error: spawned process is never `wait()`ed on
+  --> tests/ui/zombie_processes.rs:14:21
+   |
+LL |         let mut x = Command::new("").spawn().unwrap();
+   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: consider calling `.wait()`
+   = note: not doing so might leave behind zombie processes
+   = note: see https://doc.rust-lang.org/stable/std/process/struct.Child.html#warning
+   = note: `-D clippy::zombie-processes` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::zombie_processes)]`
+
+error: spawned process is never `wait()`ed on
+  --> tests/ui/zombie_processes.rs:41:21
+   |
+LL |         let mut x = Command::new("").spawn().unwrap();
+   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: consider calling `.wait()`
+   = note: not doing so might leave behind zombie processes
+   = note: see https://doc.rust-lang.org/stable/std/process/struct.Child.html#warning
+
+error: spawned process is never `wait()`ed on
+  --> tests/ui/zombie_processes.rs:66:21
+   |
+LL |         let mut x = Command::new("").spawn().unwrap();
+   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: consider calling `.wait()`
+   = note: not doing so might leave behind zombie processes
+   = note: see https://doc.rust-lang.org/stable/std/process/struct.Child.html#warning
+
+error: spawned process is never `wait()`ed on
+  --> tests/ui/zombie_processes.rs:73:21
+   |
+LL |         let mut x = Command::new("").spawn().unwrap();
+   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: consider calling `.wait()`
+   = note: not doing so might leave behind zombie processes
+   = note: see https://doc.rust-lang.org/stable/std/process/struct.Child.html#warning
+
+error: spawned process is never `wait()`ed on
+  --> tests/ui/zombie_processes.rs:99:21
+   |
+LL |         let mut x = Command::new("").spawn().unwrap();
+   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: consider calling `.wait()`
+   = note: not doing so might leave behind zombie processes
+   = note: see https://doc.rust-lang.org/stable/std/process/struct.Child.html#warning
+
+error: spawned process is never `wait()`ed on
+  --> tests/ui/zombie_processes.rs:108:21
+   |
+LL |         let mut x = Command::new("").spawn().unwrap();
+   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: consider calling `.wait()`
+   = note: not doing so might leave behind zombie processes
+   = note: see https://doc.rust-lang.org/stable/std/process/struct.Child.html#warning
+
+error: aborting due to 6 previous errors
+
diff --git a/src/tools/clippy/tests/ui/zombie_processes_fixable.fixed b/src/tools/clippy/tests/ui/zombie_processes_fixable.fixed
new file mode 100644
index 000000000000..6045262f519a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/zombie_processes_fixable.fixed
@@ -0,0 +1,26 @@
+#![warn(clippy::zombie_processes)]
+#![allow(clippy::needless_return)]
+
+use std::process::{Child, Command};
+
+fn main() {
+    let _ = Command::new("").spawn().unwrap().wait();
+    //~^ ERROR: spawned process is never `wait()`ed on
+    Command::new("").spawn().unwrap().wait();
+    //~^ ERROR: spawned process is never `wait()`ed on
+    spawn_proc().wait();
+    //~^ ERROR: spawned process is never `wait()`ed on
+    spawn_proc().wait().unwrap(); // OK
+}
+
+fn not_main() {
+    Command::new("").spawn().unwrap().wait();
+}
+
+fn spawn_proc() -> Child {
+    Command::new("").spawn().unwrap()
+}
+
+fn spawn_proc_2() -> Child {
+    return Command::new("").spawn().unwrap();
+}
diff --git a/src/tools/clippy/tests/ui/zombie_processes_fixable.rs b/src/tools/clippy/tests/ui/zombie_processes_fixable.rs
new file mode 100644
index 000000000000..e1ecb771641e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/zombie_processes_fixable.rs
@@ -0,0 +1,26 @@
+#![warn(clippy::zombie_processes)]
+#![allow(clippy::needless_return)]
+
+use std::process::{Child, Command};
+
+fn main() {
+    let _ = Command::new("").spawn().unwrap();
+    //~^ ERROR: spawned process is never `wait()`ed on
+    Command::new("").spawn().unwrap();
+    //~^ ERROR: spawned process is never `wait()`ed on
+    spawn_proc();
+    //~^ ERROR: spawned process is never `wait()`ed on
+    spawn_proc().wait().unwrap(); // OK
+}
+
+fn not_main() {
+    Command::new("").spawn().unwrap();
+}
+
+fn spawn_proc() -> Child {
+    Command::new("").spawn().unwrap()
+}
+
+fn spawn_proc_2() -> Child {
+    return Command::new("").spawn().unwrap();
+}
diff --git a/src/tools/clippy/tests/ui/zombie_processes_fixable.stderr b/src/tools/clippy/tests/ui/zombie_processes_fixable.stderr
new file mode 100644
index 000000000000..e1c40472c325
--- /dev/null
+++ b/src/tools/clippy/tests/ui/zombie_processes_fixable.stderr
@@ -0,0 +1,40 @@
+error: spawned process is never `wait()`ed on
+  --> tests/ui/zombie_processes_fixable.rs:7:13
+   |
+LL |     let _ = Command::new("").spawn().unwrap();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- help: try: `.wait()`
+   |
+   = note: not doing so might leave behind zombie processes
+   = note: see https://doc.rust-lang.org/stable/std/process/struct.Child.html#warning
+   = note: `-D clippy::zombie-processes` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::zombie_processes)]`
+
+error: spawned process is never `wait()`ed on
+  --> tests/ui/zombie_processes_fixable.rs:9:5
+   |
+LL |     Command::new("").spawn().unwrap();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- help: try: `.wait()`
+   |
+   = note: not doing so might leave behind zombie processes
+   = note: see https://doc.rust-lang.org/stable/std/process/struct.Child.html#warning
+
+error: spawned process is never `wait()`ed on
+  --> tests/ui/zombie_processes_fixable.rs:11:5
+   |
+LL |     spawn_proc();
+   |     ^^^^^^^^^^^^- help: try: `.wait()`
+   |
+   = note: not doing so might leave behind zombie processes
+   = note: see https://doc.rust-lang.org/stable/std/process/struct.Child.html#warning
+
+error: spawned process is never `wait()`ed on
+  --> tests/ui/zombie_processes_fixable.rs:17:5
+   |
+LL |     Command::new("").spawn().unwrap();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- help: try: `.wait()`
+   |
+   = note: not doing so might leave behind zombie processes
+   = note: see https://doc.rust-lang.org/stable/std/process/struct.Child.html#warning
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/triagebot.toml b/src/tools/clippy/triagebot.toml
index 99b3560a0642..dcf00e4e384b 100644
--- a/src/tools/clippy/triagebot.toml
+++ b/src/tools/clippy/triagebot.toml
@@ -20,7 +20,6 @@ new_pr = true
 [assign]
 contributing_url = "https://github.com/rust-lang/rust-clippy/blob/master/CONTRIBUTING.md"
 users_on_vacation = [
-    "flip1995",
     "matthiaskrgr",
     "giraffate",
 ]
diff --git a/src/tools/clippy/util/gh-pages/versions.html b/src/tools/clippy/util/gh-pages/versions.html
index 31ce88193295..d38fe39a4640 100644
--- a/src/tools/clippy/util/gh-pages/versions.html
+++ b/src/tools/clippy/util/gh-pages/versions.html
@@ -1,4 +1,5 @@
 
+
 
 
     
@@ -7,26 +8,15 @@
     Clippy lints documentation
 
     
-    
 
 
-    
+
-
- - - -
- - - - - - - + .github-corner:hover .octo-arm { + animation: octocat-wave 560ms ease-in-out; + } + @keyframes octocat-wave { + 0%, + 100% { + transform: rotate(0); + } + 20%, + 60% { + transform: rotate(-25deg); + } + 40%, + 80% { + transform: rotate(10deg); + } + } + @media (max-width: 500px) { + .github-corner:hover .octo-arm { + animation: none; + } + .github-corner .octo-arm { + animation: octocat-wave 560ms ease-in-out; + } + } + + diff --git a/src/tools/clippy/util/versions.py b/src/tools/clippy/util/versions.py index c041fc606a8f..fee0d292df16 100755 --- a/src/tools/clippy/util/versions.py +++ b/src/tools/clippy/util/versions.py @@ -1,22 +1,21 @@ #!/usr/bin/env python -import json -import logging as log +from string import Template +import argparse import os import sys -log.basicConfig(level=log.INFO, format="%(levelname)s: %(message)s") - - def key(v): if v == "master": - return float("inf") - if v == "stable": return sys.maxsize - if v == "beta": + if v == "stable": return sys.maxsize - 1 + if v == "beta": + return sys.maxsize - 2 if v == "pre-1.29.0": return -1 + if not v.startswith("rust-"): + return None v = v.replace("rust-", "") @@ -26,26 +25,27 @@ def key(v): return s - def main(): - if len(sys.argv) < 2: - log.error("specify output directory") - return + parser = argparse.ArgumentParser() + parser.add_argument("input", help="path to the versions.html template", type=argparse.FileType("r")) + parser.add_argument("outdir", help="path to write the output HTML") + args = parser.parse_args() - outdir = sys.argv[1] versions = [ dir - for dir in os.listdir(outdir) - if not dir.startswith(".") - and not dir.startswith("v") - and os.path.isdir(os.path.join(outdir, dir)) + for dir in os.listdir(args.outdir) + if key(dir) is not None ] - versions.sort(key=key) + versions.sort(key=key, reverse=True) + links = [f'{version}' for version in versions] - with open(os.path.join(outdir, "versions.json"), "w") as fp: - json.dump(versions, fp, indent=2) - log.info("wrote JSON for great justice") + template = Template(args.input.read()) + html = template.substitute(list="\n".join(links)) + path = os.path.join(args.outdir, "index.html") + with open(path, "w") as out: + out.write(html) + print(f"wrote HTML to {path}") if __name__ == "__main__": main() diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 177d4044ec2f..25a135aa3202 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -1963,7 +1963,7 @@ impl<'test> TestCx<'test> { #[rustfmt::skip] let tidy_args = [ - "--new-blocklevel-tags", "rustdoc-search", + "--new-blocklevel-tags", "rustdoc-search,rustdoc-toolbar", "--indent", "yes", "--indent-spaces", "2", "--wrap", "0", diff --git a/src/tools/compiletest/src/runtest/crash.rs b/src/tools/compiletest/src/runtest/crash.rs index 7f2bec4949be..885ed3b08fac 100644 --- a/src/tools/compiletest/src/runtest/crash.rs +++ b/src/tools/compiletest/src/runtest/crash.rs @@ -15,10 +15,11 @@ impl TestCx<'_> { // if a test does not crash, consider it an error if proc_res.status.success() || matches!(proc_res.status.code(), Some(1 | 0)) { self.fatal(&format!( - "crashtest no longer crashes/triggers ICE, horray! Please give it a meaningful name, \ - add a doc-comment to the start of the test explaining why it exists and \ - move it to tests/ui or wherever you see fit. Adding 'Fixes #' to your PR description \ - ensures that the corresponding ticket is auto-closed upon merge." + "crashtest no longer crashes/triggers ICE, horray! Please give it a meaningful \ + name, add a doc-comment to the start of the test explaining why it exists and \ + move it to tests/ui or wherever you see fit. Adding 'Fixes #' to your PR \ + description ensures that the corresponding ticket is auto-closed upon merge. \ + If you want to see verbose output, set `COMPILETEST_VERBOSE_CRASHES=1`." )); } } diff --git a/src/tools/html-checker/main.rs b/src/tools/html-checker/main.rs index ecfbb1955e77..5cdc4d53ab50 100644 --- a/src/tools/html-checker/main.rs +++ b/src/tools/html-checker/main.rs @@ -31,7 +31,7 @@ fn check_html_file(file: &Path) -> usize { .arg("--mute-id") // this option is useful in case we want to mute more warnings .arg("yes") .arg("--new-blocklevel-tags") - .arg("rustdoc-search") // custom elements + .arg("rustdoc-search,rustdoc-toolbar") // custom elements .arg("--mute") .arg(&to_mute_s) .arg(file); 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 e7d7cc28eeee..f792e75ad085 100644 --- a/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs +++ b/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs @@ -22,9 +22,9 @@ use crate::borrow_tracker::{ use crate::concurrency::data_race::{NaReadType, NaWriteType}; use crate::*; -use diagnostics::{RetagCause, RetagInfo}; -pub use item::{Item, Permission}; -pub use stack::Stack; +use self::diagnostics::{RetagCause, RetagInfo}; +pub use self::item::{Item, Permission}; +pub use self::stack::Stack; pub type AllocState = Stacks; 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 89b8ff1af8bd..2afe02dc2c7f 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs @@ -19,8 +19,8 @@ mod unimap; #[cfg(test)] mod exhaustive; -use perms::Permission; -pub use tree::Tree; +use self::perms::Permission; +pub use self::tree::Tree; pub type AllocState = Tree; diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs index dfb9b8637fde..28f9dec7bb98 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs @@ -47,7 +47,7 @@ enum PermissionPriv { /// rejects: all child accesses (UB). Disabled, } -use PermissionPriv::*; +use self::PermissionPriv::*; impl PartialOrd for PermissionPriv { /// PermissionPriv is ordered by the reflexive transitive closure of diff --git a/src/tools/miri/src/concurrency/mod.rs b/src/tools/miri/src/concurrency/mod.rs index 17789fe9f87f..c5082b4e40b4 100644 --- a/src/tools/miri/src/concurrency/mod.rs +++ b/src/tools/miri/src/concurrency/mod.rs @@ -7,4 +7,4 @@ pub mod thread; mod vector_clock; pub mod weak_memory; -pub use vector_clock::VClock; +pub use self::vector_clock::VClock; diff --git a/src/tools/miri/src/intrinsics/atomic.rs b/src/tools/miri/src/intrinsics/atomic.rs index d76f622e84ba..6365e0efd512 100644 --- a/src/tools/miri/src/intrinsics/atomic.rs +++ b/src/tools/miri/src/intrinsics/atomic.rs @@ -1,7 +1,7 @@ use rustc_middle::{mir, mir::BinOp, ty}; use crate::*; -use helpers::check_arg_count; +use self::helpers::check_arg_count; pub enum AtomicOp { /// The `bool` indicates whether the result of the operation should be negated (`UnOp::Not`, diff --git a/src/tools/miri/src/intrinsics/mod.rs b/src/tools/miri/src/intrinsics/mod.rs index 3eeb11dbbb4a..b8352b575a4a 100644 --- a/src/tools/miri/src/intrinsics/mod.rs +++ b/src/tools/miri/src/intrinsics/mod.rs @@ -13,9 +13,9 @@ use rustc_span::{Symbol, sym}; use rustc_target::abi::Size; use crate::*; -use atomic::EvalContextExt as _; -use helpers::{ToHost, ToSoft, check_arg_count}; -use simd::EvalContextExt as _; +use self::atomic::EvalContextExt as _; +use self::helpers::{ToHost, ToSoft, check_arg_count}; +use self::simd::EvalContextExt as _; impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs index b39f88dd1c9d..6e015813e77a 100644 --- a/src/tools/miri/src/lib.rs +++ b/src/tools/miri/src/lib.rs @@ -51,6 +51,8 @@ clippy::cast_lossless, clippy::cast_possible_truncation, )] +#![cfg_attr(not(bootstrap), feature(unqualified_local_imports))] +#![cfg_attr(not(bootstrap), warn(unqualified_local_imports))] // Needed for rustdoc from bootstrap (with `-Znormalize-docs`). #![recursion_limit = "256"] diff --git a/src/tools/miri/src/shims/env.rs b/src/tools/miri/src/shims/env.rs index 6586ea8e48cf..279df042dea9 100644 --- a/src/tools/miri/src/shims/env.rs +++ b/src/tools/miri/src/shims/env.rs @@ -3,7 +3,7 @@ use std::ffi::{OsStr, OsString}; use rustc_data_structures::fx::FxHashMap; use crate::*; -use shims::{unix::UnixEnvVars, windows::WindowsEnvVars}; +use self::shims::{unix::UnixEnvVars, windows::WindowsEnvVars}; #[derive(Default)] pub enum EnvVars<'tcx> { diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs index 67dc1476175d..11cb9740e3e6 100644 --- a/src/tools/miri/src/shims/foreign_items.rs +++ b/src/tools/miri/src/shims/foreign_items.rs @@ -15,7 +15,7 @@ use rustc_target::{ use super::alloc::EvalContextExt as _; use super::backtrace::EvalContextExt as _; use crate::*; -use helpers::{ToHost, ToSoft}; +use self::helpers::{ToHost, ToSoft}; /// Type of dynamic symbols (for `dlsym` et al) #[derive(Debug, Copy, Clone)] diff --git a/src/tools/miri/src/shims/mod.rs b/src/tools/miri/src/shims/mod.rs index 618cf8cf200c..a689ac2b3784 100644 --- a/src/tools/miri/src/shims/mod.rs +++ b/src/tools/miri/src/shims/mod.rs @@ -17,7 +17,7 @@ pub mod panic; pub mod time; pub mod tls; -pub use unix::{DirTable, EpollInterestTable, FdTable}; +pub use self::unix::{DirTable, EpollInterestTable, FdTable}; /// What needs to be done after emulating an item (a shim or an intrinsic) is done. pub enum EmulateItemResult { diff --git a/src/tools/miri/src/shims/panic.rs b/src/tools/miri/src/shims/panic.rs index 44f942cb4c58..52c4394591c2 100644 --- a/src/tools/miri/src/shims/panic.rs +++ b/src/tools/miri/src/shims/panic.rs @@ -17,7 +17,7 @@ use rustc_target::spec::PanicStrategy; use rustc_target::spec::abi::Abi; use crate::*; -use helpers::check_arg_count; +use self::helpers::check_arg_count; /// Holds all of the relevant data for when unwinding hits a `try` frame. #[derive(Debug)] diff --git a/src/tools/miri/src/shims/unix/foreign_items.rs b/src/tools/miri/src/shims/unix/foreign_items.rs index 57dae0502500..c06ce57e610a 100644 --- a/src/tools/miri/src/shims/unix/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/foreign_items.rs @@ -11,11 +11,11 @@ use crate::shims::alloc::EvalContextExt as _; use crate::shims::unix::*; use crate::*; -use shims::unix::android::foreign_items as android; -use shims::unix::freebsd::foreign_items as freebsd; -use shims::unix::linux::foreign_items as linux; -use shims::unix::macos::foreign_items as macos; -use shims::unix::solarish::foreign_items as solarish; +use self::shims::unix::android::foreign_items as android; +use self::shims::unix::freebsd::foreign_items as freebsd; +use self::shims::unix::linux::foreign_items as linux; +use self::shims::unix::macos::foreign_items as macos; +use self::shims::unix::solarish::foreign_items as solarish; pub fn is_dyn_sym(name: &str, target_os: &str) -> bool { match name { diff --git a/src/tools/miri/src/shims/unix/fs.rs b/src/tools/miri/src/shims/unix/fs.rs index 1f7e1b3bd7c0..c90839138ce6 100644 --- a/src/tools/miri/src/shims/unix/fs.rs +++ b/src/tools/miri/src/shims/unix/fs.rs @@ -15,7 +15,7 @@ use crate::shims::os_str::bytes_to_os_str; use crate::shims::unix::fd::FileDescriptionRef; use crate::shims::unix::*; use crate::*; -use shims::time::system_time_to_duration; +use self::shims::time::system_time_to_duration; use self::fd::FlockOp; 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 d64f13f63d91..6418280d0350 100644 --- a/src/tools/miri/src/shims/unix/linux/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/linux/foreign_items.rs @@ -5,10 +5,10 @@ use crate::machine::SIGRTMAX; use crate::machine::SIGRTMIN; use crate::shims::unix::*; use crate::*; -use shims::unix::linux::epoll::EvalContextExt as _; -use shims::unix::linux::eventfd::EvalContextExt as _; -use shims::unix::linux::mem::EvalContextExt as _; -use shims::unix::linux::sync::futex; +use self::shims::unix::linux::epoll::EvalContextExt as _; +use self::shims::unix::linux::eventfd::EvalContextExt as _; +use self::shims::unix::linux::mem::EvalContextExt as _; +use self::shims::unix::linux::sync::futex; pub fn is_dyn_sym(name: &str) -> bool { matches!(name, "statx") diff --git a/src/tools/miri/src/shims/unix/macos/foreign_items.rs b/src/tools/miri/src/shims/unix/macos/foreign_items.rs index 47b887dec947..95a41752059d 100644 --- a/src/tools/miri/src/shims/unix/macos/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/macos/foreign_items.rs @@ -78,6 +78,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_pointer(environ, dest)?; } + // Random data generation + "CCRandomGenerateBytes" => { + let [bytes, count] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + let bytes = this.read_pointer(bytes)?; + let count = this.read_target_usize(count)?; + let success = this.eval_libc_i32("kCCSuccess"); + this.gen_random(bytes, count)?; + this.write_int(success, dest)?; + } + // Time related shims "mach_absolute_time" => { let [] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; diff --git a/src/tools/miri/src/shims/unix/mod.rs b/src/tools/miri/src/shims/unix/mod.rs index 7da6d7b02a2a..a95b4d3d3079 100644 --- a/src/tools/miri/src/shims/unix/mod.rs +++ b/src/tools/miri/src/shims/unix/mod.rs @@ -14,18 +14,18 @@ mod linux; mod macos; mod solarish; -pub use env::UnixEnvVars; -pub use fd::{FdTable, FileDescription}; -pub use fs::DirTable; -pub use linux::epoll::EpollInterestTable; +pub use self::env::UnixEnvVars; +pub use self::fd::{FdTable, FileDescription}; +pub use self::fs::DirTable; +pub use self::linux::epoll::EpollInterestTable; // All the Unix-specific extension traits -pub use env::EvalContextExt as _; -pub use fd::EvalContextExt as _; -pub use fs::EvalContextExt as _; -pub use mem::EvalContextExt as _; -pub use sync::EvalContextExt as _; -pub use thread::EvalContextExt as _; -pub use unnamed_socket::EvalContextExt as _; +pub use self::env::EvalContextExt as _; +pub use self::fd::EvalContextExt as _; +pub use self::fs::EvalContextExt as _; +pub use self::mem::EvalContextExt as _; +pub use self::sync::EvalContextExt as _; +pub use self::thread::EvalContextExt as _; +pub use self::unnamed_socket::EvalContextExt as _; // Make up some constants. const UID: u32 = 1000; diff --git a/src/tools/miri/src/shims/windows/env.rs b/src/tools/miri/src/shims/windows/env.rs index 495df18a6eaa..178b0a1a4612 100644 --- a/src/tools/miri/src/shims/windows/env.rs +++ b/src/tools/miri/src/shims/windows/env.rs @@ -5,7 +5,7 @@ use std::io::ErrorKind; use rustc_data_structures::fx::FxHashMap; use crate::*; -use helpers::windows_check_buffer_size; +use self::helpers::windows_check_buffer_size; #[derive(Default)] pub struct WindowsEnvVars { diff --git a/src/tools/miri/src/shims/windows/foreign_items.rs b/src/tools/miri/src/shims/windows/foreign_items.rs index f840ba161650..f385ed4ac115 100644 --- a/src/tools/miri/src/shims/windows/foreign_items.rs +++ b/src/tools/miri/src/shims/windows/foreign_items.rs @@ -11,7 +11,7 @@ use rustc_target::spec::abi::Abi; use crate::shims::os_str::bytes_to_os_str; use crate::shims::windows::*; use crate::*; -use shims::windows::handle::{Handle, PseudoHandle}; +use self::shims::windows::handle::{Handle, PseudoHandle}; pub fn is_dyn_sym(name: &str) -> bool { // std does dynamic detection for these symbols diff --git a/src/tools/miri/src/shims/windows/mod.rs b/src/tools/miri/src/shims/windows/mod.rs index 65f682b9dad9..537c724e5266 100644 --- a/src/tools/miri/src/shims/windows/mod.rs +++ b/src/tools/miri/src/shims/windows/mod.rs @@ -5,9 +5,9 @@ mod handle; mod sync; mod thread; -pub use env::WindowsEnvVars; +pub use self::env::WindowsEnvVars; // All the Windows-specific extension traits -pub use env::EvalContextExt as _; -pub use handle::EvalContextExt as _; -pub use sync::EvalContextExt as _; -pub use thread::EvalContextExt as _; +pub use self::env::EvalContextExt as _; +pub use self::handle::EvalContextExt as _; +pub use self::sync::EvalContextExt as _; +pub use self::thread::EvalContextExt as _; diff --git a/src/tools/miri/src/shims/windows/thread.rs b/src/tools/miri/src/shims/windows/thread.rs index f3ddf6072af1..9f93fc5f0810 100644 --- a/src/tools/miri/src/shims/windows/thread.rs +++ b/src/tools/miri/src/shims/windows/thread.rs @@ -2,7 +2,7 @@ use rustc_middle::ty::layout::LayoutOf; use rustc_target::spec::abi::Abi; use crate::*; -use shims::windows::handle::{EvalContextExt as _, Handle, PseudoHandle}; +use self::shims::windows::handle::{EvalContextExt as _, Handle, PseudoHandle}; impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} diff --git a/src/tools/miri/src/shims/x86/mod.rs b/src/tools/miri/src/shims/x86/mod.rs index 7c7a0935c47a..9139156fd0e4 100644 --- a/src/tools/miri/src/shims/x86/mod.rs +++ b/src/tools/miri/src/shims/x86/mod.rs @@ -9,7 +9,7 @@ use rustc_target::abi::Size; use rustc_target::spec::abi::Abi; use crate::*; -use helpers::bool_to_simd_element; +use self::helpers::bool_to_simd_element; mod aesni; mod avx; diff --git a/src/tools/miri/tests/fail/dyn-call-trait-mismatch.rs b/src/tools/miri/tests/fail/dyn-call-trait-mismatch.rs index 982d57b73720..7ac619e09abf 100644 --- a/src/tools/miri/tests/fail/dyn-call-trait-mismatch.rs +++ b/src/tools/miri/tests/fail/dyn-call-trait-mismatch.rs @@ -16,5 +16,5 @@ impl T1 for i32 { fn main() { let r = Box::new(0) as Box; let r2: Box = unsafe { std::mem::transmute(r) }; - r2.method2(); //~ERROR: using vtable for trait `T1` but trait `T2` was expected + r2.method2(); //~ERROR: using vtable for `T1` but `T2` was expected } diff --git a/src/tools/miri/tests/fail/dyn-call-trait-mismatch.stderr b/src/tools/miri/tests/fail/dyn-call-trait-mismatch.stderr index 3680a84fac27..37d102c87134 100644 --- a/src/tools/miri/tests/fail/dyn-call-trait-mismatch.stderr +++ b/src/tools/miri/tests/fail/dyn-call-trait-mismatch.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: using vtable for trait `T1` but trait `T2` was expected +error: Undefined Behavior: using vtable for `T1` but `T2` was expected --> tests/fail/dyn-call-trait-mismatch.rs:LL:CC | LL | r2.method2(); - | ^^^^^^^^^^^^ using vtable for trait `T1` but trait `T2` was expected + | ^^^^^^^^^^^^ using vtable for `T1` but `T2` was expected | = 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/dyn-upcast-trait-mismatch.rs b/src/tools/miri/tests/fail/dyn-upcast-trait-mismatch.rs index 85d7582d112a..f450e7e652c5 100644 --- a/src/tools/miri/tests/fail/dyn-upcast-trait-mismatch.rs +++ b/src/tools/miri/tests/fail/dyn-upcast-trait-mismatch.rs @@ -63,6 +63,6 @@ fn main() { let baz: &dyn Baz = &1; let baz_fake: *const dyn Bar = std::mem::transmute(baz); let _err = baz_fake as *const dyn Foo; - //~^ERROR: using vtable for trait `Baz` but trait `Bar` was expected + //~^ERROR: using vtable for `Baz` but `Bar` was expected } } diff --git a/src/tools/miri/tests/fail/dyn-upcast-trait-mismatch.stderr b/src/tools/miri/tests/fail/dyn-upcast-trait-mismatch.stderr index 2129fe66e9c0..d0fd0e6fcc14 100644 --- a/src/tools/miri/tests/fail/dyn-upcast-trait-mismatch.stderr +++ b/src/tools/miri/tests/fail/dyn-upcast-trait-mismatch.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: using vtable for trait `Baz` but trait `Bar` was expected +error: Undefined Behavior: using vtable for `Baz` but `Bar` was expected --> tests/fail/dyn-upcast-trait-mismatch.rs:LL:CC | LL | let _err = baz_fake as *const dyn Foo; - | ^^^^^^^^ using vtable for trait `Baz` but trait `Bar` was expected + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ using vtable for `Baz` but `Bar` was expected | = 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/validity/wrong-dyn-trait-assoc-type.rs b/src/tools/miri/tests/fail/validity/wrong-dyn-trait-assoc-type.rs new file mode 100644 index 000000000000..1478abedee0d --- /dev/null +++ b/src/tools/miri/tests/fail/validity/wrong-dyn-trait-assoc-type.rs @@ -0,0 +1,18 @@ +trait Trait { + type Assoc; + fn foo(&self) -> Self::Assoc; +} + +impl Trait for T { + type Assoc = T; + fn foo(&self) -> T { *self } +} + +fn main() { + let v: Box> = Box::new(2); + let v: Box> = unsafe { std::mem::transmute(v) }; //~ERROR: wrong trait + + if v.foo() { + println!("huh"); + } +} diff --git a/src/tools/miri/tests/fail/validity/wrong-dyn-trait-assoc-type.stderr b/src/tools/miri/tests/fail/validity/wrong-dyn-trait-assoc-type.stderr new file mode 100644 index 000000000000..44939a5a838c --- /dev/null +++ b/src/tools/miri/tests/fail/validity/wrong-dyn-trait-assoc-type.stderr @@ -0,0 +1,15 @@ +error: Undefined Behavior: constructing invalid value: wrong trait in wide pointer vtable: expected `Trait`, but encountered `Trait` + --> tests/fail/validity/wrong-dyn-trait-assoc-type.rs:LL:CC + | +LL | let v: Box> = unsafe { std::mem::transmute(v) }; + | ^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: wrong trait in wide pointer vtable: expected `Trait`, but encountered `Trait` + | + = 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 tests/fail/validity/wrong-dyn-trait-assoc-type.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/validity/wrong-dyn-trait.stderr b/src/tools/miri/tests/fail/validity/wrong-dyn-trait.stderr index 4be3fb52bdb7..45c882bebdfc 100644 --- a/src/tools/miri/tests/fail/validity/wrong-dyn-trait.stderr +++ b/src/tools/miri/tests/fail/validity/wrong-dyn-trait.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: constructing invalid value: wrong trait in wide pointer vtable: expected `std::fmt::Debug`, but encountered `` +error: Undefined Behavior: constructing invalid value: wrong trait in wide pointer vtable: expected `std::fmt::Debug`, but encountered `std::marker::Send` --> tests/fail/validity/wrong-dyn-trait.rs:LL:CC | LL | let _y: *const dyn fmt::Debug = unsafe { mem::transmute(x) }; - | ^^^^^^^^^^^^^^^^^ constructing invalid value: wrong trait in wide pointer vtable: expected `std::fmt::Debug`, but encountered `` + | ^^^^^^^^^^^^^^^^^ constructing invalid value: wrong trait in wide pointer vtable: expected `std::fmt::Debug`, but encountered `std::marker::Send` | = 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/pass-dep/libc/ccrandomgeneratebytes_apple.rs b/src/tools/miri/tests/pass-dep/libc/ccrandomgeneratebytes_apple.rs new file mode 100644 index 000000000000..afcc02bc8397 --- /dev/null +++ b/src/tools/miri/tests/pass-dep/libc/ccrandomgeneratebytes_apple.rs @@ -0,0 +1,7 @@ +//@only-target: apple # This directly tests apple-only functions + +fn main() { + let mut bytes = [0u8; 24]; + let ret = unsafe { libc::CCRandomGenerateBytes(bytes.as_mut_ptr().cast(), bytes.len()) }; + assert_eq!(ret, libc::kCCSuccess); +} diff --git a/src/tools/run-make-support/src/external_deps/cargo.rs b/src/tools/run-make-support/src/external_deps/cargo.rs index b0e045dc80bf..e91d101cb995 100644 --- a/src/tools/run-make-support/src/external_deps/cargo.rs +++ b/src/tools/run-make-support/src/external_deps/cargo.rs @@ -1,7 +1,8 @@ use crate::command::Command; use crate::env_var; -/// Returns a command that can be used to invoke Cargo. +/// Returns a command that can be used to invoke cargo. The cargo is provided by compiletest +/// through the `CARGO` env var. pub fn cargo() -> Command { - Command::new(env_var("BOOTSTRAP_CARGO")) + Command::new(env_var("CARGO")) } diff --git a/src/tools/rust-analyzer/.typos.toml b/src/tools/rust-analyzer/.typos.toml index e7e764ce0351..febfb233bd97 100644 --- a/src/tools/rust-analyzer/.typos.toml +++ b/src/tools/rust-analyzer/.typos.toml @@ -15,6 +15,7 @@ extend-ignore-re = [ '"flate2"', "raison d'être", "inout", + "INOUT", "optin" ] diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index 4e9319f13aa8..dc820fcb28d2 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -96,6 +96,15 @@ version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +[[package]] +name = "borsh" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6362ed55def622cddc70a4746a68554d7b687713770de539e59a739b249f8ed" +dependencies = [ + "cfg_aliases 0.2.1", +] + [[package]] name = "byteorder" version = "1.5.0" @@ -167,6 +176,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + [[package]] name = "chalk-derive" version = "0.98.0" @@ -982,7 +997,7 @@ checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "lsp-server" -version = "0.7.6" +version = "0.7.7" dependencies = [ "crossbeam-channel", "ctrlc", @@ -994,9 +1009,9 @@ dependencies = [ [[package]] name = "lsp-server" -version = "0.7.6" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "248f65b78f6db5d8e1b1604b4098a28b43d21a8eb1deeca22b1c421b276c7095" +checksum = "550446e84739dcaf6d48a4a093973850669e13e8a34d8f8d64851041be267cd9" dependencies = [ "crossbeam-channel", "log", @@ -1029,8 +1044,10 @@ version = "0.0.0" dependencies = [ "arrayvec", "cov-mark", + "expect-test", "intern", "parser", + "ra-ap-rustc_lexer", "rustc-hash", "smallvec", "span", @@ -1113,7 +1130,7 @@ checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4" dependencies = [ "bitflags 2.6.0", "cfg-if", - "cfg_aliases", + "cfg_aliases 0.1.1", "libc", ] @@ -1468,9 +1485,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_abi" -version = "0.63.0" +version = "0.68.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b011c39d409940a890414e3a7b239762ac16d88029ad71b050a8374831b93790" +checksum = "2a8cb51bb4534ac3e9c74f1d9bd90e607e60f94f734b1cf1a66f753ad2af6ed7" dependencies = [ "bitflags 2.6.0", "ra-ap-rustc_index", @@ -1479,9 +1496,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_index" -version = "0.63.0" +version = "0.68.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9027acdee649b0b27eb10b7db5be833efee3362d394935c5eed8f0745a9d43ce" +checksum = "8b640fba2b7ef4f875459e2e76daeb846ef341d1d376fa758962ac0eba79bce6" dependencies = [ "arrayvec", "ra-ap-rustc_index_macros", @@ -1490,9 +1507,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_index_macros" -version = "0.63.0" +version = "0.68.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "540b86dc0384141ac8e825fc2874cd44bffd4277d99d8ec63ee416f1a98d5997" +checksum = "faef502419ba5ac9d3079b1a835c6e5b4e605388254bbe55eb5683936f541be9" dependencies = [ "proc-macro2", "quote", @@ -1501,9 +1518,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_lexer" -version = "0.63.0" +version = "0.68.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bdf98bb457b47b9ae4aeebf867d0ca440c86925e0b6381658c4a02589748c9d" +checksum = "5da7f9d533b8d5be6704558da741ff20b982ad4647b1e9e08632853e4fecf9d5" dependencies = [ "unicode-properties", "unicode-xid", @@ -1511,9 +1528,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_parse_format" -version = "0.63.0" +version = "0.68.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8fe3556ab6311bb775220563a300e2bf62ec56404521fe0c511a583937683d5" +checksum = "94389cf81c651b1bda9ac45d3de6a2d851bb6fd4cb893875daa44e419c94205f" dependencies = [ "ra-ap-rustc_index", "ra-ap-rustc_lexer", @@ -1521,9 +1538,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_pattern_analysis" -version = "0.63.0" +version = "0.68.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1709080fdeb5db630e1c2644026c2962aaa32416cd92f0190c04b0c21e114b91" +checksum = "3679d8dd0114ed6000918309f843782738e51c99d8e4baec0d0f706e4d948819" dependencies = [ "ra-ap-rustc_index", "rustc-hash", @@ -1636,7 +1653,7 @@ dependencies = [ "intern", "itertools", "load-cargo", - "lsp-server 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)", + "lsp-server 0.7.7 (registry+https://github.com/rust-lang/crates.io-index)", "lsp-types", "memchr", "mimalloc", @@ -1843,10 +1860,11 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "smol_str" -version = "0.2.2" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd538fb6910ac1099850255cf94a94df6551fbdd602454387d0adb2d1ca6dead" +checksum = "66eaf762c5af19db3108300515c8aa7a50efc90ff745f4c62288052ebf9fdd25" dependencies = [ + "borsh", "serde", ] @@ -2607,6 +2625,7 @@ version = "0.1.0" dependencies = [ "anyhow", "directories", + "either", "flate2", "itertools", "proc-macro2", diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml index aa7bd2dc5fe8..b4587a379618 100644 --- a/src/tools/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/Cargo.toml @@ -85,11 +85,11 @@ tt = { path = "./crates/tt", version = "0.0.0" } vfs-notify = { path = "./crates/vfs-notify", version = "0.0.0" } vfs = { path = "./crates/vfs", version = "0.0.0" } -ra-ap-rustc_lexer = { version = "0.63.0", default-features = false } -ra-ap-rustc_parse_format = { version = "0.63.0", default-features = false } -ra-ap-rustc_index = { version = "0.63.0", default-features = false } -ra-ap-rustc_abi = { version = "0.63.0", default-features = false } -ra-ap-rustc_pattern_analysis = { version = "0.63.0", default-features = false } +ra-ap-rustc_lexer = { version = "0.68.0", default-features = false } +ra-ap-rustc_parse_format = { version = "0.68.0", default-features = false } +ra-ap-rustc_index = { version = "0.68.0", default-features = false } +ra-ap-rustc_abi = { version = "0.68.0", default-features = false } +ra-ap-rustc_pattern_analysis = { version = "0.68.0", default-features = false } # local crates that aren't published to crates.io. These should not have versions. test-fixture = { path = "./crates/test-fixture" } @@ -145,7 +145,7 @@ smallvec = { version = "1.10.0", features = [ "union", "const_generics", ] } -smol_str = "0.2.1" +smol_str = "0.3.1" snap = "1.1.0" text-size = "1.1.1" tracing = "0.1.40" @@ -185,6 +185,7 @@ style = { level = "warn", priority = -1 } suspicious = { level = "warn", priority = -1 } ## allow following lints +too_long_first_doc_paragraph = "allow" # subjective single_match = "allow" # () makes a fine error in most cases diff --git a/src/tools/rust-analyzer/crates/base-db/src/change.rs b/src/tools/rust-analyzer/crates/base-db/src/change.rs index a9d91d64ceb6..4fb6654b612f 100644 --- a/src/tools/rust-analyzer/crates/base-db/src/change.rs +++ b/src/tools/rust-analyzer/crates/base-db/src/change.rs @@ -3,11 +3,15 @@ use std::fmt; +use rustc_hash::FxHashMap; use salsa::Durability; use triomphe::Arc; use vfs::FileId; -use crate::{CrateGraph, SourceDatabaseFileInputExt, SourceRoot, SourceRootDatabase, SourceRootId}; +use crate::{ + CrateGraph, CrateId, CrateWorkspaceData, SourceDatabaseFileInputExt, SourceRoot, + SourceRootDatabase, SourceRootId, +}; /// Encapsulate a bunch of raw `.set` calls on the database. #[derive(Default)] @@ -15,6 +19,7 @@ pub struct FileChange { pub roots: Option>, pub files_changed: Vec<(FileId, Option)>, pub crate_graph: Option, + pub ws_data: Option>>, } impl fmt::Debug for FileChange { @@ -50,6 +55,10 @@ impl FileChange { self.crate_graph = Some(graph); } + pub fn set_ws_data(&mut self, data: FxHashMap>) { + self.ws_data = Some(data); + } + pub fn apply(self, db: &mut dyn SourceRootDatabase) { let _p = tracing::info_span!("FileChange::apply").entered(); if let Some(roots) = self.roots { @@ -74,6 +83,9 @@ impl FileChange { if let Some(crate_graph) = self.crate_graph { db.set_crate_graph_with_durability(Arc::new(crate_graph), Durability::HIGH); } + if let Some(data) = self.ws_data { + db.set_crate_workspace_data_with_durability(Arc::new(data), Durability::HIGH); + } } } 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 3616fa9fd868..f5109339ad10 100644 --- a/src/tools/rust-analyzer/crates/base-db/src/input.rs +++ b/src/tools/rust-analyzer/crates/base-db/src/input.rs @@ -374,37 +374,6 @@ impl CrateGraph { self.arena.alloc(data) } - /// Remove the crate from crate graph. If any crates depend on this crate, the dependency would be replaced - /// with the second input. - pub fn remove_and_replace( - &mut self, - id: CrateId, - replace_with: CrateId, - ) -> Result<(), CyclicDependenciesError> { - for (x, data) in self.arena.iter() { - if x == id { - continue; - } - for edge in &data.dependencies { - if edge.crate_id == id { - self.check_cycle_after_dependency(edge.crate_id, replace_with)?; - } - } - } - // if everything was ok, start to replace - for (x, data) in self.arena.iter_mut() { - if x == id { - continue; - } - for edge in &mut data.dependencies { - if edge.crate_id == id { - edge.crate_id = replace_with; - } - } - } - Ok(()) - } - pub fn add_dep( &mut self, from: CrateId, @@ -412,26 +381,17 @@ impl CrateGraph { ) -> Result<(), CyclicDependenciesError> { let _p = tracing::info_span!("add_dep").entered(); - self.check_cycle_after_dependency(from, dep.crate_id)?; - - self.arena[from].add_dep(dep); - Ok(()) - } - - /// Check if adding a dep from `from` to `to` creates a cycle. To figure - /// that out, look for a path in the *opposite* direction, from `to` to - /// `from`. - fn check_cycle_after_dependency( - &self, - from: CrateId, - to: CrateId, - ) -> Result<(), CyclicDependenciesError> { - if let Some(path) = self.find_path(&mut FxHashSet::default(), to, from) { + // Check if adding a dep from `from` to `to` creates a cycle. To figure + // that out, look for a path in the *opposite* direction, from `to` to + // `from`. + if let Some(path) = self.find_path(&mut FxHashSet::default(), dep.crate_id, from) { let path = path.into_iter().map(|it| (it, self[it].display_name.clone())).collect(); let err = CyclicDependenciesError { path }; - assert!(err.from().0 == from && err.to().0 == to); + assert!(err.from().0 == from && err.to().0 == dep.crate_id); return Err(err); } + + self.arena[from].add_dep(dep); Ok(()) } @@ -531,22 +491,15 @@ impl CrateGraph { .for_each(|(_, data)| data.dependencies.sort_by_key(|dep| dep.crate_id)); } - /// Extends this crate graph by adding a complete disjoint second crate + /// Extends this crate graph by adding a complete second crate /// graph and adjust the ids in the [`ProcMacroPaths`] accordingly. /// - /// This will deduplicate the crates of the graph where possible. - /// Note that for deduplication to fully work, `self`'s crate dependencies must be sorted by crate id. - /// If the crate dependencies were sorted, the resulting graph from this `extend` call will also - /// have the crate dependencies sorted. - /// - /// Returns a mapping from `other`'s crate ids to the new crate ids in `self`. + /// Returns a map mapping `other`'s IDs to the new IDs in `self`. pub fn extend( &mut self, mut other: CrateGraph, proc_macros: &mut ProcMacroPaths, - merge: impl Fn((CrateId, &mut CrateData), (CrateId, &CrateData)) -> bool, ) -> FxHashMap { - let m = self.len(); let topo = other.crates_in_topological_order(); let mut id_map: FxHashMap = FxHashMap::default(); for topo in topo { @@ -554,20 +507,13 @@ impl CrateGraph { crate_data.dependencies.iter_mut().for_each(|dep| dep.crate_id = id_map[&dep.crate_id]); crate_data.dependencies.sort_by_key(|dep| dep.crate_id); - let res = self - .arena - .iter_mut() - .take(m) - .find_map(|(id, data)| merge((id, data), (topo, crate_data)).then_some(id)); - - let new_id = - if let Some(res) = res { res } else { self.arena.alloc(crate_data.clone()) }; + + let new_id = self.arena.alloc(crate_data.clone()); id_map.insert(topo, new_id); } *proc_macros = mem::take(proc_macros).into_iter().map(|(id, macros)| (id_map[&id], macros)).collect(); - id_map } 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 20ef45d0b372..46e258d46f5c 100644 --- a/src/tools/rust-analyzer/crates/base-db/src/lib.rs +++ b/src/tools/rust-analyzer/crates/base-db/src/lib.rs @@ -5,11 +5,12 @@ mod input; use std::panic; +use rustc_hash::FxHashMap; use salsa::Durability; use span::EditionedFileId; use syntax::{ast, Parse, SourceFile, SyntaxError}; use triomphe::Arc; -use vfs::FileId; +use vfs::{AbsPathBuf, FileId}; pub use crate::{ change::FileChange, @@ -74,19 +75,30 @@ pub trait SourceDatabase: FileLoader + std::fmt::Debug { #[salsa::input] fn crate_graph(&self) -> Arc; - // FIXME: Consider removing this, making HirDatabase::target_data_layout an input query - #[salsa::input] - fn data_layout(&self, krate: CrateId) -> TargetLayoutLoadResult; - #[salsa::input] - fn toolchain(&self, krate: CrateId) -> Option; + fn crate_workspace_data(&self) -> Arc>>; #[salsa::transparent] fn toolchain_channel(&self, krate: CrateId) -> Option; } +/// Crate related data shared by the whole workspace. +#[derive(Debug, PartialEq, Eq, Hash, Clone)] +pub struct CrateWorkspaceData { + /// The working directory to run proc-macros in. This is usually the workspace root of cargo workspaces. + pub proc_macro_cwd: Option, + // FIXME: Consider removing this, making HirDatabase::target_data_layout an input query + pub data_layout: TargetLayoutLoadResult, + /// Toolchain version used to compile the crate. + pub toolchain: Option, +} + fn toolchain_channel(db: &dyn SourceDatabase, krate: CrateId) -> Option { - db.toolchain(krate).as_ref().and_then(|v| ReleaseChannel::from_str(&v.pre)) + db.crate_workspace_data() + .get(&krate)? + .toolchain + .as_ref() + .and_then(|v| ReleaseChannel::from_str(&v.pre)) } fn parse(db: &dyn SourceDatabase, file_id: EditionedFileId) -> Parse { 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 dde1f142ab0c..9535b5aea7c7 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body.rs @@ -100,7 +100,14 @@ pub struct BodySourceMap { field_map_back: FxHashMap, pat_field_map_back: FxHashMap, - format_args_template_map: FxHashMap>, + template_map: Option< + Box<( + // format_args! + FxHashMap>, + // asm! + FxHashMap>>, + )>, + >, expansions: FxHashMap>, MacroFileId>, @@ -220,6 +227,17 @@ impl Body { pretty::print_expr_hir(db, self, owner, expr, edition) } + pub fn pretty_print_pat( + &self, + db: &dyn DefDatabase, + owner: DefWithBodyId, + pat: PatId, + oneline: bool, + edition: Edition, + ) -> String { + pretty::print_pat_hir(db, self, owner, pat, oneline, edition) + } + fn new( db: &dyn DefDatabase, owner: DefWithBodyId, @@ -426,7 +444,16 @@ impl BodySourceMap { node: InFile<&ast::FormatArgsExpr>, ) -> Option<&[(syntax::TextRange, Name)]> { let src = node.map(AstPtr::new).map(AstPtr::upcast::); - self.format_args_template_map.get(self.expr_map.get(&src)?).map(std::ops::Deref::deref) + self.template_map.as_ref()?.0.get(self.expr_map.get(&src)?).map(std::ops::Deref::deref) + } + + pub fn asm_template_args( + &self, + node: InFile<&ast::AsmExpr>, + ) -> Option<(ExprId, &[Vec<(syntax::TextRange, usize)>])> { + let src = node.map(AstPtr::new).map(AstPtr::upcast::); + let expr = self.expr_map.get(&src)?; + Some(*expr).zip(self.template_map.as_ref()?.1.get(expr).map(std::ops::Deref::deref)) } /// Get a reference to the body source map's diagnostics. @@ -446,11 +473,14 @@ impl BodySourceMap { field_map_back, pat_field_map_back, expansions, - format_args_template_map, + template_map, diagnostics, binding_definitions, } = self; - format_args_template_map.shrink_to_fit(); + if let Some(template_map) = template_map { + template_map.0.shrink_to_fit(); + template_map.1.shrink_to_fit(); + } expr_map.shrink_to_fit(); expr_map_back.shrink_to_fit(); pat_map.shrink_to_fit(); @@ -463,4 +493,13 @@ impl BodySourceMap { diagnostics.shrink_to_fit(); binding_definitions.shrink_to_fit(); } + + pub fn template_map( + &self, + ) -> Option<&( + FxHashMap, Vec<(tt::TextRange, Name)>>, + FxHashMap, Vec>>, + )> { + self.template_map.as_deref() + } } 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 abf789582920..9c547574ecb1 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs @@ -1,6 +1,8 @@ //! Transforms `ast::Expr` into an equivalent `hir_def::expr::Expr` //! representation. +mod asm; + use std::mem; use base_db::CrateId; @@ -35,8 +37,8 @@ use crate::{ FormatPlaceholder, FormatSign, FormatTrait, }, Array, Binding, BindingAnnotation, BindingId, BindingProblems, CaptureBy, ClosureKind, - Expr, ExprId, InlineAsm, Label, LabelId, Literal, LiteralOrConst, MatchArm, Movability, - OffsetOf, Pat, PatId, RecordFieldPat, RecordLitField, Statement, + Expr, ExprId, Label, LabelId, Literal, LiteralOrConst, MatchArm, Movability, OffsetOf, Pat, + PatId, RecordFieldPat, RecordLitField, Statement, }, item_scope::BuiltinShadowMode, lang_item::LangItem, @@ -693,10 +695,7 @@ impl ExprCollector<'_> { } } ast::Expr::UnderscoreExpr(_) => self.alloc_expr(Expr::Underscore, syntax_ptr), - ast::Expr::AsmExpr(e) => { - let e = self.collect_expr_opt(e.expr()); - self.alloc_expr(Expr::InlineAsm(InlineAsm { e }), syntax_ptr) - } + ast::Expr::AsmExpr(e) => self.lower_inline_asm(e, syntax_ptr), ast::Expr::OffsetOfExpr(e) => { let container = Interned::new(TypeRef::from_ast_opt(&self.ctx(), e.ty())); let fields = e.fields().map(|it| it.as_name()).collect(); @@ -737,7 +736,7 @@ impl ExprCollector<'_> { /// `try { ; }` into `': { ; ::std::ops::Try::from_output(()) }` /// and save the `` to use it as a break target for desugaring of the `?` operator. fn desugar_try_block(&mut self, e: BlockExpr) -> ExprId { - let Some(try_from_output) = LangItem::TryTraitFromOutput.path(self.db, self.krate) else { + let Some(try_from_output) = self.lang_path(LangItem::TryTraitFromOutput) else { return self.collect_block(e); }; let label = self @@ -840,10 +839,10 @@ impl ExprCollector<'_> { fn collect_for_loop(&mut self, syntax_ptr: AstPtr, e: ast::ForExpr) -> ExprId { let Some((into_iter_fn, iter_next_fn, option_some, option_none)) = (|| { Some(( - LangItem::IntoIterIntoIter.path(self.db, self.krate)?, - LangItem::IteratorNext.path(self.db, self.krate)?, - LangItem::OptionSome.path(self.db, self.krate)?, - LangItem::OptionNone.path(self.db, self.krate)?, + self.lang_path(LangItem::IntoIterIntoIter)?, + self.lang_path(LangItem::IteratorNext)?, + self.lang_path(LangItem::OptionSome)?, + self.lang_path(LangItem::OptionNone)?, )) })() else { // Some of the needed lang items are missing, so we can't desugar @@ -896,6 +895,15 @@ impl ExprCollector<'_> { Expr::Match { expr: iter_next_expr, arms: Box::new([none_arm, some_arm]) }, syntax_ptr, ); + let loop_inner = self.alloc_expr( + Expr::Block { + id: None, + statements: Box::default(), + tail: Some(loop_inner), + label: None, + }, + syntax_ptr, + ); let loop_outer = self.alloc_expr(Expr::Loop { body: loop_inner, label }, syntax_ptr); let iter_binding = self.alloc_binding(iter_name, BindingAnnotation::Mutable); let iter_pat = self.alloc_pat_desugared(Pat::Bind { id: iter_binding, subpat: None }); @@ -923,10 +931,10 @@ impl ExprCollector<'_> { fn collect_try_operator(&mut self, syntax_ptr: AstPtr, e: ast::TryExpr) -> ExprId { let Some((try_branch, cf_continue, cf_break, try_from_residual)) = (|| { Some(( - LangItem::TryTraitBranch.path(self.db, self.krate)?, - LangItem::ControlFlowContinue.path(self.db, self.krate)?, - LangItem::ControlFlowBreak.path(self.db, self.krate)?, - LangItem::TryTraitFromResidual.path(self.db, self.krate)?, + self.lang_path(LangItem::TryTraitBranch)?, + self.lang_path(LangItem::ControlFlowContinue)?, + self.lang_path(LangItem::ControlFlowBreak)?, + self.lang_path(LangItem::TryTraitFromResidual)?, )) })() else { // Some of the needed lang items are missing, so we can't desugar @@ -1839,7 +1847,7 @@ impl ExprCollector<'_> { }, syntax_ptr, ); - self.source_map.format_args_template_map.insert(idx, mappings); + self.source_map.template_map.get_or_insert_with(Default::default).0.insert(idx, mappings); idx } @@ -2052,7 +2060,12 @@ impl ExprCollector<'_> { is_assignee_expr: false, }) } + // endregion: format + + fn lang_path(&self, lang: LangItem) -> Option { + lang.path(self.db, self.krate) + } } fn pat_literal_to_hir(lit: &ast::LiteralPat) -> Option<(Literal, ast::Literal)> { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/lower/asm.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/lower/asm.rs new file mode 100644 index 000000000000..4213370ac195 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-def/src/body/lower/asm.rs @@ -0,0 +1,276 @@ +//! Lowering of inline assembly. +use hir_expand::name::Name; +use intern::Symbol; +use rustc_hash::{FxHashMap, FxHashSet}; +use syntax::{ + ast::{self, HasName, IsString}, + AstNode, AstPtr, AstToken, T, +}; +use tt::{TextRange, TextSize}; + +use crate::{ + body::lower::{ExprCollector, FxIndexSet}, + hir::{AsmOperand, AsmOptions, Expr, ExprId, InlineAsm, InlineAsmRegOrRegClass}, +}; + +impl ExprCollector<'_> { + pub(super) fn lower_inline_asm( + &mut self, + asm: ast::AsmExpr, + syntax_ptr: AstPtr, + ) -> ExprId { + let mut clobber_abis = FxIndexSet::default(); + let mut operands = vec![]; + let mut options = AsmOptions::empty(); + + let mut named_pos: FxHashMap = Default::default(); + let mut named_args: FxHashMap = Default::default(); + let mut reg_args: FxHashSet = Default::default(); + for piece in asm.asm_pieces() { + let slot = operands.len(); + let mut lower_reg = |reg: Option| { + let reg = reg?; + if let Some(string) = reg.string_token() { + reg_args.insert(slot); + Some(InlineAsmRegOrRegClass::Reg(Symbol::intern(string.text()))) + } else { + reg.name_ref().map(|name_ref| { + InlineAsmRegOrRegClass::RegClass(Symbol::intern(&name_ref.text())) + }) + } + }; + + let op = match piece { + ast::AsmPiece::AsmClobberAbi(clobber_abi) => { + if let Some(abi_name) = clobber_abi.string_token() { + clobber_abis.insert(Symbol::intern(abi_name.text())); + } + continue; + } + ast::AsmPiece::AsmOptions(opt) => { + opt.asm_options().for_each(|opt| { + options |= match opt.syntax().first_token().map_or(T![$], |it| it.kind()) { + T![att_syntax] => AsmOptions::ATT_SYNTAX, + T![may_unwind] => AsmOptions::MAY_UNWIND, + T![nomem] => AsmOptions::NOMEM, + T![noreturn] => AsmOptions::NORETURN, + T![nostack] => AsmOptions::NOSTACK, + T![preserves_flags] => AsmOptions::PRESERVES_FLAGS, + T![pure] => AsmOptions::PURE, + T![raw] => AsmOptions::RAW, + T![readonly] => AsmOptions::READONLY, + _ => return, + } + }); + continue; + } + ast::AsmPiece::AsmOperandNamed(op) => { + let name = op.name().map(|name| Symbol::intern(&name.text())); + if let Some(name) = &name { + named_args.insert(name.clone(), slot); + named_pos.insert(slot, name.clone()); + } + let Some(op) = op.asm_operand() else { continue }; + ( + name.map(Name::new_symbol_root), + match op { + ast::AsmOperand::AsmRegOperand(op) => { + let Some(dir_spec) = op.asm_dir_spec() else { + continue; + }; + let Some(reg) = lower_reg(op.asm_reg_spec()) else { + continue; + }; + if dir_spec.in_token().is_some() { + let expr = self.collect_expr_opt( + op.asm_operand_expr().and_then(|it| it.in_expr()), + ); + AsmOperand::In { reg, expr } + } else if dir_spec.out_token().is_some() { + let expr = op + .asm_operand_expr() + .and_then(|it| it.in_expr()) + .filter(|it| !matches!(it, ast::Expr::UnderscoreExpr(_))) + .map(|expr| self.collect_expr(expr)); + AsmOperand::Out { reg, expr, late: false } + } else if dir_spec.lateout_token().is_some() { + let expr = op + .asm_operand_expr() + .and_then(|it| it.in_expr()) + .filter(|it| !matches!(it, ast::Expr::UnderscoreExpr(_))) + .map(|expr| self.collect_expr(expr)); + + AsmOperand::Out { reg, expr, late: true } + } else if dir_spec.inout_token().is_some() { + let Some(op_expr) = op.asm_operand_expr() else { continue }; + let in_expr = self.collect_expr_opt(op_expr.in_expr()); + match op_expr.fat_arrow_token().is_some() { + true => { + let out_expr = op_expr + .out_expr() + .filter(|it| { + !matches!(it, ast::Expr::UnderscoreExpr(_)) + }) + .map(|expr| self.collect_expr(expr)); + + AsmOperand::SplitInOut { + reg, + in_expr, + out_expr, + late: false, + } + } + false => { + AsmOperand::InOut { reg, expr: in_expr, late: false } + } + } + } else if dir_spec.inlateout_token().is_some() { + let Some(op_expr) = op.asm_operand_expr() else { continue }; + let in_expr = self.collect_expr_opt(op_expr.in_expr()); + match op_expr.fat_arrow_token().is_some() { + true => { + let out_expr = op_expr + .out_expr() + .filter(|it| { + !matches!(it, ast::Expr::UnderscoreExpr(_)) + }) + .map(|expr| self.collect_expr(expr)); + + AsmOperand::SplitInOut { + reg, + in_expr, + out_expr, + late: true, + } + } + false => { + AsmOperand::InOut { reg, expr: in_expr, late: true } + } + } + } else { + continue; + } + } + ast::AsmOperand::AsmLabel(l) => { + AsmOperand::Label(self.collect_block_opt(l.block_expr())) + } + ast::AsmOperand::AsmConst(c) => { + AsmOperand::Const(self.collect_expr_opt(c.expr())) + } + ast::AsmOperand::AsmSym(s) => { + let Some(path) = + s.path().and_then(|p| self.expander.parse_path(self.db, p)) + else { + continue; + }; + AsmOperand::Sym(path) + } + }, + ) + } + }; + operands.push(op); + } + + let mut mappings = vec![]; + let mut curarg = 0; + if !options.contains(AsmOptions::RAW) { + // Don't treat raw asm as a format string. + asm.template() + .enumerate() + .filter_map(|(idx, it)| Some((idx, it.clone(), self.expand_macros_to_string(it)?))) + .for_each(|(idx, expr, (s, is_direct_literal))| { + mappings.resize_with(idx + 1, Vec::default); + let Ok(text) = s.value() else { + return; + }; + let mappings = &mut mappings[idx]; + let template_snippet = match expr { + ast::Expr::Literal(literal) => match literal.kind() { + ast::LiteralKind::String(s) => Some(s.text().to_owned()), + _ => None, + }, + _ => None, + }; + let str_style = match s.quote_offsets() { + Some(offsets) => { + let raw = usize::from(offsets.quotes.0.len()) - 1; + // subtract 1 for the `r` prefix + (raw != 0).then(|| raw - 1) + } + None => None, + }; + + let mut parser = rustc_parse_format::Parser::new( + &text, + str_style, + template_snippet, + false, + rustc_parse_format::ParseMode::InlineAsm, + ); + parser.curarg = curarg; + + let mut unverified_pieces = Vec::new(); + while let Some(piece) = parser.next() { + if !parser.errors.is_empty() { + break; + } else { + unverified_pieces.push(piece); + } + } + + curarg = parser.curarg; + + let to_span = |inner_span: rustc_parse_format::InnerSpan| { + is_direct_literal.then(|| { + TextRange::new( + inner_span.start.try_into().unwrap(), + inner_span.end.try_into().unwrap(), + ) - TextSize::from(str_style.map(|it| it + 1).unwrap_or(0) as u32 + 1) + }) + }; + for piece in unverified_pieces { + match piece { + rustc_parse_format::Piece::String(_) => {} + rustc_parse_format::Piece::NextArgument(arg) => { + // let span = arg_spans.next(); + + let (operand_idx, _name) = match arg.position { + rustc_parse_format::ArgumentIs(idx) + | rustc_parse_format::ArgumentImplicitlyIs(idx) => { + if idx >= operands.len() + || named_pos.contains_key(&idx) + || reg_args.contains(&idx) + { + (None, None) + } else { + (Some(idx), None) + } + } + rustc_parse_format::ArgumentNamed(name) => { + let name = Symbol::intern(name); + ( + named_args.get(&name).copied(), + Some(Name::new_symbol_root(name)), + ) + } + }; + + if let Some(operand_idx) = operand_idx { + if let Some(position_span) = to_span(arg.position_span) { + mappings.push((position_span, operand_idx)); + } + } + } + } + } + }) + }; + let idx = self.alloc_expr( + Expr::InlineAsm(InlineAsm { operands: operands.into_boxed_slice(), options }), + syntax_ptr, + ); + self.source_map.template_map.get_or_insert_with(Default::default).1.insert(idx, mappings); + idx + } +} 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 55740a68acd6..37167fcb8155 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 @@ -16,6 +16,13 @@ use crate::{ use super::*; +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(super) enum LineFormat { + Oneline, + Newline, + Indentation, +} + pub(super) fn print_body_hir( db: &dyn DefDatabase, body: &Body, @@ -52,7 +59,14 @@ pub(super) fn print_body_hir( } }; - let mut p = Printer { db, body, buf: header, indent_level: 0, needs_indent: false, edition }; + let mut p = Printer { + db, + body, + buf: header, + indent_level: 0, + line_format: LineFormat::Newline, + edition, + }; if let DefWithBodyId::FunctionId(it) = owner { p.buf.push('('); let function_data = &db.function_data(it); @@ -95,12 +109,38 @@ pub(super) fn print_expr_hir( expr: ExprId, edition: Edition, ) -> String { - let mut p = - Printer { db, body, buf: String::new(), indent_level: 0, needs_indent: false, edition }; + let mut p = Printer { + db, + body, + buf: String::new(), + indent_level: 0, + line_format: LineFormat::Newline, + edition, + }; p.print_expr(expr); p.buf } +pub(super) fn print_pat_hir( + db: &dyn DefDatabase, + body: &Body, + _owner: DefWithBodyId, + pat: PatId, + oneline: bool, + edition: Edition, +) -> String { + let mut p = Printer { + db, + body, + buf: String::new(), + indent_level: 0, + line_format: if oneline { LineFormat::Oneline } else { LineFormat::Newline }, + edition, + }; + p.print_pat(pat); + p.buf +} + macro_rules! w { ($dst:expr, $($arg:tt)*) => { { let _ = write!($dst, $($arg)*); } @@ -109,10 +149,10 @@ macro_rules! w { macro_rules! wln { ($dst:expr) => { - { let _ = writeln!($dst); } + { $dst.newline(); } }; ($dst:expr, $($arg:tt)*) => { - { let _ = writeln!($dst, $($arg)*); } + { let _ = w!($dst, $($arg)*); $dst.newline(); } }; } @@ -121,24 +161,30 @@ struct Printer<'a> { body: &'a Body, buf: String, indent_level: usize, - needs_indent: bool, + line_format: LineFormat, edition: Edition, } impl Write for Printer<'_> { fn write_str(&mut self, s: &str) -> fmt::Result { for line in s.split_inclusive('\n') { - if self.needs_indent { + if matches!(self.line_format, LineFormat::Indentation) { match self.buf.chars().rev().find(|ch| *ch != ' ') { Some('\n') | None => {} _ => self.buf.push('\n'), } self.buf.push_str(&" ".repeat(self.indent_level)); - self.needs_indent = false; } self.buf.push_str(line); - self.needs_indent = line.ends_with('\n'); + + if matches!(self.line_format, LineFormat::Newline | LineFormat::Indentation) { + self.line_format = if line.ends_with('\n') { + LineFormat::Indentation + } else { + LineFormat::Newline + }; + } } Ok(()) @@ -161,14 +207,28 @@ impl Printer<'_> { } } + // Add a newline if the current line is not empty. + // If the current line is empty, add a space instead. + // + // Do not use [`writeln!()`] or [`wln!()`] here, which will result in + // infinite recursive calls to this function. fn newline(&mut self) { - match self.buf.chars().rev().find_position(|ch| *ch != ' ') { - Some((_, '\n')) | None => {} - Some((idx, _)) => { - if idx != 0 { - self.buf.drain(self.buf.len() - idx..); + if matches!(self.line_format, LineFormat::Oneline) { + match self.buf.chars().last() { + Some(' ') | None => {} + Some(_) => { + w!(self, " "); + } + } + } else { + match self.buf.chars().rev().find_position(|ch| *ch != ' ') { + Some((_, '\n')) | None => {} + Some((idx, _)) => { + if idx != 0 { + self.buf.drain(self.buf.len() - idx..); + } + w!(self, "\n"); } - writeln!(self).unwrap() } } } @@ -539,12 +599,14 @@ impl Printer<'_> { w!(self, ")"); } Pat::Or(pats) => { + w!(self, "("); for (i, pat) in pats.iter().enumerate() { if i != 0 { w!(self, " | "); } self.print_pat(*pat); } + w!(self, ")"); } Pat::Record { path, args, ellipsis } => { match path { @@ -554,12 +616,37 @@ impl Printer<'_> { w!(self, " {{"); let edition = self.edition; + let oneline = matches!(self.line_format, LineFormat::Oneline); self.indented(|p| { - for arg in args.iter() { - w!(p, "{}: ", arg.name.display(self.db.upcast(), edition)); - p.print_pat(arg.pat); - wln!(p, ","); + for (idx, arg) in args.iter().enumerate() { + let field_name = arg.name.display(self.db.upcast(), edition).to_string(); + + let mut same_name = false; + if let Pat::Bind { id, subpat: None } = &self.body[arg.pat] { + if let Binding { name, mode: BindingAnnotation::Unannotated, .. } = + &self.body.bindings[*id] + { + if name.as_str() == field_name { + same_name = true; + } + } + } + + w!(p, "{}", field_name); + + if !same_name { + w!(p, ": "); + p.print_pat(arg.pat); + } + + // Do not print the extra comma if the line format is oneline + if oneline && idx == args.len() - 1 { + w!(p, " "); + } else { + wln!(p, ","); + } } + if *ellipsis { wln!(p, ".."); } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/tests.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/tests.rs index 38fc01d8fca5..dd3e79c874d8 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body/tests.rs @@ -142,6 +142,41 @@ mod m { ); } +#[test] +fn desugar_for_loop() { + let (db, body, def) = lower( + r#" +//- minicore: iterator +fn main() { + for ident in 0..10 { + foo(); + bar() + } +} +"#, + ); + + expect![[r#" + fn main() -> () { + match builtin#lang(into_iter)( + (0) ..(10) , + ) { + mut 11 => loop { + match builtin#lang(next)( + &mut 11, + ) { + builtin#lang(None) => break, + builtin#lang(Some)(ident) => { + foo(); + bar() + }, + } + }, + } + }"#]] + .assert_eq(&body.pretty_print(&db, def, Edition::CURRENT)) +} + #[test] fn desugar_builtin_format_args() { let (db, body, def) = lower( 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 a70710e565c2..ba54451e594f 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 @@ -14,6 +14,7 @@ use triomphe::Arc; use crate::{ builtin_type::{BuiltinInt, BuiltinUint}, db::DefDatabase, + hir::Expr, item_tree::{ AttrOwner, Field, FieldParent, FieldsShape, ItemTree, ModItem, RawVisibilityId, TreeId, }, @@ -317,6 +318,27 @@ impl EnumData { _ => IntegerType::Pointer(true), } } + + // [Adopted from rustc](https://github.com/rust-lang/rust/blob/bd53aa3bf7a24a70d763182303bd75e5fc51a9af/compiler/rustc_middle/src/ty/adt.rs#L446-L448) + pub fn is_payload_free(&self, db: &dyn DefDatabase) -> bool { + self.variants.iter().all(|(v, _)| { + // The condition check order is slightly modified from rustc + // to improve performance by early returning with relatively fast checks + let variant = &db.enum_variant_data(*v).variant_data; + if !variant.fields().is_empty() { + return false; + } + // The outer if condition is whether this variant has const ctor or not + if !matches!(variant.kind(), StructKind::Unit) { + let body = db.body((*v).into()); + // A variant with explicit discriminant + if body.exprs[body.body_expr] != Expr::Missing { + return false; + } + } + true + }) + } } impl EnumVariantData { 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 86fd092603ff..d9358a28822e 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs @@ -307,7 +307,120 @@ pub struct OffsetOf { #[derive(Debug, Clone, PartialEq, Eq)] pub struct InlineAsm { - pub e: ExprId, + pub operands: Box<[(Option, AsmOperand)]>, + pub options: AsmOptions, +} + +#[derive(Clone, Copy, PartialEq, Eq, Hash)] +pub struct AsmOptions(u16); +bitflags::bitflags! { + impl AsmOptions: u16 { + const PURE = 1 << 0; + const NOMEM = 1 << 1; + const READONLY = 1 << 2; + const PRESERVES_FLAGS = 1 << 3; + const NORETURN = 1 << 4; + const NOSTACK = 1 << 5; + const ATT_SYNTAX = 1 << 6; + const RAW = 1 << 7; + const MAY_UNWIND = 1 << 8; + } +} + +impl AsmOptions { + pub const COUNT: usize = Self::all().bits().count_ones() as usize; + + pub const GLOBAL_OPTIONS: Self = Self::ATT_SYNTAX.union(Self::RAW); + pub const NAKED_OPTIONS: Self = Self::ATT_SYNTAX.union(Self::RAW).union(Self::NORETURN); + + pub fn human_readable_names(&self) -> Vec<&'static str> { + let mut options = vec![]; + + if self.contains(AsmOptions::PURE) { + options.push("pure"); + } + if self.contains(AsmOptions::NOMEM) { + options.push("nomem"); + } + if self.contains(AsmOptions::READONLY) { + options.push("readonly"); + } + if self.contains(AsmOptions::PRESERVES_FLAGS) { + options.push("preserves_flags"); + } + if self.contains(AsmOptions::NORETURN) { + options.push("noreturn"); + } + if self.contains(AsmOptions::NOSTACK) { + options.push("nostack"); + } + if self.contains(AsmOptions::ATT_SYNTAX) { + options.push("att_syntax"); + } + if self.contains(AsmOptions::RAW) { + options.push("raw"); + } + if self.contains(AsmOptions::MAY_UNWIND) { + options.push("may_unwind"); + } + + options + } +} + +impl std::fmt::Debug for AsmOptions { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + bitflags::parser::to_writer(self, f) + } +} + +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub enum AsmOperand { + In { + reg: InlineAsmRegOrRegClass, + expr: ExprId, + }, + Out { + reg: InlineAsmRegOrRegClass, + expr: Option, + late: bool, + }, + InOut { + reg: InlineAsmRegOrRegClass, + expr: ExprId, + late: bool, + }, + SplitInOut { + reg: InlineAsmRegOrRegClass, + in_expr: ExprId, + out_expr: Option, + late: bool, + }, + Label(ExprId), + Const(ExprId), + Sym(Path), +} + +impl AsmOperand { + pub fn reg(&self) -> Option<&InlineAsmRegOrRegClass> { + match self { + Self::In { reg, .. } + | Self::Out { reg, .. } + | Self::InOut { reg, .. } + | Self::SplitInOut { reg, .. } => Some(reg), + Self::Const { .. } | Self::Sym { .. } | Self::Label { .. } => None, + } + } + + pub fn is_clobber(&self) -> bool { + matches!(self, AsmOperand::Out { reg: InlineAsmRegOrRegClass::Reg(_), late: _, expr: None }) + } +} + +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub enum InlineAsmRegOrRegClass { + Reg(Symbol), + RegClass(Symbol), } #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -372,7 +485,21 @@ impl Expr { match self { Expr::Missing => {} Expr::Path(_) | Expr::OffsetOf(_) => {} - Expr::InlineAsm(it) => f(it.e), + Expr::InlineAsm(it) => it.operands.iter().for_each(|(_, op)| match op { + AsmOperand::In { expr, .. } + | AsmOperand::Out { expr: Some(expr), .. } + | AsmOperand::InOut { expr, .. } => f(*expr), + AsmOperand::SplitInOut { in_expr, out_expr, .. } => { + f(*in_expr); + if let Some(out_expr) = out_expr { + f(*out_expr); + } + } + AsmOperand::Out { expr: None, .. } + | AsmOperand::Const(_) + | AsmOperand::Label(_) + | AsmOperand::Sym(_) => (), + }), Expr::If { condition, then_branch, else_branch } => { f(*condition); f(*then_branch); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs index b6dbba12cd6d..a3b48831a0b0 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs @@ -50,11 +50,7 @@ fn main() { let i: u64 = 3; let o: u64; unsafe { - builtin #asm ( { - $crate::format_args!("mov {0}, {1}"); - $crate::format_args!("add {0}, 5"); - } - ); + builtin #asm ("mov {0}, {1}", "add {0}, 5", out (reg)o, in (reg)i, ); } } "##]], @@ -532,3 +528,21 @@ fn main() { foobar; } "##]], ); } + +#[test] +fn test_quote_string() { + check( + r##" +#[rustc_builtin_macro] +macro_rules! stringify {} + +fn main() { stringify!("hello"); } +"##, + expect![[r##" +#[rustc_builtin_macro] +macro_rules! stringify {} + +fn main() { "\"hello\""; } +"##]], + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs index fc1460870cea..85fb90fdfb69 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs @@ -389,7 +389,7 @@ m! { foo# bar } m! { Foo,# Bar } "#, - expect![[r##" + expect![[r#" macro_rules! m { ($($i:ident),*) => ($(mod $i {} )*); ($($i:ident)#*) => ($(fn $i() {} )*); @@ -404,27 +404,29 @@ fn bar() {} struct Foo; struct Bar; -"##]], +"#]], ); } #[test] fn test_match_group_pattern_with_multiple_defs() { + // FIXME: The pretty printer breaks by leaving whitespace here, +syntaxctxt is used to avoid that check( r#" macro_rules! m { ($($i:ident),*) => ( impl Bar { $(fn $i() {})* } ); } +// +syntaxctxt m! { foo, bar } "#, expect![[r#" macro_rules! m { ($($i:ident),*) => ( impl Bar { $(fn $i() {})* } ); } -impl Bar { - fn foo() {} - fn bar() {} -} +impl#\1# Bar#\1# {#\1# + fn#\1# foo#\0#(#\1#)#\1# {#\1#}#\1# + fn#\1# bar#\0#(#\1#)#\1# {#\1#}#\1# +}#\1# "#]], ); } @@ -480,12 +482,12 @@ macro_rules! m { } m!{#abc} "#, - expect![[r##" + expect![[r#" macro_rules! m { ($($i:ident)* #abc) => ( fn baz() { $($i ();)* } ); } fn baz() {} -"##]], +"#]], ) } @@ -1189,13 +1191,13 @@ macro_rules! m { m! { cfg(target_os = "windows") } m! { hello::world } "#, - expect![[r##" + expect![[r#" macro_rules! m { ($m:meta) => ( #[$m] fn bar() {} ) } #[cfg(target_os = "windows")] fn bar() {} #[hello::world] fn bar() {} -"##]], +"#]], ); } @@ -1213,7 +1215,7 @@ m! { */ } "#, - expect![[r##" + expect![[r#" macro_rules! m { ($(#[$m:meta])+) => ( $(#[$m])+ fn bar() {} ) } @@ -1221,7 +1223,7 @@ macro_rules! m { #[doc = r" MultiLines Doc "] fn bar() {} -"##]], +"#]], ); } @@ -1234,12 +1236,12 @@ macro_rules! m { } m! { #[doc = concat!("The `", "bla", "` lang item.")] } "#, - expect![[r##" + expect![[r#" macro_rules! m { (#[$m:meta]) => ( #[$m] fn bar() {} ) } #[doc = concat!("The `", "bla", "` lang item.")] fn bar() {} -"##]], +"#]], ); } @@ -1257,7 +1259,7 @@ m! { */ } "#, - expect![[r##" + expect![[r#" macro_rules! m { ($(#[$ m:meta])+) => ( $(#[$m])+ fn bar() {} ) } @@ -1265,7 +1267,7 @@ macro_rules! m { #[doc = r" 莊生曉夢迷蝴蝶,望帝春心託杜鵑。 "] fn bar() {} -"##]], +"#]], ); } @@ -1342,10 +1344,10 @@ fn test_tt_composite2() { macro_rules! m { ($($tt:tt)*) => { abs!(=> $($tt)*); } } m! {#} "#, - expect![[r##" + expect![[r#" macro_rules! m { ($($tt:tt)*) => { abs!(=> $($tt)*); } } abs!( = > #); -"##]], +"#]], ); } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/metavar_expr.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/metavar_expr.rs index bf7011983876..1430e2a9a994 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/metavar_expr.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/metavar_expr.rs @@ -311,3 +311,150 @@ fn test() { "#]], ); } + +#[test] +fn concat() { + // FIXME: Should this error? rustc currently accepts it. + check( + r#" +macro_rules! m { + ( $a:ident, $b:literal ) => { + let ${concat($a, _, "123", _foo, $b, _, 123)}; + }; +} + +fn test() { + m!( abc, 456 ); + m!( def, "hello" ); +} +"#, + expect![[r#" +macro_rules! m { + ( $a:ident, $b:literal ) => { + let ${concat($a, _, "123", _foo, $b, _, 123)}; + }; +} + +fn test() { + let abc_123_foo456_123;; + let def_123_foohello_123;; +} +"#]], + ); +} + +#[test] +fn concat_less_than_two_elements() { + // FIXME: Should this error? rustc currently accepts it. + check( + r#" +macro_rules! m { + () => { + let ${concat(abc)}; + }; +} + +fn test() { + m!() +} +"#, + expect![[r#" +macro_rules! m { + () => { + let ${concat(abc)}; + }; +} + +fn test() { + /* error: macro definition has parse errors */ +} +"#]], + ); +} + +#[test] +fn concat_invalid_ident() { + // FIXME: Should this error? rustc currently accepts it. + check( + r#" +macro_rules! m { + () => { + let ${concat(abc, '"')}; + }; +} + +fn test() { + m!() +} +"#, + expect![[r#" +macro_rules! m { + () => { + let ${concat(abc, '"')}; + }; +} + +fn test() { + /* error: `${concat(..)}` is not generating a valid identifier */let __ra_concat_dummy; +} +"#]], + ); +} + +#[test] +fn concat_invalid_fragment() { + // FIXME: Should this error? rustc currently accepts it. + check( + r#" +macro_rules! m { + ( $e:expr ) => { + let ${concat(abc, $e)}; + }; +} + +fn test() { + m!(()) +} +"#, + expect![[r#" +macro_rules! m { + ( $e:expr ) => { + let ${concat(abc, $e)}; + }; +} + +fn test() { + /* error: metavariables of `${concat(..)}` must be of type `ident`, `literal` or `tt` */let abc; +} +"#]], + ); +} + +#[test] +fn concat_repetition() { + // FIXME: Should this error? rustc currently accepts it. + check( + r#" +macro_rules! m { + ( $($i:ident)* ) => { + let ${concat(abc, $i)}; + }; +} + +fn test() { + m!(a b c) +} +"#, + expect![[r#" +macro_rules! m { + ( $($i:ident)* ) => { + let ${concat(abc, $i)}; + }; +} + +fn test() { + /* error: expected simple binding, found nested binding `i` */let abc; +} +"#]], + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs index 485f72e92ce1..1bbed01443de 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs @@ -139,7 +139,7 @@ STRUCT!{struct D3DVSHADERCAPS2_0 {Caps: u8,}} STRUCT!{#[cfg_attr(target_arch = "x86", repr(packed))] struct D3DCONTENTPROTECTIONCAPS {Caps : u8 ,}} "#, - expect![[r##" + expect![[r#" macro_rules! STRUCT { ($(#[$attrs:meta])* struct $name:ident { $($field:ident: $ftype:ty,)+ @@ -194,7 +194,7 @@ impl Clone for D3DCONTENTPROTECTIONCAPS { } } } -"##]], +"#]], ); } @@ -214,7 +214,7 @@ macro_rules! int_base { } int_base!{Binary for isize as usize -> Binary} "#, - expect![[r##" + expect![[r#" macro_rules! int_base { ($Trait:ident for $T:ident as $U:ident -> $Radix:ident) => { #[stable(feature = "rust1", since = "1.0.0")] @@ -230,7 +230,7 @@ macro_rules! int_base { Binary.fmt_int(*self as usize, f) } } -"##]], +"#]], ); } @@ -318,7 +318,7 @@ impl_fn_for_zst ! { } "#, - expect![[r##" + expect![[r#" macro_rules! impl_fn_for_zst { {$( $( #[$attr: meta] )* struct $Name: ident impl$( <$( $lifetime : lifetime ),+> )? Fn = @@ -410,7 +410,7 @@ impl FnOnce<(char, )> for CharEscapeDefault { } } -"##]], +"#]], ); } @@ -511,7 +511,7 @@ cfg_if! { @__apply cfg(all(not(any(not(any(target_os = "solaris", target_os = "illumos")))))), } "#, - expect![[r##" + expect![[r#" macro_rules! cfg_if { ($(if #[cfg($($meta:meta),*)] { $($it:item)* } )else* else { $($it2:item)* }) => { @@ -534,7 +534,7 @@ __cfg_if_items! { } -"##]], +"#]], ); } @@ -618,7 +618,7 @@ RIDL!{interface ID3D11Asynchronous(ID3D11AsynchronousVtbl): ID3D11DeviceChild(ID fn GetDataSize(&mut self) -> UINT }} "#, - expect![[r##" + expect![[r#" #[macro_export] macro_rules! RIDL { (interface $interface:ident ($vtbl:ident) : $pinterface:ident ($pvtbl:ident) @@ -639,7 +639,7 @@ impl ID3D11Asynchronous { ((*self .lpVtbl).GetDataSize)(self ) } } -"##]], +"#]], ); } @@ -676,7 +676,7 @@ quick_error ! ( ); "#, - expect![[r##" + expect![[r#" macro_rules! quick_error { (SORT [enum $name:ident $( #[$meta:meta] )*] items [$($( #[$imeta:meta] )* @@ -697,7 +697,7 @@ macro_rules! quick_error { } quick_error!(ENUMINITION[enum Wrapped#[derive(Debug)]]body[]queue[ = > One: UNIT[] = > Two: TUPLE[s: String]]); -"##]], +"#]], ) } @@ -746,7 +746,7 @@ delegate_impl ! { [G, &'a mut G, deref] pub trait Data: GraphBase {@section type type NodeWeight;} } "#, - expect![[r##" + expect![[r#" macro_rules! delegate_impl { ([$self_type:ident, $self_wrap:ty, $self_map:ident] pub trait $name:ident $(: $sup:ident)* $(+ $more_sup:ident)* { @@ -785,7 +785,7 @@ macro_rules! delegate_impl { } } impl <> Data for &'amut G where G: Data {} -"##]], +"#]], ); } @@ -959,14 +959,14 @@ macro_rules! with_std { with_std! {mod m;mod f;} "#, - expect![[r##" + expect![[r#" macro_rules! with_std { ($($i:item)*) => ($(#[cfg(feature = "std")]$i)*) } #[cfg(feature = "std")] mod m; #[cfg(feature = "std")] mod f; -"##]], +"#]], ) } @@ -1144,3 +1144,27 @@ mod any { "#]], ); } + +#[test] +fn regression_18148() { + check( + r#" +macro_rules! m { + ( $e:expr ) => {}; +} + +fn foo() { + m!(r#const); +} +"#, + expect![[r#" +macro_rules! m { + ( $e:expr ) => {}; +} + +fn foo() { + ; +} +"#]], + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs index 7f761192517c..450a15bd66e5 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs @@ -1,6 +1,6 @@ -//! This module contains tests for macro expansion. Effectively, it covers `tt`, -//! `mbe`, `proc_macro_api` and `hir_expand` crates. This might seem like a -//! wrong architecture at the first glance, but is intentional. +//! This module contains integration tests for macro expansion with name resolution. Effectively, it +//! covers `tt`, `mbe`, `proc_macro_api` and `hir_expand` crates. This might seem like a wrong +//! architecture at the first glance, but is intentional. //! //! Physically, macro expansion process is intertwined with name resolution. You //! can not expand *just* the syntax. So, to be able to write integration tests @@ -320,6 +320,7 @@ impl ProcMacroExpander for IdentityWhenValidProcMacroExpander { _: Span, _: Span, _: Span, + _: Option, ) -> Result { let (parse, _) = syntax_bridge::token_tree_to_syntax_node( subtree, diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/proc_macros.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/proc_macros.rs index 6f605c0cb33d..c0178adc9a63 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/proc_macros.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/proc_macros.rs @@ -16,12 +16,12 @@ fn attribute_macro_attr_censoring() { #[attr1] #[proc_macros::identity] #[attr2] struct S; "#, - expect![[r##" + expect![[r#" #[attr1] #[proc_macros::identity] #[attr2] struct S; #[attr1] -#[attr2] struct S;"##]], +#[attr2] struct S;"#]], ); } @@ -39,7 +39,7 @@ fn derive_censoring() { #[attr2] struct S; "#, - expect![[r##" + expect![[r#" #[attr1] #[derive(Foo)] #[derive(proc_macros::DeriveIdentity)] @@ -49,7 +49,7 @@ struct S; #[attr1] #[derive(Bar)] -#[attr2] struct S;"##]], +#[attr2] struct S;"#]], ); } @@ -62,14 +62,14 @@ fn attribute_macro_syntax_completion_1() { #[proc_macros::identity_when_valid] fn foo() { bar.baz(); blub } "#, - expect![[r##" + expect![[r#" #[proc_macros::identity_when_valid] fn foo() { bar.baz(); blub } fn foo() { bar.baz(); blub -}"##]], +}"#]], ); } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs index 11601c683e1e..9bd7d38f0a64 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs @@ -69,7 +69,7 @@ use la_arena::Arena; use rustc_hash::{FxHashMap, FxHashSet}; use span::{Edition, EditionedFileId, FileAstId, FileId, ROOT_ERASED_FILE_AST_ID}; use stdx::format_to; -use syntax::{ast, SmolStr}; +use syntax::{ast, AstNode, SmolStr, SyntaxNode}; use triomphe::Arc; use tt::TextRange; @@ -291,7 +291,7 @@ impl ModuleOrigin { /// Returns a node which defines this module. /// That is, a file or a `mod foo {}` with items. - fn definition_source(&self, db: &dyn DefDatabase) -> InFile { + pub fn definition_source(&self, db: &dyn DefDatabase) -> InFile { match self { &ModuleOrigin::File { definition, .. } | &ModuleOrigin::CrateRoot { definition } => { let sf = db.parse(definition).tree(); @@ -728,6 +728,16 @@ pub enum ModuleSource { BlockExpr(ast::BlockExpr), } +impl ModuleSource { + pub fn node(&self) -> SyntaxNode { + match self { + ModuleSource::SourceFile(it) => it.syntax().clone(), + ModuleSource::Module(it) => it.syntax().clone(), + ModuleSource::BlockExpr(it) => it.syntax().clone(), + } + } +} + /// See `sub_namespace_match()`. #[derive(Clone, Copy, PartialEq, Eq)] pub enum MacroSubNs { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs index 96db3db8f0d8..e09ef4f205d3 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs @@ -221,7 +221,7 @@ struct DefCollector<'a> { deps: FxHashMap, glob_imports: FxHashMap>, unresolved_imports: Vec, - indeterminate_imports: Vec, + indeterminate_imports: Vec<(ImportDirective, PerNs)>, unresolved_macros: Vec, mod_dirs: FxHashMap, cfg_options: &'a CfgOptions, @@ -415,16 +415,6 @@ impl DefCollector<'_> { self.resolution_loop(); - // Resolve all indeterminate resolved imports again - // As some of the macros will expand newly import shadowing partial resolved imports - // FIXME: We maybe could skip this, if we handle the indeterminate imports in `resolve_imports` - // correctly - let partial_resolved = self.indeterminate_imports.drain(..).map(|directive| { - ImportDirective { status: PartialResolvedImport::Unresolved, ..directive } - }); - self.unresolved_imports.extend(partial_resolved); - self.resolve_imports(); - let unresolved_imports = mem::take(&mut self.unresolved_imports); // show unresolved imports in completion, etc for directive in &unresolved_imports { @@ -749,9 +739,9 @@ impl DefCollector<'_> { .filter_map(|mut directive| { directive.status = self.resolve_import(directive.module_id, &directive.import); match directive.status { - PartialResolvedImport::Indeterminate(_) => { + PartialResolvedImport::Indeterminate(resolved) => { self.record_resolved_import(&directive); - self.indeterminate_imports.push(directive); + self.indeterminate_imports.push((directive, resolved)); res = ReachedFixedPoint::No; None } @@ -764,6 +754,33 @@ impl DefCollector<'_> { } }) .collect(); + + // Resolve all indeterminate resolved imports again + // As some of the macros will expand newly import shadowing partial resolved imports + // FIXME: We maybe could skip this, if we handle the indeterminate imports in `resolve_imports` + // correctly + let mut indeterminate_imports = std::mem::take(&mut self.indeterminate_imports); + indeterminate_imports.retain_mut(|(directive, partially_resolved)| { + let partially_resolved = partially_resolved.availability(); + directive.status = self.resolve_import(directive.module_id, &directive.import); + match directive.status { + PartialResolvedImport::Indeterminate(import) + if partially_resolved != import.availability() => + { + self.record_resolved_import(directive); + res = ReachedFixedPoint::No; + false + } + PartialResolvedImport::Resolved(_) => { + self.record_resolved_import(directive); + res = ReachedFixedPoint::No; + false + } + _ => true, + } + }); + self.indeterminate_imports = indeterminate_imports; + res } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/per_ns.rs b/src/tools/rust-analyzer/crates/hir-def/src/per_ns.rs index 19485c476f72..3f3b98c6b5b5 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/per_ns.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/per_ns.rs @@ -3,6 +3,8 @@ //! //! `PerNs` (per namespace) captures this. +use bitflags::bitflags; + use crate::{ item_scope::{ImportId, ImportOrExternCrate, ItemInNs}, visibility::Visibility, @@ -16,6 +18,16 @@ pub enum Namespace { Macros, } +bitflags! { + /// Describes only the presence/absence of each namespace, without its value. + #[derive(Debug, PartialEq, Eq)] + pub(crate) struct NsAvailability : u32 { + const TYPES = 1 << 0; + const VALUES = 1 << 1; + const MACROS = 1 << 2; + } +} + #[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] pub struct PerNs { pub types: Option<(ModuleDefId, Visibility, Option)>, @@ -24,6 +36,14 @@ pub struct PerNs { } impl PerNs { + pub(crate) fn availability(&self) -> NsAvailability { + let mut result = NsAvailability::empty(); + result.set(NsAvailability::TYPES, self.types.is_some()); + result.set(NsAvailability::VALUES, self.values.is_some()); + result.set(NsAvailability::MACROS, self.macros.is_some()); + result + } + pub fn none() -> PerNs { PerNs { types: None, values: None, macros: None } } diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin.rs index 252430e4e954..7b9b7f36e2cd 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin.rs @@ -1,6 +1,6 @@ //! Builtin macros and attributes #[macro_use] -mod quote; +pub mod quote; mod attr_macro; mod derive_macro; diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs index 795d9b14df96..d04225b87227 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs @@ -119,9 +119,8 @@ register_builtin! { (module_path, ModulePath) => module_path_expand, (assert, Assert) => assert_expand, (stringify, Stringify) => stringify_expand, - (llvm_asm, LlvmAsm) => asm_expand, (asm, Asm) => asm_expand, - (global_asm, GlobalAsm) => global_asm_expand, + (global_asm, GlobalAsm) => asm_expand, (cfg, Cfg) => cfg_expand, (core_panic, CorePanic) => panic_expand, (std_panic, StdPanic) => panic_expand, @@ -324,40 +323,15 @@ fn asm_expand( tt: &tt::Subtree, span: Span, ) -> ExpandResult { - // We expand all assembly snippets to `format_args!` invocations to get format syntax - // highlighting for them. - let mut literals = Vec::new(); - for tt in tt.token_trees.chunks(2) { - match tt { - [tt::TokenTree::Leaf(tt::Leaf::Literal(lit))] - | [tt::TokenTree::Leaf(tt::Leaf::Literal(lit)), tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { char: ',', span: _, spacing: _ }))] => - { - let dollar_krate = dollar_crate(span); - literals.push(quote!(span=>#dollar_krate::format_args!(#lit);)); - } - _ => break, - } - } - + let mut tt = tt.clone(); + tt.delimiter.kind = tt::DelimiterKind::Parenthesis; let pound = mk_pound(span); let expanded = quote! {span => - builtin #pound asm ( - {##literals} - ) + builtin #pound asm #tt }; ExpandResult::ok(expanded) } -fn global_asm_expand( - _db: &dyn ExpandDatabase, - _id: MacroCallId, - _tt: &tt::Subtree, - span: Span, -) -> ExpandResult { - // Expand to nothing (at item-level) - ExpandResult::ok(quote! {span =>}) -} - fn cfg_expand( db: &dyn ExpandDatabase, id: MacroCallId, @@ -509,7 +483,7 @@ fn concat_expand( match it.kind { tt::LitKind::Char => { if let Ok(c) = unescape_char(it.symbol.as_str()) { - text.extend(c.escape_default()); + text.push(c); } record_span(it.span); } @@ -517,11 +491,11 @@ fn concat_expand( format_to!(text, "{}", it.symbol.as_str()) } tt::LitKind::Str => { - text.push_str(it.symbol.as_str()); + text.push_str(unescape_str(&it.symbol).as_str()); record_span(it.span); } tt::LitKind::StrRaw(_) => { - format_to!(text, "{}", it.symbol.as_str().escape_debug()); + format_to!(text, "{}", it.symbol.as_str()); record_span(it.span); } tt::LitKind::Byte @@ -839,7 +813,7 @@ fn include_str_expand( fn get_env_inner(db: &dyn ExpandDatabase, arg_id: MacroCallId, key: &Symbol) -> Option { let krate = db.lookup_intern_macro_call(arg_id).krate; - db.crate_graph()[krate].env.get(key.as_str()).map(|it| it.escape_debug().to_string()) + db.crate_graph()[krate].env.get(key.as_str()) } fn env_expand( diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/quote.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/quote.rs index 5c33f817f9e4..418d8d9660b5 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/quote.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/quote.rs @@ -3,6 +3,7 @@ use intern::{sym, Symbol}; use span::Span; +use syntax::ToSmolStr; use tt::IdentIsRaw; use crate::name::Name; @@ -17,6 +18,7 @@ pub(crate) fn dollar_crate(span: Span) -> tt::Ident { // 2. #()* pattern repetition not supported now // * But we can do it manually, see `test_quote_derive_copy_hack` #[doc(hidden)] +#[macro_export] macro_rules! quote_impl__ { ($span:ident) => { Vec::<$crate::tt::TokenTree>::new() @@ -26,8 +28,8 @@ macro_rules! quote_impl__ { { let children = $crate::builtin::quote::__quote!($span $($tt)*); $crate::tt::Subtree { - delimiter: crate::tt::Delimiter { - kind: crate::tt::DelimiterKind::$delim, + delimiter: $crate::tt::Delimiter { + kind: $crate::tt::DelimiterKind::$delim, open: $span, close: $span, }, @@ -39,9 +41,9 @@ macro_rules! quote_impl__ { ( @PUNCT($span:ident) $first:literal ) => { { vec![ - crate::tt::Leaf::Punct(crate::tt::Punct { + $crate::tt::Leaf::Punct($crate::tt::Punct { char: $first, - spacing: crate::tt::Spacing::Alone, + spacing: $crate::tt::Spacing::Alone, span: $span, }).into() ] @@ -51,14 +53,14 @@ macro_rules! quote_impl__ { ( @PUNCT($span:ident) $first:literal, $sec:literal ) => { { vec![ - crate::tt::Leaf::Punct(crate::tt::Punct { + $crate::tt::Leaf::Punct($crate::tt::Punct { char: $first, - spacing: crate::tt::Spacing::Joint, + spacing: $crate::tt::Spacing::Joint, span: $span, }).into(), - crate::tt::Leaf::Punct(crate::tt::Punct { + $crate::tt::Leaf::Punct($crate::tt::Punct { char: $sec, - spacing: crate::tt::Spacing::Alone, + spacing: $crate::tt::Spacing::Alone, span: $span, }).into() ] @@ -97,7 +99,7 @@ macro_rules! quote_impl__ { // Ident ($span:ident $tt:ident ) => { vec![ { - crate::tt::Leaf::Ident(crate::tt::Ident { + $crate::tt::Leaf::Ident($crate::tt::Ident { sym: intern::Symbol::intern(stringify!($tt)), span: $span, is_raw: tt::IdentIsRaw::No, @@ -108,6 +110,7 @@ macro_rules! quote_impl__ { // Puncts // FIXME: Not all puncts are handled ($span:ident -> ) => {$crate::builtin::quote::__quote!(@PUNCT($span) '-', '>')}; + ($span:ident => ) => {$crate::builtin::quote::__quote!(@PUNCT($span) '=', '>')}; ($span:ident & ) => {$crate::builtin::quote::__quote!(@PUNCT($span) '&')}; ($span:ident , ) => {$crate::builtin::quote::__quote!(@PUNCT($span) ',')}; ($span:ident : ) => {$crate::builtin::quote::__quote!(@PUNCT($span) ':')}; @@ -117,6 +120,9 @@ macro_rules! quote_impl__ { ($span:ident < ) => {$crate::builtin::quote::__quote!(@PUNCT($span) '<')}; ($span:ident > ) => {$crate::builtin::quote::__quote!(@PUNCT($span) '>')}; ($span:ident ! ) => {$crate::builtin::quote::__quote!(@PUNCT($span) '!')}; + ($span:ident # ) => {$crate::builtin::quote::__quote!(@PUNCT($span) '#')}; + ($span:ident $ ) => {$crate::builtin::quote::__quote!(@PUNCT($span) '$')}; + ($span:ident * ) => {$crate::builtin::quote::__quote!(@PUNCT($span) '*')}; ($span:ident $first:tt $($tail:tt)+ ) => { { @@ -128,18 +134,19 @@ macro_rules! quote_impl__ { } }; } -pub(super) use quote_impl__ as __quote; +pub use quote_impl__ as __quote; /// FIXME: /// It probably should implement in proc-macro -macro_rules! quote_impl { +#[macro_export] +macro_rules! quote { ($span:ident=> $($tt:tt)* ) => { $crate::builtin::quote::IntoTt::to_subtree($crate::builtin::quote::__quote!($span $($tt)*), $span) } } -pub(super) use quote_impl as quote; +pub(super) use quote; -pub(crate) trait IntoTt { +pub trait IntoTt { fn to_subtree(self, span: Span) -> crate::tt::Subtree; fn to_tokens(self) -> Vec; } @@ -167,7 +174,7 @@ impl IntoTt for crate::tt::Subtree { } } -pub(crate) trait ToTokenTree { +pub trait ToTokenTree { fn to_token(self, span: Span) -> crate::tt::TokenTree; } @@ -211,8 +218,8 @@ impl_to_to_tokentrees! { _span: crate::tt::Literal => self { self }; _span: crate::tt::Ident => self { self }; _span: crate::tt::Punct => self { self }; - span: &str => self { crate::tt::Literal{symbol: Symbol::intern(self), span, kind: tt::LitKind::Str, suffix: None }}; - span: String => self { crate::tt::Literal{symbol: Symbol::intern(&self), span, kind: tt::LitKind::Str, suffix: None }}; + span: &str => self { crate::tt::Literal{symbol: Symbol::intern(&self.escape_default().to_smolstr()), span, kind: tt::LitKind::Str, suffix: None }}; + span: String => self { crate::tt::Literal{symbol: Symbol::intern(&self.escape_default().to_smolstr()), span, kind: tt::LitKind::Str, suffix: None }}; span: Name => self { let (is_raw, s) = IdentIsRaw::split_from_symbol(self.as_str()); crate::tt::Ident{sym: Symbol::intern(s), span, is_raw } diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/cfg_process.rs b/src/tools/rust-analyzer/crates/hir-expand/src/cfg_process.rs index 147cf912da1e..01a3103af82d 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/cfg_process.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/cfg_process.rs @@ -6,7 +6,7 @@ use cfg::{CfgAtom, CfgExpr}; use intern::{sym, Symbol}; use rustc_hash::FxHashSet; use syntax::{ - ast::{self, Attr, HasAttrs, Meta, VariantList}, + ast::{self, Attr, HasAttrs, Meta, TokenTree, VariantList}, AstNode, NodeOrToken, SyntaxElement, SyntaxKind, SyntaxNode, T, }; use tracing::{debug, warn}; @@ -17,7 +17,7 @@ fn check_cfg(db: &dyn ExpandDatabase, attr: &Attr, krate: CrateId) -> Option Optio if !attr.simple_name().as_deref().map(|v| v == "cfg_attr")? { return None; } - let cfg_expr = parse_from_attr_meta(attr.meta()?)?; + check_cfg_attr_value(db, &attr.token_tree()?, krate) +} + +pub fn check_cfg_attr_value( + db: &dyn ExpandDatabase, + attr: &TokenTree, + krate: CrateId, +) -> Option { + let cfg_expr = parse_from_attr_token_tree(attr)?; let enabled = db.crate_graph()[krate].cfg_options.check(&cfg_expr) != Some(false); Some(enabled) } @@ -238,8 +246,7 @@ pub(crate) fn process_cfg_attrs( Some(remove) } /// Parses a `cfg` attribute from the meta -fn parse_from_attr_meta(meta: Meta) -> Option { - let tt = meta.token_tree()?; +fn parse_from_attr_token_tree(tt: &TokenTree) -> Option { let mut iter = tt .token_trees_and_tokens() .filter(is_not_whitespace) @@ -328,7 +335,7 @@ mod tests { use expect_test::{expect, Expect}; use syntax::{ast::Attr, AstNode, SourceFile}; - use crate::cfg_process::parse_from_attr_meta; + use crate::cfg_process::parse_from_attr_token_tree; fn check_dnf_from_syntax(input: &str, expect: Expect) { let parse = SourceFile::parse(input, span::Edition::CURRENT); @@ -342,7 +349,7 @@ mod tests { let node = node.clone_subtree(); assert_eq!(node.syntax().text_range().start(), 0.into()); - let cfg = parse_from_attr_meta(node.meta().unwrap()).unwrap(); + let cfg = parse_from_attr_token_tree(&node.meta().unwrap().token_tree().unwrap()).unwrap(); let actual = format!("#![cfg({})]", DnfExpr::new(&cfg)); expect.assert_eq(&actual); } diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/change.rs b/src/tools/rust-analyzer/crates/hir-expand/src/change.rs index 8b3f69db0277..de3a7b9f5615 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/change.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/change.rs @@ -1,10 +1,10 @@ //! Defines a unit of change that can applied to the database to get the next //! state. Changes are transactional. use base_db::{ - salsa::Durability, CrateGraph, CrateId, FileChange, SourceRoot, SourceRootDatabase, - TargetLayoutLoadResult, Version, + salsa::Durability, CrateGraph, CrateId, CrateWorkspaceData, FileChange, SourceRoot, + SourceRootDatabase, }; -use la_arena::RawIdx; +use rustc_hash::FxHashMap; use span::FileId; use triomphe::Arc; @@ -14,8 +14,6 @@ use crate::{db::ExpandDatabase, proc_macro::ProcMacros}; pub struct ChangeWithProcMacros { pub source_change: FileChange, pub proc_macros: Option, - pub toolchains: Option>>, - pub target_data_layouts: Option>, } impl ChangeWithProcMacros { @@ -28,46 +26,25 @@ impl ChangeWithProcMacros { if let Some(proc_macros) = self.proc_macros { db.set_proc_macros_with_durability(Arc::new(proc_macros), Durability::HIGH); } - if let Some(target_data_layouts) = self.target_data_layouts { - for (id, val) in target_data_layouts.into_iter().enumerate() { - db.set_data_layout_with_durability( - CrateId::from_raw(RawIdx::from(id as u32)), - val, - Durability::HIGH, - ); - } - } - if let Some(toolchains) = self.toolchains { - for (id, val) in toolchains.into_iter().enumerate() { - db.set_toolchain_with_durability( - CrateId::from_raw(RawIdx::from(id as u32)), - val, - Durability::HIGH, - ); - } - } } pub fn change_file(&mut self, file_id: FileId, new_text: Option) { self.source_change.change_file(file_id, new_text) } - pub fn set_crate_graph(&mut self, graph: CrateGraph) { - self.source_change.set_crate_graph(graph) + pub fn set_crate_graph( + &mut self, + graph: CrateGraph, + ws_data: FxHashMap>, + ) { + self.source_change.set_crate_graph(graph); + self.source_change.set_ws_data(ws_data); } pub fn set_proc_macros(&mut self, proc_macros: ProcMacros) { self.proc_macros = Some(proc_macros); } - pub fn set_toolchains(&mut self, toolchains: Vec>) { - self.toolchains = Some(toolchains); - } - - pub fn set_target_data_layouts(&mut self, target_data_layouts: Vec) { - self.target_data_layouts = Some(target_data_layouts); - } - pub fn set_roots(&mut self, roots: Vec) { self.source_change.set_roots(roots) } diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs b/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs index b1a6eed2fbc7..86dd4aef090f 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs @@ -1,4 +1,4 @@ -//! Compiled declarative macro expanders (`macro_rules!`` and `macro`) +//! Compiled declarative macro expanders (`macro_rules!` and `macro`) use base_db::CrateId; use intern::sym; diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs index 19c3c9c43f10..95380979492a 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs @@ -21,6 +21,7 @@ pub mod span_map; mod cfg_process; mod fixup; +mod prettify_macro_expansion_; use attrs::collect_attrs; use rustc_hash::FxHashMap; @@ -51,7 +52,11 @@ use crate::{ span_map::{ExpansionSpanMap, SpanMap}, }; -pub use crate::files::{AstId, ErasedAstId, FileRange, InFile, InMacroFile, InRealFile}; +pub use crate::{ + cfg_process::check_cfg_attr_value, + files::{AstId, ErasedAstId, FileRange, InFile, InMacroFile, InRealFile}, + prettify_macro_expansion_::prettify_macro_expansion, +}; pub use mbe::{DeclarativeMacro, ValueResult}; pub use span::{HirFileId, MacroCallId, MacroFileId}; diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/prettify_macro_expansion_.rs b/src/tools/rust-analyzer/crates/hir-expand/src/prettify_macro_expansion_.rs new file mode 100644 index 000000000000..d928cafdefc0 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-expand/src/prettify_macro_expansion_.rs @@ -0,0 +1,60 @@ +//! Pretty printing of macros output. + +use base_db::CrateId; +use rustc_hash::FxHashMap; +use syntax::NodeOrToken; +use syntax::{ast::make, SyntaxNode}; + +use crate::{db::ExpandDatabase, span_map::ExpansionSpanMap}; + +/// Inserts whitespace and replaces `$crate` in macro expansions. +#[expect(deprecated)] +pub fn prettify_macro_expansion( + db: &dyn ExpandDatabase, + syn: SyntaxNode, + span_map: &ExpansionSpanMap, + target_crate_id: CrateId, +) -> SyntaxNode { + let crate_graph = db.crate_graph(); + let target_crate = &crate_graph[target_crate_id]; + let mut syntax_ctx_id_to_dollar_crate_replacement = FxHashMap::default(); + syntax_bridge::prettify_macro_expansion::prettify_macro_expansion(syn, &mut |dollar_crate| { + let ctx = span_map.span_at(dollar_crate.text_range().start()).ctx; + let replacement = + syntax_ctx_id_to_dollar_crate_replacement.entry(ctx).or_insert_with(|| { + let ctx_data = db.lookup_intern_syntax_context(ctx); + let macro_call_id = + ctx_data.outer_expn.expect("`$crate` cannot come from `SyntaxContextId::ROOT`"); + let macro_call = db.lookup_intern_macro_call(macro_call_id); + let macro_def_crate = macro_call.def.krate; + // First, if this is the same crate as the macro, nothing will work but `crate`. + // If not, if the target trait has the macro's crate as a dependency, using the dependency name + // will work in inserted code and match the user's expectation. + // If not, the crate's display name is what the dependency name is likely to be once such dependency + // is inserted, and also understandable to the user. + // Lastly, if nothing else found, resort to leaving `$crate`. + if target_crate_id == macro_def_crate { + make::tokens::crate_kw() + } else if let Some(dep) = + target_crate.dependencies.iter().find(|dep| dep.crate_id == macro_def_crate) + { + make::tokens::ident(&dep.name) + } else if let Some(crate_name) = &crate_graph[macro_def_crate].display_name { + make::tokens::ident(crate_name.crate_name()) + } else { + return dollar_crate.clone(); + } + }); + if replacement.text() == "$crate" { + // The parent may have many children, and looking for the token may yield incorrect results. + return dollar_crate.clone(); + } + // We need to `clone_subtree()` but rowan doesn't provide such operation for tokens. + let parent = replacement.parent().unwrap().clone_subtree().clone_for_update(); + parent + .children_with_tokens() + .filter_map(NodeOrToken::into_token) + .find(|it| it.kind() == replacement.kind()) + .unwrap() + }) +} diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/proc_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/proc_macro.rs index 26bb3a3edda4..fe09f0307c9e 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/proc_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/proc_macro.rs @@ -29,6 +29,7 @@ pub trait ProcMacroExpander: fmt::Debug + Send + Sync + RefUnwindSafe { def_site: Span, call_site: Span, mixed_site: Span, + current_dir: Option, ) -> Result; } @@ -234,8 +235,18 @@ impl CustomProcMacroExpander { let krate_graph = db.crate_graph(); // Proc macros have access to the environment variables of the invoking crate. let env = &krate_graph[calling_crate].env; - match proc_macro.expander.expand(tt, attr_arg, env, def_site, call_site, mixed_site) - { + match proc_macro.expander.expand( + tt, + attr_arg, + env, + def_site, + call_site, + mixed_site, + db.crate_workspace_data()[&calling_crate] + .proc_macro_cwd + .as_ref() + .map(ToString::to_string), + ) { Ok(t) => ExpandResult::ok(t), Err(err) => match err { // Don't discard the item in case something unexpected happened while expanding attributes diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs index a3e4da5d1afd..e74e3d789883 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs @@ -381,9 +381,9 @@ impl chalk_solve::RustIrDatabase for ChalkContext<'_> { TyKind::Error.intern(Interner) } - fn is_object_safe(&self, _trait_id: chalk_ir::TraitId) -> bool { - // FIXME: implement actual object safety - true + fn is_object_safe(&self, trait_id: chalk_ir::TraitId) -> bool { + let trait_ = from_chalk_trait_id(trait_id); + crate::object_safety::object_safety(self.db, trait_).is_none() } fn closure_kind( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs index 8b6cde975f63..968a828e9dfe 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs @@ -11,7 +11,7 @@ use hir_def::{ ConstBlockLoc, EnumVariantId, GeneralConstId, StaticId, }; use hir_expand::Lookup; -use stdx::never; +use stdx::{never, IsNoneOr}; use triomphe::Arc; use crate::{ @@ -184,6 +184,22 @@ pub fn try_const_usize(db: &dyn HirDatabase, c: &Const) -> Option { } } +pub fn try_const_isize(db: &dyn HirDatabase, c: &Const) -> Option { + match &c.data(Interner).value { + chalk_ir::ConstValue::BoundVar(_) => None, + chalk_ir::ConstValue::InferenceVar(_) => None, + chalk_ir::ConstValue::Placeholder(_) => None, + chalk_ir::ConstValue::Concrete(c) => match &c.interned { + ConstScalar::Bytes(it, _) => Some(i128::from_le_bytes(pad16(it, true))), + ConstScalar::UnevaluatedConst(c, subst) => { + let ec = db.const_eval(*c, subst.clone(), None).ok()?; + try_const_isize(db, &ec) + } + _ => None, + }, + } +} + pub(crate) fn const_eval_recover( _: &dyn HirDatabase, _: &Cycle, @@ -256,8 +272,8 @@ pub(crate) fn const_eval_discriminant_variant( ) -> Result { let def = variant_id.into(); let body = db.body(def); + let loc = variant_id.lookup(db.upcast()); if body.exprs[body.body_expr] == Expr::Missing { - let loc = variant_id.lookup(db.upcast()); let prev_idx = loc.index.checked_sub(1); let value = match prev_idx { Some(prev_idx) => { @@ -269,13 +285,21 @@ pub(crate) fn const_eval_discriminant_variant( }; return Ok(value); } + + let repr = db.enum_data(loc.parent).repr; + let is_signed = IsNoneOr::is_none_or(repr.and_then(|repr| repr.int), |int| int.is_signed()); + let mir_body = db.monomorphized_mir_body( def, Substitution::empty(Interner), db.trait_environment_for_body(def), )?; let c = interpret_mir(db, mir_body, false, None).0?; - let c = try_const_usize(db, &c).unwrap() as i128; + let c = if is_signed { + try_const_isize(db, &c).unwrap() + } else { + try_const_usize(db, &c).unwrap() as i128 + }; Ok(c) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs index 86228250c200..7093fcadcb03 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs @@ -186,7 +186,13 @@ fn floating_point() { #[test] fn casts() { - check_number(r#"const GOAL: usize = 12 as *const i32 as usize"#, 12); + check_number( + r#" + //- minicore: sized + const GOAL: usize = 12 as *const i32 as usize + "#, + 12, + ); check_number( r#" //- minicore: coerce_unsized, index, slice @@ -204,7 +210,7 @@ fn casts() { r#" //- minicore: coerce_unsized, index, slice const GOAL: i16 = { - let a = &mut 5; + let a = &mut 5_i16; let z = a as *mut _; unsafe { *z } }; @@ -244,7 +250,13 @@ fn casts() { "#, 4, ); - check_number(r#"const GOAL: i32 = -12i8 as i32"#, -12); + check_number( + r#" + //- minicore: sized + const GOAL: i32 = -12i8 as i32 + "#, + -12, + ); } #[test] @@ -1544,7 +1556,7 @@ fn builtin_derive_macro() { Bar, } #[derive(Clone)] - struct X(i32, Z, i64) + struct X(i32, Z, i64); #[derive(Clone)] struct Y { field1: i32, @@ -1562,20 +1574,20 @@ fn builtin_derive_macro() { ); check_number( r#" - //- minicore: default, derive, builtin_impls - #[derive(Default)] - struct X(i32, Y, i64) - #[derive(Default)] - struct Y { - field1: i32, - field2: u8, - } +//- minicore: default, derive, builtin_impls +#[derive(Default)] +struct X(i32, Y, i64); +#[derive(Default)] +struct Y { + field1: i32, + field2: u8, +} - const GOAL: u8 = { - let x = X::default(); - x.1.field2 - }; - "#, +const GOAL: u8 = { + let x = X::default(); + x.1.field2 +}; +"#, 0, ); } @@ -1911,6 +1923,7 @@ fn function_pointer() { ); check_number( r#" + //- minicore: sized fn add2(x: u8) -> u8 { x + 2 } @@ -2007,7 +2020,7 @@ fn function_traits() { ); check_number( r#" - //- minicore: coerce_unsized, fn + //- minicore: coerce_unsized, fn, dispatch_from_dyn fn add2(x: u8) -> u8 { x + 2 } @@ -2062,7 +2075,7 @@ fn function_traits() { fn dyn_trait() { check_number( r#" - //- minicore: coerce_unsized, index, slice + //- minicore: coerce_unsized, index, slice, dispatch_from_dyn trait Foo { fn foo(&self) -> u8 { 10 } } @@ -2085,7 +2098,7 @@ fn dyn_trait() { ); check_number( r#" - //- minicore: coerce_unsized, index, slice + //- minicore: coerce_unsized, index, slice, dispatch_from_dyn trait Foo { fn foo(&self) -> i32 { 10 } } @@ -2109,7 +2122,7 @@ fn dyn_trait() { ); check_number( r#" - //- minicore: coerce_unsized, index, slice + //- minicore: coerce_unsized, index, slice, dispatch_from_dyn trait A { fn x(&self) -> i32; } @@ -2422,6 +2435,7 @@ fn statics() { fn extern_weak_statics() { check_number( r#" + //- minicore: sized extern "C" { #[linkage = "extern_weak"] static __dso_handle: *mut u8; @@ -2716,6 +2730,7 @@ fn const_trait_assoc() { ); check_number( r#" + //- minicore: sized struct S(*mut T); trait MySized: Sized { @@ -2813,7 +2828,7 @@ fn type_error() { y.0 }; "#, - |e| matches!(e, ConstEvalError::MirLowerError(MirLowerError::TypeMismatch(_))), + |e| matches!(e, ConstEvalError::MirLowerError(MirLowerError::HasErrors)), ); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests/intrinsics.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests/intrinsics.rs index 5972b80d1696..c5706172b20c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests/intrinsics.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests/intrinsics.rs @@ -89,7 +89,7 @@ fn size_of_val() { ); check_number( r#" - //- minicore: coerce_unsized, fmt, builtin_impls + //- minicore: coerce_unsized, fmt, builtin_impls, dispatch_from_dyn extern "rust-intrinsic" { pub fn size_of_val(_: *const T) -> usize; } @@ -311,6 +311,7 @@ fn saturating() { fn allocator() { check_number( r#" + //- minicore: sized extern "Rust" { #[rustc_allocator] fn __rust_alloc(size: usize, align: usize) -> *mut u8; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs index 9a1f2158bf75..ce5a821ea2bc 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs @@ -11,7 +11,7 @@ use base_db::{ use hir_def::{ db::DefDatabase, hir::ExprId, layout::TargetDataLayout, AdtId, BlockId, CallableDefId, ConstParamId, DefWithBodyId, EnumVariantId, FunctionId, GeneralConstId, GenericDefId, ImplId, - LifetimeParamId, LocalFieldId, StaticId, TypeAliasId, TypeOrConstParamId, VariantId, + LifetimeParamId, LocalFieldId, StaticId, TraitId, TypeAliasId, TypeOrConstParamId, VariantId, }; use la_arena::ArenaMap; use smallvec::SmallVec; @@ -24,6 +24,7 @@ use crate::{ lower::{GenericDefaults, GenericPredicates}, method_resolution::{InherentImpls, TraitImpls, TyFingerprint}, mir::{BorrowckResult, MirBody, MirLowerError}, + object_safety::ObjectSafetyViolation, Binders, ClosureId, Const, FnDefId, ImplTraitId, ImplTraits, InferenceResult, Interner, PolyFnSig, Substitution, TraitEnvironment, TraitRef, Ty, TyDefId, ValueTyDefId, }; @@ -107,6 +108,9 @@ pub trait HirDatabase: DefDatabase + Upcast { #[salsa::invoke(crate::layout::target_data_layout_query)] fn target_data_layout(&self, krate: CrateId) -> Result, Arc>; + #[salsa::invoke(crate::object_safety::object_safety_of_trait_query)] + fn object_safety_of_trait(&self, trait_: TraitId) -> Option; + #[salsa::invoke(crate::lower::ty_query)] #[salsa::cycle(crate::lower::ty_recover)] fn ty(&self, def: TyDefId) -> Binders; @@ -150,6 +154,9 @@ pub trait HirDatabase: DefDatabase + Upcast { #[salsa::invoke(crate::lower::generic_predicates_query)] fn generic_predicates(&self, def: GenericDefId) -> GenericPredicates; + #[salsa::invoke(crate::lower::generic_predicates_without_parent_query)] + fn generic_predicates_without_parent(&self, def: GenericDefId) -> GenericPredicates; + #[salsa::invoke(crate::lower::trait_environment_for_body_query)] #[salsa::transparent] fn trait_environment_for_body(&self, def: DefWithBodyId) -> Arc; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs index 024fc32f8637..82517e699175 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs @@ -16,13 +16,13 @@ mod case_conv; use std::fmt; use hir_def::{ - data::adt::VariantData, db::DefDatabase, hir::Pat, src::HasSource, AdtId, AttrDefId, ConstId, - EnumId, EnumVariantId, FunctionId, HasModule, ItemContainerId, Lookup, ModuleDefId, ModuleId, - StaticId, StructId, TraitId, TypeAliasId, + data::adt::VariantData, db::DefDatabase, hir::Pat, src::HasSource, AdtId, ConstId, EnumId, + EnumVariantId, FunctionId, HasModule, ItemContainerId, Lookup, ModuleDefId, ModuleId, StaticId, + StructId, TraitId, TypeAliasId, }; use hir_expand::{ name::{AsName, Name}, - HirFileId, HirFileIdExt, MacroFileIdExt, + HirFileId, HirFileIdExt, }; use intern::sym; use stdx::{always, never}; @@ -36,14 +36,6 @@ use crate::db::HirDatabase; use self::case_conv::{to_camel_case, to_lower_snake_case, to_upper_snake_case}; -mod allow { - pub(super) const BAD_STYLE: &str = "bad_style"; - pub(super) const NONSTANDARD_STYLE: &str = "nonstandard_style"; - pub(super) const NON_SNAKE_CASE: &str = "non_snake_case"; - pub(super) const NON_UPPER_CASE_GLOBAL: &str = "non_upper_case_globals"; - pub(super) const NON_CAMEL_CASE_TYPES: &str = "non_camel_case_types"; -} - pub fn incorrect_case(db: &dyn HirDatabase, owner: ModuleDefId) -> Vec { let _p = tracing::info_span!("incorrect_case").entered(); let mut validator = DeclValidator::new(db); @@ -160,92 +152,7 @@ impl<'a> DeclValidator<'a> { } } - /// Checks whether not following the convention is allowed for this item. - fn allowed(&self, id: AttrDefId, allow_name: &str, recursing: bool) -> bool { - let is_allowed = |def_id| { - let attrs = self.db.attrs(def_id); - // don't bug the user about directly no_mangle annotated stuff, they can't do anything about it - (!recursing && attrs.by_key(&sym::no_mangle).exists()) - || attrs.by_key(&sym::allow).tt_values().any(|tt| { - let allows = tt.to_string(); - allows.contains(allow_name) - || allows.contains(allow::BAD_STYLE) - || allows.contains(allow::NONSTANDARD_STYLE) - }) - }; - let db = self.db.upcast(); - let file_id_is_derive = || { - match id { - AttrDefId::ModuleId(m) => { - m.def_map(db)[m.local_id].origin.file_id().map(Into::into) - } - AttrDefId::FunctionId(f) => Some(f.lookup(db).id.file_id()), - AttrDefId::StaticId(sid) => Some(sid.lookup(db).id.file_id()), - AttrDefId::ConstId(cid) => Some(cid.lookup(db).id.file_id()), - AttrDefId::TraitId(tid) => Some(tid.lookup(db).id.file_id()), - AttrDefId::TraitAliasId(taid) => Some(taid.lookup(db).id.file_id()), - AttrDefId::ImplId(iid) => Some(iid.lookup(db).id.file_id()), - AttrDefId::ExternBlockId(id) => Some(id.lookup(db).id.file_id()), - AttrDefId::ExternCrateId(id) => Some(id.lookup(db).id.file_id()), - AttrDefId::UseId(id) => Some(id.lookup(db).id.file_id()), - // These warnings should not explore macro definitions at all - AttrDefId::MacroId(_) => None, - AttrDefId::AdtId(aid) => match aid { - AdtId::StructId(sid) => Some(sid.lookup(db).id.file_id()), - AdtId::EnumId(eid) => Some(eid.lookup(db).id.file_id()), - // Unions aren't yet supported - AdtId::UnionId(_) => None, - }, - AttrDefId::FieldId(_) => None, - AttrDefId::EnumVariantId(_) => None, - AttrDefId::TypeAliasId(_) => None, - AttrDefId::GenericParamId(_) => None, - } - .map_or(false, |file_id| { - matches!(file_id.macro_file(), Some(file_id) if file_id.is_custom_derive(db.upcast()) || file_id.is_builtin_derive(db.upcast())) - }) - }; - - let parent = || { - match id { - AttrDefId::ModuleId(m) => m.containing_module(db).map(|v| v.into()), - AttrDefId::FunctionId(f) => Some(f.lookup(db).container.into()), - AttrDefId::StaticId(sid) => Some(sid.lookup(db).container.into()), - AttrDefId::ConstId(cid) => Some(cid.lookup(db).container.into()), - AttrDefId::TraitId(tid) => Some(tid.lookup(db).container.into()), - AttrDefId::TraitAliasId(taid) => Some(taid.lookup(db).container.into()), - AttrDefId::ImplId(iid) => Some(iid.lookup(db).container.into()), - AttrDefId::ExternBlockId(id) => Some(id.lookup(db).container.into()), - AttrDefId::ExternCrateId(id) => Some(id.lookup(db).container.into()), - AttrDefId::UseId(id) => Some(id.lookup(db).container.into()), - // These warnings should not explore macro definitions at all - AttrDefId::MacroId(_) => None, - AttrDefId::AdtId(aid) => match aid { - AdtId::StructId(sid) => Some(sid.lookup(db).container.into()), - AdtId::EnumId(eid) => Some(eid.lookup(db).container.into()), - // Unions aren't yet supported - AdtId::UnionId(_) => None, - }, - AttrDefId::FieldId(_) => None, - AttrDefId::EnumVariantId(_) => None, - AttrDefId::TypeAliasId(_) => None, - AttrDefId::GenericParamId(_) => None, - } - .is_some_and(|mid| self.allowed(mid, allow_name, true)) - }; - is_allowed(id) - // FIXME: this is a hack to avoid false positives in derive macros currently - || file_id_is_derive() - // go upwards one step or give up - || parent() - } - fn validate_module(&mut self, module_id: ModuleId) { - // Check whether non-snake case identifiers are allowed for this module. - if self.allowed(module_id.into(), allow::NON_SNAKE_CASE, false) { - return; - } - // Check the module name. let Some(module_name) = module_id.name(self.db.upcast()) else { return }; let Some(module_name_replacement) = @@ -270,11 +177,6 @@ impl<'a> DeclValidator<'a> { } fn validate_trait(&mut self, trait_id: TraitId) { - // Check whether non-snake case identifiers are allowed for this trait. - if self.allowed(trait_id.into(), allow::NON_CAMEL_CASE_TYPES, false) { - return; - } - // Check the trait name. let data = self.db.trait_data(trait_id); self.create_incorrect_case_diagnostic_for_item_name( @@ -292,21 +194,24 @@ impl<'a> DeclValidator<'a> { return; } - // Check whether non-snake case identifiers are allowed for this function. - if self.allowed(func.into(), allow::NON_SNAKE_CASE, false) { - return; - } - // Check the function name. // Skipped if function is an associated item of a trait implementation. if !self.is_trait_impl_container(container) { let data = self.db.function_data(func); - self.create_incorrect_case_diagnostic_for_item_name( - func, - &data.name, - CaseType::LowerSnakeCase, - IdentType::Function, - ); + + // Don't run the lint on extern "[not Rust]" fn items with the + // #[no_mangle] attribute. + let no_mangle = data.attrs.by_key(&sym::no_mangle).exists(); + if no_mangle && data.abi.as_ref().is_some_and(|abi| *abi != sym::Rust) { + cov_mark::hit!(extern_func_no_mangle_ignored); + } else { + self.create_incorrect_case_diagnostic_for_item_name( + func, + &data.name, + CaseType::LowerSnakeCase, + IdentType::Function, + ); + } } else { cov_mark::hit!(trait_impl_assoc_func_name_incorrect_case_ignored); } @@ -389,17 +294,13 @@ impl<'a> DeclValidator<'a> { fn validate_struct(&mut self, struct_id: StructId) { // Check the structure name. - let non_camel_case_allowed = - self.allowed(struct_id.into(), allow::NON_CAMEL_CASE_TYPES, false); - if !non_camel_case_allowed { - let data = self.db.struct_data(struct_id); - self.create_incorrect_case_diagnostic_for_item_name( - struct_id, - &data.name, - CaseType::UpperCamelCase, - IdentType::Structure, - ); - } + let data = self.db.struct_data(struct_id); + self.create_incorrect_case_diagnostic_for_item_name( + struct_id, + &data.name, + CaseType::UpperCamelCase, + IdentType::Structure, + ); // Check the field names. self.validate_struct_fields(struct_id); @@ -407,10 +308,6 @@ impl<'a> DeclValidator<'a> { /// Check incorrect names for struct fields. fn validate_struct_fields(&mut self, struct_id: StructId) { - if self.allowed(struct_id.into(), allow::NON_SNAKE_CASE, false) { - return; - } - let data = self.db.struct_data(struct_id); let VariantData::Record(fields) = data.variant_data.as_ref() else { return; @@ -484,11 +381,6 @@ impl<'a> DeclValidator<'a> { fn validate_enum(&mut self, enum_id: EnumId) { let data = self.db.enum_data(enum_id); - // Check whether non-camel case names are allowed for this enum. - if self.allowed(enum_id.into(), allow::NON_CAMEL_CASE_TYPES, false) { - return; - } - // Check the enum name. self.create_incorrect_case_diagnostic_for_item_name( enum_id, @@ -653,10 +545,6 @@ impl<'a> DeclValidator<'a> { return; } - if self.allowed(const_id.into(), allow::NON_UPPER_CASE_GLOBAL, false) { - return; - } - let data = self.db.const_data(const_id); let Some(name) = &data.name else { return; @@ -676,10 +564,6 @@ impl<'a> DeclValidator<'a> { return; } - if self.allowed(static_id.into(), allow::NON_UPPER_CASE_GLOBAL, false) { - return; - } - self.create_incorrect_case_diagnostic_for_item_name( static_id, &data.name, @@ -695,11 +579,6 @@ impl<'a> DeclValidator<'a> { return; } - // Check whether non-snake case identifiers are allowed for this type alias. - if self.allowed(type_alias_id.into(), allow::NON_CAMEL_CASE_TYPES, false) { - return; - } - // Check the type alias name. let data = self.db.type_alias_data(type_alias_id); self.create_incorrect_case_diagnostic_for_item_name( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs index 3f54cdd20cee..ff45c725c73c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs @@ -5,6 +5,7 @@ use hir_def::{ body::Body, hir::{Expr, ExprId, UnaryOp}, resolver::{resolver_for_expr, ResolveValueResult, Resolver, ValueNs}, + type_ref::Rawness, DefWithBodyId, }; @@ -12,7 +13,10 @@ use crate::{ db::HirDatabase, utils::is_fn_unsafe_to_call, InferenceResult, Interner, TyExt, TyKind, }; -pub fn missing_unsafe(db: &dyn HirDatabase, def: DefWithBodyId) -> Vec { +/// Returns `(unsafe_exprs, fn_is_unsafe)`. +/// +/// If `fn_is_unsafe` is false, `unsafe_exprs` are hard errors. If true, they're `unsafe_op_in_unsafe_fn`. +pub fn missing_unsafe(db: &dyn HirDatabase, def: DefWithBodyId) -> (Vec, bool) { let _p = tracing::info_span!("missing_unsafe").entered(); let mut res = Vec::new(); @@ -23,9 +27,6 @@ pub fn missing_unsafe(db: &dyn HirDatabase, def: DefWithBodyId) -> Vec { | DefWithBodyId::VariantId(_) | DefWithBodyId::InTypeConstId(_) => false, }; - if is_unsafe { - return res; - } let body = db.body(def); let infer = db.infer(def); @@ -35,7 +36,7 @@ pub fn missing_unsafe(db: &dyn HirDatabase, def: DefWithBodyId) -> Vec { } }); - res + (res, is_unsafe) } pub struct UnsafeExpr { @@ -87,12 +88,20 @@ fn walk_unsafe( let g = resolver.update_to_inner_scope(db.upcast(), def, current); let value_or_partial = resolver.resolve_path_in_value_ns(db.upcast(), path); if let Some(ResolveValueResult::ValueNs(ValueNs::StaticId(id), _)) = value_or_partial { - if db.static_data(id).mutable { + let static_data = db.static_data(id); + if static_data.mutable || static_data.is_extern { unsafe_expr_cb(UnsafeExpr { expr: current, inside_unsafe_block }); } } resolver.reset_to_guard(g); } + Expr::Ref { expr, rawness: Rawness::RawPtr, mutability: _ } => { + if let Expr::Path(_) = body.exprs[*expr] { + // Do not report unsafe for `addr_of[_mut]!(EXTERN_OR_MUT_STATIC)`, + // see https://github.com/rust-lang/rust/pull/125834. + return; + } + } Expr::MethodCall { .. } => { if infer .method_resolution(current) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs b/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs index a96c101a388b..89ca707c2e69 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs @@ -225,6 +225,23 @@ impl Generics { } } +pub(crate) fn trait_self_param_idx(db: &dyn DefDatabase, def: GenericDefId) -> Option { + match def { + GenericDefId::TraitId(_) | GenericDefId::TraitAliasId(_) => { + let params = db.generic_params(def); + params.trait_self_param().map(|idx| idx.into_raw().into_u32() as usize) + } + GenericDefId::ImplId(_) => None, + _ => { + let parent_def = parent_generic_def(db, def)?; + let parent_params = db.generic_params(parent_def); + let parent_self_idx = parent_params.trait_self_param()?.into_raw().into_u32() as usize; + let self_params = db.generic_params(def); + Some(self_params.len() + parent_self_idx) + } + } +} + fn parent_generic_def(db: &dyn DefDatabase, def: GenericDefId) -> Option { let container = match def { GenericDefId::FunctionId(it) => it.lookup(db).container, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs index 062ea278151b..8bc3c50725d9 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -13,7 +13,7 @@ //! to certain types. To record this, we use the union-find implementation from //! the `ena` crate, which is extracted from rustc. -mod cast; +pub(crate) mod cast; pub(crate) mod closure; mod coerce; mod expr; @@ -76,7 +76,7 @@ pub use coerce::could_coerce; #[allow(unreachable_pub)] pub use unify::{could_unify, could_unify_deeply}; -use cast::CastCheck; +use cast::{CastCheck, CastError}; pub(crate) use closure::{CaptureKind, CapturedItem, CapturedItemWithoutTy}; /// The entry point of type inference. @@ -254,6 +254,16 @@ pub enum InferenceDiagnostic { expr: ExprId, expected: Ty, }, + CastToUnsized { + expr: ExprId, + cast_ty: Ty, + }, + InvalidCast { + expr: ExprId, + error: CastError, + expr_ty: Ty, + cast_ty: Ty, + }, } /// A mismatch between an expected and an inferred type. @@ -456,6 +466,7 @@ pub struct InferenceResult { pub(crate) closure_info: FxHashMap, FnTrait)>, // FIXME: remove this field pub mutated_bindings_in_closure: FxHashSet, + pub coercion_casts: FxHashSet, } impl InferenceResult { @@ -666,7 +677,7 @@ impl<'a> InferenceContext<'a> { let InferenceContext { mut table, mut result, - deferred_cast_checks, + mut deferred_cast_checks, tuple_field_accesses_rev, .. } = self; @@ -695,15 +706,25 @@ impl<'a> InferenceContext<'a> { closure_info: _, mutated_bindings_in_closure: _, tuple_field_access_types: _, + coercion_casts, } = &mut result; - table.fallback_if_possible(); // Comment from rustc: // Even though coercion casts provide type hints, we check casts after fallback for // backwards compatibility. This makes fallback a stronger type hint than a cast coercion. - for cast in deferred_cast_checks { - cast.check(&mut table); + let mut apply_adjustments = |expr, adj| { + expr_adjustments.insert(expr, adj); + }; + let mut set_coercion_cast = |expr| { + coercion_casts.insert(expr); + }; + for cast in deferred_cast_checks.iter_mut() { + if let Err(diag) = + cast.check(&mut table, &mut apply_adjustments, &mut set_coercion_cast) + { + diagnostics.push(diag); + } } // FIXME resolve obligations as well (use Guidance if necessary) @@ -732,7 +753,7 @@ impl<'a> InferenceContext<'a> { *has_errors = *has_errors || ty.contains_unknown(); } - *has_errors = !type_mismatches.is_empty(); + *has_errors |= !type_mismatches.is_empty(); type_mismatches.retain(|_, mismatch| { mismatch.expected = table.resolve_completely(mismatch.expected.clone()); @@ -775,20 +796,30 @@ impl<'a> InferenceContext<'a> { }); for (_, subst) in method_resolutions.values_mut() { *subst = table.resolve_completely(subst.clone()); + *has_errors = + *has_errors || subst.type_parameters(Interner).any(|ty| ty.contains_unknown()); } for (_, subst) in assoc_resolutions.values_mut() { *subst = table.resolve_completely(subst.clone()); + *has_errors = + *has_errors || subst.type_parameters(Interner).any(|ty| ty.contains_unknown()); } for adjustment in expr_adjustments.values_mut().flatten() { adjustment.target = table.resolve_completely(adjustment.target.clone()); + *has_errors = *has_errors || adjustment.target.contains_unknown(); } for adjustment in pat_adjustments.values_mut().flatten() { *adjustment = table.resolve_completely(adjustment.clone()); + *has_errors = *has_errors || adjustment.contains_unknown(); } result.tuple_field_access_types = tuple_field_accesses_rev .into_iter() .enumerate() .map(|(idx, subst)| (TupleId(idx as u32), table.resolve_completely(subst))) + .inspect(|(_, subst)| { + *has_errors = + *has_errors || subst.type_parameters(Interner).any(|ty| ty.contains_unknown()); + }) .collect(); result } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs index 060b5f36f292..caa3960a227c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs @@ -1,47 +1,451 @@ //! Type cast logic. Basically coercion + additional casts. -use crate::{infer::unify::InferenceTable, Interner, Ty, TyExt, TyKind}; +use chalk_ir::{Mutability, Scalar, TyVariableKind, UintTy}; +use hir_def::{hir::ExprId, AdtId}; +use stdx::never; + +use crate::{ + infer::unify::InferenceTable, Adjustment, Binders, DynTy, InferenceDiagnostic, Interner, + PlaceholderIndex, QuantifiedWhereClauses, Ty, TyExt, TyKind, TypeFlags, WhereClause, +}; + +#[derive(Debug)] +pub(crate) enum Int { + I, + U(UintTy), + Bool, + Char, + CEnum, + InferenceVar, +} + +#[derive(Debug)] +pub(crate) enum CastTy { + Int(Int), + Float, + FnPtr, + Ptr(Ty, Mutability), + // `DynStar` is Not supported yet in r-a +} + +impl CastTy { + pub(crate) fn from_ty(table: &mut InferenceTable<'_>, t: &Ty) -> Option { + match t.kind(Interner) { + TyKind::Scalar(Scalar::Bool) => Some(Self::Int(Int::Bool)), + TyKind::Scalar(Scalar::Char) => Some(Self::Int(Int::Char)), + TyKind::Scalar(Scalar::Int(_)) => Some(Self::Int(Int::I)), + TyKind::Scalar(Scalar::Uint(it)) => Some(Self::Int(Int::U(*it))), + TyKind::InferenceVar(_, TyVariableKind::Integer) => Some(Self::Int(Int::InferenceVar)), + TyKind::InferenceVar(_, TyVariableKind::Float) => Some(Self::Float), + TyKind::Scalar(Scalar::Float(_)) => Some(Self::Float), + TyKind::Adt(..) => { + let (AdtId::EnumId(id), _) = t.as_adt()? else { + return None; + }; + let enum_data = table.db.enum_data(id); + if enum_data.is_payload_free(table.db.upcast()) { + Some(Self::Int(Int::CEnum)) + } else { + None + } + } + TyKind::Raw(m, ty) => Some(Self::Ptr(table.resolve_ty_shallow(ty), *m)), + TyKind::Function(_) => Some(Self::FnPtr), + _ => None, + } + } +} + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum CastError { + Unknown, + CastToBool, + CastToChar, + DifferingKinds, + SizedUnsizedCast, + IllegalCast, + IntToFatCast, + NeedDeref, + NeedViaPtr, + NeedViaThinPtr, + NeedViaInt, + NonScalar, + UnknownCastPtrKind, + UnknownExprPtrKind, +} + +impl CastError { + fn into_diagnostic(self, expr: ExprId, expr_ty: Ty, cast_ty: Ty) -> InferenceDiagnostic { + InferenceDiagnostic::InvalidCast { expr, error: self, expr_ty, cast_ty } + } +} #[derive(Clone, Debug)] pub(super) struct CastCheck { + expr: ExprId, + source_expr: ExprId, expr_ty: Ty, cast_ty: Ty, } impl CastCheck { - pub(super) fn new(expr_ty: Ty, cast_ty: Ty) -> Self { - Self { expr_ty, cast_ty } + pub(super) fn new(expr: ExprId, source_expr: ExprId, expr_ty: Ty, cast_ty: Ty) -> Self { + Self { expr, source_expr, expr_ty, cast_ty } } - pub(super) fn check(self, table: &mut InferenceTable<'_>) { - // FIXME: This function currently only implements the bits that influence the type - // inference. We should return the adjustments on success and report diagnostics on error. - let expr_ty = table.resolve_ty_shallow(&self.expr_ty); - let cast_ty = table.resolve_ty_shallow(&self.cast_ty); + pub(super) fn check( + &mut self, + table: &mut InferenceTable<'_>, + apply_adjustments: &mut F, + set_coercion_cast: &mut G, + ) -> Result<(), InferenceDiagnostic> + where + F: FnMut(ExprId, Vec), + G: FnMut(ExprId), + { + table.resolve_obligations_as_possible(); + self.expr_ty = table.resolve_ty_shallow(&self.expr_ty); + self.cast_ty = table.resolve_ty_shallow(&self.cast_ty); + + if self.expr_ty.contains_unknown() || self.cast_ty.contains_unknown() { + return Ok(()); + } + + if !self.cast_ty.data(Interner).flags.contains(TypeFlags::HAS_TY_INFER) + && !table.is_sized(&self.cast_ty) + { + return Err(InferenceDiagnostic::CastToUnsized { + expr: self.expr, + cast_ty: self.cast_ty.clone(), + }); + } - if table.coerce(&expr_ty, &cast_ty).is_ok() { - return; + // Chalk doesn't support trait upcasting and fails to solve some obvious goals + // when the trait environment contains some recursive traits (See issue #18047) + // We skip cast checks for such cases for now, until the next-gen solver. + if contains_dyn_trait(&self.cast_ty) { + return Ok(()); } - if check_ref_to_ptr_cast(expr_ty, cast_ty, table) { - // Note that this type of cast is actually split into a coercion to a - // pointer type and a cast: - // &[T; N] -> *[T; N] -> *T + if let Ok((adj, _)) = table.coerce(&self.expr_ty, &self.cast_ty) { + apply_adjustments(self.source_expr, adj); + set_coercion_cast(self.source_expr); + return Ok(()); } - // FIXME: Check other kinds of non-coercion casts and report error if any? + self.do_check(table, apply_adjustments) + .map_err(|e| e.into_diagnostic(self.expr, self.expr_ty.clone(), self.cast_ty.clone())) + } + + fn do_check( + &self, + table: &mut InferenceTable<'_>, + apply_adjustments: &mut F, + ) -> Result<(), CastError> + where + F: FnMut(ExprId, Vec), + { + let (t_from, t_cast) = + match (CastTy::from_ty(table, &self.expr_ty), CastTy::from_ty(table, &self.cast_ty)) { + (Some(t_from), Some(t_cast)) => (t_from, t_cast), + (None, Some(t_cast)) => match self.expr_ty.kind(Interner) { + TyKind::FnDef(..) => { + let sig = self.expr_ty.callable_sig(table.db).expect("FnDef had no sig"); + let sig = table.normalize_associated_types_in(sig); + let fn_ptr = TyKind::Function(sig.to_fn_ptr()).intern(Interner); + if let Ok((adj, _)) = table.coerce(&self.expr_ty, &fn_ptr) { + apply_adjustments(self.source_expr, adj); + } else { + return Err(CastError::IllegalCast); + } + + (CastTy::FnPtr, t_cast) + } + TyKind::Ref(mutbl, _, inner_ty) => { + let inner_ty = table.resolve_ty_shallow(inner_ty); + return match t_cast { + CastTy::Int(_) | CastTy::Float => match inner_ty.kind(Interner) { + TyKind::Scalar( + Scalar::Int(_) | Scalar::Uint(_) | Scalar::Float(_), + ) + | TyKind::InferenceVar( + _, + TyVariableKind::Integer | TyVariableKind::Float, + ) => Err(CastError::NeedDeref), + + _ => Err(CastError::NeedViaPtr), + }, + // array-ptr-cast + CastTy::Ptr(t, m) => { + let t = table.resolve_ty_shallow(&t); + if !table.is_sized(&t) { + return Err(CastError::IllegalCast); + } + self.check_ref_cast( + table, + &inner_ty, + *mutbl, + &t, + m, + apply_adjustments, + ) + } + _ => Err(CastError::NonScalar), + }; + } + _ => return Err(CastError::NonScalar), + }, + _ => return Err(CastError::NonScalar), + }; + + // rustc checks whether the `expr_ty` is foreign adt with `non_exhaustive` sym + + match (t_from, t_cast) { + (_, CastTy::Int(Int::CEnum) | CastTy::FnPtr) => Err(CastError::NonScalar), + (_, CastTy::Int(Int::Bool)) => Err(CastError::CastToBool), + (CastTy::Int(Int::U(UintTy::U8)), CastTy::Int(Int::Char)) => Ok(()), + (_, CastTy::Int(Int::Char)) => Err(CastError::CastToChar), + (CastTy::Int(Int::Bool | Int::CEnum | Int::Char), CastTy::Float) => { + Err(CastError::NeedViaInt) + } + (CastTy::Int(Int::Bool | Int::CEnum | Int::Char) | CastTy::Float, CastTy::Ptr(..)) + | (CastTy::Ptr(..) | CastTy::FnPtr, CastTy::Float) => Err(CastError::IllegalCast), + (CastTy::Ptr(src, _), CastTy::Ptr(dst, _)) => { + self.check_ptr_ptr_cast(table, &src, &dst) + } + (CastTy::Ptr(src, _), CastTy::Int(_)) => self.check_ptr_addr_cast(table, &src), + (CastTy::Int(_), CastTy::Ptr(dst, _)) => self.check_addr_ptr_cast(table, &dst), + (CastTy::FnPtr, CastTy::Ptr(dst, _)) => self.check_fptr_ptr_cast(table, &dst), + (CastTy::Int(Int::CEnum), CastTy::Int(_)) => Ok(()), + (CastTy::Int(Int::Char | Int::Bool), CastTy::Int(_)) => Ok(()), + (CastTy::Int(_) | CastTy::Float, CastTy::Int(_) | CastTy::Float) => Ok(()), + (CastTy::FnPtr, CastTy::Int(_)) => Ok(()), + } + } + + fn check_ref_cast( + &self, + table: &mut InferenceTable<'_>, + t_expr: &Ty, + m_expr: Mutability, + t_cast: &Ty, + m_cast: Mutability, + apply_adjustments: &mut F, + ) -> Result<(), CastError> + where + F: FnMut(ExprId, Vec), + { + // Mutability order is opposite to rustc. `Mut < Not` + if m_expr <= m_cast { + if let TyKind::Array(ety, _) = t_expr.kind(Interner) { + // Coerce to a raw pointer so that we generate RawPtr in MIR. + let array_ptr_type = TyKind::Raw(m_expr, t_expr.clone()).intern(Interner); + if let Ok((adj, _)) = table.coerce(&self.expr_ty, &array_ptr_type) { + apply_adjustments(self.source_expr, adj); + } else { + never!( + "could not cast from reference to array to pointer to array ({:?} to {:?})", + self.expr_ty, + array_ptr_type + ); + } + + // This is a less strict condition than rustc's `demand_eqtype`, + // but false negative is better than false positive + if table.coerce(ety, t_cast).is_ok() { + return Ok(()); + } + } + } + + Err(CastError::IllegalCast) + } + + fn check_ptr_ptr_cast( + &self, + table: &mut InferenceTable<'_>, + src: &Ty, + dst: &Ty, + ) -> Result<(), CastError> { + let src_kind = pointer_kind(src, table).map_err(|_| CastError::Unknown)?; + let dst_kind = pointer_kind(dst, table).map_err(|_| CastError::Unknown)?; + + match (src_kind, dst_kind) { + (Some(PointerKind::Error), _) | (_, Some(PointerKind::Error)) => Ok(()), + (_, None) => Err(CastError::UnknownCastPtrKind), + (_, Some(PointerKind::Thin)) => Ok(()), + (None, _) => Err(CastError::UnknownExprPtrKind), + (Some(PointerKind::Thin), _) => Err(CastError::SizedUnsizedCast), + (Some(PointerKind::VTable(src_tty)), Some(PointerKind::VTable(dst_tty))) => { + let principal = |tty: &Binders| { + tty.skip_binders().as_slice(Interner).first().and_then(|pred| { + if let WhereClause::Implemented(tr) = pred.skip_binders() { + Some(tr.trait_id) + } else { + None + } + }) + }; + match (principal(&src_tty), principal(&dst_tty)) { + (Some(src_principal), Some(dst_principal)) => { + if src_principal == dst_principal { + return Ok(()); + } + let src_principal = + table.db.trait_datum(table.trait_env.krate, src_principal); + let dst_principal = + table.db.trait_datum(table.trait_env.krate, dst_principal); + if src_principal.is_auto_trait() && dst_principal.is_auto_trait() { + Ok(()) + } else { + Err(CastError::DifferingKinds) + } + } + _ => Err(CastError::Unknown), + } + } + (Some(src_kind), Some(dst_kind)) if src_kind == dst_kind => Ok(()), + (_, _) => Err(CastError::DifferingKinds), + } + } + + fn check_ptr_addr_cast( + &self, + table: &mut InferenceTable<'_>, + expr_ty: &Ty, + ) -> Result<(), CastError> { + match pointer_kind(expr_ty, table).map_err(|_| CastError::Unknown)? { + None => Err(CastError::UnknownExprPtrKind), + Some(PointerKind::Error) => Ok(()), + Some(PointerKind::Thin) => Ok(()), + _ => Err(CastError::NeedViaThinPtr), + } + } + + fn check_addr_ptr_cast( + &self, + table: &mut InferenceTable<'_>, + cast_ty: &Ty, + ) -> Result<(), CastError> { + match pointer_kind(cast_ty, table).map_err(|_| CastError::Unknown)? { + None => Err(CastError::UnknownCastPtrKind), + Some(PointerKind::Error) => Ok(()), + Some(PointerKind::Thin) => Ok(()), + Some(PointerKind::VTable(_)) => Err(CastError::IntToFatCast), + Some(PointerKind::Length) => Err(CastError::IntToFatCast), + Some(PointerKind::OfAlias | PointerKind::OfParam(_)) => Err(CastError::IntToFatCast), + } + } + + fn check_fptr_ptr_cast( + &self, + table: &mut InferenceTable<'_>, + cast_ty: &Ty, + ) -> Result<(), CastError> { + match pointer_kind(cast_ty, table).map_err(|_| CastError::Unknown)? { + None => Err(CastError::UnknownCastPtrKind), + Some(PointerKind::Error) => Ok(()), + Some(PointerKind::Thin) => Ok(()), + _ => Err(CastError::IllegalCast), + } } } -fn check_ref_to_ptr_cast(expr_ty: Ty, cast_ty: Ty, table: &mut InferenceTable<'_>) -> bool { - let Some((expr_inner_ty, _, _)) = expr_ty.as_reference() else { - return false; - }; - let Some((cast_inner_ty, _)) = cast_ty.as_raw_ptr() else { - return false; - }; - let TyKind::Array(expr_elt_ty, _) = expr_inner_ty.kind(Interner) else { - return false; +#[derive(PartialEq, Eq)] +enum PointerKind { + // thin pointer + Thin, + // trait object + VTable(Binders), + // slice + Length, + OfAlias, + OfParam(PlaceholderIndex), + Error, +} + +fn pointer_kind(ty: &Ty, table: &mut InferenceTable<'_>) -> Result, ()> { + let ty = table.resolve_ty_shallow(ty); + + if table.is_sized(&ty) { + return Ok(Some(PointerKind::Thin)); + } + + match ty.kind(Interner) { + TyKind::Slice(_) | TyKind::Str => Ok(Some(PointerKind::Length)), + TyKind::Dyn(DynTy { bounds, .. }) => Ok(Some(PointerKind::VTable(bounds.clone()))), + TyKind::Adt(chalk_ir::AdtId(id), subst) => { + let AdtId::StructId(id) = *id else { + never!("`{:?}` should be sized but is not?", ty); + return Err(()); + }; + + let struct_data = table.db.struct_data(id); + if let Some((last_field, _)) = struct_data.variant_data.fields().iter().last() { + let last_field_ty = + table.db.field_types(id.into())[last_field].clone().substitute(Interner, subst); + pointer_kind(&last_field_ty, table) + } else { + Ok(Some(PointerKind::Thin)) + } + } + TyKind::Tuple(_, subst) => { + match subst.iter(Interner).last().and_then(|arg| arg.ty(Interner)) { + None => Ok(Some(PointerKind::Thin)), + Some(ty) => pointer_kind(ty, table), + } + } + TyKind::Foreign(_) => Ok(Some(PointerKind::Thin)), + TyKind::Alias(_) | TyKind::AssociatedType(..) | TyKind::OpaqueType(..) => { + Ok(Some(PointerKind::OfAlias)) + } + TyKind::Error => Ok(Some(PointerKind::Error)), + TyKind::Placeholder(idx) => Ok(Some(PointerKind::OfParam(*idx))), + TyKind::BoundVar(_) | TyKind::InferenceVar(..) => Ok(None), + TyKind::Scalar(_) + | TyKind::Array(..) + | TyKind::CoroutineWitness(..) + | TyKind::Raw(..) + | TyKind::Ref(..) + | TyKind::FnDef(..) + | TyKind::Function(_) + | TyKind::Closure(..) + | TyKind::Coroutine(..) + | TyKind::Never => { + never!("`{:?}` should be sized but is not?", ty); + Err(()) + } + } +} + +fn contains_dyn_trait(ty: &Ty) -> bool { + use std::ops::ControlFlow; + + use chalk_ir::{ + visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor}, + DebruijnIndex, }; - table.coerce(expr_elt_ty, cast_inner_ty).is_ok() + + struct DynTraitVisitor; + + impl TypeVisitor for DynTraitVisitor { + type BreakTy = (); + + fn as_dyn(&mut self) -> &mut dyn TypeVisitor { + self + } + + fn interner(&self) -> Interner { + Interner + } + + fn visit_ty(&mut self, ty: &Ty, outer_binder: DebruijnIndex) -> ControlFlow { + match ty.kind(Interner) { + TyKind::Dyn(_) => ControlFlow::Break(()), + _ => ty.super_visit_with(self.as_dyn(), outer_binder), + } + } + } + + ty.visit_with(DynTraitVisitor.as_dyn(), DebruijnIndex::INNERMOST).is_break() } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs index 36327d1d49c0..5cad08b93956 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs @@ -10,7 +10,10 @@ use chalk_ir::{ use either::Either; use hir_def::{ data::adt::VariantData, - hir::{Array, BinaryOp, BindingId, CaptureBy, Expr, ExprId, Pat, PatId, Statement, UnaryOp}, + hir::{ + Array, AsmOperand, BinaryOp, BindingId, CaptureBy, Expr, ExprId, Pat, PatId, Statement, + UnaryOp, + }, lang_item::LangItem, resolver::{resolver_for_expr, ResolveValueResult, ValueNs}, DefWithBodyId, FieldId, HasModule, TupleFieldId, TupleId, VariantId, @@ -666,7 +669,21 @@ impl InferenceContext<'_> { fn walk_expr_without_adjust(&mut self, tgt_expr: ExprId) { match &self.body[tgt_expr] { Expr::OffsetOf(_) => (), - Expr::InlineAsm(e) => self.walk_expr_without_adjust(e.e), + Expr::InlineAsm(e) => e.operands.iter().for_each(|(_, op)| match op { + AsmOperand::In { expr, .. } + | AsmOperand::Out { expr: Some(expr), .. } + | AsmOperand::InOut { expr, .. } => self.walk_expr_without_adjust(*expr), + AsmOperand::SplitInOut { in_expr, out_expr, .. } => { + self.walk_expr_without_adjust(*in_expr); + if let Some(out_expr) = out_expr { + self.walk_expr_without_adjust(*out_expr); + } + } + AsmOperand::Out { expr: None, .. } + | AsmOperand::Const(_) + | AsmOperand::Label(_) + | AsmOperand::Sym(_) => (), + }), Expr::If { condition, then_branch, else_branch } => { self.consume_expr(*condition); self.consume_expr(*then_branch); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs index 89d92ea9af0b..a04e7b17ae6e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs @@ -9,7 +9,8 @@ use chalk_ir::{cast::Cast, fold::Shift, DebruijnIndex, Mutability, TyVariableKin use either::Either; use hir_def::{ hir::{ - ArithOp, Array, BinaryOp, ClosureKind, Expr, ExprId, LabelId, Literal, Statement, UnaryOp, + ArithOp, Array, AsmOperand, AsmOptions, BinaryOp, ClosureKind, Expr, ExprId, LabelId, + Literal, Statement, UnaryOp, }, lang_item::{LangItem, LangItemTarget}, path::{GenericArg, GenericArgs, Path}, @@ -41,9 +42,9 @@ use crate::{ primitive::{self, UintTy}, static_lifetime, to_chalk_trait_id, traits::FnTrait, - Adjust, Adjustment, AdtId, AutoBorrow, Binders, CallableDefId, FnAbi, FnPointer, FnSig, - FnSubst, Interner, Rawness, Scalar, Substitution, TraitEnvironment, TraitRef, Ty, TyBuilder, - TyExt, TyKind, + Adjust, Adjustment, AdtId, AutoBorrow, Binders, CallableDefId, CallableSig, FnAbi, FnPointer, + FnSig, FnSubst, Interner, Rawness, Scalar, Substitution, TraitEnvironment, TraitRef, Ty, + TyBuilder, TyExt, TyKind, }; use super::{ @@ -610,7 +611,12 @@ impl InferenceContext<'_> { Expr::Cast { expr, type_ref } => { let cast_ty = self.make_ty(type_ref); let expr_ty = self.infer_expr(*expr, &Expectation::Castable(cast_ty.clone())); - self.deferred_cast_checks.push(CastCheck::new(expr_ty, cast_ty.clone())); + self.deferred_cast_checks.push(CastCheck::new( + tgt_expr, + *expr, + expr_ty, + cast_ty.clone(), + )); cast_ty } Expr::Ref { expr, rawness, mutability } => { @@ -845,7 +851,7 @@ impl InferenceContext<'_> { }; for (expr, ty) in exprs.iter().zip(tys.iter_mut()) { - self.infer_expr_coerce(*expr, &Expectation::has_type(ty.clone())); + *ty = self.infer_expr_coerce(*expr, &Expectation::has_type(ty.clone())); } TyKind::Tuple(tys.len(), Substitution::from_iter(Interner, tys)).intern(Interner) @@ -889,21 +895,52 @@ impl InferenceContext<'_> { TyKind::Scalar(Scalar::Int(primitive::int_ty_from_builtin(*int_ty))) .intern(Interner) } - None => self.table.new_integer_var(), + None => { + let expected_ty = expected.to_option(&mut self.table); + let opt_ty = match expected_ty.as_ref().map(|it| it.kind(Interner)) { + Some(TyKind::Scalar(Scalar::Int(_) | Scalar::Uint(_))) => expected_ty, + Some(TyKind::Scalar(Scalar::Char)) => { + Some(TyKind::Scalar(Scalar::Uint(UintTy::U8)).intern(Interner)) + } + Some(TyKind::Raw(..) | TyKind::FnDef(..) | TyKind::Function(..)) => { + Some(TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(Interner)) + } + _ => None, + }; + opt_ty.unwrap_or_else(|| self.table.new_integer_var()) + } }, Literal::Uint(_v, ty) => match ty { Some(int_ty) => { TyKind::Scalar(Scalar::Uint(primitive::uint_ty_from_builtin(*int_ty))) .intern(Interner) } - None => self.table.new_integer_var(), + None => { + let expected_ty = expected.to_option(&mut self.table); + let opt_ty = match expected_ty.as_ref().map(|it| it.kind(Interner)) { + Some(TyKind::Scalar(Scalar::Int(_) | Scalar::Uint(_))) => expected_ty, + Some(TyKind::Scalar(Scalar::Char)) => { + Some(TyKind::Scalar(Scalar::Uint(UintTy::U8)).intern(Interner)) + } + Some(TyKind::Raw(..) | TyKind::FnDef(..) | TyKind::Function(..)) => { + Some(TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(Interner)) + } + _ => None, + }; + opt_ty.unwrap_or_else(|| self.table.new_integer_var()) + } }, Literal::Float(_v, ty) => match ty { Some(float_ty) => { TyKind::Scalar(Scalar::Float(primitive::float_ty_from_builtin(*float_ty))) .intern(Interner) } - None => self.table.new_float_var(), + None => { + let opt_ty = expected.to_option(&mut self.table).filter(|ty| { + matches!(ty.kind(Interner), TyKind::Scalar(Scalar::Float(_))) + }); + opt_ty.unwrap_or_else(|| self.table.new_float_var()) + } }, }, Expr::Underscore => { @@ -919,9 +956,61 @@ impl InferenceContext<'_> { expected } Expr::OffsetOf(_) => TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(Interner), - Expr::InlineAsm(it) => { - self.infer_expr_no_expect(it.e); - self.result.standard_types.unit.clone() + Expr::InlineAsm(asm) => { + let mut check_expr_asm_operand = |expr, is_input: bool| { + let ty = self.infer_expr_no_expect(expr); + + // If this is an input value, we require its type to be fully resolved + // at this point. This allows us to provide helpful coercions which help + // pass the type candidate list in a later pass. + // + // We don't require output types to be resolved at this point, which + // allows them to be inferred based on how they are used later in the + // function. + if is_input { + let ty = self.resolve_ty_shallow(&ty); + match ty.kind(Interner) { + TyKind::FnDef(def, parameters) => { + let fnptr_ty = TyKind::Function( + CallableSig::from_def(self.db, *def, parameters).to_fn_ptr(), + ) + .intern(Interner); + _ = self.coerce(Some(expr), &ty, &fnptr_ty); + } + TyKind::Ref(mutbl, _, base_ty) => { + let ptr_ty = TyKind::Raw(*mutbl, base_ty.clone()).intern(Interner); + _ = self.coerce(Some(expr), &ty, &ptr_ty); + } + _ => {} + } + } + }; + + let diverge = asm.options.contains(AsmOptions::NORETURN); + asm.operands.iter().for_each(|(_, operand)| match *operand { + AsmOperand::In { expr, .. } => check_expr_asm_operand(expr, true), + AsmOperand::Out { expr: Some(expr), .. } | AsmOperand::InOut { expr, .. } => { + check_expr_asm_operand(expr, false) + } + AsmOperand::Out { expr: None, .. } => (), + AsmOperand::SplitInOut { in_expr, out_expr, .. } => { + check_expr_asm_operand(in_expr, true); + if let Some(out_expr) = out_expr { + check_expr_asm_operand(out_expr, false); + } + } + // FIXME + AsmOperand::Label(_) => (), + // FIXME + AsmOperand::Const(_) => (), + // FIXME + AsmOperand::Sym(_) => (), + }); + if diverge { + self.result.standard_types.never.clone() + } else { + self.result.standard_types.unit.clone() + } } }; // use a new type variable if we got unknown here diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs index 7fed5f0203ba..8e52725e5360 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs @@ -3,7 +3,9 @@ use chalk_ir::{cast::Cast, Mutability}; use hir_def::{ - hir::{Array, BinaryOp, BindingAnnotation, Expr, ExprId, PatId, Statement, UnaryOp}, + hir::{ + Array, AsmOperand, BinaryOp, BindingAnnotation, Expr, ExprId, PatId, Statement, UnaryOp, + }, lang_item::LangItem, }; use hir_expand::name::Name; @@ -39,7 +41,25 @@ impl InferenceContext<'_> { fn infer_mut_expr_without_adjust(&mut self, tgt_expr: ExprId, mutability: Mutability) { match &self.body[tgt_expr] { Expr::Missing => (), - Expr::InlineAsm(e) => self.infer_mut_expr_without_adjust(e.e, Mutability::Not), + Expr::InlineAsm(e) => { + e.operands.iter().for_each(|(_, op)| match op { + AsmOperand::In { expr, .. } + | AsmOperand::Out { expr: Some(expr), .. } + | AsmOperand::InOut { expr, .. } => { + self.infer_mut_expr_without_adjust(*expr, Mutability::Not) + } + AsmOperand::SplitInOut { in_expr, out_expr, .. } => { + self.infer_mut_expr_without_adjust(*in_expr, Mutability::Not); + if let Some(out_expr) = out_expr { + self.infer_mut_expr_without_adjust(*out_expr, Mutability::Not); + } + } + AsmOperand::Out { expr: None, .. } + | AsmOperand::Label(_) + | AsmOperand::Sym(_) + | AsmOperand::Const(_) => (), + }); + } Expr::OffsetOf(_) => (), &Expr::If { condition, then_branch, else_branch } => { self.infer_mut_expr(condition, Mutability::Not); @@ -129,7 +149,7 @@ impl InferenceContext<'_> { target, }) = base_adjustments { - // For assignee exprs `IndexMut` obiligations are already applied + // For assignee exprs `IndexMut` obligations are already applied if !is_assignee_expr { if let TyKind::Ref(_, _, ty) = target.kind(Interner) { base_ty = Some(ty.clone()); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs index 0b44bbec70f7..e4841c7b15b6 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs @@ -247,8 +247,12 @@ impl InferenceContext<'_> { &self.resolver, self.owner.into(), ); - let trait_ref = - ctx.lower_trait_ref_from_resolved_path(trait_, resolved_segment, None); + let trait_ref = ctx.lower_trait_ref_from_resolved_path( + trait_, + resolved_segment, + self.table.new_type_var(), + ); + self.resolve_trait_assoc_item(trait_ref, segment, id) } (def, _) => { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs index c0f5ddddcbe3..7300453ff001 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs @@ -9,6 +9,7 @@ use chalk_ir::{ use chalk_solve::infer::ParameterEnaVariableExt; use either::Either; use ena::unify::UnifyKey; +use hir_def::{lang_item::LangItem, AdtId}; use hir_expand::name::Name; use intern::sym; use rustc_hash::FxHashMap; @@ -21,7 +22,7 @@ use crate::{ to_chalk_trait_id, traits::FnTrait, AliasEq, AliasTy, BoundVar, Canonical, Const, ConstValue, DebruijnIndex, DomainGoal, GenericArg, GenericArgData, Goal, GoalData, Guidance, InEnvironment, InferenceVar, Interner, Lifetime, OpaqueTyId, ParamKind, ProjectionTy, ProjectionTyExt, Scalar, - Solution, Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, TyKind, VariableKind, + Solution, Substitution, TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt, TyKind, VariableKind, WhereClause, }; @@ -265,14 +266,16 @@ impl<'a> InferenceTable<'a> { } let v = InferenceVar::from(i as u32); let root = self.var_unification_table.inference_var_root(v); - if let Some(data) = self.type_variable_table.get_mut(root.index() as usize) { - *data |= TypeVariableFlags::DIVERGING; - } + self.modify_type_variable_flag(root, |f| { + *f |= TypeVariableFlags::DIVERGING; + }); } } pub(super) fn set_diverging(&mut self, iv: InferenceVar, diverging: bool) { - self.type_variable_table[iv.index() as usize].set(TypeVariableFlags::DIVERGING, diverging); + self.modify_type_variable_flag(iv, |f| { + f.set(TypeVariableFlags::DIVERGING, diverging); + }); } fn fallback_value(&self, iv: InferenceVar, kind: TyVariableKind) -> Ty { @@ -369,6 +372,18 @@ impl<'a> InferenceTable<'a> { var } + fn modify_type_variable_flag(&mut self, var: InferenceVar, cb: F) + where + F: FnOnce(&mut TypeVariableFlags), + { + let idx = var.index() as usize; + if self.type_variable_table.len() <= idx { + self.extend_type_variable_table(idx); + } + if let Some(f) = self.type_variable_table.get_mut(idx) { + cb(f); + } + } fn extend_type_variable_table(&mut self, to_index: usize) { let count = to_index - self.type_variable_table.len() + 1; self.type_variable_table.extend(iter::repeat(TypeVariableFlags::default()).take(count)); @@ -898,6 +913,37 @@ impl<'a> InferenceTable<'a> { _ => c, } } + + /// Check if given type is `Sized` or not + pub(crate) fn is_sized(&mut self, ty: &Ty) -> bool { + // Early return for some obvious types + if matches!(ty.kind(Interner), TyKind::Scalar(..) | TyKind::Ref(..) | TyKind::Raw(..)) { + return true; + } + if let Some((AdtId::StructId(id), subst)) = ty.as_adt() { + let struct_data = self.db.struct_data(id); + if let Some((last_field, _)) = struct_data.variant_data.fields().iter().last() { + let last_field_ty = + self.db.field_types(id.into())[last_field].clone().substitute(Interner, subst); + // Structs can have DST as its last field and such cases are not handled + // as unsized by the chalk, so we do this manually + return self.is_sized(&last_field_ty); + } + } + let Some(sized) = self + .db + .lang_item(self.trait_env.krate, LangItem::Sized) + .and_then(|sized| sized.as_trait()) + else { + return false; + }; + let sized_pred = WhereClause::Implemented(TraitRef { + trait_id: to_chalk_trait_id(sized), + substitution: Substitution::from1(Interner, ty.clone()), + }); + let goal = GoalData::DomainGoal(chalk_ir::DomainGoal::Holds(sized_pred)).intern(Interner); + matches!(self.try_obligation(goal), Some(Solution::Unique(_))) + } } impl fmt::Debug for InferenceTable<'_> { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs index 25362d23d554..4cdc0db46a15 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs @@ -72,6 +72,7 @@ pub type Variants = hir_def::layout::Variants) -> fmt::Result { match self { + LayoutError::EmptyUnion => write!(f, "type is an union with no fields"), LayoutError::HasErrorConst => write!(f, "type contains an unevaluatable const"), LayoutError::HasErrorType => write!(f, "type contains an error"), LayoutError::HasPlaceholder => write!(f, "type contains placeholders"), @@ -98,6 +101,9 @@ impl fmt::Display for LayoutError { } LayoutError::SizeOverflow => write!(f, "size overflow"), LayoutError::TargetLayoutNotAvailable => write!(f, "target layout not available"), + LayoutError::UnexpectedUnsized => { + write!(f, "an unsized type was found where a sized type was expected") + } LayoutError::Unknown => write!(f, "unknown"), LayoutError::UserReprTooSmall => { write!(f, "the `#[repr]` hint is too small to hold the discriminants of the enum") @@ -109,9 +115,8 @@ impl fmt::Display for LayoutError { impl From> for LayoutError { fn from(err: LayoutCalculatorError) -> Self { match err { - LayoutCalculatorError::UnexpectedUnsized(_) | LayoutCalculatorError::EmptyUnion => { - LayoutError::Unknown - } + LayoutCalculatorError::EmptyUnion => LayoutError::EmptyUnion, + LayoutCalculatorError::UnexpectedUnsized(_) => LayoutError::UnexpectedUnsized, LayoutCalculatorError::SizeOverflow => LayoutError::SizeOverflow, } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/target.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/target.rs index 9b1424548c2a..7d77f6d0731a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/target.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/target.rs @@ -11,8 +11,8 @@ pub fn target_data_layout_query( db: &dyn HirDatabase, krate: CrateId, ) -> Result, Arc> { - match db.data_layout(krate) { - Ok(it) => match TargetDataLayout::parse_from_llvm_datalayout_string(&it) { + match &db.crate_workspace_data()[&krate].data_layout { + Ok(it) => match TargetDataLayout::parse_from_llvm_datalayout_string(it) { Ok(it) => Ok(Arc::new(it)), Err(e) => { Err(match e { @@ -42,6 +42,6 @@ pub fn target_data_layout_query( }.into()) } }, - Err(e) => Err(e), + Err(e) => Err(e.clone()), } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs index 26ab02558a1b..5ed41b99ba3e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -42,6 +42,7 @@ pub mod lang_items; pub mod layout; pub mod method_resolution; pub mod mir; +pub mod object_safety; pub mod primitive; pub mod traits; @@ -82,6 +83,7 @@ pub use autoderef::autoderef; pub use builder::{ParamKind, TyBuilder}; pub use chalk_ext::*; pub use infer::{ + cast::CastError, closure::{CaptureKind, CapturedItem}, could_coerce, could_unify, could_unify_deeply, Adjust, Adjustment, AutoBorrow, BindingMode, InferenceDiagnostic, InferenceResult, OverloadedDeref, PointerCast, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs index 370d9ba99cb0..c6c2108e34af 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -58,7 +58,7 @@ use crate::{ }, db::HirDatabase, error_lifetime, - generics::{generics, Generics}, + generics::{generics, trait_self_param_idx, Generics}, make_binders, mapping::{from_chalk_trait_id, lt_to_placeholder_idx, ToChalk}, static_lifetime, to_assoc_type_id, to_chalk_trait_id, to_placeholder_idx, @@ -516,8 +516,11 @@ impl<'a> TyLoweringContext<'a> { TypeNs::TraitId(trait_) => { let ty = match remaining_segments.len() { 1 => { - let trait_ref = - self.lower_trait_ref_from_resolved_path(trait_, resolved_segment, None); + let trait_ref = self.lower_trait_ref_from_resolved_path( + trait_, + resolved_segment, + TyKind::Error.intern(Interner), + ); let segment = remaining_segments.first().unwrap(); let found = self .db @@ -952,11 +955,17 @@ impl<'a> TyLoweringContext<'a> { Substitution::from_iter(Interner, substs) } - fn lower_trait_ref_from_path( + pub(crate) fn lower_trait_ref_from_resolved_path( &self, - path: &Path, - explicit_self_ty: Option, - ) -> Option { + resolved: TraitId, + segment: PathSegment<'_>, + explicit_self_ty: Ty, + ) -> TraitRef { + let substs = self.trait_ref_substs_from_path(segment, resolved, explicit_self_ty); + TraitRef { trait_id: to_chalk_trait_id(resolved), substitution: substs } + } + + fn lower_trait_ref_from_path(&self, path: &Path, explicit_self_ty: Ty) -> Option { let resolved = match self.resolver.resolve_path_in_type_ns_fully(self.db.upcast(), path)? { // FIXME(trait_alias): We need to handle trait alias here. TypeNs::TraitId(tr) => tr, @@ -966,21 +975,7 @@ impl<'a> TyLoweringContext<'a> { Some(self.lower_trait_ref_from_resolved_path(resolved, segment, explicit_self_ty)) } - pub(crate) fn lower_trait_ref_from_resolved_path( - &self, - resolved: TraitId, - segment: PathSegment<'_>, - explicit_self_ty: Option, - ) -> TraitRef { - let substs = self.trait_ref_substs_from_path(segment, resolved, explicit_self_ty); - TraitRef { trait_id: to_chalk_trait_id(resolved), substitution: substs } - } - - fn lower_trait_ref( - &self, - trait_ref: &HirTraitRef, - explicit_self_ty: Option, - ) -> Option { + fn lower_trait_ref(&self, trait_ref: &HirTraitRef, explicit_self_ty: Ty) -> Option { self.lower_trait_ref_from_path(&trait_ref.path, explicit_self_ty) } @@ -988,9 +983,9 @@ impl<'a> TyLoweringContext<'a> { &self, segment: PathSegment<'_>, resolved: TraitId, - explicit_self_ty: Option, + explicit_self_ty: Ty, ) -> Substitution { - self.substs_from_path_segment(segment, Some(resolved.into()), false, explicit_self_ty) + self.substs_from_path_segment(segment, Some(resolved.into()), false, Some(explicit_self_ty)) } pub(crate) fn lower_where_predicate<'b>( @@ -1041,7 +1036,7 @@ impl<'a> TyLoweringContext<'a> { let mut trait_ref = None; let clause = match bound.as_ref() { TypeBound::Path(path, TraitBoundModifier::None) => { - trait_ref = self.lower_trait_ref_from_path(path, Some(self_ty)); + trait_ref = self.lower_trait_ref_from_path(path, self_ty); trait_ref.clone().map(WhereClause::Implemented).map(crate::wrap_empty_binders) } TypeBound::Path(path, TraitBoundModifier::Maybe) => { @@ -1053,7 +1048,7 @@ impl<'a> TyLoweringContext<'a> { // `?Sized` has no of them. // If we got another trait here ignore the bound completely. let trait_id = self - .lower_trait_ref_from_path(path, Some(self_ty.clone())) + .lower_trait_ref_from_path(path, self_ty.clone()) .map(|trait_ref| trait_ref.hir_trait_id()); if trait_id == sized_trait { self.unsized_types.borrow_mut().insert(self_ty); @@ -1062,7 +1057,7 @@ impl<'a> TyLoweringContext<'a> { } TypeBound::ForLifetime(_, path) => { // FIXME Don't silently drop the hrtb lifetimes here - trait_ref = self.lower_trait_ref_from_path(path, Some(self_ty)); + trait_ref = self.lower_trait_ref_from_path(path, self_ty); trait_ref.clone().map(WhereClause::Implemented).map(crate::wrap_empty_binders) } TypeBound::Lifetime(l) => { @@ -1700,6 +1695,28 @@ pub(crate) fn generic_predicates_query( db: &dyn HirDatabase, def: GenericDefId, ) -> GenericPredicates { + generic_predicates_filtered_by(db, def, |_, _| true) +} + +/// Resolve the where clause(s) of an item with generics, +/// except the ones inherited from the parent +pub(crate) fn generic_predicates_without_parent_query( + db: &dyn HirDatabase, + def: GenericDefId, +) -> GenericPredicates { + generic_predicates_filtered_by(db, def, |_, d| *d == def) +} + +/// Resolve the where clause(s) of an item with generics, +/// except the ones inherited from the parent +fn generic_predicates_filtered_by( + db: &dyn HirDatabase, + def: GenericDefId, + filter: F, +) -> GenericPredicates +where + F: Fn(&WherePredicate, &GenericDefId) -> bool, +{ let resolver = def.resolver(db.upcast()); let (impl_trait_lowering, param_lowering) = match def { GenericDefId::FunctionId(_) => { @@ -1714,6 +1731,7 @@ pub(crate) fn generic_predicates_query( let mut predicates = resolver .where_predicates_in_scope() + .filter(|(pred, def)| filter(pred, def)) .flat_map(|(pred, def)| { ctx.lower_where_predicate(pred, def, false).map(|p| make_binders(db, &generics, p)) }) @@ -1747,21 +1765,7 @@ fn implicitly_sized_clauses<'a, 'subst: 'a>( .lang_item(resolver.krate(), LangItem::Sized) .and_then(|lang_item| lang_item.as_trait().map(to_chalk_trait_id))?; - let get_trait_self_idx = |container: ItemContainerId| { - if matches!(container, ItemContainerId::TraitId(_)) { - let generics = generics(db.upcast(), def); - Some(generics.len_self()) - } else { - None - } - }; - let trait_self_idx = match def { - GenericDefId::TraitId(_) => Some(0), - GenericDefId::FunctionId(it) => get_trait_self_idx(it.lookup(db.upcast()).container), - GenericDefId::ConstId(it) => get_trait_self_idx(it.lookup(db.upcast()).container), - GenericDefId::TypeAliasId(it) => get_trait_self_idx(it.lookup(db.upcast()).container), - _ => None, - }; + let trait_self_idx = trait_self_param_idx(db.upcast(), def); Some( substitution @@ -2117,7 +2121,7 @@ pub(crate) fn impl_trait_query(db: &dyn HirDatabase, impl_id: ImplId) -> Option< .with_type_param_mode(ParamLoweringMode::Variable); let (self_ty, binders) = db.impl_self_ty(impl_id).into_value_and_skipped_binders(); let target_trait = impl_data.target_trait.as_ref()?; - Some(Binders::new(binders, ctx.lower_trait_ref(target_trait, Some(self_ty))?)) + Some(Binders::new(binders, ctx.lower_trait_ref(target_trait, self_ty)?)) } pub(crate) fn return_type_impl_traits( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs index 22d4da0e755a..8e815aabf207 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs @@ -185,8 +185,8 @@ impl ProjectionElem { never!("Out of bound tuple field"); TyKind::Error.intern(Interner) }), - _ => { - never!("Only tuple has tuple field"); + ty => { + never!("Only tuple has tuple field: {:?}", ty); TyKind::Error.intern(Interner) } }, @@ -837,7 +837,9 @@ pub enum CastKind { PointerFromExposedAddress, /// All sorts of pointer-to-pointer casts. Note that reference-to-raw-ptr casts are /// translated into `&raw mut/const *r`, i.e., they are not actually casts. - Pointer(PointerCast), + PtrToPtr, + /// Pointer related casts that are done by coercions. + PointerCoercion(PointerCast), /// Cast into a dyn* object. DynStar, IntToInt, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs index 1bb0c1886857..0d42617d185c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs @@ -421,9 +421,25 @@ impl MirEvalError { } MirEvalError::MirLowerError(func, err) => { let function_name = db.function_data(*func); + let self_ = match func.lookup(db.upcast()).container { + ItemContainerId::ImplId(impl_id) => Some({ + let generics = crate::generics::generics(db.upcast(), impl_id.into()); + let substs = generics.placeholder_subst(db); + db.impl_self_ty(impl_id) + .substitute(Interner, &substs) + .display(db, edition) + .to_string() + }), + ItemContainerId::TraitId(it) => { + Some(db.trait_data(it).name.display(db.upcast(), edition).to_string()) + } + _ => None, + }; writeln!( f, - "MIR lowering for function `{}` ({:?}) failed due:", + "MIR lowering for function `{}{}{}` ({:?}) failed due:", + self_.as_deref().unwrap_or_default(), + if self_.is_some() { "::" } else { "" }, function_name.name.display(db.upcast(), edition), func )?; @@ -1475,7 +1491,7 @@ impl Evaluator<'_> { } } Rvalue::Cast(kind, operand, target_ty) => match kind { - CastKind::Pointer(cast) => match cast { + CastKind::PointerCoercion(cast) => match cast { PointerCast::ReifyFnPointer | PointerCast::ClosureFnPointer(_) => { let current_ty = self.operand_ty(operand, locals)?; if let TyKind::FnDef(_, _) | TyKind::Closure(_, _) = @@ -1506,6 +1522,7 @@ impl Evaluator<'_> { }, CastKind::DynStar => not_supported!("dyn star cast"), CastKind::IntToInt + | CastKind::PtrToPtr | CastKind::PointerExposeAddress | CastKind::PointerFromExposedAddress => { let current_ty = self.operand_ty(operand, locals)?; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs index 371a5278dc9a..595a78da10fb 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs @@ -399,7 +399,7 @@ extern "C" { fn memcmp(s1: *const u8, s2: *const u8, n: usize) -> i32; } -fn my_cmp(x: &[u8], y: &[u8]) -> i32 { +fn my_cmp(x: &[u8; 3], y: &[u8; 3]) -> i32 { memcmp(x as *const u8, y as *const u8, x.len()) } @@ -779,6 +779,7 @@ fn main() { fn posix_getenv() { check_pass( r#" +//- minicore: sized //- /main.rs env:foo=bar type c_char = u8; @@ -849,7 +850,7 @@ fn main() { fn regression_14966() { check_pass( r#" -//- minicore: fn, copy, coerce_unsized +//- minicore: fn, copy, coerce_unsized, dispatch_from_dyn trait A { fn a(&self) {} } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs index 9e235504519e..a2cb122c5439 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs @@ -31,7 +31,7 @@ use crate::{ display::HirDisplay, error_lifetime, generics::generics, - infer::{CaptureKind, CapturedItem, TypeMismatch}, + infer::{cast::CastTy, unify::InferenceTable, CaptureKind, CapturedItem, TypeMismatch}, inhabitedness::is_ty_uninhabited_from, layout::LayoutError, mapping::ToChalk, @@ -94,7 +94,8 @@ pub enum MirLowerError { UnresolvedField, UnsizedTemporary(Ty), MissingFunctionDefinition(DefWithBodyId, ExprId), - TypeMismatch(Option), + TypeMismatch(TypeMismatch), + HasErrors, /// This should never happen. Type mismatch should catch everything. TypeError(&'static str), NotSupported(String), @@ -179,15 +180,13 @@ impl MirLowerError { body.pretty_print_expr(db.upcast(), *owner, *it, edition) )?; } - MirLowerError::TypeMismatch(e) => match e { - Some(e) => writeln!( - f, - "Type mismatch: Expected {}, found {}", - e.expected.display(db, edition), - e.actual.display(db, edition), - )?, - None => writeln!(f, "Type mismatch: types mismatch with {{unknown}}",)?, - }, + MirLowerError::HasErrors => writeln!(f, "Type inference result contains errors")?, + MirLowerError::TypeMismatch(e) => writeln!( + f, + "Type mismatch: Expected {}, found {}", + e.expected.display(db, edition), + e.actual.display(db, edition), + )?, MirLowerError::GenericArgNotProvided(id, subst) => { let parent = id.parent; let param = &db.generic_params(parent)[id.local_id]; @@ -362,7 +361,7 @@ impl<'ctx> MirLowerCtx<'ctx> { current, place, Rvalue::Cast( - CastKind::Pointer(*cast), + CastKind::PointerCoercion(*cast), Operand::Copy(p), last.target.clone(), ), @@ -898,14 +897,26 @@ impl<'ctx> MirLowerCtx<'ctx> { let Some((it, current)) = self.lower_expr_to_some_operand(*expr, current)? else { return Ok(None); }; - let source_ty = self.infer[*expr].clone(); - let target_ty = self.infer[expr_id].clone(); - self.push_assignment( - current, - place, - Rvalue::Cast(cast_kind(&source_ty, &target_ty)?, it, target_ty), - expr_id.into(), - ); + // Since we don't have THIR, this is the "zipped" version of [rustc's HIR lowering](https://github.com/rust-lang/rust/blob/e71f9529121ca8f687e4b725e3c9adc3f1ebab4d/compiler/rustc_mir_build/src/thir/cx/expr.rs#L165-L178) + // and [THIR lowering as RValue](https://github.com/rust-lang/rust/blob/a4601859ae3875732797873612d424976d9e3dd0/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs#L193-L313) + let rvalue = if self.infer.coercion_casts.contains(expr) { + Rvalue::Use(it) + } else { + let source_ty = self.infer[*expr].clone(); + let target_ty = self.infer[expr_id].clone(); + let cast_kind = if source_ty.as_reference().is_some() { + CastKind::PointerCoercion(PointerCast::ArrayToPointer) + } else { + let mut table = InferenceTable::new( + self.db, + self.db.trait_environment_for_body(self.owner), + ); + cast_kind(&mut table, &source_ty, &target_ty)? + }; + + Rvalue::Cast(cast_kind, it, target_ty) + }; + self.push_assignment(current, place, rvalue, expr_id.into()); Ok(Some(current)) } Expr::Ref { expr, rawness: _, mutability } => { @@ -2005,40 +2016,21 @@ impl<'ctx> MirLowerCtx<'ctx> { } } -fn cast_kind(source_ty: &Ty, target_ty: &Ty) -> Result { - Ok(match (source_ty.kind(Interner), target_ty.kind(Interner)) { - (TyKind::FnDef(..), TyKind::Function(_)) => CastKind::Pointer(PointerCast::ReifyFnPointer), - (TyKind::Scalar(s), TyKind::Scalar(t)) => match (s, t) { - (chalk_ir::Scalar::Float(_), chalk_ir::Scalar::Float(_)) => CastKind::FloatToFloat, - (chalk_ir::Scalar::Float(_), _) => CastKind::FloatToInt, - (_, chalk_ir::Scalar::Float(_)) => CastKind::IntToFloat, - (_, _) => CastKind::IntToInt, - }, - (TyKind::Scalar(_), TyKind::Raw(..)) => CastKind::PointerFromExposedAddress, - (TyKind::Raw(..), TyKind::Scalar(_)) => CastKind::PointerExposeAddress, - (TyKind::Raw(_, a) | TyKind::Ref(_, _, a), TyKind::Raw(_, b) | TyKind::Ref(_, _, b)) => { - CastKind::Pointer(if a == b { - PointerCast::MutToConstPointer - } else if matches!(b.kind(Interner), TyKind::Slice(_)) - && matches!(a.kind(Interner), TyKind::Array(_, _)) - || matches!(b.kind(Interner), TyKind::Dyn(_)) - { - PointerCast::Unsize - } else if matches!(a.kind(Interner), TyKind::Slice(s) if s == b) { - PointerCast::ArrayToPointer - } else { - // cast between two sized pointer, like *const i32 to *const i8, or two unsized pointer, like - // slice to slice, slice to str, ... . These are no-ops (even in the unsized case, no metadata - // will be touched) but there is no specific variant - // for it in `PointerCast` so we use `MutToConstPointer` - PointerCast::MutToConstPointer - }) - } - // Enum to int casts - (TyKind::Scalar(_), TyKind::Adt(..)) | (TyKind::Adt(..), TyKind::Scalar(_)) => { - CastKind::IntToInt +fn cast_kind(table: &mut InferenceTable<'_>, source_ty: &Ty, target_ty: &Ty) -> Result { + let from = CastTy::from_ty(table, source_ty); + let cast = CastTy::from_ty(table, target_ty); + Ok(match (from, cast) { + (Some(CastTy::Ptr(..) | CastTy::FnPtr), Some(CastTy::Int(_))) => { + CastKind::PointerExposeAddress } - (a, b) => not_supported!("Unknown cast between {a:?} and {b:?}"), + (Some(CastTy::Int(_)), Some(CastTy::Ptr(..))) => CastKind::PointerFromExposedAddress, + (Some(CastTy::Int(_)), Some(CastTy::Int(_))) => CastKind::IntToInt, + (Some(CastTy::FnPtr), Some(CastTy::Ptr(..))) => CastKind::FnPtrToPtr, + (Some(CastTy::Float), Some(CastTy::Int(_))) => CastKind::FloatToInt, + (Some(CastTy::Int(_)), Some(CastTy::Float)) => CastKind::IntToFloat, + (Some(CastTy::Float), Some(CastTy::Float)) => CastKind::FloatToFloat, + (Some(CastTy::Ptr(..)), Some(CastTy::Ptr(..))) => CastKind::PtrToPtr, + _ => not_supported!("Unknown cast between {source_ty:?} and {target_ty:?}"), }) } @@ -2191,7 +2183,7 @@ pub fn lower_to_mir( root_expr: ExprId, ) -> Result { if infer.has_errors { - return Err(MirLowerError::TypeMismatch(None)); + return Err(MirLowerError::HasErrors); } let mut ctx = MirLowerCtx::new(db, owner, body, infer); // 0 is return local diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/object_safety.rs b/src/tools/rust-analyzer/crates/hir-ty/src/object_safety.rs new file mode 100644 index 000000000000..a4c662685552 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/object_safety.rs @@ -0,0 +1,612 @@ +//! Compute the object-safety of a trait + +use std::ops::ControlFlow; + +use chalk_ir::{ + cast::Cast, + visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor}, + DebruijnIndex, +}; +use chalk_solve::rust_ir::InlineBound; +use hir_def::{ + lang_item::LangItem, AssocItemId, ConstId, FunctionId, GenericDefId, HasModule, TraitId, + TypeAliasId, +}; +use rustc_hash::FxHashSet; +use smallvec::SmallVec; + +use crate::{ + all_super_traits, + db::HirDatabase, + from_assoc_type_id, from_chalk_trait_id, + generics::{generics, trait_self_param_idx}, + lower::callable_item_sig, + to_assoc_type_id, to_chalk_trait_id, + utils::elaborate_clause_supertraits, + AliasEq, AliasTy, Binders, BoundVar, CallableSig, GoalData, ImplTraitId, Interner, OpaqueTyId, + ProjectionTyExt, Solution, Substitution, TraitRef, Ty, TyKind, WhereClause, +}; + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum ObjectSafetyViolation { + SizedSelf, + SelfReferential, + Method(FunctionId, MethodViolationCode), + AssocConst(ConstId), + GAT(TypeAliasId), + // This doesn't exist in rustc, but added for better visualization + HasNonSafeSuperTrait(TraitId), +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum MethodViolationCode { + StaticMethod, + ReferencesSelfInput, + ReferencesSelfOutput, + ReferencesImplTraitInTrait, + AsyncFn, + WhereClauseReferencesSelf, + Generic, + UndispatchableReceiver, +} + +pub fn object_safety(db: &dyn HirDatabase, trait_: TraitId) -> Option { + for super_trait in all_super_traits(db.upcast(), trait_).into_iter().skip(1).rev() { + if db.object_safety_of_trait(super_trait).is_some() { + return Some(ObjectSafetyViolation::HasNonSafeSuperTrait(super_trait)); + } + } + + db.object_safety_of_trait(trait_) +} + +pub fn object_safety_with_callback( + db: &dyn HirDatabase, + trait_: TraitId, + cb: &mut F, +) -> ControlFlow<()> +where + F: FnMut(ObjectSafetyViolation) -> ControlFlow<()>, +{ + for super_trait in all_super_traits(db.upcast(), trait_).into_iter().skip(1).rev() { + if db.object_safety_of_trait(super_trait).is_some() { + cb(ObjectSafetyViolation::HasNonSafeSuperTrait(trait_))?; + } + } + + object_safety_of_trait_with_callback(db, trait_, cb) +} + +pub fn object_safety_of_trait_with_callback( + db: &dyn HirDatabase, + trait_: TraitId, + cb: &mut F, +) -> ControlFlow<()> +where + F: FnMut(ObjectSafetyViolation) -> ControlFlow<()>, +{ + // Check whether this has a `Sized` bound + if generics_require_sized_self(db, trait_.into()) { + cb(ObjectSafetyViolation::SizedSelf)?; + } + + // Check if there exist bounds that referencing self + if predicates_reference_self(db, trait_) { + cb(ObjectSafetyViolation::SelfReferential)?; + } + if bounds_reference_self(db, trait_) { + cb(ObjectSafetyViolation::SelfReferential)?; + } + + // rustc checks for non-lifetime binders here, but we don't support HRTB yet + + let trait_data = db.trait_data(trait_); + for (_, assoc_item) in &trait_data.items { + object_safety_violation_for_assoc_item(db, trait_, *assoc_item, cb)?; + } + + ControlFlow::Continue(()) +} + +pub fn object_safety_of_trait_query( + db: &dyn HirDatabase, + trait_: TraitId, +) -> Option { + let mut res = None; + object_safety_of_trait_with_callback(db, trait_, &mut |osv| { + res = Some(osv); + ControlFlow::Break(()) + }); + + res +} + +fn generics_require_sized_self(db: &dyn HirDatabase, def: GenericDefId) -> bool { + let krate = def.module(db.upcast()).krate(); + let Some(sized) = db.lang_item(krate, LangItem::Sized).and_then(|l| l.as_trait()) else { + return false; + }; + + let Some(trait_self_param_idx) = trait_self_param_idx(db.upcast(), def) else { + return false; + }; + + let predicates = &*db.generic_predicates(def); + let predicates = predicates.iter().map(|p| p.skip_binders().skip_binders().clone()); + elaborate_clause_supertraits(db, predicates).any(|pred| match pred { + WhereClause::Implemented(trait_ref) => { + if from_chalk_trait_id(trait_ref.trait_id) == sized { + if let TyKind::BoundVar(it) = + *trait_ref.self_type_parameter(Interner).kind(Interner) + { + // Since `generic_predicates` is `Binder>`, the `DebrujinIndex` of + // self-parameter is `1` + return it + .index_if_bound_at(DebruijnIndex::ONE) + .is_some_and(|idx| idx == trait_self_param_idx); + } + } + false + } + _ => false, + }) +} + +// rustc gathers all the spans that references `Self` for error rendering, +// but we don't have good way to render such locations. +// So, just return single boolean value for existence of such `Self` reference +fn predicates_reference_self(db: &dyn HirDatabase, trait_: TraitId) -> bool { + db.generic_predicates(trait_.into()) + .iter() + .any(|pred| predicate_references_self(db, trait_, pred, AllowSelfProjection::No)) +} + +// Same as the above, `predicates_reference_self` +fn bounds_reference_self(db: &dyn HirDatabase, trait_: TraitId) -> bool { + let trait_data = db.trait_data(trait_); + trait_data + .items + .iter() + .filter_map(|(_, it)| match *it { + AssocItemId::TypeAliasId(id) => { + let assoc_ty_id = to_assoc_type_id(id); + let assoc_ty_data = db.associated_ty_data(assoc_ty_id); + Some(assoc_ty_data) + } + _ => None, + }) + .any(|assoc_ty_data| { + assoc_ty_data.binders.skip_binders().bounds.iter().any(|bound| { + let def = from_assoc_type_id(assoc_ty_data.id).into(); + match bound.skip_binders() { + InlineBound::TraitBound(it) => it.args_no_self.iter().any(|arg| { + contains_illegal_self_type_reference( + db, + def, + trait_, + arg, + DebruijnIndex::ONE, + AllowSelfProjection::Yes, + ) + }), + InlineBound::AliasEqBound(it) => it.parameters.iter().any(|arg| { + contains_illegal_self_type_reference( + db, + def, + trait_, + arg, + DebruijnIndex::ONE, + AllowSelfProjection::Yes, + ) + }), + } + }) + }) +} + +#[derive(Clone, Copy)] +enum AllowSelfProjection { + Yes, + No, +} + +fn predicate_references_self( + db: &dyn HirDatabase, + trait_: TraitId, + predicate: &Binders>, + allow_self_projection: AllowSelfProjection, +) -> bool { + match predicate.skip_binders().skip_binders() { + WhereClause::Implemented(trait_ref) => { + trait_ref.substitution.iter(Interner).skip(1).any(|arg| { + contains_illegal_self_type_reference( + db, + trait_.into(), + trait_, + arg, + DebruijnIndex::ONE, + allow_self_projection, + ) + }) + } + WhereClause::AliasEq(AliasEq { alias: AliasTy::Projection(proj), .. }) => { + proj.substitution.iter(Interner).skip(1).any(|arg| { + contains_illegal_self_type_reference( + db, + trait_.into(), + trait_, + arg, + DebruijnIndex::ONE, + allow_self_projection, + ) + }) + } + _ => false, + } +} + +fn contains_illegal_self_type_reference>( + db: &dyn HirDatabase, + def: GenericDefId, + trait_: TraitId, + t: &T, + outer_binder: DebruijnIndex, + allow_self_projection: AllowSelfProjection, +) -> bool { + let Some(trait_self_param_idx) = trait_self_param_idx(db.upcast(), def) else { + return false; + }; + struct IllegalSelfTypeVisitor<'a> { + db: &'a dyn HirDatabase, + trait_: TraitId, + super_traits: Option>, + trait_self_param_idx: usize, + allow_self_projection: AllowSelfProjection, + } + impl<'a> TypeVisitor for IllegalSelfTypeVisitor<'a> { + type BreakTy = (); + + fn as_dyn(&mut self) -> &mut dyn TypeVisitor { + self + } + + fn interner(&self) -> Interner { + Interner + } + + fn visit_ty(&mut self, ty: &Ty, outer_binder: DebruijnIndex) -> ControlFlow { + match ty.kind(Interner) { + TyKind::BoundVar(BoundVar { debruijn, index }) => { + if *debruijn == outer_binder && *index == self.trait_self_param_idx { + ControlFlow::Break(()) + } else { + ty.super_visit_with(self.as_dyn(), outer_binder) + } + } + TyKind::Alias(AliasTy::Projection(proj)) => match self.allow_self_projection { + AllowSelfProjection::Yes => { + let trait_ = proj.trait_(self.db); + if self.super_traits.is_none() { + self.super_traits = + Some(all_super_traits(self.db.upcast(), self.trait_)); + } + if self.super_traits.as_ref().is_some_and(|s| s.contains(&trait_)) { + ControlFlow::Continue(()) + } else { + ty.super_visit_with(self.as_dyn(), outer_binder) + } + } + AllowSelfProjection::No => ty.super_visit_with(self.as_dyn(), outer_binder), + }, + _ => ty.super_visit_with(self.as_dyn(), outer_binder), + } + } + + fn visit_const( + &mut self, + constant: &chalk_ir::Const, + outer_binder: DebruijnIndex, + ) -> std::ops::ControlFlow { + constant.data(Interner).ty.super_visit_with(self.as_dyn(), outer_binder) + } + } + + let mut visitor = IllegalSelfTypeVisitor { + db, + trait_, + super_traits: None, + trait_self_param_idx, + allow_self_projection, + }; + t.visit_with(visitor.as_dyn(), outer_binder).is_break() +} + +fn object_safety_violation_for_assoc_item( + db: &dyn HirDatabase, + trait_: TraitId, + item: AssocItemId, + cb: &mut F, +) -> ControlFlow<()> +where + F: FnMut(ObjectSafetyViolation) -> ControlFlow<()>, +{ + // Any item that has a `Self : Sized` requisite is otherwise + // exempt from the regulations. + if generics_require_sized_self(db, item.into()) { + return ControlFlow::Continue(()); + } + + match item { + AssocItemId::ConstId(it) => cb(ObjectSafetyViolation::AssocConst(it)), + AssocItemId::FunctionId(it) => { + virtual_call_violations_for_method(db, trait_, it, &mut |mvc| { + cb(ObjectSafetyViolation::Method(it, mvc)) + }) + } + AssocItemId::TypeAliasId(it) => { + let def_map = db.crate_def_map(trait_.krate(db.upcast())); + if def_map.is_unstable_feature_enabled(&intern::sym::generic_associated_type_extended) { + ControlFlow::Continue(()) + } else { + let generic_params = db.generic_params(item.into()); + if !generic_params.is_empty() { + cb(ObjectSafetyViolation::GAT(it)) + } else { + ControlFlow::Continue(()) + } + } + } + } +} + +fn virtual_call_violations_for_method( + db: &dyn HirDatabase, + trait_: TraitId, + func: FunctionId, + cb: &mut F, +) -> ControlFlow<()> +where + F: FnMut(MethodViolationCode) -> ControlFlow<()>, +{ + let func_data = db.function_data(func); + if !func_data.has_self_param() { + cb(MethodViolationCode::StaticMethod)?; + } + + if func_data.is_async() { + cb(MethodViolationCode::AsyncFn)?; + } + + let sig = callable_item_sig(db, func.into()); + if sig.skip_binders().params().iter().skip(1).any(|ty| { + contains_illegal_self_type_reference( + db, + func.into(), + trait_, + ty, + DebruijnIndex::INNERMOST, + AllowSelfProjection::Yes, + ) + }) { + cb(MethodViolationCode::ReferencesSelfInput)?; + } + + if contains_illegal_self_type_reference( + db, + func.into(), + trait_, + sig.skip_binders().ret(), + DebruijnIndex::INNERMOST, + AllowSelfProjection::Yes, + ) { + cb(MethodViolationCode::ReferencesSelfOutput)?; + } + + if !func_data.is_async() { + if let Some(mvc) = contains_illegal_impl_trait_in_trait(db, &sig) { + cb(mvc)?; + } + } + + let generic_params = db.generic_params(func.into()); + if generic_params.len_type_or_consts() > 0 { + cb(MethodViolationCode::Generic)?; + } + + if func_data.has_self_param() && !receiver_is_dispatchable(db, trait_, func, &sig) { + cb(MethodViolationCode::UndispatchableReceiver)?; + } + + let predicates = &*db.generic_predicates_without_parent(func.into()); + let trait_self_idx = trait_self_param_idx(db.upcast(), func.into()); + for pred in predicates { + let pred = pred.skip_binders().skip_binders(); + + if matches!(pred, WhereClause::TypeOutlives(_)) { + continue; + } + + // Allow `impl AutoTrait` predicates + if let WhereClause::Implemented(TraitRef { trait_id, substitution }) = pred { + let trait_data = db.trait_data(from_chalk_trait_id(*trait_id)); + if trait_data.is_auto + && substitution + .as_slice(Interner) + .first() + .and_then(|arg| arg.ty(Interner)) + .and_then(|ty| ty.bound_var(Interner)) + .is_some_and(|b| { + b.debruijn == DebruijnIndex::ONE && Some(b.index) == trait_self_idx + }) + { + continue; + } + } + + if contains_illegal_self_type_reference( + db, + func.into(), + trait_, + pred, + DebruijnIndex::ONE, + AllowSelfProjection::Yes, + ) { + cb(MethodViolationCode::WhereClauseReferencesSelf)?; + break; + } + } + + ControlFlow::Continue(()) +} + +fn receiver_is_dispatchable( + db: &dyn HirDatabase, + trait_: TraitId, + func: FunctionId, + sig: &Binders, +) -> bool { + let Some(trait_self_idx) = trait_self_param_idx(db.upcast(), func.into()) else { + return false; + }; + + // `self: Self` can't be dispatched on, but this is already considered object safe. + // See rustc's comment on https://github.com/rust-lang/rust/blob/3f121b9461cce02a703a0e7e450568849dfaa074/compiler/rustc_trait_selection/src/traits/object_safety.rs#L433-L437 + if sig + .skip_binders() + .params() + .first() + .and_then(|receiver| receiver.bound_var(Interner)) + .is_some_and(|b| { + b == BoundVar { debruijn: DebruijnIndex::INNERMOST, index: trait_self_idx } + }) + { + return true; + } + + let placeholder_subst = generics(db.upcast(), func.into()).placeholder_subst(db); + + let substituted_sig = sig.clone().substitute(Interner, &placeholder_subst); + let Some(receiver_ty) = substituted_sig.params().first() else { + return false; + }; + + let krate = func.module(db.upcast()).krate(); + let traits = ( + db.lang_item(krate, LangItem::Unsize).and_then(|it| it.as_trait()), + db.lang_item(krate, LangItem::DispatchFromDyn).and_then(|it| it.as_trait()), + ); + let (Some(unsize_did), Some(dispatch_from_dyn_did)) = traits else { + return false; + }; + + // Type `U` + let unsized_self_ty = + TyKind::Scalar(chalk_ir::Scalar::Uint(chalk_ir::UintTy::U32)).intern(Interner); + // `Receiver[Self => U]` + let Some(unsized_receiver_ty) = receiver_for_self_ty(db, func, unsized_self_ty.clone()) else { + return false; + }; + + let self_ty = placeholder_subst.as_slice(Interner)[trait_self_idx].assert_ty_ref(Interner); + let unsized_predicate = WhereClause::Implemented(TraitRef { + trait_id: to_chalk_trait_id(unsize_did), + substitution: Substitution::from_iter(Interner, [self_ty.clone(), unsized_self_ty.clone()]), + }); + let trait_predicate = WhereClause::Implemented(TraitRef { + trait_id: to_chalk_trait_id(trait_), + substitution: Substitution::from_iter( + Interner, + std::iter::once(unsized_self_ty.clone().cast(Interner)) + .chain(placeholder_subst.iter(Interner).skip(1).cloned()), + ), + }); + + let generic_predicates = &*db.generic_predicates(func.into()); + + let clauses = std::iter::once(unsized_predicate) + .chain(std::iter::once(trait_predicate)) + .chain(generic_predicates.iter().map(|pred| { + pred.clone().substitute(Interner, &placeholder_subst).into_value_and_skipped_binders().0 + })) + .map(|pred| { + pred.cast::>(Interner).into_from_env_clause(Interner) + }); + let env = chalk_ir::Environment::new(Interner).add_clauses(Interner, clauses); + + let obligation = WhereClause::Implemented(TraitRef { + trait_id: to_chalk_trait_id(dispatch_from_dyn_did), + substitution: Substitution::from_iter(Interner, [receiver_ty.clone(), unsized_receiver_ty]), + }); + let goal = GoalData::DomainGoal(chalk_ir::DomainGoal::Holds(obligation)).intern(Interner); + + let in_env = chalk_ir::InEnvironment::new(&env, goal); + + let mut table = chalk_solve::infer::InferenceTable::::new(); + let canonicalized = table.canonicalize(Interner, in_env); + let solution = db.trait_solve(krate, None, canonicalized.quantified); + + matches!(solution, Some(Solution::Unique(_))) +} + +fn receiver_for_self_ty(db: &dyn HirDatabase, func: FunctionId, ty: Ty) -> Option { + let generics = generics(db.upcast(), func.into()); + let trait_self_idx = trait_self_param_idx(db.upcast(), func.into())?; + let subst = generics.placeholder_subst(db); + let subst = Substitution::from_iter( + Interner, + subst.iter(Interner).enumerate().map(|(idx, arg)| { + if idx == trait_self_idx { + ty.clone().cast(Interner) + } else { + arg.clone() + } + }), + ); + let sig = callable_item_sig(db, func.into()); + let sig = sig.substitute(Interner, &subst); + sig.params_and_return.first().cloned() +} + +fn contains_illegal_impl_trait_in_trait( + db: &dyn HirDatabase, + sig: &Binders, +) -> Option { + struct OpaqueTypeCollector(FxHashSet); + + impl TypeVisitor for OpaqueTypeCollector { + type BreakTy = (); + + fn as_dyn(&mut self) -> &mut dyn TypeVisitor { + self + } + + fn interner(&self) -> Interner { + Interner + } + + fn visit_ty(&mut self, ty: &Ty, outer_binder: DebruijnIndex) -> ControlFlow { + if let TyKind::OpaqueType(opaque_ty_id, _) = ty.kind(Interner) { + self.0.insert(*opaque_ty_id); + } + ty.super_visit_with(self.as_dyn(), outer_binder) + } + } + + let ret = sig.skip_binders().ret(); + let mut visitor = OpaqueTypeCollector(FxHashSet::default()); + ret.visit_with(visitor.as_dyn(), DebruijnIndex::INNERMOST); + + // Since we haven't implemented RPITIT in proper way like rustc yet, + // just check whether `ret` contains RPIT for now + for opaque_ty in visitor.0 { + let impl_trait_id = db.lookup_intern_impl_trait_id(opaque_ty.into()); + if matches!(impl_trait_id, ImplTraitId::ReturnTypeImplTrait(..)) { + return Some(MethodViolationCode::ReferencesImplTraitInTrait); + } + } + + None +} + +#[cfg(test)] +mod tests; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/object_safety/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/object_safety/tests.rs new file mode 100644 index 000000000000..c2a9117c5be4 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/object_safety/tests.rs @@ -0,0 +1,393 @@ +use std::ops::ControlFlow; + +use hir_def::db::DefDatabase; +use rustc_hash::{FxHashMap, FxHashSet}; +use syntax::ToSmolStr; +use test_fixture::WithFixture; + +use crate::{object_safety::object_safety_with_callback, test_db::TestDB}; + +use super::{ + MethodViolationCode::{self, *}, + ObjectSafetyViolation, +}; + +use ObjectSafetyViolationKind::*; + +#[allow(clippy::upper_case_acronyms)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +enum ObjectSafetyViolationKind { + SizedSelf, + SelfReferential, + Method(MethodViolationCode), + AssocConst, + GAT, + HasNonSafeSuperTrait, +} + +fn check_object_safety<'a>( + ra_fixture: &str, + expected: impl IntoIterator)>, +) { + let mut expected: FxHashMap<_, _> = + expected.into_iter().map(|(id, osvs)| (id, FxHashSet::from_iter(osvs))).collect(); + let (db, file_ids) = TestDB::with_many_files(ra_fixture); + for (trait_id, name) in file_ids.into_iter().flat_map(|file_id| { + let module_id = db.module_for_file(file_id); + let def_map = module_id.def_map(&db); + let scope = &def_map[module_id.local_id].scope; + scope + .declarations() + .filter_map(|def| { + if let hir_def::ModuleDefId::TraitId(trait_id) = def { + let name = + db.trait_data(trait_id).name.display_no_db(file_id.edition()).to_smolstr(); + Some((trait_id, name)) + } else { + None + } + }) + .collect::>() + }) { + let Some(expected) = expected.remove(name.as_str()) else { + continue; + }; + let mut osvs = FxHashSet::default(); + object_safety_with_callback(&db, trait_id, &mut |osv| { + osvs.insert(match osv { + ObjectSafetyViolation::SizedSelf => SizedSelf, + ObjectSafetyViolation::SelfReferential => SelfReferential, + ObjectSafetyViolation::Method(_, mvc) => Method(mvc), + ObjectSafetyViolation::AssocConst(_) => AssocConst, + ObjectSafetyViolation::GAT(_) => GAT, + ObjectSafetyViolation::HasNonSafeSuperTrait(_) => HasNonSafeSuperTrait, + }); + ControlFlow::Continue(()) + }); + assert_eq!(osvs, expected, "Object safety violations for `{name}` do not match;"); + } + + let remains: Vec<_> = expected.keys().collect(); + assert!(remains.is_empty(), "Following traits do not exist in the test fixture; {remains:?}"); +} + +#[test] +fn item_bounds_can_reference_self() { + check_object_safety( + r#" +//- minicore: eq +pub trait Foo { + type X: PartialEq; + type Y: PartialEq; + type Z: PartialEq; +} +"#, + [("Foo", vec![])], + ); +} + +#[test] +fn associated_consts() { + check_object_safety( + r#" +trait Bar { + const X: usize; +} +"#, + [("Bar", vec![AssocConst])], + ); +} + +#[test] +fn bounds_reference_self() { + check_object_safety( + r#" +//- minicore: eq +trait X { + type U: PartialEq; +} +"#, + [("X", vec![SelfReferential])], + ); +} + +#[test] +fn by_value_self() { + check_object_safety( + r#" +//- minicore: dispatch_from_dyn +trait Bar { + fn bar(self); +} + +trait Baz { + fn baz(self: Self); +} + +trait Quux { + // Legal because of the where clause: + fn baz(self: Self) where Self : Sized; +} +"#, + [("Bar", vec![]), ("Baz", vec![]), ("Quux", vec![])], + ); +} + +#[test] +fn generic_methods() { + check_object_safety( + r#" +//- minicore: dispatch_from_dyn +trait Bar { + fn bar(&self, t: T); +} + +trait Quux { + fn bar(&self, t: T) + where Self : Sized; +} + +trait Qax { + fn bar<'a>(&self, t: &'a ()); +} +"#, + [("Bar", vec![Method(Generic)]), ("Quux", vec![]), ("Qax", vec![])], + ); +} + +#[test] +fn mentions_self() { + check_object_safety( + r#" +//- minicore: dispatch_from_dyn +trait Bar { + fn bar(&self, x: &Self); +} + +trait Baz { + fn baz(&self) -> Self; +} + +trait Quux { + fn quux(&self, s: &Self) -> Self where Self : Sized; +} +"#, + [ + ("Bar", vec![Method(ReferencesSelfInput)]), + ("Baz", vec![Method(ReferencesSelfOutput)]), + ("Quux", vec![]), + ], + ); +} + +#[test] +fn no_static() { + check_object_safety( + r#" +//- minicore: dispatch_from_dyn +trait Foo { + fn foo() {} +} +"#, + [("Foo", vec![Method(StaticMethod)])], + ); +} + +#[test] +fn sized_self() { + check_object_safety( + r#" +//- minicore: dispatch_from_dyn +trait Bar: Sized { + fn bar(&self, t: T); +} +"#, + [("Bar", vec![SizedSelf])], + ); + + check_object_safety( + r#" +//- minicore: dispatch_from_dyn +trait Bar + where Self : Sized +{ + fn bar(&self, t: T); +} +"#, + [("Bar", vec![SizedSelf])], + ); +} + +#[test] +fn supertrait_gat() { + check_object_safety( + r#" +//- minicore: dispatch_from_dyn +trait GatTrait { + type Gat; +} + +trait SuperTrait: GatTrait {} +"#, + [("GatTrait", vec![GAT]), ("SuperTrait", vec![HasNonSafeSuperTrait])], + ); +} + +#[test] +fn supertrait_mentions_self() { + check_object_safety( + r#" +//- minicore: dispatch_from_dyn +trait Bar { + fn bar(&self, x: &T); +} + +trait Baz : Bar { +} +"#, + [("Bar", vec![]), ("Baz", vec![SizedSelf, SelfReferential])], + ); +} + +#[test] +fn rustc_issue_19538() { + check_object_safety( + r#" +//- minicore: dispatch_from_dyn +trait Foo { + fn foo(&self, val: T); +} + +trait Bar: Foo {} +"#, + [("Foo", vec![Method(Generic)]), ("Bar", vec![HasNonSafeSuperTrait])], + ); +} + +#[test] +fn rustc_issue_22040() { + check_object_safety( + r#" +//- minicore: fmt, eq, dispatch_from_dyn +use core::fmt::Debug; + +trait Expr: Debug + PartialEq { + fn print_element_count(&self); +} +"#, + [("Expr", vec![SelfReferential])], + ); +} + +#[test] +fn rustc_issue_102762() { + check_object_safety( + r#" +//- minicore: future, send, sync, dispatch_from_dyn, deref +use core::pin::Pin; + +struct Box {} +impl core::ops::Deref for Box { + type Target = T; + + fn deref(&self) -> &Self::Target { + loop {} + } +} +impl, U: ?Sized> DispatchFromDyn> for Box {} + +struct Vec {} + +pub trait Fetcher: Send + Sync { + fn get<'a>(self: &'a Box) -> Pin> + 'a>> + where + Self: Sync, + { + loop {} + } +} +"#, + [("Fetcher", vec![Method(UndispatchableReceiver)])], + ); +} + +#[test] +fn rustc_issue_102933() { + check_object_safety( + r#" +//- minicore: future, dispatch_from_dyn, deref +use core::future::Future; + +struct Box {} +impl core::ops::Deref for Box { + type Target = T; + + fn deref(&self) -> &Self::Target { + loop {} + } +} +impl, U: ?Sized> DispatchFromDyn> for Box {} + +pub trait Service { + type Response; + type Future: Future; +} + +pub trait A1: Service {} + +pub trait A2: Service>> + A1 { + fn foo(&self) {} +} + +pub trait B1: Service>> {} + +pub trait B2: Service + B1 { + fn foo(&self) {} +} + "#, + [("A2", vec![]), ("B2", vec![])], + ); +} + +#[test] +fn rustc_issue_106247() { + check_object_safety( + r#" +//- minicore: sync, dispatch_from_dyn +pub trait Trait { + fn method(&self) where Self: Sync; +} +"#, + [("Trait", vec![])], + ); +} + +#[test] +fn std_error_is_object_safe() { + check_object_safety( + r#" +//- minicore: fmt, dispatch_from_dyn +trait Erased<'a>: 'a {} + +pub struct Request<'a>(dyn Erased<'a> + 'a); + +pub trait Error: core::fmt::Debug + core::fmt::Display { + fn provide<'a>(&'a self, request: &mut Request<'a>); +} +"#, + [("Error", vec![])], + ); +} + +#[test] +fn lifetime_gat_is_object_unsafe() { + check_object_safety( + r#" +//- minicore: dispatch_from_dyn +trait Foo { + type Bar<'a>; +} +"#, + [("Foo", vec![ObjectSafetyViolationKind::GAT])], + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs index 908bbc248c60..273571901ad5 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs @@ -49,7 +49,7 @@ fn let_stmt_coerce() { //- minicore: coerce_unsized fn test() { let x: &[isize] = &[1]; - // ^^^^ adjustments: Deref(None), Borrow(Ref('?3, Not)), Pointer(Unsize) + // ^^^^ adjustments: Deref(None), Borrow(Ref('?2, Not)), Pointer(Unsize) let x: *const [isize] = &[1]; // ^^^^ adjustments: Deref(None), Borrow(RawPtr(Not)), Pointer(Unsize) } @@ -148,7 +148,7 @@ fn foo(x: &[T]) -> &[T] { x } fn test(i: i32) { let x = match i { 2 => foo(&[2]), - // ^^^^ adjustments: Deref(None), Borrow(Ref('?10, Not)), Pointer(Unsize) + // ^^^^ adjustments: Deref(None), Borrow(Ref('?8, Not)), Pointer(Unsize) 1 => &[1], _ => &[3], }; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs index 5454a496ba8c..53b69c12f05d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs @@ -1,7 +1,7 @@ use expect_test::expect; use test_utils::{bench, bench_fixture, skip_slow_tests}; -use crate::tests::check_infer_with_mismatches; +use crate::tests::{check_infer_with_mismatches, check_no_mismatches}; use super::{check_infer, check_types}; @@ -206,6 +206,7 @@ fn expr_macro_def_expanded_in_various_places() { 100..119 'for _ ...!() {}': () 100..119 'for _ ...!() {}': () 100..119 'for _ ...!() {}': () + 100..119 'for _ ...!() {}': () 104..105 '_': IntoIterator::Item 117..119 '{}': () 124..134 '|| spam!()': impl Fn() -> isize @@ -299,6 +300,7 @@ fn expr_macro_rules_expanded_in_various_places() { 114..133 'for _ ...!() {}': () 114..133 'for _ ...!() {}': () 114..133 'for _ ...!() {}': () + 114..133 'for _ ...!() {}': () 118..119 '_': IntoIterator::Item 131..133 '{}': () 138..148 '|| spam!()': impl Fn() -> isize @@ -1404,3 +1406,105 @@ fn foo(t: Tensor) { "#, ); } + +#[test] +fn asm_unit() { + check_no_mismatches( + r#" +//- minicore: asm +fn unit() { + core::arch::asm!("") +} +"#, + ); +} + +#[test] +fn asm_no_return() { + check_no_mismatches( + r#" +//- minicore: asm +fn unit() -> ! { + core::arch::asm!("", options(noreturn)) +} +"#, + ); +} + +#[test] +fn asm_things() { + check_infer( + r#" +//- minicore: asm, concat +fn main() { + unsafe { + let foo = 1; + let mut o = 0; + core::arch::asm!( + "%input = OpLoad _ {0}", + concat!("%result = ", bar, " _ %input"), + "OpStore {1} %result", + in(reg) &foo, + in(reg) &mut o, + ); + o + + let thread_id: usize; + core::arch::asm!(" + mov {0}, gs:[0x30] + mov {0}, [{0}+0x48] + ", out(reg) thread_id, options(pure, readonly, nostack)); + + static UNMAP_BASE: usize; + const MEM_RELEASE: usize; + static VirtualFree: usize; + const OffPtr: usize; + const OffFn: usize; + core::arch::asm!(" + push {free_type} + push {free_size} + push {base} + + mov eax, fs:[30h] + mov eax, [eax+8h] + add eax, {off_fn} + mov [eax-{off_fn}+{off_ptr}], eax + + push eax + + jmp {virtual_free} + ", + off_ptr = const OffPtr, + off_fn = const OffFn, + + free_size = const 0, + free_type = const MEM_RELEASE, + + virtual_free = sym VirtualFree, + + base = sym UNMAP_BASE, + options(noreturn), + ); + } +} +"#, + expect![[r#" + !0..122 'builti...muto,)': () + !0..136 'builti...tack))': () + !0..449 'builti...urn),)': ! + 10..1236 '{ ... } }': () + 16..1234 'unsafe... }': () + 37..40 'foo': i32 + 43..44 '1': i32 + 58..63 'mut o': i32 + 66..67 '0': i32 + !95..104 'thread_id': usize + !103..107 '&foo': &'? i32 + !104..107 'foo': i32 + !115..120 '&muto': &'? mut i32 + !119..120 'o': i32 + 293..294 'o': i32 + 308..317 'thread_id': usize + "#]], + ) +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs index 610fc9b6b456..74acf23b75ab 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs @@ -1286,6 +1286,7 @@ fn main() { fn method_on_dyn_impl() { check_types( r#" +//- minicore: coerce_unsized trait Foo {} impl Foo for u32 {} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs index 0ccbcf63e2b6..5c63cd00f97d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs @@ -371,6 +371,7 @@ fn diverging_expression_3_break() { 151..172 'for a ...eak; }': () 151..172 'for a ...eak; }': () 151..172 'for a ...eak; }': () + 151..172 'for a ...eak; }': () 155..156 'a': {unknown} 160..161 'b': {unknown} 162..172 '{ break; }': () @@ -387,6 +388,7 @@ fn diverging_expression_3_break() { 237..250 'for a in b {}': () 237..250 'for a in b {}': () 237..250 'for a in b {}': () + 237..250 'for a in b {}': () 241..242 'a': {unknown} 246..247 'b': {unknown} 248..250 '{}': () @@ -402,6 +404,7 @@ fn diverging_expression_3_break() { 315..337 'for a ...urn; }': () 315..337 'for a ...urn; }': () 315..337 'for a ...urn; }': () + 315..337 'for a ...urn; }': () 319..320 'a': {unknown} 324..325 'b': {unknown} 326..337 '{ return; }': () diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs index 57866acc0637..51c27f8714ac 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs @@ -57,6 +57,7 @@ fn infer_pattern() { 101..151 'for (e... }': () 101..151 'for (e... }': () 101..151 'for (e... }': () + 101..151 'for (e... }': () 105..111 '(e, f)': ({unknown}, {unknown}) 106..107 'e': {unknown} 109..110 'f': {unknown} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs index 17fbe4db3179..a3cf12d8a16e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs @@ -275,6 +275,7 @@ fn infer_std_crash_5() { 32..320 'for co... }': () 32..320 'for co... }': () 32..320 'for co... }': () + 32..320 'for co... }': () 36..43 'content': {unknown} 47..60 'doesnt_matter': {unknown} 61..320 '{ ... }': () @@ -1065,7 +1066,7 @@ fn test() { fn bare_dyn_trait_binders_9639() { check_no_mismatches( r#" -//- minicore: fn, coerce_unsized +//- minicore: fn, coerce_unsized, dispatch_from_dyn fn infix_parse(_state: S, _level_code: &Fn(S)) -> T { loop {} } @@ -1244,6 +1245,7 @@ fn test() { 16..66 'for _ ... }': () 16..66 'for _ ... }': () 16..66 'for _ ... }': () + 16..66 'for _ ... }': () 20..21 '_': IntoIterator::Item<()> 25..39 '{ let x = 0; }': () 31..32 'x': i32 @@ -1907,6 +1909,7 @@ fn dont_unify_on_casts() { // #15246 check_types( r#" +//- minicore: sized fn unify(_: [bool; 1]) {} fn casted(_: *const bool) {} fn default() -> T { loop {} } @@ -1926,6 +1929,7 @@ fn test() { fn rustc_test_issue_52437() { check_types( r#" + //- minicore: sized fn main() { let x = [(); &(&'static: loop { |x| {}; }) as *const _ as usize] //^ [(); _] @@ -2228,3 +2232,66 @@ async fn f() -> Bar {} "#]], ); } + +#[test] +fn issue_18109() { + check_infer( + r#" +//- minicore: option +struct Map(T, U); + +impl Map { + fn new() -> Self { loop {} } + fn get(&self, _: &T) -> Option<&U> { loop {} } +} + +fn test(x: bool) { + let map = Map::new(); + let _ = match x { + true => { + let Some(val) = map.get(&8) else { return }; + *val + } + false => return, + _ => 42, + }; +} +"#, + expect![[r#" + 69..80 '{ loop {} }': Map + 71..78 'loop {}': ! + 76..78 '{}': () + 93..97 'self': &'? Map + 99..100 '_': &'? T + 120..131 '{ loop {} }': Option<&'? U> + 122..129 'loop {}': ! + 127..129 '{}': () + 143..144 'x': bool + 152..354 '{ ... }; }': () + 162..165 'map': Map + 168..176 'Map::new': fn new() -> Map + 168..178 'Map::new()': Map + 188..189 '_': i32 + 192..351 'match ... }': i32 + 198..199 'x': bool + 210..214 'true': bool + 210..214 'true': bool + 218..303 '{ ... }': i32 + 236..245 'Some(val)': Option<&'? i32> + 241..244 'val': &'? i32 + 248..251 'map': Map + 248..259 'map.get(&8)': Option<&'? i32> + 256..258 '&8': &'? i32 + 257..258 '8': i32 + 265..275 '{ return }': ! + 267..273 'return': ! + 289..293 '*val': i32 + 290..293 'val': &'? i32 + 312..317 'false': bool + 312..317 'false': bool + 321..327 'return': ! + 337..338 '_': bool + 342..344 '42': i32 + "#]], + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs index 1c6fa62e30ce..0473ee02fab1 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs @@ -917,7 +917,7 @@ fn test(a: A) { 278..279 'A': extern "rust-call" A(*mut i32) -> A 278..292 'A(0 as *mut _)': A 278..307 'A(0 as...B(a)))': &'? i32 - 280..281 '0': i32 + 280..281 '0': usize 280..291 '0 as *mut _': *mut i32 297..306 '&&B(B(a))': &'? &'? B>> 298..306 '&B(B(a))': &'? B>> @@ -3572,6 +3572,7 @@ fn f(t: Ark) { fn ref_to_array_to_ptr_cast() { check_types( r#" +//- minicore: sized fn default() -> T { loop {} } fn foo() { let arr = [default()]; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs index a98cff2a08b2..0b2d6bdd2593 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs @@ -1448,14 +1448,20 @@ fn foo() -> Foo> { fn dyn_trait() { check_infer( r#" -//- minicore: sized +//- minicore: deref, dispatch_from_dyn trait Trait { fn foo(&self) -> T; fn foo2(&self) -> i64; } -fn bar() -> dyn Trait {} -fn test(x: dyn Trait, y: &dyn Trait) { +struct Box {} +impl core::ops::Deref for Box { + type Target = T; +} + +fn bar() -> Box> {} + +fn test(x: Box>, y: &dyn Trait) { x; y; let z = bar(); @@ -1469,27 +1475,27 @@ fn test(x: dyn Trait, y: &dyn Trait) { expect![[r#" 29..33 'self': &'? Self 54..58 'self': &'? Self - 97..99 '{}': dyn Trait - 109..110 'x': dyn Trait - 128..129 'y': &'? dyn Trait - 148..265 '{ ...2(); }': () - 154..155 'x': dyn Trait - 161..162 'y': &'? dyn Trait - 172..173 'z': dyn Trait - 176..179 'bar': fn bar() -> dyn Trait - 176..181 'bar()': dyn Trait - 187..188 'x': dyn Trait - 187..194 'x.foo()': u64 - 200..201 'y': &'? dyn Trait - 200..207 'y.foo()': u64 - 213..214 'z': dyn Trait - 213..220 'z.foo()': u64 - 226..227 'x': dyn Trait - 226..234 'x.foo2()': i64 - 240..241 'y': &'? dyn Trait - 240..248 'y.foo2()': i64 - 254..255 'z': dyn Trait - 254..262 'z.foo2()': i64 + 198..200 '{}': Box> + 210..211 'x': Box> + 234..235 'y': &'? dyn Trait + 254..371 '{ ...2(); }': () + 260..261 'x': Box> + 267..268 'y': &'? dyn Trait + 278..279 'z': Box> + 282..285 'bar': fn bar() -> Box> + 282..287 'bar()': Box> + 293..294 'x': Box> + 293..300 'x.foo()': u64 + 306..307 'y': &'? dyn Trait + 306..313 'y.foo()': u64 + 319..320 'z': Box> + 319..326 'z.foo()': u64 + 332..333 'x': Box> + 332..340 'x.foo2()': i64 + 346..347 'y': &'? dyn Trait + 346..354 'y.foo2()': i64 + 360..361 'z': Box> + 360..368 'z.foo2()': i64 "#]], ); } @@ -1534,7 +1540,7 @@ fn test(s: S) { fn dyn_trait_bare() { check_infer( r#" -//- minicore: sized +//- minicore: sized, dispatch_from_dyn trait Trait { fn foo(&self) -> u64; } @@ -1570,7 +1576,7 @@ fn test(x: Trait, y: &Trait) -> u64 { check_infer_with_mismatches( r#" -//- minicore: fn, coerce_unsized +//- minicore: fn, coerce_unsized, dispatch_from_dyn struct S; impl S { fn foo(&self) {} @@ -3106,7 +3112,7 @@ fn dyn_fn_param_informs_call_site_closure_signature() { cov_mark::check!(dyn_fn_param_informs_call_site_closure_signature); check_types( r#" -//- minicore: fn, coerce_unsized +//- minicore: fn, coerce_unsized, dispatch_from_dyn struct S; impl S { fn inherent(&self) -> u8 { 0 } @@ -3151,7 +3157,7 @@ fn infer_box_fn_arg() { // The type mismatch is because we don't define Unsize and CoerceUnsized check_infer_with_mismatches( r#" -//- minicore: fn, deref, option +//- minicore: fn, deref, option, dispatch_from_dyn #[lang = "owned_box"] pub struct Box { inner: *mut T, diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs index ffb972475f8f..0b3cdb2f3790 100644 --- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs @@ -4,7 +4,9 @@ //! This probably isn't the best way to do this -- ideally, diagnostics should //! be expressed in terms of hir types themselves. pub use hir_ty::diagnostics::{CaseType, IncorrectCase}; -use hir_ty::{db::HirDatabase, diagnostics::BodyValidationDiagnostic, InferenceDiagnostic}; +use hir_ty::{ + db::HirDatabase, diagnostics::BodyValidationDiagnostic, CastError, InferenceDiagnostic, +}; use cfg::{CfgExpr, CfgOptions}; use either::Either; @@ -50,10 +52,12 @@ macro_rules! diagnostics { diagnostics![ AwaitOutsideOfAsync, BreakOutsideOfLoop, + CastToUnsized, ExpectedFunction, InactiveCode, IncoherentImpl, IncorrectCase, + InvalidCast, InvalidDeriveTarget, MacroDefError, MacroError, @@ -254,6 +258,8 @@ pub struct PrivateField { #[derive(Debug)] pub struct MissingUnsafe { pub expr: InFile>, + /// If true, the diagnostics is an `unsafe_op_in_unsafe_fn` lint instead of a hard error. + pub only_lint: bool, } #[derive(Debug)] @@ -364,6 +370,20 @@ pub struct RemoveUnnecessaryElse { pub if_expr: InFile>, } +#[derive(Debug)] +pub struct CastToUnsized { + pub expr: InFile>, + pub cast_ty: Type, +} + +#[derive(Debug)] +pub struct InvalidCast { + pub expr: InFile>, + pub error: CastError, + pub expr_ty: Type, + pub cast_ty: Type, +} + impl AnyDiagnostic { pub(crate) fn body_validation_diagnostic( db: &dyn HirDatabase, @@ -620,6 +640,16 @@ impl AnyDiagnostic { }; MismatchedTupleStructPatArgCount { expr_or_pat, expected, found }.into() } + InferenceDiagnostic::CastToUnsized { expr, cast_ty } => { + let expr = expr_syntax(*expr)?; + CastToUnsized { expr, cast_ty: Type::new(db, def, cast_ty.clone()) }.into() + } + InferenceDiagnostic::InvalidCast { expr, error, expr_ty, cast_ty } => { + let expr = expr_syntax(*expr)?; + let expr_ty = Type::new(db, def, expr_ty.clone()); + let cast_ty = Type::new(db, def, cast_ty.clone()); + InvalidCast { expr, error: *error, expr_ty, cast_ty }.into() + } }) } } diff --git a/src/tools/rust-analyzer/crates/hir/src/display.rs b/src/tools/rust-analyzer/crates/hir/src/display.rs index 923dca646673..c2b2fbef7517 100644 --- a/src/tools/rust-analyzer/crates/hir/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir/src/display.rs @@ -99,17 +99,20 @@ impl HirDisplay for Function { } // FIXME: Use resolved `param.ty` once we no longer discard lifetimes + let body = db.body(self.id.into()); for (type_ref, param) in data.params.iter().zip(self.assoc_fn_params(db)).skip(skip_self) { - let local = param.as_local(db).map(|it| it.name(db)); if !first { f.write_str(", ")?; } else { first = false; } - match local { - Some(name) => write!(f, "{}: ", name.display(f.db.upcast(), f.edition()))?, - None => f.write_str("_: ")?, - } + + let pat_id = body.params[param.idx - body.self_param.is_some() as usize]; + let pat_str = + body.pretty_print_pat(db.upcast(), self.id.into(), pat_id, true, f.edition()); + f.write_str(&pat_str)?; + + f.write_str(": ")?; type_ref.hir_fmt(f)?; } diff --git a/src/tools/rust-analyzer/crates/hir/src/has_source.rs b/src/tools/rust-analyzer/crates/hir/src/has_source.rs index 7d52a28b91e9..82c90ac30101 100644 --- a/src/tools/rust-analyzer/crates/hir/src/has_source.rs +++ b/src/tools/rust-analyzer/crates/hir/src/has_source.rs @@ -14,8 +14,8 @@ use tt::TextRange; use crate::{ db::HirDatabase, Adt, Callee, Const, Enum, ExternCrateDecl, Field, FieldSource, Function, Impl, - Label, LifetimeParam, LocalSource, Macro, Module, Param, SelfParam, Static, Struct, Trait, - TraitAlias, TypeAlias, TypeOrConstParam, Union, Variant, + InlineAsmOperand, Label, LifetimeParam, LocalSource, Macro, Module, Param, SelfParam, Static, + Struct, Trait, TraitAlias, TypeAlias, TypeOrConstParam, Union, Variant, }; pub trait HasSource { @@ -292,3 +292,26 @@ impl HasSource for ExternCrateDecl { Some(self.id.lookup(db.upcast()).source(db.upcast())) } } + +impl HasSource for InlineAsmOperand { + type Ast = ast::AsmOperandNamed; + fn source(self, db: &dyn HirDatabase) -> Option> { + let (_body, source_map) = db.body_with_source_map(self.owner); + if let Ok(src) = source_map.expr_syntax(self.expr) { + let root = src.file_syntax(db.upcast()); + return src + .map(|ast| match ast.to_node(&root) { + ast::Expr::AsmExpr(asm) => asm + .asm_pieces() + .filter_map(|it| match it { + ast::AsmPiece::AsmOperandNamed(it) => Some(it), + _ => None, + }) + .nth(self.index), + _ => None, + }) + .transpose(); + } + None + } +} diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 58340c74fea9..8f5db32f9576 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -43,7 +43,7 @@ use hir_def::{ body::{BodyDiagnostic, SyntheticSyntax}, data::adt::VariantData, generics::{LifetimeParamData, TypeOrConstParamData, TypeParamProvenance}, - hir::{BindingAnnotation, BindingId, ExprOrPatId, LabelId, Pat}, + hir::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, LabelId, Pat}, item_tree::{AttrOwner, FieldParent, ItemTreeFieldId, ItemTreeNode}, lang_item::LangItemTarget, layout::{self, ReprOptions, TargetDataLayout}, @@ -66,7 +66,7 @@ use hir_ty::{ diagnostics::BodyValidationDiagnostic, error_lifetime, known_const_to_ast, layout::{Layout as TyLayout, RustcEnumVariantIdx, RustcFieldIdx, TagEncoding}, - method_resolution::{self}, + method_resolution, mir::{interpret_mir, MutBorrowKind}, primitive::UintTy, traits::FnTrait, @@ -80,7 +80,7 @@ use nameres::diagnostics::DefDiagnosticKind; use rustc_hash::FxHashSet; use smallvec::SmallVec; use span::{Edition, EditionedFileId, FileId, MacroCallId, SyntaxContextId}; -use stdx::{impl_from, never}; +use stdx::{format_to, impl_from, never}; use syntax::{ ast::{self, HasAttrs as _, HasGenericParams, HasName}, format_smolstr, AstNode, AstPtr, SmolStr, SyntaxNode, SyntaxNodePtr, TextRange, ToSmolStr, T, @@ -137,6 +137,7 @@ pub use { hygiene::{marks_rev, SyntaxContextExt}, inert_attr_macro::AttributeTemplate, name::Name, + prettify_macro_expansion, proc_macro::{ProcMacros, ProcMacrosBuilder}, tt, ExpandResult, HirFileId, HirFileIdExt, MacroFileId, MacroFileIdExt, }, @@ -145,7 +146,8 @@ pub use { display::{ClosureStyle, HirDisplay, HirDisplayError, HirWrite}, layout::LayoutError, mir::{MirEvalError, MirLowerError}, - FnAbi, PointerCast, Safety, + object_safety::{MethodViolationCode, ObjectSafetyViolation}, + CastError, FnAbi, PointerCast, Safety, }, // FIXME: Properly encapsulate mir hir_ty::{mir, Interner as ChalkTyInterner}, @@ -1882,9 +1884,10 @@ impl DefWithBody { ); } - for expr in hir_ty::diagnostics::missing_unsafe(db, self.into()) { + let (unafe_exprs, only_lint) = hir_ty::diagnostics::missing_unsafe(db, self.into()); + for expr in unafe_exprs { match source_map.expr_syntax(expr) { - Ok(expr) => acc.push(MissingUnsafe { expr }.into()), + Ok(expr) => acc.push(MissingUnsafe { expr, only_lint }.into()), Err(SyntheticSyntax) => { // FIXME: Here and elsewhere in this file, the `expr` was // desugared, report or assert that this doesn't happen. @@ -2206,6 +2209,35 @@ impl Function { db.function_data(self.id).is_async() } + pub fn returns_impl_future(self, db: &dyn HirDatabase) -> bool { + if self.is_async(db) { + return true; + } + + let Some(impl_traits) = self.ret_type(db).as_impl_traits(db) else { return false }; + let Some(future_trait_id) = + db.lang_item(self.ty(db).env.krate, LangItem::Future).and_then(|t| t.as_trait()) + else { + return false; + }; + let Some(sized_trait_id) = + db.lang_item(self.ty(db).env.krate, LangItem::Sized).and_then(|t| t.as_trait()) + else { + return false; + }; + + let mut has_impl_future = false; + impl_traits + .filter(|t| { + let fut = t.id == future_trait_id; + has_impl_future |= fut; + !fut && t.id != sized_trait_id + }) + // all traits but the future trait must be auto traits + .all(|t| t.is_auto(db)) + && has_impl_future + } + /// Does this function have `#[test]` attribute? pub fn is_test(self, db: &dyn HirDatabase) -> bool { db.function_data(self.id).attrs.is_test() @@ -2522,6 +2554,17 @@ impl Const { Type::from_value_def(db, self.id) } + /// Evaluate the constant and return the result as a string. + /// + /// This function is intended for IDE assistance, different from [`Const::render_eval`]. + pub fn eval(self, db: &dyn HirDatabase, edition: Edition) -> Result { + let c = db.const_eval(self.id.into(), Substitution::empty(Interner), None)?; + Ok(format!("{}", c.display(db, edition))) + } + + /// Evaluate the constant and return the result as a string, with more detailed information. + /// + /// This function is intended for user-facing display. pub fn render_eval( self, db: &dyn HirDatabase, @@ -2536,10 +2579,16 @@ impl Const { let value = u128::from_le_bytes(mir::pad16(b, false)); let value_signed = i128::from_le_bytes(mir::pad16(b, matches!(s, Scalar::Int(_)))); + let mut result = if let Scalar::Int(_) = s { + value_signed.to_string() + } else { + value.to_string() + }; if value >= 10 { - return Ok(format!("{value_signed} ({value:#X})")); + format_to!(result, " ({value:#X})"); + return Ok(result); } else { - return Ok(format!("{value_signed}")); + return Ok(result); } } } @@ -2641,6 +2690,10 @@ impl Trait { .count() } + pub fn object_safety(&self, db: &dyn HirDatabase) -> Option { + hir_ty::object_safety::object_safety(db, self.id) + } + fn all_macro_calls(&self, db: &dyn HirDatabase) -> Box<[(AstId, MacroCallId)]> { db.trait_data(self.id) .macro_calls @@ -5211,6 +5264,26 @@ impl Type { } } +#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)] +pub struct InlineAsmOperand { + owner: DefWithBodyId, + expr: ExprId, + index: usize, +} + +impl InlineAsmOperand { + pub fn parent(self, _db: &dyn HirDatabase) -> DefWithBody { + self.owner.into() + } + + pub fn name(&self, db: &dyn HirDatabase) -> Option { + match &db.body(self.owner)[self.expr] { + hir_def::hir::Expr::InlineAsm(e) => e.operands.get(self.index)?.0.clone(), + _ => None, + } + } +} + // FIXME: Document this #[derive(Debug)] pub struct Callable { diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index 763f53031e4c..fa14b53dbc3d 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -13,7 +13,8 @@ use either::Either; use hir_def::{ hir::Expr, lower::LowerCtx, - nameres::MacroSubNs, + nameres::{MacroSubNs, ModuleOrigin}, + path::ModPath, resolver::{self, HasResolver, Resolver, TypeNs}, type_ref::Mutability, AsMacroCall, DefWithBodyId, FunctionId, MacroId, TraitId, VariantId, @@ -31,7 +32,7 @@ use intern::Symbol; use itertools::Itertools; use rustc_hash::{FxHashMap, FxHashSet}; use smallvec::{smallvec, SmallVec}; -use span::{EditionedFileId, FileId}; +use span::{EditionedFileId, FileId, HirFileIdRepr}; use stdx::TupleExt; use syntax::{ algo::skip_trivia_token, @@ -46,9 +47,9 @@ use crate::{ source_analyzer::{resolve_hir_path, SourceAnalyzer}, Access, Adjust, Adjustment, Adt, AutoBorrow, BindingMode, BuiltinAttr, Callable, Const, ConstParam, Crate, DeriveHelper, Enum, Field, Function, HasSource, HirFileId, Impl, InFile, - Label, LifetimeParam, Local, Macro, Module, ModuleDef, Name, OverloadedDeref, Path, ScopeDef, - Static, Struct, ToolModule, Trait, TraitAlias, TupleField, Type, TypeAlias, TypeParam, Union, - Variant, VariantDef, + InlineAsmOperand, ItemInNs, Label, LifetimeParam, Local, Macro, Module, ModuleDef, Name, + OverloadedDeref, Path, ScopeDef, Static, Struct, ToolModule, Trait, TraitAlias, TupleField, + Type, TypeAlias, TypeParam, Union, Variant, VariantDef, }; const CONTINUE_NO_BREAKS: ControlFlow = ControlFlow::Continue(()); @@ -322,6 +323,47 @@ impl<'db> SemanticsImpl<'db> { tree } + pub fn find_parent_file(&self, file_id: HirFileId) -> Option> { + match file_id.repr() { + HirFileIdRepr::FileId(file_id) => { + let module = self.file_to_module_defs(file_id.file_id()).next()?; + let def_map = self.db.crate_def_map(module.krate().id); + match def_map[module.id.local_id].origin { + ModuleOrigin::CrateRoot { .. } => None, + ModuleOrigin::File { declaration, declaration_tree_id, .. } => { + let file_id = declaration_tree_id.file_id(); + let in_file = InFile::new(file_id, declaration); + let node = in_file.to_node(self.db.upcast()); + let root = find_root(node.syntax()); + self.cache(root, file_id); + Some(in_file.with_value(node.syntax().clone())) + } + _ => unreachable!("FileId can only belong to a file module"), + } + } + HirFileIdRepr::MacroFile(macro_file) => { + let node = self + .db + .lookup_intern_macro_call(macro_file.macro_call_id) + .to_node(self.db.upcast()); + let root = find_root(&node.value); + self.cache(root, node.file_id); + Some(node) + } + } + } + + /// Returns the `SyntaxNode` of the module. If this is a file module, returns + /// the `SyntaxNode` of the *definition* file, not of the *declaration*. + pub fn module_definition_node(&self, module: Module) -> InFile { + let def_map = module.id.def_map(self.db.upcast()); + let definition = def_map[module.id.local_id].origin.definition_source(self.db.upcast()); + let definition = definition.map(|it| it.node()); + let root_node = find_root(&definition.value); + self.cache(root_node, definition.file_id); + definition + } + pub fn parse_or_expand(&self, file_id: HirFileId) -> SyntaxNode { let node = self.db.parse_or_expand(file_id); self.cache(node.clone(), file_id); @@ -344,6 +386,19 @@ impl<'db> SemanticsImpl<'db> { Some(node) } + pub fn check_cfg_attr(&self, attr: &ast::TokenTree) -> Option { + let file_id = self.find_file(attr.syntax()).file_id; + let krate = match file_id.repr() { + HirFileIdRepr::FileId(file_id) => { + self.file_to_module_defs(file_id.file_id()).next()?.krate().id + } + HirFileIdRepr::MacroFile(macro_file) => { + self.db.lookup_intern_macro_call(macro_file.macro_call_id).krate + } + }; + hir_expand::check_cfg_attr_value(self.db.upcast(), attr, krate) + } + /// Expands the macro if it isn't one of the built-in ones that expand to custom syntax or dummy /// expansions. pub fn expand_allowed_builtins(&self, macro_call: &ast::MacroCall) -> Option { @@ -367,7 +422,6 @@ impl<'db> SemanticsImpl<'db> { | BuiltinFnLikeExpander::File | BuiltinFnLikeExpander::ModulePath | BuiltinFnLikeExpander::Asm - | BuiltinFnLikeExpander::LlvmAsm | BuiltinFnLikeExpander::GlobalAsm | BuiltinFnLikeExpander::LogSyntax | BuiltinFnLikeExpander::TraceMacros @@ -408,7 +462,7 @@ impl<'db> SemanticsImpl<'db> { Some( calls .into_iter() - .map(|call| macro_call_to_macro_id(ctx, call?).map(|id| Macro { id })) + .map(|call| macro_call_to_macro_id(self, ctx, call?).map(|id| Macro { id })) .collect(), ) }) @@ -546,11 +600,11 @@ impl<'db> SemanticsImpl<'db> { ) } - /// Retrieves all the formatting parts of the format_args! template string. + /// Retrieves all the formatting parts of the format_args! (or `asm!`) template string. pub fn as_format_args_parts( &self, string: &ast::String, - ) -> Option)>> { + ) -> Option>)>> { let quote = string.open_quote_text_range()?; let token = self.wrap_token_infile(string.syntax().clone()).into_real_file().ok()?; @@ -560,14 +614,33 @@ impl<'db> SemanticsImpl<'db> { let string = ast::String::cast(token)?; let literal = string.syntax().parent().filter(|it| it.kind() == SyntaxKind::LITERAL)?; - let format_args = ast::FormatArgsExpr::cast(literal.parent()?)?; - let source_analyzer = self.analyze_no_infer(format_args.syntax())?; - let format_args = self.wrap_node_infile(format_args); - let res = source_analyzer - .as_format_args_parts(self.db, format_args.as_ref())? - .map(|(range, res)| (range + quote.end(), res)) - .collect(); - Some(res) + let parent = literal.parent()?; + if let Some(format_args) = ast::FormatArgsExpr::cast(parent.clone()) { + let source_analyzer = self.analyze_no_infer(format_args.syntax())?; + let format_args = self.wrap_node_infile(format_args); + let res = source_analyzer + .as_format_args_parts(self.db, format_args.as_ref())? + .map(|(range, res)| (range + quote.end(), res.map(Either::Left))) + .collect(); + Some(res) + } else { + let asm = ast::AsmExpr::cast(parent)?; + let source_analyzer = self.analyze_no_infer(asm.syntax())?; + let line = asm.template().position(|it| *it.syntax() == literal)?; + let asm = self.wrap_node_infile(asm); + let (owner, (expr, asm_parts)) = source_analyzer.as_asm_parts(asm.as_ref())?; + let res = asm_parts + .get(line)? + .iter() + .map(|&(range, index)| { + ( + range + quote.end(), + Some(Either::Right(InlineAsmOperand { owner, expr, index })), + ) + }) + .collect(); + Some(res) + } })() .map_or(ControlFlow::Continue(()), ControlFlow::Break) }) @@ -578,7 +651,7 @@ impl<'db> SemanticsImpl<'db> { &self, original_token: SyntaxToken, offset: TextSize, - ) -> Option<(TextRange, Option)> { + ) -> Option<(TextRange, Option>)> { let original_string = ast::String::cast(original_token.clone())?; let original_token = self.wrap_token_infile(original_token).into_real_file().ok()?; let quote = original_string.open_quote_text_range()?; @@ -599,13 +672,27 @@ impl<'db> SemanticsImpl<'db> { &self, string: ast::String, offset: TextSize, - ) -> Option<(TextRange, Option)> { + ) -> Option<(TextRange, Option>)> { debug_assert!(offset <= string.syntax().text_range().len()); let literal = string.syntax().parent().filter(|it| it.kind() == SyntaxKind::LITERAL)?; - let format_args = ast::FormatArgsExpr::cast(literal.parent()?)?; - let source_analyzer = &self.analyze_no_infer(format_args.syntax())?; - let format_args = self.wrap_node_infile(format_args); - source_analyzer.resolve_offset_in_format_args(self.db, format_args.as_ref(), offset) + let parent = literal.parent()?; + if let Some(format_args) = ast::FormatArgsExpr::cast(parent.clone()) { + let source_analyzer = &self.analyze_no_infer(format_args.syntax())?; + let format_args = self.wrap_node_infile(format_args); + source_analyzer + .resolve_offset_in_format_args(self.db, format_args.as_ref(), offset) + .map(|(range, res)| (range, res.map(Either::Left))) + } else { + let asm = ast::AsmExpr::cast(parent)?; + let source_analyzer = &self.analyze_no_infer(asm.syntax())?; + let line = asm.template().position(|it| *it.syntax() == literal)?; + let asm = self.wrap_node_infile(asm); + source_analyzer.resolve_offset_in_asm_template(asm.as_ref(), line, offset).map( + |(owner, (expr, range, index))| { + (range, Some(Either::Right(InlineAsmOperand { owner, expr, index }))) + }, + ) + } } /// Maps a node down by mapping its first and last token down. @@ -818,16 +905,7 @@ impl<'db> SemanticsImpl<'db> { let InMacroFile { file_id, value: mapped_tokens } = self.with_ctx(|ctx| { Some( ctx.cache - .expansion_info_cache - .entry(macro_file) - .or_insert_with(|| { - let exp_info = macro_file.expansion_info(self.db.upcast()); - - let InMacroFile { file_id, value } = exp_info.expanded(); - self.cache(value, file_id.into()); - - exp_info - }) + .get_or_insert_expansion(self, macro_file) .map_range_down(span)? .map(SmallVec::<[_; 2]>::from_iter), ) @@ -1113,11 +1191,7 @@ impl<'db> SemanticsImpl<'db> { let macro_file = file_id.macro_file()?; self.with_ctx(|ctx| { - let expansion_info = ctx - .cache - .expansion_info_cache - .entry(macro_file) - .or_insert_with(|| macro_file.expansion_info(self.db.upcast())); + let expansion_info = ctx.cache.get_or_insert_expansion(self, macro_file); expansion_info.arg().map(|node| node?.parent()).transpose() }) } @@ -1333,7 +1407,7 @@ impl<'db> SemanticsImpl<'db> { let macro_call = self.find_file(macro_call.syntax()).with_value(macro_call); self.with_ctx(|ctx| { ctx.macro_call_to_macro_call(macro_call) - .and_then(|call| macro_call_to_macro_id(ctx, call)) + .and_then(|call| macro_call_to_macro_id(self, ctx, call)) .map(Into::into) }) .or_else(|| { @@ -1375,7 +1449,7 @@ impl<'db> SemanticsImpl<'db> { let item_in_file = self.wrap_node_infile(item.clone()); let id = self.with_ctx(|ctx| { let macro_call_id = ctx.item_to_macro_call(item_in_file.as_ref())?; - macro_call_to_macro_id(ctx, macro_call_id) + macro_call_to_macro_id(self, ctx, macro_call_id) })?; Some(Macro { id }) } @@ -1384,6 +1458,16 @@ impl<'db> SemanticsImpl<'db> { self.analyze(path.syntax())?.resolve_path(self.db, path) } + pub fn resolve_mod_path( + &self, + scope: &SyntaxNode, + path: &ModPath, + ) -> Option> { + let analyze = self.analyze(scope)?; + let items = analyze.resolver.resolve_module_path_in_items(self.db.upcast(), path); + Some(items.iter_items().map(|(item, _)| item.into())) + } + fn resolve_variant(&self, record_lit: ast::RecordExpr) -> Option { self.analyze(record_lit.syntax())?.resolve_variant(self.db, record_lit) } @@ -1685,6 +1769,7 @@ impl<'db> SemanticsImpl<'db> { } fn macro_call_to_macro_id( + sema: &SemanticsImpl<'_>, ctx: &mut SourceToDefCtx<'_, '_>, macro_call_id: MacroCallId, ) -> Option { @@ -1700,11 +1785,7 @@ fn macro_call_to_macro_id( it.to_ptr(db).to_node(&db.parse(file_id).syntax_node()) } HirFileIdRepr::MacroFile(macro_file) => { - let expansion_info = ctx - .cache - .expansion_info_cache - .entry(macro_file) - .or_insert_with(|| macro_file.expansion_info(ctx.db.upcast())); + let expansion_info = ctx.cache.get_or_insert_expansion(sema, macro_file); it.to_ptr(db).to_node(&expansion_info.expanded().value) } }; @@ -1716,11 +1797,7 @@ fn macro_call_to_macro_id( it.to_ptr(db).to_node(&db.parse(file_id).syntax_node()) } HirFileIdRepr::MacroFile(macro_file) => { - let expansion_info = ctx - .cache - .expansion_info_cache - .entry(macro_file) - .or_insert_with(|| macro_file.expansion_info(ctx.db.upcast())); + let expansion_info = ctx.cache.get_or_insert_expansion(sema, macro_file); it.to_ptr(db).to_node(&expansion_info.expanded().value) } }; @@ -1771,6 +1848,7 @@ to_def_impls![ (crate::Label, ast::Label, label_to_def), (crate::Adt, ast::Adt, adt_to_def), (crate::ExternCrateDecl, ast::ExternCrate, extern_crate_to_def), + (crate::InlineAsmOperand, ast::AsmOperandNamed, asm_operand_to_def), (MacroCallId, ast::MacroCall, macro_call_to_macro_call), ]; diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs b/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs index 09df639d4a81..c1e4e1d1e271 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs @@ -99,7 +99,8 @@ use hir_def::{ VariantId, }; use hir_expand::{ - attrs::AttrId, name::AsName, ExpansionInfo, HirFileId, HirFileIdExt, MacroCallId, + attrs::AttrId, name::AsName, ExpansionInfo, HirFileId, HirFileIdExt, InMacroFile, MacroCallId, + MacroFileIdExt, }; use rustc_hash::FxHashMap; use smallvec::SmallVec; @@ -110,15 +111,32 @@ use syntax::{ AstNode, AstPtr, SyntaxNode, }; -use crate::{db::HirDatabase, InFile}; +use crate::{db::HirDatabase, InFile, InlineAsmOperand, SemanticsImpl}; #[derive(Default)] pub(super) struct SourceToDefCache { pub(super) dynmap_cache: FxHashMap<(ChildContainer, HirFileId), DynMap>, - pub(super) expansion_info_cache: FxHashMap, + expansion_info_cache: FxHashMap, pub(super) file_to_def_cache: FxHashMap>, } +impl SourceToDefCache { + pub(super) fn get_or_insert_expansion( + &mut self, + sema: &SemanticsImpl<'_>, + macro_file: MacroFileId, + ) -> &ExpansionInfo { + self.expansion_info_cache.entry(macro_file).or_insert_with(|| { + let exp_info = macro_file.expansion_info(sema.db.upcast()); + + let InMacroFile { file_id, value } = exp_info.expanded(); + sema.cache(value, file_id.into()); + + exp_info + }) + } +} + pub(super) struct SourceToDefCtx<'db, 'cache> { pub(super) db: &'db dyn HirDatabase, pub(super) cache: &'cache mut SourceToDefCache, @@ -273,6 +291,25 @@ impl SourceToDefCtx<'_, '_> { ast::Adt::Union(it) => self.union_to_def(InFile::new(file_id, it)).map(AdtId::UnionId), } } + + pub(super) fn asm_operand_to_def( + &mut self, + src: InFile<&ast::AsmOperandNamed>, + ) -> Option { + let asm = src.value.syntax().parent().and_then(ast::AsmExpr::cast)?; + let index = asm + .asm_pieces() + .filter_map(|it| match it { + ast::AsmPiece::AsmOperandNamed(it) => Some(it), + _ => None, + }) + .position(|it| it == *src.value)?; + let container = self.find_pat_or_label_container(src.syntax_ref())?; + let (_, source_map) = self.db.body_with_source_map(container); + let expr = source_map.node_expr(src.with_value(&ast::Expr::AsmExpr(asm)))?; + Some(InlineAsmOperand { owner: container, expr, index }) + } + pub(super) fn bind_pat_to_def( &mut self, src: InFile<&ast::IdentPat>, @@ -281,7 +318,7 @@ impl SourceToDefCtx<'_, '_> { let (body, source_map) = self.db.body_with_source_map(container); let src = src.cloned().map(ast::Pat::from); let pat_id = source_map.node_pat(src.as_ref())?; - // the pattern could resolve to a constant, verify that that is not the case + // the pattern could resolve to a constant, verify that this is not the case if let crate::Pat::Bind { id, .. } = body[pat_id] { Some((container, id)) } else { diff --git a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs index be0116862b9c..3da67ae23f83 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -904,6 +904,22 @@ impl SourceAnalyzer { }) } + pub(crate) fn resolve_offset_in_asm_template( + &self, + asm: InFile<&ast::AsmExpr>, + line: usize, + offset: TextSize, + ) -> Option<(DefWithBodyId, (ExprId, TextRange, usize))> { + let (def, _, body_source_map) = self.def.as_ref()?; + let (expr, args) = body_source_map.asm_template_args(asm)?; + Some(*def).zip( + args.get(line)? + .iter() + .find(|(range, _)| range.contains_inclusive(offset)) + .map(|(range, idx)| (expr, *range, *idx)), + ) + } + pub(crate) fn as_format_args_parts<'a>( &'a self, db: &'a dyn HirDatabase, @@ -927,6 +943,14 @@ impl SourceAnalyzer { )) } + pub(crate) fn as_asm_parts( + &self, + asm: InFile<&ast::AsmExpr>, + ) -> Option<(DefWithBodyId, (ExprId, &[Vec<(TextRange, usize)>]))> { + let (def, _, body_source_map) = self.def.as_ref()?; + Some(*def).zip(body_source_map.asm_template_args(asm)) + } + fn resolve_impl_method_or_trait_def( &self, db: &dyn HirDatabase, diff --git a/src/tools/rust-analyzer/crates/ide-assists/Cargo.toml b/src/tools/rust-analyzer/crates/ide-assists/Cargo.toml index df52562b6a10..2a14fbe1e0a2 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/Cargo.toml +++ b/src/tools/rust-analyzer/crates/ide-assists/Cargo.toml @@ -34,5 +34,8 @@ expect-test = "1.4.0" test-utils.workspace = true test-fixture.workspace = true +[features] +in-rust-tree = [] + [lints] workspace = true diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs index b6abb06a2afc..a211ca8f2d67 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs @@ -1,12 +1,13 @@ use std::iter::{self, Peekable}; use either::Either; -use hir::{sym, Adt, Crate, HasAttrs, HasSource, ImportPathConfig, ModuleDef, Semantics}; +use hir::{sym, Adt, Crate, HasAttrs, ImportPathConfig, ModuleDef, Semantics}; +use ide_db::syntax_helpers::suggest_name; use ide_db::RootDatabase; use ide_db::{famous_defs::FamousDefs, helpers::mod_path_to_ast}; use itertools::Itertools; use syntax::ast::edit_in_place::Removable; -use syntax::ast::{self, make, AstNode, HasName, MatchArmList, MatchExpr, Pat}; +use syntax::ast::{self, make, AstNode, MatchArmList, MatchExpr, Pat}; use crate::{utils, AssistContext, AssistId, AssistKind, Assists}; @@ -90,7 +91,7 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) .into_iter() .filter_map(|variant| { Some(( - build_pat(ctx.db(), module, variant, cfg)?, + build_pat(ctx, module, variant, cfg)?, variant.should_be_hidden(ctx.db(), module.krate()), )) }) @@ -141,9 +142,8 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) let is_hidden = variants .iter() .any(|variant| variant.should_be_hidden(ctx.db(), module.krate())); - let patterns = variants - .into_iter() - .filter_map(|variant| build_pat(ctx.db(), module, variant, cfg)); + let patterns = + variants.into_iter().filter_map(|variant| build_pat(ctx, module, variant, cfg)); (ast::Pat::from(make::tuple_pat(patterns)), is_hidden) }) @@ -174,9 +174,8 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) let is_hidden = variants .iter() .any(|variant| variant.should_be_hidden(ctx.db(), module.krate())); - let patterns = variants - .into_iter() - .filter_map(|variant| build_pat(ctx.db(), module, variant, cfg)); + let patterns = + variants.into_iter().filter_map(|variant| build_pat(ctx, module, variant, cfg)); (ast::Pat::from(make::slice_pat(patterns)), is_hidden) }) .filter(|(variant_pat, _)| is_variant_missing(&top_lvl_pats, variant_pat)); @@ -438,33 +437,39 @@ fn resolve_array_of_enum_def( } fn build_pat( - db: &RootDatabase, + ctx: &AssistContext<'_>, module: hir::Module, var: ExtendedVariant, cfg: ImportPathConfig, ) -> Option { + let db = ctx.db(); match var { ExtendedVariant::Variant(var) => { let edition = module.krate().edition(db); let path = mod_path_to_ast(&module.find_path(db, ModuleDef::from(var), cfg)?, edition); - // FIXME: use HIR for this; it doesn't currently expose struct vs. tuple vs. unit variants though - Some(match var.source(db)?.value.kind() { - ast::StructKind::Tuple(field_list) => { - let pats = - iter::repeat(make::wildcard_pat().into()).take(field_list.fields().count()); + let fields = var.fields(db); + let pat = match var.kind(db) { + hir::StructKind::Tuple => { + let mut name_generator = suggest_name::NameGenerator::new(); + let pats = fields.into_iter().map(|f| { + let name = name_generator.for_type(&f.ty(db), db, edition); + match name { + Some(name) => make::ext::simple_ident_pat(make::name(&name)).into(), + None => make::wildcard_pat().into(), + } + }); make::tuple_struct_pat(path, pats).into() } - ast::StructKind::Record(field_list) => { - let pats = field_list.fields().map(|f| { - make::ext::simple_ident_pat( - f.name().expect("Record field must have a name"), - ) - .into() - }); + hir::StructKind::Record => { + let pats = fields + .into_iter() + .map(|f| make::name(f.name(db).as_str())) + .map(|name| make::ext::simple_ident_pat(name).into()); make::record_pat(path, pats).into() } - ast::StructKind::Unit => make::path_pat(path), - }) + hir::StructKind::Unit => make::path_pat(path), + }; + Some(pat) } ExtendedVariant::True => Some(ast::Pat::from(make::literal_pat("true"))), ExtendedVariant::False => Some(ast::Pat::from(make::literal_pat("false"))), @@ -1976,4 +1981,81 @@ fn a() { }"#, ) } + + #[test] + fn suggest_name_for_tuple_struct_patterns() { + // single tuple struct + check_assist( + add_missing_match_arms, + r#" +struct S; + +pub enum E { + A + B(S), +} + +fn f() { + let value = E::A; + match value { + $0 + } +} +"#, + r#" +struct S; + +pub enum E { + A + B(S), +} + +fn f() { + let value = E::A; + match value { + $0E::A => todo!(), + E::B(s) => todo!(), + } +} +"#, + ); + + // multiple tuple struct patterns + check_assist( + add_missing_match_arms, + r#" +struct S1; +struct S2; + +pub enum E { + A + B(S1, S2), +} + +fn f() { + let value = E::A; + match value { + $0 + } +} +"#, + r#" +struct S1; +struct S2; + +pub enum E { + A + B(S1, S2), +} + +fn f() { + let value = E::A; + match value { + $0E::A => todo!(), + E::B(s1, s2) => todo!(), + } +} +"#, + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/explicit_enum_discriminant.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/explicit_enum_discriminant.rs new file mode 100644 index 000000000000..fafc3448a87c --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/explicit_enum_discriminant.rs @@ -0,0 +1,206 @@ +use hir::Semantics; +use ide_db::{ + assists::{AssistId, AssistKind}, + source_change::SourceChangeBuilder, + RootDatabase, +}; +use syntax::{ast, AstNode}; + +use crate::{AssistContext, Assists}; + +// Assist: explicit_enum_discriminant +// +// Adds explicit discriminant to all enum variants. +// +// ``` +// enum TheEnum$0 { +// Foo, +// Bar, +// Baz = 42, +// Quux, +// } +// ``` +// -> +// ``` +// enum TheEnum { +// Foo = 0, +// Bar = 1, +// Baz = 42, +// Quux = 43, +// } +// ``` +pub(crate) fn explicit_enum_discriminant(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { + let enum_node = ctx.find_node_at_offset::()?; + let enum_def = ctx.sema.to_def(&enum_node)?; + + let is_data_carrying = enum_def.is_data_carrying(ctx.db()); + let has_primitive_repr = enum_def.repr(ctx.db()).and_then(|repr| repr.int).is_some(); + + // Data carrying enums without a primitive repr have no stable discriminants. + if is_data_carrying && !has_primitive_repr { + return None; + } + + let variant_list = enum_node.variant_list()?; + + // Don't offer the assist if the enum has no variants or if all variants already have an + // explicit discriminant. + if variant_list.variants().all(|variant_node| variant_node.expr().is_some()) { + return None; + } + + acc.add( + AssistId("explicit_enum_discriminant", AssistKind::RefactorRewrite), + "Add explicit enum discriminants", + enum_node.syntax().text_range(), + |builder| { + for variant_node in variant_list.variants() { + add_variant_discriminant(&ctx.sema, builder, &variant_node); + } + }, + ); + + Some(()) +} + +fn add_variant_discriminant( + sema: &Semantics<'_, RootDatabase>, + builder: &mut SourceChangeBuilder, + variant_node: &ast::Variant, +) { + if variant_node.expr().is_some() { + return; + } + + let Some(variant_def) = sema.to_def(variant_node) else { + return; + }; + let Ok(discriminant) = variant_def.eval(sema.db) else { + return; + }; + + let variant_range = variant_node.syntax().text_range(); + + builder.insert(variant_range.end(), format!(" = {discriminant}")); +} + +#[cfg(test)] +mod tests { + use crate::tests::{check_assist, check_assist_not_applicable}; + + use super::explicit_enum_discriminant; + + #[test] + fn non_primitive_repr_non_data_bearing_add_discriminant() { + check_assist( + explicit_enum_discriminant, + r#" +enum TheEnum$0 { + Foo, + Bar, + Baz = 42, + Quux, + FooBar = -5, + FooBaz, +} +"#, + r#" +enum TheEnum { + Foo = 0, + Bar = 1, + Baz = 42, + Quux = 43, + FooBar = -5, + FooBaz = -4, +} +"#, + ); + } + + #[test] + fn primitive_repr_data_bearing_add_discriminant() { + check_assist( + explicit_enum_discriminant, + r#" +#[repr(u8)] +$0enum TheEnum { + Foo { x: u32 }, + Bar, + Baz(String), + Quux, +} +"#, + r#" +#[repr(u8)] +enum TheEnum { + Foo { x: u32 } = 0, + Bar = 1, + Baz(String) = 2, + Quux = 3, +} +"#, + ); + } + + #[test] + fn non_primitive_repr_data_bearing_not_applicable() { + check_assist_not_applicable( + explicit_enum_discriminant, + r#" +enum TheEnum$0 { + Foo, + Bar(u16), + Baz, +} +"#, + ); + } + + #[test] + fn primitive_repr_non_data_bearing_add_discriminant() { + check_assist( + explicit_enum_discriminant, + r#" +#[repr(i64)] +enum TheEnum { + Foo = 1 << 63, + Bar, + Baz$0 = 0x7fff_ffff_ffff_fffe, + Quux, +} +"#, + r#" +#[repr(i64)] +enum TheEnum { + Foo = 1 << 63, + Bar = -9223372036854775807, + Baz = 0x7fff_ffff_ffff_fffe, + Quux = 9223372036854775807, +} +"#, + ); + } + + #[test] + fn discriminants_already_explicit_not_applicable() { + check_assist_not_applicable( + explicit_enum_discriminant, + r#" +enum TheEnum$0 { + Foo = 0, + Bar = 4, +} +"#, + ); + } + + #[test] + fn empty_enum_not_applicable() { + check_assist_not_applicable( + explicit_enum_discriminant, + r#" +enum TheEnum$0 {} +"#, + ); + } +} diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_type_alias.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_type_alias.rs index dcf16e89b2c4..1eaf31628f31 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_type_alias.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_type_alias.rs @@ -2,7 +2,7 @@ use either::Either; use ide_db::syntax_helpers::node_ext::walk_ty; use syntax::{ ast::{self, edit::IndentLevel, make, AstNode, HasGenericArgs, HasGenericParams, HasName}, - ted, + syntax_editor, }; use crate::{AssistContext, AssistId, AssistKind, Assists}; @@ -43,9 +43,8 @@ pub(crate) fn extract_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>) -> AssistId("extract_type_alias", AssistKind::RefactorExtract), "Extract type as type alias", target, - |edit| { - let node = edit.make_syntax_mut(node.clone()); - let target_ty = edit.make_mut(ty.clone()); + |builder| { + let mut edit = builder.make_editor(node); let mut known_generics = match item.generic_param_list() { Some(it) => it.generic_params().collect(), @@ -67,25 +66,28 @@ pub(crate) fn extract_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>) -> .map_or(String::new(), |it| it.to_generic_args().to_string()); // FIXME: replace with a `ast::make` constructor let new_ty = make::ty(&format!("Type{ty_args}")).clone_for_update(); - ted::replace(target_ty.syntax(), new_ty.syntax()); + edit.replace(ty.syntax(), new_ty.syntax()); // Insert new alias - let indent = IndentLevel::from_node(&node); let ty_alias = make::ty_alias("Type", generic_params, None, None, Some((ty, None))) .clone_for_update(); - ted::insert_all( - ted::Position::before(node), + + if let Some(cap) = ctx.config.snippet_cap { + if let Some(name) = ty_alias.name() { + edit.add_annotation(name.syntax(), builder.make_tabstop_before(cap)); + } + } + + let indent = IndentLevel::from_node(node); + edit.insert_all( + syntax_editor::Position::before(node), vec![ ty_alias.syntax().clone().into(), make::tokens::whitespace(&format!("\n\n{indent}")).into(), ], ); - if let Some(cap) = ctx.config.snippet_cap { - if let Some(name) = ty_alias.name() { - edit.add_tabstop_before(cap, name); - } - } + builder.add_file_edits(ctx.file_id(), edit); }, ) } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs index 5ae75bb1ff8e..a43a4b5e1a06 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs @@ -1,4 +1,5 @@ use hir::TypeInfo; +use ide_db::syntax_helpers::suggest_name; use syntax::{ ast::{self, edit::IndentLevel, edit_in_place::Indent, make, AstNode, HasName}, ted, NodeOrToken, @@ -6,7 +7,7 @@ use syntax::{ SyntaxNode, T, }; -use crate::{utils::suggest_name, AssistContext, AssistId, AssistKind, Assists}; +use crate::{AssistContext, AssistId, AssistKind, Assists}; // Assist: extract_variable // diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/flip_comma.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/flip_comma.rs index f40f2713ad13..af2c2c759ec7 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/flip_comma.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/flip_comma.rs @@ -1,4 +1,8 @@ -use syntax::{algo::non_trivia_sibling, Direction, SyntaxKind, T}; +use ide_db::base_db::SourceDatabase; +use syntax::TextSize; +use syntax::{ + algo::non_trivia_sibling, ast, AstNode, Direction, SyntaxKind, SyntaxToken, TextRange, T, +}; use crate::{AssistContext, AssistId, AssistKind, Assists}; @@ -21,6 +25,8 @@ pub(crate) fn flip_comma(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<( let comma = ctx.find_token_syntax_at_offset(T![,])?; let prev = non_trivia_sibling(comma.clone().into(), Direction::Prev)?; let next = non_trivia_sibling(comma.clone().into(), Direction::Next)?; + let (mut prev_text, mut next_text) = (prev.to_string(), next.to_string()); + let (mut prev_range, mut next_range) = (prev.text_range(), next.text_range()); // Don't apply a "flip" in case of a last comma // that typically comes before punctuation @@ -34,17 +40,55 @@ pub(crate) fn flip_comma(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<( return None; } + if let Some(parent) = comma.parent().and_then(ast::TokenTree::cast) { + // An attribute. It often contains a path followed by a token tree (e.g. `align(2)`), so we have + // to be smarter. + let prev_start = + match comma.siblings_with_tokens(Direction::Prev).skip(1).find(|it| it.kind() == T![,]) + { + Some(it) => position_after_token(it.as_token().unwrap()), + None => position_after_token(&parent.left_delimiter_token()?), + }; + let prev_end = prev.text_range().end(); + let next_start = next.text_range().start(); + let next_end = + match comma.siblings_with_tokens(Direction::Next).skip(1).find(|it| it.kind() == T![,]) + { + Some(it) => position_before_token(it.as_token().unwrap()), + None => position_before_token(&parent.right_delimiter_token()?), + }; + prev_range = TextRange::new(prev_start, prev_end); + next_range = TextRange::new(next_start, next_end); + let file_text = ctx.db().file_text(ctx.file_id().file_id()); + prev_text = file_text[prev_range].to_owned(); + next_text = file_text[next_range].to_owned(); + } + acc.add( AssistId("flip_comma", AssistKind::RefactorRewrite), "Flip comma", comma.text_range(), |edit| { - edit.replace(prev.text_range(), next.to_string()); - edit.replace(next.text_range(), prev.to_string()); + edit.replace(prev_range, next_text); + edit.replace(next_range, prev_text); }, ) } +fn position_before_token(token: &SyntaxToken) -> TextSize { + match non_trivia_sibling(token.clone().into(), Direction::Prev) { + Some(prev_token) => prev_token.text_range().end(), + None => token.text_range().start(), + } +} + +fn position_after_token(token: &SyntaxToken) -> TextSize { + match non_trivia_sibling(token.clone().into(), Direction::Next) { + Some(prev_token) => prev_token.text_range().start(), + None => token.text_range().end(), + } +} + #[cfg(test)] mod tests { use super::*; @@ -89,4 +133,18 @@ mod tests { // See https://github.com/rust-lang/rust-analyzer/issues/7693 check_assist_not_applicable(flip_comma, r#"bar!(a,$0 b)"#); } + + #[test] + fn flip_comma_attribute() { + check_assist( + flip_comma, + r#"#[repr(align(2),$0 C)] struct Foo;"#, + r#"#[repr(C, align(2))] struct Foo;"#, + ); + check_assist( + flip_comma, + r#"#[foo(bar, baz(1 + 1),$0 qux, other)] struct Foo;"#, + r#"#[foo(bar, qux, baz(1 + 1), other)] struct Foo;"#, + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_trait.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_trait.rs index bf4ce5c907e8..66bf9b018686 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_trait.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_trait.rs @@ -2,13 +2,14 @@ use std::ops::Not; use crate::{ assist_context::{AssistContext, Assists}, - utils::{convert_param_list_to_arg_list, suggest_name}, + utils::convert_param_list_to_arg_list, }; use either::Either; use hir::{db::HirDatabase, HasVisibility}; use ide_db::{ assists::{AssistId, GroupLabel}, path_transform::PathTransform, + syntax_helpers::suggest_name, FxHashMap, FxHashSet, }; use itertools::Itertools; @@ -281,8 +282,11 @@ fn generate_impl( ai.assoc_items() .filter(|item| matches!(item, AssocItem::MacroCall(_)).not()) .for_each(|item| { - let assoc = - process_assoc_item(item, qualified_path_type.clone(), field_name); + let assoc = process_assoc_item( + item.clone_for_update(), + qualified_path_type.clone(), + field_name, + ); if let Some(assoc) = assoc { delegate_assoc_items.add_item(assoc); } @@ -583,7 +587,7 @@ fn resolve_name_conflicts( for old_strukt_param in old_strukt_params.generic_params() { // Get old name from `strukt` - let mut name = SmolStr::from(match &old_strukt_param { + let name = SmolStr::from(match &old_strukt_param { ast::GenericParam::ConstParam(c) => c.name()?.to_string(), ast::GenericParam::LifetimeParam(l) => { l.lifetime()?.lifetime_ident_token()?.to_string() @@ -592,8 +596,19 @@ fn resolve_name_conflicts( }); // The new name cannot be conflicted with generics in trait, and the renamed names. - name = suggest_name::for_unique_generic_name(&name, old_impl_params); - name = suggest_name::for_unique_generic_name(&name, ¶ms); + let param_list_to_names = |param_list: &GenericParamList| { + param_list.generic_params().flat_map(|param| match param { + ast::GenericParam::TypeParam(t) => t.name().map(|name| name.to_string()), + p => Some(p.to_string()), + }) + }; + let existing_names = param_list_to_names(old_impl_params) + .chain(param_list_to_names(¶ms)) + .collect_vec(); + let mut name_generator = suggest_name::NameGenerator::new_with_names( + existing_names.iter().map(|s| s.as_str()), + ); + let name = name_generator.suggest_name(&name); match old_strukt_param { ast::GenericParam::ConstParam(c) => { if let Some(const_ty) = c.ty() { @@ -1212,9 +1227,9 @@ struct S { b : B, } -impl Trait for S { - fn f(&self, a: T0) -> T0 { - as Trait>::f(&self.b, a) +impl Trait for S { + fn f(&self, a: T1) -> T1 { + as Trait>::f(&self.b, a) } } "#, @@ -1526,12 +1541,12 @@ where b : B, } -impl Trait for S +impl Trait for S where - T10: AnotherTrait + T3: AnotherTrait { fn f(&self, a: T) -> T { - as Trait>::f(&self.b, a) + as Trait>::f(&self.b, a) } }"#, ); @@ -1588,12 +1603,12 @@ where b : B, } -impl Trait for S +impl Trait for S where - T0: AnotherTrait + T2: AnotherTrait { fn f(&self, a: T) -> T { - as Trait>::f(&self.b, a) + as Trait>::f(&self.b, a) } }"#, ); @@ -1785,4 +1800,40 @@ impl T for B { "#, ); } + + #[test] + fn assoc_items_attributes_mutably_cloned() { + check_assist( + generate_delegate_trait, + r#" +pub struct A; +pub trait C { + #[allow(clippy::dead_code)] + fn a_funk(&self) -> &D; +} + +pub struct B> { + has_dr$0ain: T, +} +"#, + r#" +pub struct A; +pub trait C { + #[allow(clippy::dead_code)] + fn a_funk(&self) -> &D; +} + +pub struct B> { + has_drain: T, +} + +impl> C for B { + #[allow(clippy::dead_code)] + fn a_funk(&self) -> &D { + >::a_funk(&self.has_drain) + } +} +"#, + ) + } } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs index 821783c28313..7b7dac9a3d6c 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs @@ -1,10 +1,22 @@ use syntax::{ - ast::{self, make, AstNode, HasName}, + ast::{self, edit_in_place::Indent, make, AstNode, HasName}, ted, }; use crate::{utils, AssistContext, AssistId, AssistKind, Assists}; +fn insert_impl(impl_: ast::Impl, nominal: &ast::Adt) { + let indent = nominal.indent_level(); + ted::insert_all_raw( + ted::Position::after(nominal.syntax()), + vec![ + // Add a blank line after the ADT, and indentation for the impl to match the ADT + make::tokens::whitespace(&format!("\n\n{indent}")).into(), + impl_.syntax().clone().into(), + ], + ); +} + // Assist: generate_impl // // Adds a new inherent impl for a type. @@ -46,12 +58,7 @@ pub(crate) fn generate_impl(acc: &mut Assists, ctx: &AssistContext<'_>) -> Optio } } - // Add the impl after the adt - let nominal = edit.make_mut(nominal); - ted::insert_all_raw( - ted::Position::after(nominal.syntax()), - vec![make::tokens::blank_line().into(), impl_.syntax().clone().into()], - ); + insert_impl(impl_, &edit.make_mut(nominal)); }, ) } @@ -97,12 +104,7 @@ pub(crate) fn generate_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_>) -> } } - // Add the impl after the adt - let nominal = edit.make_mut(nominal); - ted::insert_all_raw( - ted::Position::after(nominal.syntax()), - vec![make::tokens::blank_line().into(), impl_.syntax().clone().into()], - ); + insert_impl(impl_, &edit.make_mut(nominal)); }, ) } @@ -418,4 +420,65 @@ mod tests { "/// Has a lifetime parameter\nstruct Foo<'a, T: Foo<'a>> {}", ); } + + #[test] + fn add_impl_with_indent() { + check_assist( + generate_impl, + r#" + mod foo { + struct Bar$0 {} + } + "#, + r#" + mod foo { + struct Bar {} + + impl Bar {$0} + } + "#, + ); + } + + #[test] + fn add_impl_with_multiple_indent() { + check_assist( + generate_impl, + r#" + mod foo { + fn bar() { + struct Baz$0 {} + } + } + "#, + r#" + mod foo { + fn bar() { + struct Baz {} + + impl Baz {$0} + } + } + "#, + ); + } + + #[test] + fn add_trait_impl_with_indent() { + check_assist( + generate_trait_impl, + r#" + mod foo { + struct Bar$0 {} + } + "#, + r#" + mod foo { + struct Bar {} + + impl ${0:_} for Bar {} + } + "#, + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs index 5bd204dd573b..9e09f198feb4 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs @@ -2,14 +2,18 @@ use std::collections::BTreeSet; use ast::make; use either::Either; -use hir::{db::HirDatabase, sym, FileRange, PathResolution, Semantics, TypeInfo}; +use hir::{ + db::{ExpandDatabase, HirDatabase}, + sym, FileRange, PathResolution, Semantics, TypeInfo, +}; use ide_db::{ + base_db::CrateId, defs::Definition, imports::insert_use::remove_path_if_in_use_stmt, path_transform::PathTransform, search::{FileReference, FileReferenceNode, SearchScope}, source_change::SourceChangeBuilder, - syntax_helpers::{insert_whitespace_into_node::insert_ws_into, node_ext::expr_as_name_ref}, + syntax_helpers::{node_ext::expr_as_name_ref, prettify_macro_expansion}, EditionedFileId, RootDatabase, }; use itertools::{izip, Itertools}; @@ -102,12 +106,13 @@ pub(crate) fn inline_into_callers(acc: &mut Assists, ctx: &AssistContext<'_>) -> let mut remove_def = true; let mut inline_refs_for_file = |file_id, refs: Vec| { builder.edit_file(file_id); + let call_krate = ctx.sema.file_to_module_def(file_id).map(|it| it.krate()); let count = refs.len(); // The collects are required as we are otherwise iterating while mutating 🙅‍♀️🙅‍♂️ let (name_refs, name_refs_use) = split_refs_and_uses(builder, refs, Some); let call_infos: Vec<_> = name_refs .into_iter() - .filter_map(CallInfo::from_name_ref) + .filter_map(|it| CallInfo::from_name_ref(it, call_krate?.into())) // FIXME: do not handle callsites in macros' parameters, because // directly inlining into macros may cause errors. .filter(|call_info| !ctx.sema.hir_file_for(call_info.node.syntax()).is_macro()) @@ -185,7 +190,10 @@ pub(super) fn split_refs_and_uses( // ``` pub(crate) fn inline_call(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { let name_ref: ast::NameRef = ctx.find_node_at_offset()?; - let call_info = CallInfo::from_name_ref(name_ref.clone())?; + let call_info = CallInfo::from_name_ref( + name_ref.clone(), + ctx.sema.file_to_module_def(ctx.file_id())?.krate().into(), + )?; let (function, label) = match &call_info.node { ast::CallableExpr::Call(call) => { let path = match call.expr()? { @@ -243,10 +251,11 @@ struct CallInfo { node: ast::CallableExpr, arguments: Vec, generic_arg_list: Option, + krate: CrateId, } impl CallInfo { - fn from_name_ref(name_ref: ast::NameRef) -> Option { + fn from_name_ref(name_ref: ast::NameRef, krate: CrateId) -> Option { let parent = name_ref.syntax().parent()?; if let Some(call) = ast::MethodCallExpr::cast(parent.clone()) { let receiver = call.receiver()?; @@ -256,6 +265,7 @@ impl CallInfo { generic_arg_list: call.generic_arg_list(), node: ast::CallableExpr::MethodCall(call), arguments, + krate, }) } else if let Some(segment) = ast::PathSegment::cast(parent) { let path = segment.syntax().parent().and_then(ast::Path::cast)?; @@ -266,6 +276,7 @@ impl CallInfo { arguments: call.arg_list()?.args().collect(), node: ast::CallableExpr::Call(call), generic_arg_list: segment.generic_arg_list(), + krate, }) } else { None @@ -307,11 +318,15 @@ fn inline( function: hir::Function, fn_body: &ast::BlockExpr, params: &[(ast::Pat, Option, hir::Param)], - CallInfo { node, arguments, generic_arg_list }: &CallInfo, + CallInfo { node, arguments, generic_arg_list, krate }: &CallInfo, ) -> ast::Expr { - let mut body = if sema.hir_file_for(fn_body.syntax()).is_macro() { + let file_id = sema.hir_file_for(fn_body.syntax()); + let mut body = if let Some(macro_file) = file_id.macro_file() { cov_mark::hit!(inline_call_defined_in_macro); - if let Some(body) = ast::BlockExpr::cast(insert_ws_into(fn_body.syntax().clone())) { + let span_map = sema.db.expansion_span_map(macro_file); + let body_prettified = + prettify_macro_expansion(sema.db, fn_body.syntax().clone(), &span_map, *krate); + if let Some(body) = ast::BlockExpr::cast(body_prettified) { body } else { fn_body.clone_for_update() @@ -420,8 +435,16 @@ fn inline( let mut insert_let_stmt = || { let param_ty = param_ty.clone().map(|param_ty| { - if sema.hir_file_for(param_ty.syntax()).is_macro() { - ast::Type::cast(insert_ws_into(param_ty.syntax().clone())).unwrap_or(param_ty) + let file_id = sema.hir_file_for(param_ty.syntax()); + if let Some(macro_file) = file_id.macro_file() { + let span_map = sema.db.expansion_span_map(macro_file); + let param_ty_prettified = prettify_macro_expansion( + sema.db, + param_ty.syntax().clone(), + &span_map, + *krate, + ); + ast::Type::cast(param_ty_prettified).unwrap_or(param_ty) } else { param_ty } @@ -1020,6 +1043,7 @@ fn main() { check_assist( inline_call, r#" +//- minicore: sized fn foo(x: *const u32) -> u32 { x as u32 } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_const_as_literal.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_const_as_literal.rs index f1c2acdd3ed9..6b504a918b40 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_const_as_literal.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_const_as_literal.rs @@ -53,10 +53,7 @@ pub(crate) fn inline_const_as_literal(acc: &mut Assists, ctx: &AssistContext<'_> | ast::Expr::BinExpr(_) | ast::Expr::CallExpr(_) => { let edition = ctx.sema.scope(variable.syntax())?.krate().edition(ctx.db()); - match konst.render_eval(ctx.sema.db, edition) { - Ok(result) => result, - Err(_) => return None, - } + konst.eval(ctx.sema.db, edition).ok()? } _ => return None, }; @@ -127,12 +124,14 @@ mod tests { ("u64", "0", NUMBER), ("u128", "0", NUMBER), ("usize", "0", NUMBER), + ("usize", "16", NUMBER), ("i8", "0", NUMBER), ("i16", "0", NUMBER), ("i32", "0", NUMBER), ("i64", "0", NUMBER), ("i128", "0", NUMBER), ("isize", "0", NUMBER), + ("isize", "16", NUMBER), ("bool", "false", BOOL), ("&str", "\"str\"", STR), ("char", "'c'", CHAR), diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_local_variable.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_local_variable.rs index 6a1f7f26c92c..b9fc075ae838 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_local_variable.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_local_variable.rs @@ -333,7 +333,8 @@ fn foo() { check_assist( inline_local_variable, r" -fn bar(a: usize): usize { a } +//- minicore: sized +fn bar(a: usize) -> usize { a } fn foo() { let a$0 = bar(1) as u64; a + 1; @@ -347,7 +348,7 @@ fn foo() { bar(a); }", r" -fn bar(a: usize): usize { a } +fn bar(a: usize) -> usize { a } fn foo() { (bar(1) as u64) + 1; if (bar(1) as u64) > 10 { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_macro.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_macro.rs index 4708be616964..d558ec3bec7d 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_macro.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_macro.rs @@ -1,4 +1,5 @@ -use ide_db::syntax_helpers::insert_whitespace_into_node::insert_ws_into; +use hir::db::ExpandDatabase; +use ide_db::syntax_helpers::prettify_macro_expansion; use syntax::ast::{self, AstNode}; use crate::{AssistContext, AssistId, AssistKind, Assists}; @@ -36,7 +37,15 @@ use crate::{AssistContext, AssistId, AssistKind, Assists}; // ``` pub(crate) fn inline_macro(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { let unexpanded = ctx.find_node_at_offset::()?; - let expanded = insert_ws_into(ctx.sema.expand(&unexpanded)?.clone_for_update()); + let macro_call = ctx.sema.to_def(&unexpanded)?; + let expanded = ctx.sema.parse_or_expand(macro_call.as_file()); + let span_map = ctx.sema.db.expansion_span_map(macro_call.as_macro_file()); + let expanded = prettify_macro_expansion( + ctx.db(), + expanded, + &span_map, + ctx.sema.file_to_module_def(ctx.file_id())?.krate().into(), + ); let text_range = unexpanded.syntax().text_range(); acc.add( @@ -295,6 +304,75 @@ fn main() { } }; } +"#, + ); + } + + #[test] + fn dollar_crate() { + check_assist( + inline_macro, + r#" +pub struct Foo; +#[macro_export] +macro_rules! m { + () => { $crate::Foo }; +} +fn bar() { + m$0!(); +} +"#, + r#" +pub struct Foo; +#[macro_export] +macro_rules! m { + () => { $crate::Foo }; +} +fn bar() { + crate::Foo; +} +"#, + ); + check_assist( + inline_macro, + r#" +//- /a.rs crate:a +pub struct Foo; +#[macro_export] +macro_rules! m { + () => { $crate::Foo }; +} +//- /b.rs crate:b deps:a +fn bar() { + a::m$0!(); +} +"#, + r#" +fn bar() { + a::Foo; +} +"#, + ); + check_assist( + inline_macro, + r#" +//- /a.rs crate:a +pub struct Foo; +#[macro_export] +macro_rules! m { + () => { $crate::Foo }; +} +//- /b.rs crate:b deps:a +pub use a::m; +//- /c.rs crate:c deps:b +fn bar() { + b::m$0!(); +} +"#, + r#" +fn bar() { + a::Foo; +} "#, ); } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_type_alias.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_type_alias.rs index f6624d6c872d..66dffde505c1 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_type_alias.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_type_alias.rs @@ -43,6 +43,7 @@ use super::inline_call::split_refs_and_uses; // fn foo() { // let _: i32 = 3; // } +// ``` pub(crate) fn inline_type_alias_uses(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { let name = ctx.find_node_at_offset::()?; let ast_alias = name.syntax().parent().and_then(ast::TypeAlias::cast)?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_generic.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_generic.rs index 543b7f7ab632..bf6ac1719f31 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_generic.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_generic.rs @@ -1,9 +1,11 @@ +use ide_db::syntax_helpers::suggest_name; +use itertools::Itertools; use syntax::{ - ast::{self, edit_in_place::GenericParamsOwnerEdit, make, AstNode, HasGenericParams}, + ast::{self, edit_in_place::GenericParamsOwnerEdit, make, AstNode, HasGenericParams, HasName}, ted, }; -use crate::{utils::suggest_name, AssistContext, AssistId, AssistKind, Assists}; +use crate::{AssistContext, AssistId, AssistKind, Assists}; // Assist: introduce_named_generic // @@ -32,8 +34,18 @@ pub(crate) fn introduce_named_generic(acc: &mut Assists, ctx: &AssistContext<'_> let impl_trait_type = edit.make_mut(impl_trait_type); let fn_ = edit.make_mut(fn_); let fn_generic_param_list = fn_.get_or_create_generic_param_list(); - let type_param_name = - suggest_name::for_impl_trait_as_generic(&impl_trait_type, &fn_generic_param_list); + + let existing_names = fn_generic_param_list + .generic_params() + .flat_map(|param| match param { + ast::GenericParam::TypeParam(t) => t.name().map(|name| name.to_string()), + p => Some(p.to_string()), + }) + .collect_vec(); + let type_param_name = suggest_name::NameGenerator::new_with_names( + existing_names.iter().map(|s| s.as_str()), + ) + .for_impl_trait_as_generic(&impl_trait_type); let type_param = make::type_param(make::name(&type_param_name), Some(type_bound_list)) .clone_for_update(); @@ -115,7 +127,7 @@ fn foo<$0B: Bar check_assist( introduce_named_generic, r#"fn foo(bar: $0impl Bar) {}"#, - r#"fn foo(bar: B0) {}"#, + r#"fn foo(bar: B1) {}"#, ); } @@ -124,7 +136,7 @@ fn foo<$0B: Bar check_assist( introduce_named_generic, r#"fn foo(bar: $0impl Bar) {}"#, - r#"fn foo(bar: B2) {}"#, + r#"fn foo(bar: B4) {}"#, ); } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_imports.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_imports.rs index d4fdc072fb5b..c6f99d68748d 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_imports.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_imports.rs @@ -6,7 +6,10 @@ use ide_db::{ search::{FileReference, ReferenceCategory, SearchScope}, FxHashMap, RootDatabase, }; -use syntax::{ast, AstNode}; +use syntax::{ + ast::{self, Rename}, + AstNode, +}; use text_edit::TextRange; use crate::{AssistContext, AssistId, AssistKind, Assists}; @@ -100,19 +103,19 @@ pub(crate) fn remove_unused_imports(acc: &mut Assists, ctx: &AssistContext<'_>) hir::ScopeDef::ModuleDef(d) => Some(Definition::from(*d)), _ => None, }) - .any(|d| used_once_in_scope(ctx, d, scope)) + .any(|d| used_once_in_scope(ctx, d, u.rename(), scope)) { return Some(u); } } else if let Definition::Trait(ref t) = def { // If the trait or any item is used. - if !std::iter::once(def) - .chain(t.items(ctx.db()).into_iter().map(Definition::from)) - .any(|d| used_once_in_scope(ctx, d, scope)) + if !std::iter::once((def, u.rename())) + .chain(t.items(ctx.db()).into_iter().map(|item| (item.into(), None))) + .any(|(d, rename)| used_once_in_scope(ctx, d, rename, scope)) { return Some(u); } - } else if !used_once_in_scope(ctx, def, scope) { + } else if !used_once_in_scope(ctx, def, u.rename(), scope) { return Some(u); } @@ -138,7 +141,12 @@ pub(crate) fn remove_unused_imports(acc: &mut Assists, ctx: &AssistContext<'_>) } } -fn used_once_in_scope(ctx: &AssistContext<'_>, def: Definition, scopes: &Vec) -> bool { +fn used_once_in_scope( + ctx: &AssistContext<'_>, + def: Definition, + rename: Option, + scopes: &Vec, +) -> bool { let mut found = false; for scope in scopes { @@ -151,7 +159,10 @@ fn used_once_in_scope(ctx: &AssistContext<'_>, def: Definition, scopes: &Vec Quxx for T {} +} + +use foo::{Foo as Bar, Bar as Baz, Qux as _, Quxx as _}$0; + +fn test(_: Bar) { + let a = (); + a.quxx(); +} +"#, + r#" +mod foo { + pub struct Foo {} + pub struct Bar {} + pub struct Qux {} + pub trait Quux { + fn quxx(&self) {} + } + impl Quxx for T {} +} + +use foo::{Foo as Bar, Quxx as _}; + +fn test(_: Bar) { + let a = (); + a.quxx(); +} "#, ); } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs index 59bb0c45e143..a856da092153 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs @@ -1,9 +1,10 @@ +use ide_db::syntax_helpers::suggest_name; use syntax::{ ast::{self, make, AstNode}, ted, }; -use crate::{utils::suggest_name, AssistContext, AssistId, AssistKind, Assists}; +use crate::{AssistContext, AssistId, AssistKind, Assists}; // Assist: replace_is_some_with_if_let_some // diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs index 65330b34c4ad..1101c20f1b78 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs @@ -29,7 +29,7 @@ pub(crate) fn replace_qualified_name_with_use( acc: &mut Assists, ctx: &AssistContext<'_>, ) -> Option<()> { - let original_path: ast::Path = ctx.find_node_at_offset()?; + let mut original_path: ast::Path = ctx.find_node_at_offset()?; // We don't want to mess with use statements if original_path.syntax().ancestors().find_map(ast::UseTree::cast).is_some() { cov_mark::hit!(not_applicable_in_use); @@ -37,8 +37,7 @@ pub(crate) fn replace_qualified_name_with_use( } if original_path.qualifier().is_none() { - cov_mark::hit!(dont_import_trivial_paths); - return None; + original_path = original_path.parent_path()?; } // only offer replacement for non assoc items @@ -236,12 +235,6 @@ fs::Path ); } - #[test] - fn dont_import_trivial_paths() { - cov_mark::check!(dont_import_trivial_paths); - check_assist_not_applicable(replace_qualified_name_with_use, r"impl foo$0 for () {}"); - } - #[test] fn test_replace_not_applicable_in_use() { cov_mark::check!(not_applicable_in_use); @@ -271,6 +264,29 @@ fn main() { ); } + #[test] + fn assist_runs_on_first_segment() { + check_assist( + replace_qualified_name_with_use, + r" +mod std { pub mod fmt { pub trait Debug {} } } +fn main() { + $0std::fmt::Debug; + let x: std::fmt::Debug = std::fmt::Debug; +} + ", + r" +use std::fmt; + +mod std { pub mod fmt { pub trait Debug {} } } +fn main() { + fmt::Debug; + let x: fmt::Debug = fmt::Debug; +} + ", + ); + } + #[test] fn does_not_replace_in_submodules() { check_assist( diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs index eedb2ea3b9ad..e452b5f77870 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs @@ -64,12 +64,9 @@ pub(crate) fn toggle_macro_delimiter(acc: &mut Assists, ctx: &AssistContext<'_>) acc.add( AssistId("toggle_macro_delimiter", AssistKind::Refactor), match token { - MacroDelims::LPar => "Replace delimiters with braces", - MacroDelims::RPar => "Replace delimiters with braces", - MacroDelims::LBra => "Replace delimiters with parentheses", - MacroDelims::RBra => "Replace delimiters with parentheses", - MacroDelims::LCur => "Replace delimiters with brackets", - MacroDelims::RCur => "Replace delimiters with brackets", + MacroDelims::LPar | MacroDelims::RPar => "Replace delimiters with braces", + MacroDelims::LBra | MacroDelims::RBra => "Replace delimiters with parentheses", + MacroDelims::LCur | MacroDelims::RCur => "Replace delimiters with brackets", }, token_tree.syntax().text_range(), |builder| { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_return_type_in_result.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_return_type_in_result.rs index b68ed00f7721..8f0e9b4fe09d 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_return_type_in_result.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_return_type_in_result.rs @@ -1,12 +1,14 @@ use std::iter; +use hir::HasSource; use ide_db::{ famous_defs::FamousDefs, syntax_helpers::node_ext::{for_each_tail_expr, walk_expr}, }; +use itertools::Itertools; use syntax::{ - ast::{self, make, Expr}, - match_ast, ted, AstNode, + ast::{self, make, Expr, HasGenericParams}, + match_ast, ted, AstNode, ToSmolStr, }; use crate::{AssistContext, AssistId, AssistKind, Assists}; @@ -39,25 +41,22 @@ pub(crate) fn wrap_return_type_in_result(acc: &mut Assists, ctx: &AssistContext< }; let type_ref = &ret_type.ty()?; - let ty = ctx.sema.resolve_type(type_ref)?.as_adt(); - let result_enum = + let core_result = FamousDefs(&ctx.sema, ctx.sema.scope(type_ref.syntax())?.krate()).core_result_Result()?; - if matches!(ty, Some(hir::Adt::Enum(ret_type)) if ret_type == result_enum) { + let ty = ctx.sema.resolve_type(type_ref)?.as_adt(); + if matches!(ty, Some(hir::Adt::Enum(ret_type)) if ret_type == core_result) { + // The return type is already wrapped in a Result cov_mark::hit!(wrap_return_type_in_result_simple_return_type_already_result); return None; } - let new_result_ty = - make::ext::ty_result(type_ref.clone(), make::ty_placeholder()).clone_for_update(); - let generic_args = new_result_ty.syntax().descendants().find_map(ast::GenericArgList::cast)?; - let last_genarg = generic_args.generic_args().last()?; - acc.add( AssistId("wrap_return_type_in_result", AssistKind::RefactorRewrite), "Wrap return type in Result", type_ref.syntax().text_range(), |edit| { + let new_result_ty = result_type(ctx, &core_result, type_ref).clone_for_update(); let body = edit.make_mut(ast::Expr::BlockExpr(body)); let mut exprs_to_wrap = Vec::new(); @@ -81,16 +80,72 @@ pub(crate) fn wrap_return_type_in_result(acc: &mut Assists, ctx: &AssistContext< } let old_result_ty = edit.make_mut(type_ref.clone()); - ted::replace(old_result_ty.syntax(), new_result_ty.syntax()); - if let Some(cap) = ctx.config.snippet_cap { - edit.add_placeholder_snippet(cap, last_genarg); + // Add a placeholder snippet at the first generic argument that doesn't equal the return type. + // This is normally the error type, but that may not be the case when we inserted a type alias. + let args = new_result_ty.syntax().descendants().find_map(ast::GenericArgList::cast); + let error_type_arg = args.and_then(|list| { + list.generic_args().find(|arg| match arg { + ast::GenericArg::TypeArg(_) => arg.syntax().text() != type_ref.syntax().text(), + ast::GenericArg::LifetimeArg(_) => false, + _ => true, + }) + }); + if let Some(error_type_arg) = error_type_arg { + if let Some(cap) = ctx.config.snippet_cap { + edit.add_placeholder_snippet(cap, error_type_arg); + } } }, ) } +fn result_type( + ctx: &AssistContext<'_>, + core_result: &hir::Enum, + ret_type: &ast::Type, +) -> ast::Type { + // Try to find a Result type alias in the current scope (shadowing the default). + let result_path = hir::ModPath::from_segments( + hir::PathKind::Plain, + iter::once(hir::Name::new_symbol_root(hir::sym::Result.clone())), + ); + let alias = ctx.sema.resolve_mod_path(ret_type.syntax(), &result_path).and_then(|def| { + def.filter_map(|def| match def.as_module_def()? { + hir::ModuleDef::TypeAlias(alias) => { + let enum_ty = alias.ty(ctx.db()).as_adt()?.as_enum()?; + (&enum_ty == core_result).then_some(alias) + } + _ => None, + }) + .find_map(|alias| { + let mut inserted_ret_type = false; + let generic_params = alias + .source(ctx.db())? + .value + .generic_param_list()? + .generic_params() + .map(|param| match param { + // Replace the very first type parameter with the functions return type. + ast::GenericParam::TypeParam(_) if !inserted_ret_type => { + inserted_ret_type = true; + ret_type.to_smolstr() + } + ast::GenericParam::LifetimeParam(_) => make::lifetime("'_").to_smolstr(), + _ => make::ty_placeholder().to_smolstr(), + }) + .join(", "); + + let name = alias.name(ctx.db()); + let name = name.as_str(); + Some(make::ty(&format!("{name}<{generic_params}>"))) + }) + }); + // If there is no applicable alias in scope use the default Result type. + alias.unwrap_or_else(|| make::ext::ty_result(ret_type.clone(), make::ty_placeholder())) +} + fn tail_cb_impl(acc: &mut Vec, e: &ast::Expr) { match e { Expr::BreakExpr(break_expr) => { @@ -998,4 +1053,216 @@ fn foo(the_field: u32) -> Result { "#, ); } + + #[test] + fn wrap_return_type_in_local_result_type() { + check_assist( + wrap_return_type_in_result, + r#" +//- minicore: result +type Result = core::result::Result; + +fn foo() -> i3$02 { + return 42i32; +} +"#, + r#" +type Result = core::result::Result; + +fn foo() -> Result { + return Ok(42i32); +} +"#, + ); + + check_assist( + wrap_return_type_in_result, + r#" +//- minicore: result +type Result2 = core::result::Result; + +fn foo() -> i3$02 { + return 42i32; +} +"#, + r#" +type Result2 = core::result::Result; + +fn foo() -> Result { + return Ok(42i32); +} +"#, + ); + } + + #[test] + fn wrap_return_type_in_imported_local_result_type() { + check_assist( + wrap_return_type_in_result, + r#" +//- minicore: result +mod some_module { + pub type Result = core::result::Result; +} + +use some_module::Result; + +fn foo() -> i3$02 { + return 42i32; +} +"#, + r#" +mod some_module { + pub type Result = core::result::Result; +} + +use some_module::Result; + +fn foo() -> Result { + return Ok(42i32); +} +"#, + ); + + check_assist( + wrap_return_type_in_result, + r#" +//- minicore: result +mod some_module { + pub type Result = core::result::Result; +} + +use some_module::*; + +fn foo() -> i3$02 { + return 42i32; +} +"#, + r#" +mod some_module { + pub type Result = core::result::Result; +} + +use some_module::*; + +fn foo() -> Result { + return Ok(42i32); +} +"#, + ); + } + + #[test] + fn wrap_return_type_in_local_result_type_from_function_body() { + check_assist( + wrap_return_type_in_result, + r#" +//- minicore: result +fn foo() -> i3$02 { + type Result = core::result::Result; + 0 +} +"#, + r#" +fn foo() -> Result { + type Result = core::result::Result; + Ok(0) +} +"#, + ); + } + + #[test] + fn wrap_return_type_in_local_result_type_already_using_alias() { + check_assist_not_applicable( + wrap_return_type_in_result, + r#" +//- minicore: result +pub type Result = core::result::Result; + +fn foo() -> Result { + return Ok(42i32); +} +"#, + ); + } + + #[test] + fn wrap_return_type_in_local_result_type_multiple_generics() { + check_assist( + wrap_return_type_in_result, + r#" +//- minicore: result +type Result = core::result::Result; + +fn foo() -> i3$02 { + 0 +} +"#, + r#" +type Result = core::result::Result; + +fn foo() -> Result { + Ok(0) +} +"#, + ); + + check_assist( + wrap_return_type_in_result, + r#" +//- minicore: result +type Result = core::result::Result, ()>; + +fn foo() -> i3$02 { + 0 +} + "#, + r#" +type Result = core::result::Result, ()>; + +fn foo() -> Result { + Ok(0) +} + "#, + ); + + check_assist( + wrap_return_type_in_result, + r#" +//- minicore: result +type Result<'a, T, E> = core::result::Result, &'a ()>; + +fn foo() -> i3$02 { + 0 +} + "#, + r#" +type Result<'a, T, E> = core::result::Result, &'a ()>; + +fn foo() -> Result<'_, i32, ${0:_}> { + Ok(0) +} + "#, + ); + + check_assist( + wrap_return_type_in_result, + r#" +//- minicore: result +type Result = core::result::Result, Bar>; + +fn foo() -> i3$02 { + 0 +} + "#, + r#" +type Result = core::result::Result, Bar>; + +fn foo() -> Result { + Ok(0) +} + "#, + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs index 0fa46ef43a1e..149cb4c43849 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs @@ -25,6 +25,7 @@ use crate::{AssistContext, AssistId, AssistKind, Assists}; // struct S { // field: i32 // } +// ``` enum WrapUnwrapOption { WrapDerive { derive: TextRange, attr: ast::Attr }, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs b/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs index c88cb3d5eaf0..c98655b42302 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs @@ -58,6 +58,8 @@ //! See also this post: //! +#![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] + mod assist_config; mod assist_context; #[cfg(test)] @@ -136,6 +138,7 @@ mod handlers { mod destructure_tuple_binding; mod desugar_doc_comment; mod expand_glob_import; + mod explicit_enum_discriminant; mod extract_expressions_from_format_string; mod extract_function; mod extract_module; @@ -266,6 +269,7 @@ mod handlers { destructure_tuple_binding::destructure_tuple_binding, destructure_struct_binding::destructure_struct_binding, expand_glob_import::expand_glob_import, + explicit_enum_discriminant::explicit_enum_discriminant, extract_expressions_from_format_string::extract_expressions_from_format_string, extract_struct_from_enum_variant::extract_struct_from_enum_variant, extract_type_alias::extract_type_alias, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs index dce7bbf342f7..48e12a810735 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs @@ -909,6 +909,29 @@ fn qux(bar: Bar, baz: Baz) {} ) } +#[test] +fn doctest_explicit_enum_discriminant() { + check_doc_test( + "explicit_enum_discriminant", + r#####" +enum TheEnum$0 { + Foo, + Bar, + Baz = 42, + Quux, +} +"#####, + r#####" +enum TheEnum { + Foo = 0, + Bar = 1, + Baz = 42, + Quux = 43, +} +"#####, + ) +} + #[test] fn doctest_extract_expressions_from_format_string() { check_doc_test( diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs index b8a6f3b6dbeb..0830017bd0fd 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs @@ -1,10 +1,13 @@ //! Assorted functions shared by several assists. pub(crate) use gen_trait_fn_body::gen_trait_fn_body; -use hir::{db::HirDatabase, HasAttrs as HirHasAttrs, HirDisplay, InFile, Semantics}; +use hir::{ + db::{ExpandDatabase, HirDatabase}, + HasAttrs as HirHasAttrs, HirDisplay, InFile, Semantics, +}; use ide_db::{ famous_defs::FamousDefs, path_transform::PathTransform, - syntax_helpers::insert_whitespace_into_node::insert_ws_into, RootDatabase, + syntax_helpers::prettify_macro_expansion, RootDatabase, }; use stdx::format_to; use syntax::{ @@ -23,7 +26,6 @@ use crate::assist_context::{AssistContext, SourceChangeBuilder}; mod gen_trait_fn_body; pub(crate) mod ref_field_expr; -pub(crate) mod suggest_name; pub(crate) fn unwrap_trivial_block(block_expr: ast::BlockExpr) -> ast::Expr { extract_trivial_expression(&block_expr) @@ -179,10 +181,15 @@ pub fn add_trait_assoc_items_to_impl( let new_indent_level = IndentLevel::from_node(impl_.syntax()) + 1; let items = original_items.iter().map(|InFile { file_id, value: original_item }| { let cloned_item = { - if file_id.is_macro() { - if let Some(formatted) = - ast::AssocItem::cast(insert_ws_into(original_item.syntax().clone())) - { + if let Some(macro_file) = file_id.macro_file() { + let span_map = sema.db.expansion_span_map(macro_file); + let item_prettified = prettify_macro_expansion( + sema.db, + original_item.syntax().clone(), + &span_map, + target_scope.krate().into(), + ); + if let Some(formatted) = ast::AssocItem::cast(item_prettified) { return formatted; } else { stdx::never!("formatted `AssocItem` could not be cast back to `AssocItem`"); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs index b537150608bb..414627fbabae 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs @@ -617,6 +617,16 @@ impl Completions { } self.add_opt(render_struct_pat(RenderContext::new(ctx), pattern_ctx, strukt, local_name)); } + + pub(crate) fn suggest_name(&mut self, ctx: &CompletionContext<'_>, name: &str) { + let item = CompletionItem::new( + CompletionItemKind::Binding, + ctx.source_range(), + SmolStr::from(name), + ctx.edition, + ); + item.add_to(self, ctx.db); + } } /// Calls the callback for each variant of the provided enum with the path to the variant. diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs index 9821fb4a2fa9..d0b489c4e836 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs @@ -56,7 +56,7 @@ pub(crate) fn complete_known_attribute_input( &parse_tt_as_comma_sep_paths(tt, ctx.edition)?, FEATURES, ), - "allow" | "warn" | "deny" | "forbid" => { + "allow" | "expect" | "deny" | "forbid" | "warn" => { let existing_lints = parse_tt_as_comma_sep_paths(tt, ctx.edition)?; let lints: Vec = CLIPPY_LINT_GROUPS @@ -222,7 +222,7 @@ macro_rules! attrs { [@ {} {$($tt:tt)*}] => { &[$($tt)*] as _ }; // starting matcher [$($tt:tt),*] => { - attrs!(@ { $($tt)* } { "allow", "cfg", "cfg_attr", "deny", "forbid", "warn" }) + attrs!(@ { $($tt)* } { "allow", "cfg", "cfg_attr", "deny", "expect", "forbid", "warn" }) }; } @@ -303,6 +303,7 @@ const ATTRIBUTES: &[AttrCompletion] = &[ attr(r#"doc = "…""#, Some("doc"), Some(r#"doc = "${0:docs}""#)), attr(r#"doc(alias = "…")"#, Some("docalias"), Some(r#"doc(alias = "${0:docs}")"#)), attr(r#"doc(hidden)"#, Some("dochidden"), Some(r#"doc(hidden)"#)), + attr("expect(…)", Some("expect"), Some("expect(${0:lint})")), attr( r#"export_name = "…""#, Some("export_name"), diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs index d55bc3ea5d03..f2c360a9d5bf 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs @@ -600,7 +600,7 @@ fn foo(a: A) { a.$0 } struct A {} trait Trait { fn the_method(&self); } impl Trait for A {} -fn foo(a: A) { a.the_method()$0 } +fn foo(a: A) { a.the_method();$0 } "#, ); } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs index fc6e1ebf05fc..672e1796d1ef 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs @@ -31,14 +31,14 @@ //! } //! ``` -use hir::HasAttrs; +use hir::{db::ExpandDatabase, HasAttrs, MacroFileId, Name}; use ide_db::{ documentation::HasDocs, path_transform::PathTransform, - syntax_helpers::insert_whitespace_into_node, traits::get_missing_assoc_items, SymbolKind, + syntax_helpers::prettify_macro_expansion, traits::get_missing_assoc_items, SymbolKind, }; use syntax::{ - ast::{self, edit_in_place::AttrsOwnerEdit, HasTypeBounds}, - format_smolstr, AstNode, SmolStr, SyntaxElement, SyntaxKind, TextRange, ToSmolStr, T, + ast::{self, edit_in_place::AttrsOwnerEdit, make, HasGenericArgs, HasTypeBounds}, + format_smolstr, ted, AstNode, SmolStr, SyntaxElement, SyntaxKind, TextRange, ToSmolStr, T, }; use text_edit::TextEdit; @@ -178,12 +178,36 @@ fn add_function_impl( func: hir::Function, impl_def: hir::Impl, ) { - let fn_name = func.name(ctx.db); + let fn_name = &func.name(ctx.db); + let sugar: &[_] = if func.is_async(ctx.db) { + &[AsyncSugaring::Async, AsyncSugaring::Desugar] + } else if func.returns_impl_future(ctx.db) { + &[AsyncSugaring::Plain, AsyncSugaring::Resugar] + } else { + &[AsyncSugaring::Plain] + }; + for &sugaring in sugar { + add_function_impl_(acc, ctx, replacement_range, func, impl_def, fn_name, sugaring); + } +} - let is_async = func.is_async(ctx.db); +fn add_function_impl_( + acc: &mut Completions, + ctx: &CompletionContext<'_>, + replacement_range: TextRange, + func: hir::Function, + impl_def: hir::Impl, + fn_name: &Name, + async_sugaring: AsyncSugaring, +) { + let async_ = if let AsyncSugaring::Async | AsyncSugaring::Resugar = async_sugaring { + "async " + } else { + "" + }; let label = format_smolstr!( "{}fn {}({})", - if is_async { "async " } else { "" }, + async_, fn_name.display(ctx.db, ctx.edition), if func.assoc_fn_params(ctx.db).is_empty() { "" } else { ".." } ); @@ -195,23 +219,16 @@ fn add_function_impl( }); let mut item = CompletionItem::new(completion_kind, replacement_range, label, ctx.edition); - item.lookup_by(format!( - "{}fn {}", - if is_async { "async " } else { "" }, - fn_name.display(ctx.db, ctx.edition) - )) - .set_documentation(func.docs(ctx.db)) - .set_relevance(CompletionRelevance { is_item_from_trait: true, ..Default::default() }); + item.lookup_by(format!("{}fn {}", async_, fn_name.display(ctx.db, ctx.edition))) + .set_documentation(func.docs(ctx.db)) + .set_relevance(CompletionRelevance { exact_name_match: true, ..Default::default() }); if let Some(source) = ctx.sema.source(func) { - let assoc_item = ast::AssocItem::Fn(source.value); - if let Some(transformed_item) = get_transformed_assoc_item(ctx, assoc_item, impl_def) { - let transformed_fn = match transformed_item { - ast::AssocItem::Fn(func) => func, - _ => unreachable!(), - }; - - let function_decl = function_declaration(&transformed_fn, source.file_id.is_macro()); + if let Some(transformed_fn) = + get_transformed_fn(ctx, source.value, impl_def, async_sugaring) + { + let function_decl = + function_declaration(ctx, &transformed_fn, source.file_id.macro_file()); match ctx.config.snippet_cap { Some(cap) => { let snippet = format!("{function_decl} {{\n $0\n}}"); @@ -227,6 +244,14 @@ fn add_function_impl( } } +#[derive(Copy, Clone)] +enum AsyncSugaring { + Desugar, + Resugar, + Async, + Plain, +} + /// Transform a relevant associated item to inline generics from the impl, remove attrs and docs, etc. fn get_transformed_assoc_item( ctx: &CompletionContext<'_>, @@ -251,6 +276,82 @@ fn get_transformed_assoc_item( Some(assoc_item) } +/// Transform a relevant associated item to inline generics from the impl, remove attrs and docs, etc. +fn get_transformed_fn( + ctx: &CompletionContext<'_>, + fn_: ast::Fn, + impl_def: hir::Impl, + async_: AsyncSugaring, +) -> Option { + let trait_ = impl_def.trait_(ctx.db)?; + let source_scope = &ctx.sema.scope(fn_.syntax())?; + let target_scope = &ctx.sema.scope(ctx.sema.source(impl_def)?.syntax().value)?; + let transform = PathTransform::trait_impl( + target_scope, + source_scope, + trait_, + ctx.sema.source(impl_def)?.value, + ); + + let fn_ = fn_.clone_for_update(); + // FIXME: Paths in nested macros are not handled well. See + // `macro_generated_assoc_item2` test. + transform.apply(fn_.syntax()); + fn_.remove_attrs_and_docs(); + match async_ { + AsyncSugaring::Desugar => { + match fn_.ret_type() { + Some(ret_ty) => { + let ty = ret_ty.ty()?; + ted::replace( + ty.syntax(), + make::ty(&format!("impl Future")) + .syntax() + .clone_for_update(), + ); + } + None => ted::append_child( + fn_.param_list()?.syntax(), + make::ret_type(make::ty("impl Future")) + .syntax() + .clone_for_update(), + ), + } + fn_.async_token().unwrap().detach(); + } + AsyncSugaring::Resugar => { + let ty = fn_.ret_type()?.ty()?; + match &ty { + // best effort guessing here + ast::Type::ImplTraitType(t) => { + let output = t.type_bound_list()?.bounds().find_map(|b| match b.ty()? { + ast::Type::PathType(p) => { + let p = p.path()?.segment()?; + if p.name_ref()?.text() != "Future" { + return None; + } + match p.generic_arg_list()?.generic_args().next()? { + ast::GenericArg::AssocTypeArg(a) + if a.name_ref()?.text() == "Output" => + { + a.ty() + } + _ => None, + } + } + _ => None, + })?; + ted::replace(ty.syntax(), output.syntax()); + } + _ => (), + } + ted::prepend_child(fn_.syntax(), make::token(T![async])); + } + AsyncSugaring::Async | AsyncSugaring::Plain => (), + } + Some(fn_) +} + fn add_type_alias_impl( acc: &mut Completions, ctx: &CompletionContext<'_>, @@ -266,7 +367,7 @@ fn add_type_alias_impl( CompletionItem::new(SymbolKind::TypeAlias, replacement_range, label, ctx.edition); item.lookup_by(format!("type {alias_name}")) .set_documentation(type_alias.docs(ctx.db)) - .set_relevance(CompletionRelevance { is_item_from_trait: true, ..Default::default() }); + .set_relevance(CompletionRelevance { exact_name_match: true, ..Default::default() }); if let Some(source) = ctx.sema.source(type_alias) { let assoc_item = ast::AssocItem::TypeAlias(source.value); @@ -332,7 +433,8 @@ fn add_const_impl( _ => unreachable!(), }; - let label = make_const_compl_syntax(&transformed_const, source.file_id.is_macro()); + let label = + make_const_compl_syntax(ctx, &transformed_const, source.file_id.macro_file()); let replacement = format!("{label} "); let mut item = @@ -340,7 +442,7 @@ fn add_const_impl( item.lookup_by(format_smolstr!("const {const_name}")) .set_documentation(const_.docs(ctx.db)) .set_relevance(CompletionRelevance { - is_item_from_trait: true, + exact_name_match: true, ..Default::default() }); match ctx.config.snippet_cap { @@ -356,9 +458,14 @@ fn add_const_impl( } } -fn make_const_compl_syntax(const_: &ast::Const, needs_whitespace: bool) -> SmolStr { - let const_ = if needs_whitespace { - insert_whitespace_into_node::insert_ws_into(const_.syntax().clone()) +fn make_const_compl_syntax( + ctx: &CompletionContext<'_>, + const_: &ast::Const, + macro_file: Option, +) -> SmolStr { + let const_ = if let Some(macro_file) = macro_file { + let span_map = ctx.db.expansion_span_map(macro_file); + prettify_macro_expansion(ctx.db, const_.syntax().clone(), &span_map, ctx.krate.into()) } else { const_.syntax().clone() }; @@ -379,9 +486,14 @@ fn make_const_compl_syntax(const_: &ast::Const, needs_whitespace: bool) -> SmolS format_smolstr!("{} =", syntax.trim_end()) } -fn function_declaration(node: &ast::Fn, needs_whitespace: bool) -> String { - let node = if needs_whitespace { - insert_whitespace_into_node::insert_ws_into(node.syntax().clone()) +fn function_declaration( + ctx: &CompletionContext<'_>, + node: &ast::Fn, + macro_file: Option, +) -> String { + let node = if let Some(macro_file) = macro_file { + let span_map = ctx.db.expansion_span_map(macro_file); + prettify_macro_expansion(ctx.db, node.syntax().clone(), &span_map, ctx.krate.into()) } else { node.syntax().clone() }; @@ -1401,6 +1513,134 @@ trait Tr { impl Tr for () { type Item = $0; } +"#, + ); + } + + #[test] + fn impl_fut() { + check_edit( + "fn foo", + r#" +//- minicore: future, send, sized +use core::future::Future; + +trait DesugaredAsyncTrait { + fn foo(&self) -> impl Future + Send; +} + +impl DesugaredAsyncTrait for () { + $0 +} +"#, + r#" +use core::future::Future; + +trait DesugaredAsyncTrait { + fn foo(&self) -> impl Future + Send; +} + +impl DesugaredAsyncTrait for () { + fn foo(&self) -> impl Future + Send { + $0 +} +} +"#, + ); + } + + #[test] + fn impl_fut_resugared() { + check_edit( + "async fn foo", + r#" +//- minicore: future, send, sized +use core::future::Future; + +trait DesugaredAsyncTrait { + fn foo(&self) -> impl Future + Send; +} + +impl DesugaredAsyncTrait for () { + $0 +} +"#, + r#" +use core::future::Future; + +trait DesugaredAsyncTrait { + fn foo(&self) -> impl Future + Send; +} + +impl DesugaredAsyncTrait for () { + async fn foo(&self) -> usize { + $0 +} +} +"#, + ); + } + + #[test] + fn async_desugared() { + check_edit( + "fn foo", + r#" +//- minicore: future, send, sized +use core::future::Future; + +trait DesugaredAsyncTrait { + async fn foo(&self) -> usize; +} + +impl DesugaredAsyncTrait for () { + $0 +} +"#, + r#" +use core::future::Future; + +trait DesugaredAsyncTrait { + async fn foo(&self) -> usize; +} + +impl DesugaredAsyncTrait for () { + fn foo(&self) -> impl Future { + $0 +} +} +"#, + ); + } + + #[test] + fn async_() { + check_edit( + "async fn foo", + r#" +//- minicore: future, send, sized +use core::future::Future; + +trait DesugaredAsyncTrait { + async fn foo(&self) -> usize; +} + +impl DesugaredAsyncTrait for () { + $0 +} +"#, + r#" +use core::future::Future; + +trait DesugaredAsyncTrait { + async fn foo(&self) -> usize; +} + +impl DesugaredAsyncTrait for () { + async fn foo(&self) -> usize { + $0 +} +} "#, ); } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/keyword.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/keyword.rs index 3f50cd55cb36..0acb87872f5e 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/keyword.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/keyword.rs @@ -150,6 +150,68 @@ fn foo(a: A) { a.$0 } ); } + #[test] + fn for_in_impl() { + check_edit( + "for", + r#" +struct X; +impl X $0 {} +"#, + r#" +struct X; +impl X for $0 {} +"#, + ); + check_edit( + "for", + r#" +fn foo() { + struct X; + impl X $0 {} +} +"#, + r#" +fn foo() { + struct X; + impl X for $0 {} +} +"#, + ); + check_edit( + "for", + r#" +fn foo() { + struct X; + impl X $0 +} +"#, + r#" +fn foo() { + struct X; + impl X for $0 +} +"#, + ); + check_edit( + "for", + r#" +fn foo() { + struct X; + impl X { fn bar() { $0 } } +} +"#, + r#" +fn foo() { + struct X; + impl X { fn bar() { for $1 in $2 { + $0 +} } } +} +"#, + ); + } + #[test] fn let_semi() { cov_mark::check!(let_semi); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/pattern.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/pattern.rs index 60cfb7e5a8c5..8f38e02ed768 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/pattern.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/pattern.rs @@ -1,6 +1,7 @@ //! Completes constants and paths in unqualified patterns. use hir::{db::DefDatabase, AssocItem, ScopeDef}; +use ide_db::syntax_helpers::suggest_name; use syntax::ast::Pat; use crate::{ @@ -45,6 +46,19 @@ pub(crate) fn complete_pattern( return; } + // Suggest name only in let-stmt and fn param + if pattern_ctx.should_suggest_name { + let mut name_generator = suggest_name::NameGenerator::new(); + if let Some(suggested) = ctx + .expected_type + .as_ref() + .map(|ty| ty.strip_references()) + .and_then(|ty| name_generator.for_type(&ty, ctx.db, ctx.edition)) + { + acc.suggest_name(ctx, &suggested); + } + } + let refutable = pattern_ctx.refutability == PatternRefutability::Refutable; let single_variant_enum = |enum_: hir::Enum| ctx.db.enum_data(enum_.into()).variants.len() == 1; diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs index a632f148936d..d3579fd8cc6e 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs @@ -294,6 +294,18 @@ fn include_references(initial_element: &ast::Expr) -> (ast::Expr, ast::Expr) { let mut new_element_opt = initial_element.clone(); + while let Some(parent_deref_element) = + resulting_element.syntax().parent().and_then(ast::PrefixExpr::cast) + { + if parent_deref_element.op_kind() != Some(ast::UnaryOp::Deref) { + break; + } + + resulting_element = ast::Expr::from(parent_deref_element); + + new_element_opt = make::expr_prefix(syntax::T![*], new_element_opt); + } + if let Some(first_ref_expr) = resulting_element.syntax().parent().and_then(ast::RefExpr::cast) { if let Some(expr) = first_ref_expr.expr() { resulting_element = expr; @@ -302,9 +314,10 @@ fn include_references(initial_element: &ast::Expr) -> (ast::Expr, ast::Expr) { while let Some(parent_ref_element) = resulting_element.syntax().parent().and_then(ast::RefExpr::cast) { + let exclusive = parent_ref_element.mut_token().is_some(); resulting_element = ast::Expr::from(parent_ref_element); - new_element_opt = make::expr_ref(new_element_opt, false); + new_element_opt = make::expr_ref(new_element_opt, exclusive); } } else { // If we do not find any ref expressions, restore @@ -855,4 +868,42 @@ fn test() { expect![[r#""#]], ); } + + #[test] + fn mut_ref_consuming() { + check_edit( + "call", + r#" +fn main() { + let mut x = &mut 2; + &mut x.$0; +} +"#, + r#" +fn main() { + let mut x = &mut 2; + ${1}(&mut x); +} +"#, + ); + } + + #[test] + fn deref_consuming() { + check_edit( + "call", + r#" +fn main() { + let mut x = &mut 2; + &mut *x.$0; +} +"#, + r#" +fn main() { + let mut x = &mut 2; + ${1}(&mut *x); +} +"#, + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/config.rs b/src/tools/rust-analyzer/crates/ide-completion/src/config.rs index d885b82ec90d..0d403f49b7a8 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/config.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/config.rs @@ -19,6 +19,7 @@ pub struct CompletionConfig { pub term_search_fuel: u64, pub full_function_signatures: bool, pub callable: Option, + pub add_semicolon_to_unit: bool, pub snippet_cap: Option, pub insert_use: InsertUseConfig, pub prefer_no_std: bool, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs index bcd9df941947..192f1b43face 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs @@ -4,7 +4,7 @@ mod analysis; #[cfg(test)] mod tests; -use std::iter; +use std::{iter, ops::ControlFlow}; use hir::{ HasAttrs, Local, Name, PathResolution, ScopeDef, Semantics, SemanticsScope, Type, TypeInfo, @@ -15,7 +15,7 @@ use ide_db::{ }; use syntax::{ ast::{self, AttrKind, NameOrNameRef}, - AstNode, Edition, SmolStr, + match_ast, AstNode, Edition, SmolStr, SyntaxKind::{self, *}, SyntaxToken, TextRange, TextSize, T, }; @@ -26,7 +26,7 @@ use crate::{ CompletionConfig, }; -const COMPLETION_MARKER: &str = "intellijRulezz"; +const COMPLETION_MARKER: &str = "raCompletionMarker"; #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub(crate) enum PatternRefutability { @@ -264,6 +264,7 @@ pub(crate) struct PatternContext { pub(crate) refutability: PatternRefutability, pub(crate) param_ctx: Option, pub(crate) has_type_ascription: bool, + pub(crate) should_suggest_name: bool, pub(crate) parent_pat: Option, pub(crate) ref_token: Option, pub(crate) mut_token: Option, @@ -456,6 +457,16 @@ pub(crate) struct CompletionContext<'a> { /// /// Here depth will be 2 pub(crate) depth_from_crate_root: usize, + + /// Whether and how to complete semicolon for unit-returning functions. + pub(crate) complete_semicolon: CompleteSemicolon, +} + +#[derive(Debug)] +pub(crate) enum CompleteSemicolon { + DoNotComplete, + CompleteSemi, + CompleteComma, } impl CompletionContext<'_> { @@ -734,6 +745,53 @@ impl<'a> CompletionContext<'a> { let depth_from_crate_root = iter::successors(module.parent(db), |m| m.parent(db)).count(); + let complete_semicolon = if config.add_semicolon_to_unit { + let inside_closure_ret = token.parent_ancestors().try_for_each(|ancestor| { + match_ast! { + match ancestor { + ast::BlockExpr(_) => ControlFlow::Break(false), + ast::ClosureExpr(_) => ControlFlow::Break(true), + _ => ControlFlow::Continue(()) + } + } + }); + + if inside_closure_ret == ControlFlow::Break(true) { + CompleteSemicolon::DoNotComplete + } else { + let next_non_trivia_token = + std::iter::successors(token.next_token(), |it| it.next_token()) + .find(|it| !it.kind().is_trivia()); + let in_match_arm = token.parent_ancestors().try_for_each(|ancestor| { + if ast::MatchArm::can_cast(ancestor.kind()) { + ControlFlow::Break(true) + } else if matches!( + ancestor.kind(), + SyntaxKind::EXPR_STMT | SyntaxKind::BLOCK_EXPR + ) { + ControlFlow::Break(false) + } else { + ControlFlow::Continue(()) + } + }); + // FIXME: This will assume expr macros are not inside match, we need to somehow go to the "parent" of the root node. + let in_match_arm = match in_match_arm { + ControlFlow::Continue(()) => false, + ControlFlow::Break(it) => it, + }; + let complete_token = if in_match_arm { T![,] } else { T![;] }; + if next_non_trivia_token.map(|it| it.kind()) == Some(complete_token) { + CompleteSemicolon::DoNotComplete + } else if in_match_arm { + CompleteSemicolon::CompleteComma + } else { + CompleteSemicolon::CompleteSemi + } + } + } else { + CompleteSemicolon::DoNotComplete + }; + let ctx = CompletionContext { sema, scope, @@ -751,6 +809,7 @@ impl<'a> CompletionContext<'a> { qualifier_ctx, locals, depth_from_crate_root, + complete_semicolon, }; Some((ctx, analysis)) } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs index ed359394f1c4..1f9e3edf625d 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs @@ -1132,10 +1132,18 @@ fn classify_name_ref( ast::PathType(it) => make_path_kind_type(it.into()), ast::PathExpr(it) => { if let Some(p) = it.syntax().parent() { - if ast::ExprStmt::can_cast(p.kind()) { - if let Some(kind) = inbetween_body_and_decl_check(p) { - return Some(make_res(NameRefKind::Keyword(kind))); - } + let p_kind = p.kind(); + // The syntax node of interest, for which we want to check whether + // it is sandwiched between an item decl signature and its body. + let probe = if ast::ExprStmt::can_cast(p_kind) { + Some(p) + } else if ast::StmtList::can_cast(p_kind) { + Some(it.syntax().clone()) + } else { + None + }; + if let Some(kind) = probe.and_then(inbetween_body_and_decl_check) { + return Some(make_res(NameRefKind::Keyword(kind))); } } @@ -1199,7 +1207,13 @@ fn classify_name_ref( } } }, - ast::RecordExpr(it) => make_path_kind_expr(it.into()), + ast::RecordExpr(it) => { + // A record expression in this position is usually a result of parsing recovery, so check that + if let Some(kind) = inbetween_body_and_decl_check(it.syntax().clone()) { + return Some(make_res(NameRefKind::Keyword(kind))); + } + make_path_kind_expr(it.into()) + }, _ => return None, } }; @@ -1416,10 +1430,23 @@ fn pattern_context_for( _ => (None, None), }; + // Only suggest name in let-stmt or fn param + let should_suggest_name = matches!( + &pat, + ast::Pat::IdentPat(it) + if it.syntax() + .parent() + .map_or(false, |node| { + let kind = node.kind(); + ast::LetStmt::can_cast(kind) || ast::Param::can_cast(kind) + }) + ); + PatternContext { refutability, param_ctx, has_type_ascription, + should_suggest_name, parent_pat: pat.syntax().parent().and_then(ast::Pat::cast), mut_token, ref_token, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs index a30a115da1f1..8c97ebd55003 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs @@ -19,8 +19,10 @@ use crate::{ }; /// `CompletionItem` describes a single completion entity which expands to 1 or more entries in the -/// editor pop-up. It is basically a POD with various properties. To construct a -/// [`CompletionItem`], use [`Builder::new`] method and the [`Builder`] struct. +/// editor pop-up. +/// +/// It is basically a POD with various properties. To construct a [`CompletionItem`], +/// use [`Builder::new`] method and the [`Builder`] struct. #[derive(Clone)] #[non_exhaustive] pub struct CompletionItem { @@ -129,7 +131,8 @@ impl fmt::Debug for CompletionItem { #[derive(Debug, Clone, Copy, Eq, PartialEq, Default)] pub struct CompletionRelevance { - /// This is set in cases like these: + /// This is set when the identifier being completed matches up with the name that is expected, + /// like in a function argument. /// /// ``` /// fn f(spam: String) {} @@ -139,9 +142,9 @@ pub struct CompletionRelevance { /// } /// ``` pub exact_name_match: bool, - /// See CompletionRelevanceTypeMatch doc comments for cases where this is set. + /// See [`CompletionRelevanceTypeMatch`]. pub type_match: Option, - /// This is set in cases like these: + /// Set for local variables. /// /// ``` /// fn foo(a: u32) { @@ -150,25 +153,26 @@ pub struct CompletionRelevance { /// } /// ``` pub is_local: bool, - /// This is set when trait items are completed in an impl of that trait. - pub is_item_from_trait: bool, - /// This is set for when trait items are from traits with `#[doc(notable_trait)]` - pub is_item_from_notable_trait: bool, - /// This is set when an import is suggested whose name is already imported. + /// Populated when the completion item comes from a trait (impl). + pub trait_: Option, + /// This is set when an import is suggested in a use item whose name is already imported. pub is_name_already_imported: bool, /// This is set for completions that will insert a `use` item. pub requires_import: bool, - /// Set for method completions of the `core::ops` and `core::cmp` family. - pub is_op_method: bool, /// Set for item completions that are private but in the workspace. pub is_private_editable: bool, /// Set for postfix snippet item completions pub postfix_match: Option, - /// This is set for type inference results - pub is_definite: bool, /// This is set for items that are function (associated or method) pub function: Option, } +#[derive(Debug, Clone, Copy, Eq, PartialEq)] +pub struct CompletionRelevanceTraitInfo { + /// The trait this item is from is a `#[doc(notable_trait)]` + pub notable_trait: bool, + /// Set for method completions of the `core::ops` and `core::cmp` family. + pub is_op_method: bool, +} #[derive(Debug, Clone, Copy, Eq, PartialEq)] pub enum CompletionRelevanceTypeMatch { @@ -182,7 +186,7 @@ pub enum CompletionRelevanceTypeMatch { /// } /// ``` CouldUnify, - /// This is set in cases like these: + /// This is set in cases where the type matches the expected type, like: /// /// ``` /// fn f(spam: String) {} @@ -238,90 +242,82 @@ impl CompletionRelevance { /// See is_relevant if you need to make some judgement about score /// in an absolute sense. pub fn score(self) -> u32 { - let mut score = 0; + let mut score = !0 / 2; let CompletionRelevance { exact_name_match, type_match, is_local, - is_item_from_trait, is_name_already_imported, requires_import, - is_op_method, is_private_editable, postfix_match, - is_definite, - is_item_from_notable_trait, + trait_, function, } = self; + // only applicable for completions within use items + // lower rank for conflicting import names + if is_name_already_imported { + score -= 1; + } + // slightly prefer locals + if is_local { + score += 1; + } + // lower rank private things if !is_private_editable { score += 1; } - // lower rank trait op methods - if !is_op_method { - score += 10; + + if let Some(trait_) = trait_ { + // lower rank trait methods unless its notable + if !trait_.notable_trait { + score -= 5; + } + // lower rank trait op methods + if trait_.is_op_method { + score -= 5; + } } - // lower rank for conflicting import names - if !is_name_already_imported { - score += 1; - } - // lower rank for items that don't need an import - if !requires_import { - score += 1; + // lower rank for items that need an import + if requires_import { + score -= 1; } if exact_name_match { - score += 10; + score += 20; } - score += match postfix_match { - Some(CompletionRelevancePostfixMatch::Exact) => 100, - Some(CompletionRelevancePostfixMatch::NonExact) => 0, - None => 3, + match postfix_match { + Some(CompletionRelevancePostfixMatch::Exact) => score += 100, + Some(CompletionRelevancePostfixMatch::NonExact) => score -= 5, + None => (), }; score += match type_match { - Some(CompletionRelevanceTypeMatch::Exact) => 8, - Some(CompletionRelevanceTypeMatch::CouldUnify) => 3, + Some(CompletionRelevanceTypeMatch::Exact) => 18, + Some(CompletionRelevanceTypeMatch::CouldUnify) => 5, None => 0, }; - // slightly prefer locals - if is_local { - score += 1; - } - if is_item_from_trait { - score += 1; - } - if is_item_from_notable_trait { - score += 1; - } - if is_definite { - score += 10; - } - - score += function - .map(|asf| { - let mut fn_score = match asf.return_type { - CompletionRelevanceReturnType::DirectConstructor => 15, - CompletionRelevanceReturnType::Builder => 10, - CompletionRelevanceReturnType::Constructor => 5, - CompletionRelevanceReturnType::Other => 0, - }; - - // When a fn is bumped due to return type: - // Bump Constructor or Builder methods with no arguments, - // over them than with self arguments - if fn_score > 0 { - if !asf.has_params { - // bump associated functions - fn_score += 1; - } else if asf.has_self_param { - // downgrade methods (below Constructor) - fn_score = 1; - } - } + if let Some(function) = function { + let mut fn_score = match function.return_type { + CompletionRelevanceReturnType::DirectConstructor => 15, + CompletionRelevanceReturnType::Builder => 10, + CompletionRelevanceReturnType::Constructor => 5, + CompletionRelevanceReturnType::Other => 0u32, + }; + + // When a fn is bumped due to return type: + // Bump Constructor or Builder methods with no arguments, + // over them than with self arguments + if function.has_params { + // bump associated functions + fn_score = fn_score.saturating_sub(1); + } else if function.has_self_param { + // downgrade methods (below Constructor) + fn_score = fn_score.min(1); + } - fn_score - }) - .unwrap_or_default(); + score += fn_score; + }; score } @@ -364,6 +360,7 @@ impl CompletionItemKind { SymbolKind::Field => "fd", SymbolKind::Function => "fn", SymbolKind::Impl => "im", + SymbolKind::InlineAsmRegOrRegClass => "ar", SymbolKind::Label => "lb", SymbolKind::LifetimeParam => "lt", SymbolKind::Local => "lc", @@ -701,8 +698,21 @@ mod tests { // that any items in the same vec have the same score. let expected_relevance_order = vec![ vec![], - vec![Cr { is_op_method: true, is_private_editable: true, ..default }], - vec![Cr { is_op_method: true, ..default }], + vec![Cr { + trait_: Some(crate::item::CompletionRelevanceTraitInfo { + notable_trait: false, + is_op_method: true, + }), + is_private_editable: true, + ..default + }], + vec![Cr { + trait_: Some(crate::item::CompletionRelevanceTraitInfo { + notable_trait: false, + is_op_method: true, + }), + ..default + }], vec![Cr { postfix_match: Some(CompletionRelevancePostfixMatch::NonExact), ..default }], vec![Cr { is_private_editable: true, ..default }], vec![default], diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs index ff5ec3a29f3e..f2e9de9382c6 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs @@ -249,7 +249,11 @@ pub(crate) fn render_type_inference( ty_string, ctx.edition, ); - builder.set_relevance(CompletionRelevance { is_definite: true, ..Default::default() }); + builder.set_relevance(CompletionRelevance { + type_match: Some(CompletionRelevanceTypeMatch::Exact), + exact_name_match: true, + ..Default::default() + }); builder.build(ctx.db) } @@ -756,7 +760,7 @@ mod tests { relevance.postfix_match == Some(CompletionRelevancePostfixMatch::Exact), "snippet", ), - (relevance.is_op_method, "op_method"), + (relevance.trait_.map_or(false, |it| it.is_op_method), "op_method"), (relevance.requires_import, "requires_import"), ] .into_iter() @@ -1181,7 +1185,7 @@ fn main() { fo$0 } label: "main()", source_range: 68..70, delete: 68..70, - insert: "main()$0", + insert: "main();$0", kind: SymbolKind( Function, ), @@ -1240,7 +1244,7 @@ fn main() { let _: m::Spam = S$0 } label: "main()", source_range: 75..76, delete: 75..76, - insert: "main()$0", + insert: "main();$0", kind: SymbolKind( Function, ), @@ -1272,14 +1276,11 @@ fn main() { let _: m::Spam = S$0 } Exact, ), is_local: false, - is_item_from_trait: false, - is_item_from_notable_trait: false, + trait_: None, is_name_already_imported: false, requires_import: false, - is_op_method: false, is_private_editable: false, postfix_match: None, - is_definite: false, function: None, }, trigger_call_info: true, @@ -1300,14 +1301,11 @@ fn main() { let _: m::Spam = S$0 } Exact, ), is_local: false, - is_item_from_trait: false, - is_item_from_notable_trait: false, + trait_: None, is_name_already_imported: false, requires_import: false, - is_op_method: false, is_private_editable: false, postfix_match: None, - is_definite: false, function: None, }, trigger_call_info: true, @@ -1333,7 +1331,7 @@ fn main() { som$0 } label: "main()", source_range: 56..59, delete: 56..59, - insert: "main()$0", + insert: "main();$0", kind: SymbolKind( Function, ), @@ -1344,7 +1342,7 @@ fn main() { som$0 } label: "something_deprecated()", source_range: 56..59, delete: 56..59, - insert: "something_deprecated()$0", + insert: "something_deprecated();$0", kind: SymbolKind( Function, ), @@ -1380,14 +1378,11 @@ fn foo() { A { the$0 } } CouldUnify, ), is_local: false, - is_item_from_trait: false, - is_item_from_notable_trait: false, + trait_: None, is_name_already_imported: false, requires_import: false, - is_op_method: false, is_private_editable: false, postfix_match: None, - is_definite: false, function: None, }, }, @@ -1418,7 +1413,7 @@ impl S { label: "bar()", source_range: 94..94, delete: 94..94, - insert: "bar()$0", + insert: "bar();$0", kind: SymbolKind( Method, ), @@ -1431,14 +1426,11 @@ impl S { exact_name_match: false, type_match: None, is_local: false, - is_item_from_trait: false, - is_item_from_notable_trait: false, + trait_: None, is_name_already_imported: false, requires_import: false, - is_op_method: false, is_private_editable: false, postfix_match: None, - is_definite: false, function: Some( CompletionRelevanceFn { has_params: true, @@ -1548,7 +1540,7 @@ fn foo(s: S) { s.$0 } label: "the_method()", source_range: 81..81, delete: 81..81, - insert: "the_method()$0", + insert: "the_method();$0", kind: SymbolKind( Method, ), @@ -1558,14 +1550,11 @@ fn foo(s: S) { s.$0 } exact_name_match: false, type_match: None, is_local: false, - is_item_from_trait: false, - is_item_from_notable_trait: false, + trait_: None, is_name_already_imported: false, requires_import: false, - is_op_method: false, is_private_editable: false, postfix_match: None, - is_definite: false, function: Some( CompletionRelevanceFn { has_params: true, @@ -1774,14 +1763,11 @@ fn f() -> i32 { Exact, ), is_local: false, - is_item_from_trait: false, - is_item_from_notable_trait: false, + trait_: None, is_name_already_imported: false, requires_import: false, - is_op_method: false, is_private_editable: false, postfix_match: None, - is_definite: false, function: None, }, }, @@ -2492,14 +2478,11 @@ fn foo(f: Foo) { let _: &u32 = f.b$0 } exact_name_match: false, type_match: None, is_local: false, - is_item_from_trait: false, - is_item_from_notable_trait: false, + trait_: None, is_name_already_imported: false, requires_import: false, - is_op_method: false, is_private_editable: false, postfix_match: None, - is_definite: false, function: Some( CompletionRelevanceFn { has_params: true, @@ -2574,14 +2557,11 @@ fn foo() { Exact, ), is_local: false, - is_item_from_trait: false, - is_item_from_notable_trait: false, + trait_: None, is_name_already_imported: false, requires_import: false, - is_op_method: false, is_private_editable: false, postfix_match: None, - is_definite: false, function: None, }, }, @@ -2624,14 +2604,11 @@ fn main() { exact_name_match: false, type_match: None, is_local: false, - is_item_from_trait: false, - is_item_from_notable_trait: false, + trait_: None, is_name_already_imported: false, requires_import: false, - is_op_method: false, is_private_editable: false, postfix_match: None, - is_definite: false, function: Some( CompletionRelevanceFn { has_params: false, @@ -2812,7 +2789,7 @@ fn main() { r#" mod m { pub fn r#type {} } fn main() { - m::r#type()$0 + m::r#type();$0 } "#, ) @@ -2986,7 +2963,7 @@ fn main() { label: "flush()", source_range: 193..193, delete: 193..193, - insert: "flush()$0", + insert: "flush();$0", kind: SymbolKind( Method, ), @@ -2996,14 +2973,16 @@ fn main() { exact_name_match: false, type_match: None, is_local: false, - is_item_from_trait: false, - is_item_from_notable_trait: true, + trait_: Some( + CompletionRelevanceTraitInfo { + notable_trait: true, + is_op_method: false, + }, + ), is_name_already_imported: false, requires_import: false, - is_op_method: false, is_private_editable: false, postfix_match: None, - is_definite: false, function: None, }, }, @@ -3011,7 +2990,7 @@ fn main() { label: "write()", source_range: 193..193, delete: 193..193, - insert: "write()$0", + insert: "write();$0", kind: SymbolKind( Method, ), @@ -3021,14 +3000,16 @@ fn main() { exact_name_match: false, type_match: None, is_local: false, - is_item_from_trait: false, - is_item_from_notable_trait: true, + trait_: Some( + CompletionRelevanceTraitInfo { + notable_trait: true, + is_op_method: false, + }, + ), is_name_already_imported: false, requires_import: false, - is_op_method: false, is_private_editable: false, postfix_match: None, - is_definite: false, function: None, }, }, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs index 74092b53f523..a859d79e2433 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs @@ -7,10 +7,12 @@ use stdx::{format_to, to_lower_snake_case}; use syntax::{format_smolstr, AstNode, Edition, SmolStr, ToSmolStr}; use crate::{ - context::{CompletionContext, DotAccess, DotAccessKind, PathCompletionCtx, PathKind}, + context::{ + CompleteSemicolon, CompletionContext, DotAccess, DotAccessKind, PathCompletionCtx, PathKind, + }, item::{ Builder, CompletionItem, CompletionItemKind, CompletionRelevance, CompletionRelevanceFn, - CompletionRelevanceReturnType, + CompletionRelevanceReturnType, CompletionRelevanceTraitInfo, }, render::{ compute_exact_name_match, compute_ref_match, compute_type_match, match_types, RenderContext, @@ -88,11 +90,13 @@ fn render( let ret_type = func.ret_type(db); let assoc_item = func.as_assoc_item(db); - let trait_ = assoc_item.and_then(|trait_| trait_.container_or_implemented_trait(db)); - let is_op_method = trait_.map_or(false, |trait_| completion.is_ops_trait(trait_)); - - let is_item_from_notable_trait = - trait_.map_or(false, |trait_| completion.is_doc_notable_trait(trait_)); + let trait_info = + assoc_item.and_then(|trait_| trait_.container_or_implemented_trait(db)).map(|trait_| { + CompletionRelevanceTraitInfo { + notable_trait: completion.is_doc_notable_trait(trait_), + is_op_method: completion.is_ops_trait(trait_), + } + }); let (has_dot_receiver, has_call_parens, cap) = match func_kind { FuncKind::Function(&PathCompletionCtx { @@ -129,8 +133,7 @@ fn render( }, exact_name_match: compute_exact_name_match(completion, &call), function, - is_op_method, - is_item_from_notable_trait, + trait_: trait_info, ..ctx.completion_relevance() }); @@ -159,7 +162,16 @@ fn render( .lookup_by(name.unescaped().display(db).to_smolstr()); if let Some((cap, (self_param, params))) = complete_call_parens { - add_call_parens(&mut item, completion, cap, call, escaped_call, self_param, params); + add_call_parens( + &mut item, + completion, + cap, + call, + escaped_call, + self_param, + params, + &ret_type, + ); } match ctx.import_to_add { @@ -216,10 +228,11 @@ pub(super) fn add_call_parens<'b>( escaped_name: SmolStr, self_param: Option, params: Vec, + ret_type: &hir::Type, ) -> &'b mut Builder { cov_mark::hit!(inserts_parens_for_function_calls); - let (snippet, label_suffix) = if self_param.is_none() && params.is_empty() { + let (mut snippet, label_suffix) = if self_param.is_none() && params.is_empty() { (format!("{escaped_name}()$0"), "()") } else { builder.trigger_call_info(); @@ -264,6 +277,24 @@ pub(super) fn add_call_parens<'b>( (snippet, "(…)") }; + if ret_type.is_unit() { + match ctx.complete_semicolon { + CompleteSemicolon::DoNotComplete => {} + CompleteSemicolon::CompleteSemi | CompleteSemicolon::CompleteComma => { + cov_mark::hit!(complete_semicolon); + let ch = if matches!(ctx.complete_semicolon, CompleteSemicolon::CompleteComma) { + ',' + } else { + ';' + }; + if snippet.ends_with("$0") { + snippet.insert(snippet.len() - "$0".len(), ch); + } else { + snippet.push(ch); + } + } + } + } builder.label(SmolStr::from_iter([&name, label_suffix])).insert_snippet(cap, snippet) } @@ -392,7 +423,7 @@ fn main() { no_$0 } "#, r#" fn no_args() {} -fn main() { no_args()$0 } +fn main() { no_args();$0 } "#, ); @@ -404,7 +435,7 @@ fn main() { with_$0 } "#, r#" fn with_args(x: i32, y: String) {} -fn main() { with_args(${1:x}, ${2:y})$0 } +fn main() { with_args(${1:x}, ${2:y});$0 } "#, ); @@ -413,14 +444,14 @@ fn main() { with_args(${1:x}, ${2:y})$0 } r#" struct S; impl S { - fn foo(&self) {} + fn foo(&self) -> i32 { 0 } } fn bar(s: &S) { s.f$0 } "#, r#" struct S; impl S { - fn foo(&self) {} + fn foo(&self) -> i32 { 0 } } fn bar(s: &S) { s.foo()$0 } "#, @@ -443,7 +474,7 @@ impl S { fn foo(&self, x: i32) {} } fn bar(s: &S) { - s.foo(${1:x})$0 + s.foo(${1:x});$0 } "#, ); @@ -462,7 +493,7 @@ impl S { struct S {} impl S { fn foo(&self, x: i32) { - self.foo(${1:x})$0 + self.foo(${1:x});$0 } } "#, @@ -485,7 +516,7 @@ struct S; impl S { fn foo(&self) {} } -fn main() { S::foo(${1:&self})$0 } +fn main() { S::foo(${1:&self});$0 } "#, ); } @@ -502,7 +533,7 @@ fn main() { with_$0 } "#, r#" fn with_args(x: i32, y: String) {} -fn main() { with_args($0) } +fn main() { with_args($0); } "#, ); } @@ -517,7 +548,7 @@ fn main() { f$0 } "#, r#" fn foo(_foo: i32, ___bar: bool, ho_ge_: String) {} -fn main() { foo(${1:foo}, ${2:bar}, ${3:ho_ge_})$0 } +fn main() { foo(${1:foo}, ${2:bar}, ${3:ho_ge_});$0 } "#, ); } @@ -539,7 +570,7 @@ struct Foo {} fn ref_arg(x: &Foo) {} fn main() { let x = Foo {}; - ref_arg(${1:&x})$0 + ref_arg(${1:&x});$0 } "#, ); @@ -562,7 +593,7 @@ struct Foo {} fn ref_arg(x: &mut Foo) {} fn main() { let x = Foo {}; - ref_arg(${1:&mut x})$0 + ref_arg(${1:&mut x});$0 } "#, ); @@ -595,7 +626,7 @@ impl Bar { fn main() { let x = Foo {}; let y = Bar {}; - y.apply_foo(${1:&x})$0 + y.apply_foo(${1:&x});$0 } "#, ); @@ -616,7 +647,7 @@ fn main() { fn take_mutably(mut x: &i32) {} fn main() { - take_mutably(${1:x})$0 + take_mutably(${1:x});$0 } "#, ); @@ -649,7 +680,7 @@ fn qux(Foo { bar }: Foo) { } fn main() { - qux(${1:foo})$0 + qux(${1:foo});$0 } "#, ); @@ -735,6 +766,136 @@ fn g(foo: ()#[baz = "qux"] mut ba$0) r#" fn f(foo: (), #[baz = "qux"] mut bar: u32) {} fn g(foo: (), #[baz = "qux"] mut bar: u32) +"#, + ); + } + + #[test] + fn complete_semicolon_for_unit() { + cov_mark::check!(complete_semicolon); + check_edit( + r#"foo"#, + r#" +fn foo() {} +fn bar() { + foo$0 +} +"#, + r#" +fn foo() {} +fn bar() { + foo();$0 +} +"#, + ); + check_edit( + r#"foo"#, + r#" +fn foo(a: i32) {} +fn bar() { + foo$0 +} +"#, + r#" +fn foo(a: i32) {} +fn bar() { + foo(${1:a});$0 +} +"#, + ); + check_edit( + r#"foo"#, + r#" +fn foo(a: i32) {} +fn bar() { + foo$0; +} +"#, + r#" +fn foo(a: i32) {} +fn bar() { + foo(${1:a})$0; +} +"#, + ); + check_edit_with_config( + CompletionConfig { add_semicolon_to_unit: false, ..TEST_CONFIG }, + r#"foo"#, + r#" +fn foo(a: i32) {} +fn bar() { + foo$0 +} +"#, + r#" +fn foo(a: i32) {} +fn bar() { + foo(${1:a})$0 +} +"#, + ); + } + + #[test] + fn complete_comma_for_unit_match_arm() { + cov_mark::check!(complete_semicolon); + check_edit( + r#"foo"#, + r#" +fn foo() {} +fn bar() { + match Some(false) { + v => fo$0 + } +} +"#, + r#" +fn foo() {} +fn bar() { + match Some(false) { + v => foo(),$0 + } +} +"#, + ); + check_edit( + r#"foo"#, + r#" +fn foo() {} +fn bar() { + match Some(false) { + v => fo$0, + } +} +"#, + r#" +fn foo() {} +fn bar() { + match Some(false) { + v => foo()$0, + } +} +"#, + ); + } + + #[test] + fn no_semicolon_in_closure_ret() { + check_edit( + r#"foo"#, + r#" +fn foo() {} +fn baz(_: impl FnOnce()) {} +fn bar() { + baz(|| fo$0); +} +"#, + r#" +fn foo() {} +fn baz(_: impl FnOnce()) {} +fn bar() { + baz(|| foo()$0); +} "#, ); } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs index 04ba7e1f41b6..9d77d9700718 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs @@ -70,6 +70,7 @@ pub(crate) const TEST_CONFIG: CompletionConfig = CompletionConfig { term_search_fuel: 200, full_function_signatures: false, callable: Some(CallableSnippets::FillArguments), + add_semicolon_to_unit: true, snippet_cap: SnippetCap::new(true), insert_use: InsertUseConfig { granularity: ImportGranularity::Crate, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs index 351abe9850b9..1bbe097cc6c8 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs @@ -26,6 +26,7 @@ struct Foo; at doc = "…" at doc(alias = "…") at doc(hidden) + at expect(…) at forbid(…) at must_use at no_mangle @@ -75,6 +76,7 @@ fn with_existing_attr() { at cfg(…) at cfg_attr(…) at deny(…) + at expect(…) at forbid(…) at warn(…) kw crate:: @@ -97,6 +99,7 @@ fn attr_on_source_file() { at doc = "…" at doc(alias = "…") at doc(hidden) + at expect(…) at feature(…) at forbid(…) at must_use @@ -127,6 +130,7 @@ fn attr_on_module() { at doc = "…" at doc(alias = "…") at doc(hidden) + at expect(…) at forbid(…) at macro_use at must_use @@ -149,6 +153,7 @@ fn attr_on_module() { at doc = "…" at doc(alias = "…") at doc(hidden) + at expect(…) at forbid(…) at must_use at no_implicit_prelude @@ -174,6 +179,7 @@ fn attr_on_macro_rules() { at doc = "…" at doc(alias = "…") at doc(hidden) + at expect(…) at forbid(…) at macro_export at macro_use @@ -199,6 +205,7 @@ fn attr_on_macro_def() { at doc = "…" at doc(alias = "…") at doc(hidden) + at expect(…) at forbid(…) at must_use at no_mangle @@ -222,6 +229,7 @@ fn attr_on_extern_crate() { at doc = "…" at doc(alias = "…") at doc(hidden) + at expect(…) at forbid(…) at macro_use at must_use @@ -246,6 +254,7 @@ fn attr_on_use() { at doc = "…" at doc(alias = "…") at doc(hidden) + at expect(…) at forbid(…) at must_use at no_mangle @@ -269,6 +278,7 @@ fn attr_on_type_alias() { at doc = "…" at doc(alias = "…") at doc(hidden) + at expect(…) at forbid(…) at must_use at no_mangle @@ -299,6 +309,7 @@ struct Foo; at doc = "…" at doc(alias = "…") at doc(hidden) + at expect(…) at forbid(…) at must_use at no_mangle @@ -326,6 +337,7 @@ fn attr_on_enum() { at doc = "…" at doc(alias = "…") at doc(hidden) + at expect(…) at forbid(…) at must_use at no_mangle @@ -351,6 +363,7 @@ fn attr_on_const() { at doc = "…" at doc(alias = "…") at doc(hidden) + at expect(…) at forbid(…) at must_use at no_mangle @@ -374,6 +387,7 @@ fn attr_on_static() { at doc = "…" at doc(alias = "…") at doc(hidden) + at expect(…) at export_name = "…" at forbid(…) at global_allocator @@ -402,6 +416,7 @@ fn attr_on_trait() { at doc = "…" at doc(alias = "…") at doc(hidden) + at expect(…) at forbid(…) at must_use at must_use @@ -427,6 +442,7 @@ fn attr_on_impl() { at doc = "…" at doc(alias = "…") at doc(hidden) + at expect(…) at forbid(…) at must_use at no_mangle @@ -446,6 +462,7 @@ fn attr_on_impl() { at doc = "…" at doc(alias = "…") at doc(hidden) + at expect(…) at forbid(…) at must_use at no_mangle @@ -469,6 +486,7 @@ fn attr_on_extern_block() { at doc = "…" at doc(alias = "…") at doc(hidden) + at expect(…) at forbid(…) at link at must_use @@ -489,6 +507,7 @@ fn attr_on_extern_block() { at doc = "…" at doc(alias = "…") at doc(hidden) + at expect(…) at forbid(…) at link at must_use @@ -509,6 +528,7 @@ fn attr_on_variant() { at cfg(…) at cfg_attr(…) at deny(…) + at expect(…) at forbid(…) at non_exhaustive at warn(…) @@ -532,6 +552,7 @@ fn attr_on_fn() { at doc = "…" at doc(alias = "…") at doc(hidden) + at expect(…) at export_name = "…" at forbid(…) at ignore = "…" @@ -572,6 +593,7 @@ fn attr_in_source_file_end() { at doc = "…" at doc(alias = "…") at doc(hidden) + at expect(…) at export_name = "…" at forbid(…) at global_allocator diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs index 8350fdcc4c07..0b532064fb2b 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs @@ -53,7 +53,7 @@ fn main() { use dep::io::stdin; fn main() { - stdin()$0 + stdin();$0 } "#, ); @@ -274,7 +274,7 @@ fn trait_function_fuzzy_completion() { use dep::test_mod::TestTrait; fn main() { - dep::test_mod::TestStruct::weird_function()$0 + dep::test_mod::TestStruct::weird_function();$0 } "#, ); @@ -368,7 +368,7 @@ use dep::test_mod::TestTrait; fn main() { let test_struct = dep::test_mod::TestStruct {}; - test_struct.random_method()$0 + test_struct.random_method();$0 } "#, ); @@ -419,7 +419,7 @@ impl foo::TestTrait for fundamental::Box { fn main() { let t = fundamental::Box(TestStruct); - t.some_method()$0 + t.some_method();$0 } "#, ); @@ -466,7 +466,7 @@ impl foo::TestTrait for &TestStruct { fn main() { let t = &TestStruct; - t.some_method()$0 + t.some_method();$0 } "#, ); @@ -507,7 +507,7 @@ fn completion(whatever: T) { use foo::{NotInScope, Wrapper}; fn completion(whatever: T) { - whatever.inner().not_in_scope()$0 + whatever.inner().not_in_scope();$0 } "#, ); @@ -579,7 +579,7 @@ fn main() { use dep::test_mod::TestTrait; fn main() { - dep::test_mod::TestAlias::random_method()$0 + dep::test_mod::TestAlias::random_method();$0 } "#, ); @@ -702,7 +702,7 @@ fn main() { use dep::test_mod::TestTrait; fn main() { - dep::test_mod::TestStruct::another_function()$0 + dep::test_mod::TestStruct::another_function();$0 } "#, ); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/item_list.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/item_list.rs index 8aad7bfc3adc..532d4928eff9 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/item_list.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/item_list.rs @@ -313,6 +313,7 @@ impl Test for () { ct const CONST1: () = fn async fn function2() fn fn function1() + fn fn function2() ma makro!(…) macro_rules! makro md module ta type Type1 = diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/pattern.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/pattern.rs index 6a0b67e291af..bd3e7c72bcd6 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/pattern.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/pattern.rs @@ -198,6 +198,7 @@ fn foo(a$0: Tuple) { st Unit bn Record {…} Record { field$1 }$0 bn Tuple(…) Tuple($1)$0 + bn tuple kw mut kw ref "#]], @@ -850,3 +851,75 @@ fn foo() { "#, ); } + +#[test] +fn suggest_name_for_pattern() { + check_edit( + "s1", + r#" +struct S1; + +fn foo() { + let $0 = S1; +} +"#, + r#" +struct S1; + +fn foo() { + let s1 = S1; +} +"#, + ); + + check_edit( + "s1", + r#" +struct S1; + +fn foo(s$0: S1) { +} +"#, + r#" +struct S1; + +fn foo(s1: S1) { +} +"#, + ); + + // Tests for &adt + check_edit( + "s1", + r#" +struct S1; + +fn foo() { + let $0 = &S1; +} +"#, + r#" +struct S1; + +fn foo() { + let s1 = &S1; +} +"#, + ); + + // Do not suggest reserved keywords + check_empty( + r#" +struct Struct; + +fn foo() { + let $0 = Struct; +} +"#, + expect![[r#" + st Struct + kw mut + kw ref + "#]], + ); +} diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/raw_identifiers.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/raw_identifiers.rs index bc630189edc9..d81b3d697aa8 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/raw_identifiers.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/raw_identifiers.rs @@ -30,7 +30,7 @@ fn foo() { "#, expect![[r#" fn foo() { - a::dyn()$0 + a::dyn();$0 "#]], ); @@ -45,7 +45,7 @@ fn foo() { "#, expect![[r#" fn foo() { - a::dyn()$0 + a::dyn();$0 "#]], ); } @@ -63,7 +63,7 @@ fn foo() { "#, expect![[r#" fn foo() { - a::r#dyn()$0 + a::r#dyn();$0 "#]], ); @@ -78,7 +78,7 @@ fn foo() { "#, expect![[r#" fn foo() { - a::r#dyn()$0 + a::r#dyn();$0 "#]], ); } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/special.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/special.rs index 2ae7d37889e5..508f6248dd41 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/special.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/special.rs @@ -63,7 +63,7 @@ fn _alpha() {} "#, r#" fn main() { - _alpha()$0 + _alpha();$0 } fn _alpha() {} "#, diff --git a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs index 5d4b1999088a..099f26eba78a 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs @@ -10,8 +10,8 @@ use either::Either; use hir::{ Adt, AsAssocItem, AsExternAssocItem, AssocItem, AttributeTemplate, BuiltinAttr, BuiltinType, Const, Crate, DefWithBody, DeriveHelper, DocLinkDef, ExternAssocItem, ExternCrateDecl, Field, - Function, GenericParam, HasVisibility, HirDisplay, Impl, Label, Local, Macro, Module, - ModuleDef, Name, PathResolution, Semantics, Static, StaticLifetime, ToolModule, Trait, + Function, GenericParam, HasVisibility, HirDisplay, Impl, InlineAsmOperand, Label, Local, Macro, + Module, ModuleDef, Name, PathResolution, Semantics, Static, StaticLifetime, ToolModule, Trait, TraitAlias, TupleField, TypeAlias, Variant, VariantDef, Visibility, }; use span::Edition; @@ -50,6 +50,8 @@ pub enum Definition { BuiltinAttr(BuiltinAttr), ToolModule(ToolModule), ExternCrateDecl(ExternCrateDecl), + InlineAsmRegOrRegClass(()), + InlineAsmOperand(InlineAsmOperand), } impl Definition { @@ -83,11 +85,13 @@ impl Definition { Definition::Label(it) => it.module(db), Definition::ExternCrateDecl(it) => it.module(db), Definition::DeriveHelper(it) => it.derive().module(db), + Definition::InlineAsmOperand(it) => it.parent(db).module(db), Definition::BuiltinAttr(_) | Definition::BuiltinType(_) | Definition::BuiltinLifetime(_) | Definition::TupleField(_) - | Definition::ToolModule(_) => return None, + | Definition::ToolModule(_) + | Definition::InlineAsmRegOrRegClass(_) => return None, }; Some(module) } @@ -121,7 +125,9 @@ impl Definition { | Definition::Local(_) | Definition::GenericParam(_) | Definition::Label(_) - | Definition::DeriveHelper(_) => return None, + | Definition::DeriveHelper(_) + | Definition::InlineAsmRegOrRegClass(_) + | Definition::InlineAsmOperand(_) => return None, }; Some(vis) } @@ -150,6 +156,8 @@ impl Definition { Definition::ToolModule(_) => return None, // FIXME Definition::DeriveHelper(it) => it.name(db), Definition::ExternCrateDecl(it) => return it.alias_or_name(db), + Definition::InlineAsmRegOrRegClass(_) => return None, + Definition::InlineAsmOperand(op) => return op.name(db), }; Some(name) } @@ -212,6 +220,7 @@ impl Definition { Definition::ToolModule(_) => None, Definition::DeriveHelper(_) => None, Definition::TupleField(_) => None, + Definition::InlineAsmRegOrRegClass(_) | Definition::InlineAsmOperand(_) => None, }; docs.or_else(|| { @@ -268,6 +277,9 @@ impl Definition { Definition::DeriveHelper(it) => { format!("derive_helper {}", it.name(db).display(db, edition)) } + // FIXME + Definition::InlineAsmRegOrRegClass(_) => "inline_asm_reg_or_reg_class".to_owned(), + Definition::InlineAsmOperand(_) => "inline_asm_reg_operand".to_owned(), } } } @@ -429,7 +441,6 @@ impl NameClass { let _p = tracing::info_span!("NameClass::classify").entered(); let parent = name.syntax().parent()?; - let definition = match_ast! { match parent { ast::Item(it) => classify_item(sema, it)?, @@ -440,6 +451,7 @@ impl NameClass { ast::Variant(it) => Definition::Variant(sema.to_def(&it)?), ast::TypeParam(it) => Definition::GenericParam(sema.to_def(&it)?.into()), ast::ConstParam(it) => Definition::GenericParam(sema.to_def(&it)?.into()), + ast::AsmOperandNamed(it) => Definition::InlineAsmOperand(sema.to_def(&it)?), _ => return None, } }; @@ -699,6 +711,9 @@ impl NameRefClass { NameRefClass::ExternCrateShorthand { krate, decl: extern_crate } }) }, + ast::AsmRegSpec(_) => { + Some(NameRefClass::Definition(Definition::InlineAsmRegOrRegClass(()))) + }, _ => None } } @@ -753,6 +768,18 @@ impl From for Definition { } } +impl From for Definition { + fn from(value: InlineAsmOperand) -> Self { + Definition::InlineAsmOperand(value) + } +} + +impl From> for Definition { + fn from(value: Either) -> Self { + value.either(Definition::from, Definition::from) + } +} + impl AsAssocItem for Definition { fn as_assoc_item(self, db: &dyn hir::db::HirDatabase) -> Option { match self { diff --git a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs index 3cf29987fa14..a45ff9a95455 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs @@ -36,8 +36,9 @@ pub mod generated { pub mod syntax_helpers { pub mod format_string; pub mod format_string_exprs; - pub mod insert_whitespace_into_node; + pub use hir::prettify_macro_expansion; pub mod node_ext; + pub mod suggest_name; pub use parser::LexedStr; } @@ -223,6 +224,7 @@ pub enum SymbolKind { Function, Method, Impl, + InlineAsmRegOrRegClass, Label, LifetimeParam, Local, diff --git a/src/tools/rust-analyzer/crates/ide-db/src/rename.rs b/src/tools/rust-analyzer/crates/ide-db/src/rename.rs index 262eefeec00e..f1404ed9f22c 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/rename.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/rename.rs @@ -200,12 +200,14 @@ impl Definition { .and_then(syn_ctx_is_root) } } + Definition::InlineAsmOperand(it) => name_range(it, sema).and_then(syn_ctx_is_root), Definition::BuiltinType(_) | Definition::BuiltinLifetime(_) | Definition::BuiltinAttr(_) | Definition::SelfType(_) | Definition::ToolModule(_) - | Definition::TupleField(_) => return None, + | Definition::TupleField(_) + | Definition::InlineAsmRegOrRegClass(_) => return None, // FIXME: This should be doable in theory Definition::DeriveHelper(_) => return None, }; diff --git a/src/tools/rust-analyzer/crates/ide-db/src/search.rs b/src/tools/rust-analyzer/crates/ide-db/src/search.rs index 12ce5a403fe8..4166b08339bf 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/search.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/search.rs @@ -8,17 +8,18 @@ use std::mem; use std::{cell::LazyCell, cmp::Reverse}; use base_db::{salsa::Database, SourceDatabase, SourceRootDatabase}; +use either::Either; use hir::{ sym, Adt, AsAssocItem, DefWithBody, FileRange, FileRangeWrapper, HasAttrs, HasContainer, - HasSource, HirFileIdExt, InFile, InFileWrapper, InRealFile, ItemContainer, ModuleSource, - PathResolution, Semantics, Visibility, + HasSource, HirFileIdExt, InFile, InFileWrapper, InRealFile, InlineAsmOperand, ItemContainer, + ModuleSource, PathResolution, Semantics, Visibility, }; use memchr::memmem::Finder; use parser::SyntaxKind; use rustc_hash::{FxHashMap, FxHashSet}; use span::EditionedFileId; use syntax::{ - ast::{self, HasName}, + ast::{self, HasName, Rename}, match_ast, AstNode, AstToken, SmolStr, SyntaxElement, SyntaxNode, TextRange, TextSize, ToSmolStr, }; @@ -318,6 +319,23 @@ impl Definition { }; } + if let Definition::InlineAsmOperand(op) = self { + let def = match op.parent(db) { + DefWithBody::Function(f) => f.source(db).map(|src| src.syntax().cloned()), + DefWithBody::Const(c) => c.source(db).map(|src| src.syntax().cloned()), + DefWithBody::Static(s) => s.source(db).map(|src| src.syntax().cloned()), + DefWithBody::Variant(v) => v.source(db).map(|src| src.syntax().cloned()), + // FIXME: implement + DefWithBody::InTypeConst(_) => return SearchScope::empty(), + }; + return match def { + Some(def) => SearchScope::file_range( + def.as_ref().original_file_range_with_macro_call_body(db), + ), + None => SearchScope::single_file(file_id), + }; + } + if let Definition::SelfType(impl_) = self { return match impl_.source(db).map(|src| src.syntax().cloned()) { Some(def) => SearchScope::file_range( @@ -387,6 +405,7 @@ impl Definition { pub fn usages<'a>(self, sema: &'a Semantics<'_, RootDatabase>) -> FindUsages<'a> { FindUsages { def: self, + rename: None, assoc_item_container: self.as_assoc_item(sema.db).map(|a| a.container(sema.db)), sema, scope: None, @@ -399,6 +418,7 @@ impl Definition { #[derive(Clone)] pub struct FindUsages<'a> { def: Definition, + rename: Option<&'a Rename>, sema: &'a Semantics<'a, RootDatabase>, scope: Option<&'a SearchScope>, /// The container of our definition should it be an assoc item @@ -429,6 +449,14 @@ impl<'a> FindUsages<'a> { self } + // FIXME: This is just a temporary fix for not handling import aliases like + // `use Foo as Bar`. We need to support them in a proper way. + // See issue #14079 + pub fn with_rename(mut self, rename: Option<&'a Rename>) -> Self { + self.rename = rename; + self + } + pub fn at_least_one(&self) -> bool { let mut found = false; self.search(&mut |_, _| { @@ -866,9 +894,16 @@ impl<'a> FindUsages<'a> { } }; - let name = match self.def { + let name = match (self.rename, self.def) { + (Some(rename), _) => { + if rename.underscore_token().is_some() { + None + } else { + rename.name().map(|n| n.to_smolstr()) + } + } // special case crate modules as these do not have a proper name - Definition::Module(module) if module.is_crate_root() => { + (_, Definition::Module(module)) if module.is_crate_root() => { // FIXME: This assumes the crate name is always equal to its display name when it // really isn't // we should instead look at the dependency edge name and recursively search our way @@ -908,7 +943,6 @@ impl<'a> FindUsages<'a> { let finder = &Finder::new(name); let include_self_kw_refs = self.include_self_kw_refs.as_ref().map(|ty| (ty, Finder::new("Self"))); - for (text, file_id, search_range) in Self::scope_files(sema.db, &search_scope) { self.sema.db.unwind_if_cancelled(); let tree = LazyCell::new(move || sema.parse(file_id).syntax().clone()); @@ -917,7 +951,7 @@ impl<'a> FindUsages<'a> { for offset in Self::match_indices(&text, finder, search_range) { tree.token_at_offset(offset).for_each(|token| { let Some(str_token) = ast::String::cast(token.clone()) else { return }; - if let Some((range, nameres)) = + if let Some((range, Some(nameres))) = sema.check_for_format_args_template(token, offset) { if self.found_format_args_ref(file_id, range, str_token, nameres, sink) {} @@ -1087,19 +1121,19 @@ impl<'a> FindUsages<'a> { file_id: EditionedFileId, range: TextRange, token: ast::String, - res: Option, + res: Either, sink: &mut dyn FnMut(EditionedFileId, FileReference) -> bool, ) -> bool { - match res.map(Definition::from) { - Some(def) if def == self.def => { - let reference = FileReference { - range, - name: FileReferenceNode::FormatStringEntry(token, range), - category: ReferenceCategory::READ, - }; - sink(file_id, reference) - } - _ => false, + let def = res.either(Definition::from, Definition::from); + if def == self.def { + let reference = FileReference { + range, + name: FileReferenceNode::FormatStringEntry(token, range), + category: ReferenceCategory::READ, + }; + sink(file_id, reference) + } else { + false } } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs b/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs index a83f8473c396..73073e92f787 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs @@ -9,10 +9,13 @@ use crate::{assists::Command, SnippetCap}; use base_db::AnchoredPathBuf; use itertools::Itertools; use nohash_hasher::IntMap; +use rustc_hash::FxHashMap; use span::FileId; use stdx::never; use syntax::{ - algo, AstNode, SyntaxElement, SyntaxNode, SyntaxNodePtr, SyntaxToken, TextRange, TextSize, + algo, + syntax_editor::{SyntaxAnnotation, SyntaxEditor}, + AstNode, SyntaxElement, SyntaxNode, SyntaxNodePtr, SyntaxToken, TextRange, TextSize, }; use text_edit::{TextEdit, TextEditBuilder}; @@ -197,6 +200,11 @@ pub struct SourceChangeBuilder { pub source_change: SourceChange, pub command: Option, + /// Keeps track of all edits performed on each file + pub file_editors: FxHashMap, + /// Keeps track of which annotations correspond to which snippets + pub snippet_annotations: Vec<(AnnotationSnippet, SyntaxAnnotation)>, + /// Maps the original, immutable `SyntaxNode` to a `clone_for_update` twin. pub mutated_tree: Option, /// Keeps track of where to place snippets @@ -238,6 +246,8 @@ impl SourceChangeBuilder { file_id: file_id.into(), source_change: SourceChange::default(), command: None, + file_editors: FxHashMap::default(), + snippet_annotations: vec![], mutated_tree: None, snippet_builder: None, } @@ -248,7 +258,75 @@ impl SourceChangeBuilder { self.file_id = file_id.into(); } + pub fn make_editor(&self, node: &SyntaxNode) -> SyntaxEditor { + SyntaxEditor::new(node.ancestors().last().unwrap_or_else(|| node.clone())) + } + + pub fn add_file_edits(&mut self, file_id: impl Into, edit: SyntaxEditor) { + match self.file_editors.entry(file_id.into()) { + Entry::Occupied(mut entry) => entry.get_mut().merge(edit), + Entry::Vacant(entry) => { + entry.insert(edit); + } + } + } + + pub fn make_placeholder_snippet(&mut self, _cap: SnippetCap) -> SyntaxAnnotation { + self.add_snippet_annotation(AnnotationSnippet::Over) + } + + pub fn make_tabstop_before(&mut self, _cap: SnippetCap) -> SyntaxAnnotation { + self.add_snippet_annotation(AnnotationSnippet::Before) + } + + pub fn make_tabstop_after(&mut self, _cap: SnippetCap) -> SyntaxAnnotation { + self.add_snippet_annotation(AnnotationSnippet::After) + } + fn commit(&mut self) { + // Apply syntax editor edits + for (file_id, editor) in mem::take(&mut self.file_editors) { + let edit_result = editor.finish(); + let mut snippet_edit = vec![]; + + // Find snippet edits + for (kind, annotation) in &self.snippet_annotations { + let elements = edit_result.find_annotation(*annotation); + + let snippet = match (kind, elements) { + (AnnotationSnippet::Before, [element]) => { + Snippet::Tabstop(element.text_range().start()) + } + (AnnotationSnippet::After, [element]) => { + Snippet::Tabstop(element.text_range().end()) + } + (AnnotationSnippet::Over, [element]) => { + Snippet::Placeholder(element.text_range()) + } + (AnnotationSnippet::Over, elements) if !elements.is_empty() => { + Snippet::PlaceholderGroup( + elements.iter().map(|it| it.text_range()).collect(), + ) + } + _ => continue, + }; + + snippet_edit.push(snippet); + } + + let mut edit = TextEdit::builder(); + algo::diff(edit_result.old_root(), edit_result.new_root()).into_text_edit(&mut edit); + let edit = edit.finish(); + + let snippet_edit = + if !snippet_edit.is_empty() { Some(SnippetEdit::new(snippet_edit)) } else { None }; + + if !edit.is_empty() || snippet_edit.is_some() { + self.source_change.insert_source_and_snippet_edit(file_id, edit, snippet_edit); + } + } + + // Apply mutable edits let snippet_edit = self.snippet_builder.take().map(|builder| { SnippetEdit::new( builder.places.into_iter().flat_map(PlaceSnippet::finalize_position).collect(), @@ -369,6 +447,13 @@ impl SourceChangeBuilder { self.source_change.is_snippet = true; } + fn add_snippet_annotation(&mut self, kind: AnnotationSnippet) -> SyntaxAnnotation { + let annotation = SyntaxAnnotation::new(); + self.snippet_annotations.push((kind, annotation)); + self.source_change.is_snippet = true; + annotation + } + pub fn finish(mut self) -> SourceChange { self.commit(); @@ -416,6 +501,15 @@ pub enum Snippet { PlaceholderGroup(Vec), } +pub enum AnnotationSnippet { + /// Place a tabstop before an element + Before, + /// Place a tabstop before an element + After, + /// Place a placeholder snippet in place of the element(s) + Over, +} + enum PlaceSnippet { /// Place a tabstop before an element Before(SyntaxElement), diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/utils/suggest_name.rs b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs similarity index 70% rename from src/tools/rust-analyzer/crates/ide-assists/src/utils/suggest_name.rs rename to src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs index 3130ef069557..2679cbef61b3 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/utils/suggest_name.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs @@ -1,14 +1,18 @@ //! This module contains functions to suggest names for expressions, functions and other items +use std::{collections::hash_map::Entry, str::FromStr}; + use hir::Semantics; -use ide_db::{FxHashSet, RootDatabase}; use itertools::Itertools; +use rustc_hash::FxHashMap; use stdx::to_lower_snake_case; use syntax::{ ast::{self, HasName}, - match_ast, AstNode, Edition, SmolStr, + match_ast, AstNode, Edition, SmolStr, SmolStrBuilder, }; +use crate::RootDatabase; + /// Trait names, that will be ignored when in `impl Trait` and `dyn Trait` const USELESS_TRAITS: &[&str] = &["Send", "Sync", "Copy", "Clone", "Eq", "PartialEq"]; @@ -16,7 +20,9 @@ const USELESS_TRAITS: &[&str] = &["Send", "Sync", "Copy", "Clone", "Eq", "Partia /// /// **NOTE**: they all must be snake lower case const USELESS_NAMES: &[&str] = - &["new", "default", "option", "some", "none", "ok", "err", "str", "string"]; + &["new", "default", "option", "some", "none", "ok", "err", "str", "string", "from", "into"]; + +const USELESS_NAME_PREFIXES: &[&str] = &["from_", "with_", "into_"]; /// Generic types replaced by their first argument /// @@ -58,59 +64,131 @@ const USELESS_METHODS: &[&str] = &[ "into_future", ]; -/// Suggest a unique name for generic parameter. +/// Generator for new names /// -/// `existing_params` is used to check if the name conflicts with existing -/// generic parameters. -/// -/// The function checks if the name conflicts with existing generic parameters. -/// If so, it will try to resolve the conflict by adding a number suffix, e.g. -/// `T`, `T0`, `T1`, ... -pub(crate) fn for_unique_generic_name( - name: &str, - existing_params: &ast::GenericParamList, -) -> SmolStr { - let param_names = existing_params - .generic_params() - .map(|param| match param { - ast::GenericParam::TypeParam(t) => t.name().unwrap().to_string(), - p => p.to_string(), - }) - .collect::>(); - let mut name = name.to_owned(); - let base_len = name.len(); - let mut count = 0; - while param_names.contains(&name) { - name.truncate(base_len); - name.push_str(&count.to_string()); - count += 1; - } - - name.into() -} - -/// Suggest name of impl trait type +/// The generator keeps track of existing names and suggests new names that do +/// not conflict with existing names. /// -/// `existing_params` is used to check if the name conflicts with existing -/// generic parameters. +/// The generator will try to resolve conflicts by adding a numeric suffix to +/// the name, e.g. `a`, `a1`, `a2`, ... /// -/// # Current implementation -/// -/// In current implementation, the function tries to get the name from the first -/// character of the name for the first type bound. +/// # Examples +/// ```rust +/// let mut generator = NameGenerator::new(); +/// assert_eq!(generator.suggest_name("a"), "a"); +/// assert_eq!(generator.suggest_name("a"), "a1"); /// -/// If the name conflicts with existing generic parameters, it will try to -/// resolve the conflict with `for_unique_generic_name`. -pub(crate) fn for_impl_trait_as_generic( - ty: &ast::ImplTraitType, - existing_params: &ast::GenericParamList, -) -> SmolStr { - let c = ty - .type_bound_list() - .and_then(|bounds| bounds.syntax().text().char_at(0.into())) - .unwrap_or('T'); - - for_unique_generic_name(c.encode_utf8(&mut [0; 4]), existing_params) +/// assert_eq!(generator.suggest_name("b2"), "b2"); +/// assert_eq!(generator.suggest_name("b"), "b3"); +/// ``` +#[derive(Debug, Default)] +pub struct NameGenerator { + pool: FxHashMap, +} + +impl NameGenerator { + /// Create a new empty generator + pub fn new() -> Self { + Self { pool: FxHashMap::default() } + } + + /// Create a new generator with existing names. When suggesting a name, it will + /// avoid conflicts with existing names. + pub fn new_with_names<'a>(existing_names: impl Iterator) -> Self { + let mut generator = Self::new(); + existing_names.for_each(|name| generator.insert(name)); + generator + } + + /// Suggest a name without conflicts. If the name conflicts with existing names, + /// it will try to resolve the conflict by adding a numeric suffix. + pub fn suggest_name(&mut self, name: &str) -> SmolStr { + let (prefix, suffix) = Self::split_numeric_suffix(name); + let prefix = SmolStr::new(prefix); + let suffix = suffix.unwrap_or(0); + + match self.pool.entry(prefix.clone()) { + Entry::Vacant(entry) => { + entry.insert(suffix); + SmolStr::from_str(name).unwrap() + } + Entry::Occupied(mut entry) => { + let count = entry.get_mut(); + *count = (*count + 1).max(suffix); + + let mut new_name = SmolStrBuilder::new(); + new_name.push_str(&prefix); + new_name.push_str(count.to_string().as_str()); + new_name.finish() + } + } + } + + /// Suggest a name for given type. + /// + /// The function will strip references first, and suggest name from the inner type. + /// + /// - If `ty` is an ADT, it will suggest the name of the ADT. + /// + If `ty` is wrapped in `Box`, `Option` or `Result`, it will suggest the name from the inner type. + /// - If `ty` is a trait, it will suggest the name of the trait. + /// - If `ty` is an `impl Trait`, it will suggest the name of the first trait. + /// + /// If the suggested name conflicts with reserved keywords, it will return `None`. + pub fn for_type( + &mut self, + ty: &hir::Type, + db: &RootDatabase, + edition: Edition, + ) -> Option { + let name = name_of_type(ty, db, edition)?; + Some(self.suggest_name(&name)) + } + + /// Suggest name of impl trait type + /// + /// # Current implementation + /// + /// In current implementation, the function tries to get the name from the first + /// character of the name for the first type bound. + /// + /// If the name conflicts with existing generic parameters, it will try to + /// resolve the conflict with `for_unique_generic_name`. + pub fn for_impl_trait_as_generic(&mut self, ty: &ast::ImplTraitType) -> SmolStr { + let c = ty + .type_bound_list() + .and_then(|bounds| bounds.syntax().text().char_at(0.into())) + .unwrap_or('T'); + + self.suggest_name(&c.to_string()) + } + + /// Insert a name into the pool + fn insert(&mut self, name: &str) { + let (prefix, suffix) = Self::split_numeric_suffix(name); + let prefix = SmolStr::new(prefix); + let suffix = suffix.unwrap_or(0); + + match self.pool.entry(prefix) { + Entry::Vacant(entry) => { + entry.insert(suffix); + } + Entry::Occupied(mut entry) => { + let count = entry.get_mut(); + *count = (*count).max(suffix); + } + } + } + + /// Remove the numeric suffix from the name + /// + /// # Examples + /// `a1b2c3` -> `a1b2c` + fn split_numeric_suffix(name: &str) -> (&str, Option) { + let pos = + name.rfind(|c: char| !c.is_numeric()).expect("Name cannot be empty or all-numeric"); + let (prefix, suffix) = name.split_at(pos + 1); + (prefix, suffix.parse().ok()) + } } /// Suggest name of variable for given expression @@ -132,7 +210,7 @@ pub(crate) fn for_impl_trait_as_generic( /// /// Currently it sticks to the first name found. // FIXME: Microoptimize and return a `SmolStr` here. -pub(crate) fn for_variable(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> String { +pub fn for_variable(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> String { // `from_param` does not benefit from stripping // it need the largest context possible // so we check firstmost @@ -175,6 +253,10 @@ fn normalize(name: &str) -> Option { return None; } + if USELESS_NAME_PREFIXES.iter().any(|prefix| name.starts_with(prefix)) { + return None; + } + if !is_valid_name(&name) { return None; } @@ -184,7 +266,7 @@ fn normalize(name: &str) -> Option { fn is_valid_name(name: &str) -> bool { matches!( - ide_db::syntax_helpers::LexedStr::single_token(syntax::Edition::CURRENT_FIXME, name), + super::LexedStr::single_token(syntax::Edition::CURRENT_FIXME, name), Some((syntax::SyntaxKind::IDENT, _error)) ) } @@ -818,4 +900,117 @@ fn foo(some_struct: S) { $0some_struct.some_field$0 } "some_field", ); } + + #[test] + fn from_and_to_func() { + check( + r#" +//- minicore: from +struct Foo; +struct Bar; + +impl From for Bar { + fn from(_: Foo) -> Self { + Bar; + } +} + +fn f(_: Bar) {} + +fn main() { + let foo = Foo {}; + f($0Bar::from(foo)$0); +} +"#, + "bar", + ); + + check( + r#" +//- minicore: from +struct Foo; +struct Bar; + +impl From for Bar { + fn from(_: Foo) -> Self { + Bar; + } +} + +fn f(_: Bar) {} + +fn main() { + let foo = Foo {}; + f($0Into::::into(foo)$0); +} +"#, + "bar", + ); + } + + #[test] + fn useless_name_prefix() { + check( + r#" +struct Foo; +struct Bar; + +impl Bar { + fn from_foo(_: Foo) -> Self { + Foo {} + } +} + +fn main() { + let foo = Foo {}; + let _ = $0Bar::from_foo(foo)$0; +} +"#, + "bar", + ); + + check( + r#" +struct Foo; +struct Bar; + +impl Bar { + fn with_foo(_: Foo) -> Self { + Bar {} + } +} + +fn main() { + let foo = Foo {}; + let _ = $0Bar::with_foo(foo)$0; +} +"#, + "bar", + ); + } + + #[test] + fn conflicts_with_existing_names() { + let mut generator = NameGenerator::new(); + assert_eq!(generator.suggest_name("a"), "a"); + assert_eq!(generator.suggest_name("a"), "a1"); + assert_eq!(generator.suggest_name("a"), "a2"); + assert_eq!(generator.suggest_name("a"), "a3"); + + assert_eq!(generator.suggest_name("b"), "b"); + assert_eq!(generator.suggest_name("b2"), "b2"); + assert_eq!(generator.suggest_name("b"), "b3"); + assert_eq!(generator.suggest_name("b"), "b4"); + assert_eq!(generator.suggest_name("b3"), "b5"); + + // --------- + let mut generator = NameGenerator::new_with_names(["a", "b", "b2", "c4"].into_iter()); + assert_eq!(generator.suggest_name("a"), "a1"); + assert_eq!(generator.suggest_name("a"), "a2"); + + assert_eq!(generator.suggest_name("b"), "b3"); + assert_eq!(generator.suggest_name("b2"), "b4"); + + assert_eq!(generator.suggest_name("c"), "c5"); + } } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs index 18a95f0963dd..83a1eb44a616 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs @@ -369,6 +369,23 @@ mod F { ); } + #[test] + fn external_macro() { + check_diagnostics( + r#" +//- /library.rs library crate:library +#[macro_export] +macro_rules! trigger_lint { + () => { let FOO: () }; +} +//- /user.rs crate:user deps:library +fn foo() { + library::trigger_lint!(); +} + "#, + ); + } + #[test] fn complex_ignore() { check_diagnostics( @@ -418,6 +435,64 @@ fn f((_O): u8) {} ) } + #[test] + fn ignores_no_mangle_items() { + cov_mark::check!(extern_func_no_mangle_ignored); + check_diagnostics( + r#" +#[no_mangle] +extern "C" fn NonSnakeCaseName(some_var: u8) -> u8; + "#, + ); + } + + #[test] + fn ignores_no_mangle_items_with_no_abi() { + cov_mark::check!(extern_func_no_mangle_ignored); + check_diagnostics( + r#" +#[no_mangle] +extern fn NonSnakeCaseName(some_var: u8) -> u8; + "#, + ); + } + + #[test] + fn no_mangle_items_with_rust_abi() { + check_diagnostics( + r#" +#[no_mangle] +extern "Rust" fn NonSnakeCaseName(some_var: u8) -> u8; + // ^^^^^^^^^^^^^^^^ 💡 warn: Function `NonSnakeCaseName` should have snake_case name, e.g. `non_snake_case_name` + "#, + ); + } + + #[test] + fn no_mangle_items_non_extern() { + check_diagnostics( + r#" +#[no_mangle] +fn NonSnakeCaseName(some_var: u8) -> u8; +// ^^^^^^^^^^^^^^^^ 💡 warn: Function `NonSnakeCaseName` should have snake_case name, e.g. `non_snake_case_name` + "#, + ); + } + + #[test] + fn extern_fn_name() { + check_diagnostics( + r#" +extern "C" fn NonSnakeCaseName(some_var: u8) -> u8; + // ^^^^^^^^^^^^^^^^ 💡 warn: Function `NonSnakeCaseName` should have snake_case name, e.g. `non_snake_case_name` +extern "Rust" fn NonSnakeCaseName(some_var: u8) -> u8; + // ^^^^^^^^^^^^^^^^ 💡 warn: Function `NonSnakeCaseName` should have snake_case name, e.g. `non_snake_case_name` +extern fn NonSnakeCaseName(some_var: u8) -> u8; + // ^^^^^^^^^^^^^^^^ 💡 warn: Function `NonSnakeCaseName` should have snake_case name, e.g. `non_snake_case_name` + "#, + ); + } + #[test] fn ignores_extern_items() { cov_mark::check!(extern_func_incorrect_case_ignored); @@ -593,7 +668,7 @@ mod CheckBadStyle { } mod F { - //^ 💡 warn: Module `F` should have snake_case name, e.g. `f` + //^ 💡 error: Module `F` should have snake_case name, e.g. `f` #![deny(non_snake_case)] fn CheckItWorksWithModAttr() {} //^^^^^^^^^^^^^^^^^^^^^^^ 💡 error: Function `CheckItWorksWithModAttr` should have snake_case name, e.g. `check_it_works_with_mod_attr` @@ -856,4 +931,104 @@ fn func() { "#, ); } + + #[test] + fn override_lint_level() { + check_diagnostics( + r#" +#[warn(nonstandard_style)] +fn foo() { + let BAR; + // ^^^ 💡 warn: Variable `BAR` should have snake_case name, e.g. `bar` + #[allow(non_snake_case)] + let FOO; +} + +#[warn(nonstandard_style)] +fn foo() { + let BAR; + // ^^^ 💡 warn: Variable `BAR` should have snake_case name, e.g. `bar` + #[expect(non_snake_case)] + let FOO; + #[allow(non_snake_case)] + struct qux; + // ^^^ 💡 warn: Structure `qux` should have CamelCase name, e.g. `Qux` + + fn BAZ() { + // ^^^ 💡 error: Function `BAZ` should have snake_case name, e.g. `baz` + #![forbid(bad_style)] + } +} + "#, + ); + } + + #[test] + fn different_files() { + check_diagnostics( + r#" +//- /lib.rs +#![expect(nonstandard_style)] + +mod BAD_CASE; + +fn BAD_CASE() {} + +//- /BAD_CASE.rs +mod OtherBadCase; + // ^^^^^^^^^^^^ 💡 error: Module `OtherBadCase` should have snake_case name, e.g. `other_bad_case` + +//- /BAD_CASE/OtherBadCase.rs +#![deny(non_snake_case)] + +fn FOO() {} +// ^^^ 💡 error: Function `FOO` should have snake_case name, e.g. `foo` + +#[allow(bad_style)] +mod FINE_WITH_BAD_CASE; + +//- /BAD_CASE/OtherBadCase/FINE_WITH_BAD_CASE.rs +struct QUX; +const foo: i32 = 0; +fn BAR() { + let BAZ; +} + "#, + ); + } + + #[test] + fn cfged_lint_attrs() { + check_diagnostics( + r#" +//- /lib.rs cfg:feature=cool_feature +#[cfg_attr(any(), allow(non_snake_case))] +fn FOO() {} +// ^^^ 💡 warn: Function `FOO` should have snake_case name, e.g. `foo` + +#[cfg_attr(non_existent, allow(non_snake_case))] +fn BAR() {} +// ^^^ 💡 warn: Function `BAR` should have snake_case name, e.g. `bar` + +#[cfg_attr(feature = "cool_feature", allow(non_snake_case))] +fn BAZ() {} + +#[cfg_attr(feature = "cool_feature", cfg_attr ( all ( ) , allow ( non_snake_case ) ) ) ] +fn QUX() {} + "#, + ); + } + + #[test] + fn allow_with_comment() { + check_diagnostics( + r#" +#[allow( + // Yo, sup + non_snake_case +)] +fn foo(_HelloWorld: ()) {} + "#, + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs new file mode 100644 index 000000000000..ad4baf5e3a40 --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs @@ -0,0 +1,1114 @@ +use hir::{CastError, ClosureStyle, HirDisplay}; + +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; + +macro_rules! format_ty { + ($ctx:expr, $fmt:literal, $($arg:expr),* $(,)?) => {{ + std::format!( + $fmt, + $( + $arg + .display($ctx.sema.db, $ctx.edition) + .with_closure_style(ClosureStyle::ClosureWithId) + ),* + ) + }} +} + +// Diagnostic: invalid-cast +// +// This diagnostic is triggered if the code contains an illegal cast +pub(crate) fn invalid_cast(ctx: &DiagnosticsContext<'_>, d: &hir::InvalidCast) -> Diagnostic { + let display_range = ctx.sema.diagnostics_display_range(d.expr.map(|it| it.into())); + let (code, message) = match d.error { + CastError::CastToBool => ( + DiagnosticCode::RustcHardError("E0054"), + format_ty!(ctx, "cannot cast `{}` as `bool`", d.expr_ty), + ), + CastError::CastToChar => ( + DiagnosticCode::RustcHardError("E0604"), + format_ty!(ctx, "only `u8` can be cast as `char`, not {}", d.expr_ty), + ), + CastError::DifferingKinds => ( + DiagnosticCode::RustcHardError("E0606"), + format_ty!( + ctx, + "casting `{}` as `{}` is invalid: vtable kinds may not match", + d.expr_ty, + d.cast_ty + ), + ), + CastError::SizedUnsizedCast => ( + DiagnosticCode::RustcHardError("E0607"), + format_ty!( + ctx, + "cannot cast thin pointer `{}` to fat pointer `{}`", + d.expr_ty, + d.cast_ty + ), + ), + CastError::Unknown | CastError::IllegalCast => ( + DiagnosticCode::RustcHardError("E0606"), + format_ty!(ctx, "casting `{}` as `{}` is invalid", d.expr_ty, d.cast_ty), + ), + CastError::IntToFatCast => ( + DiagnosticCode::RustcHardError("E0606"), + format_ty!(ctx, "cannot cast `{}` to a fat pointer `{}`", d.expr_ty, d.cast_ty), + ), + CastError::NeedDeref => ( + DiagnosticCode::RustcHardError("E0606"), + format_ty!( + ctx, + "casting `{}` as `{}` is invalid: needs defererence or removal of unneeded borrow", + d.expr_ty, + d.cast_ty + ), + ), + CastError::NeedViaPtr => ( + DiagnosticCode::RustcHardError("E0606"), + format_ty!( + ctx, + "casting `{}` as `{}` is invalid: needs casting through a raw pointer first", + d.expr_ty, + d.cast_ty + ), + ), + CastError::NeedViaThinPtr => ( + DiagnosticCode::RustcHardError("E0606"), + format_ty!( + ctx, + "casting `{}` as `{}` is invalid: needs casting through a thin pointer first", + d.expr_ty, + d.cast_ty + ), + ), + CastError::NeedViaInt => ( + DiagnosticCode::RustcHardError("E0606"), + format_ty!( + ctx, + "casting `{}` as `{}` is invalid: needs casting through an integer first", + d.expr_ty, + d.cast_ty + ), + ), + CastError::NonScalar => ( + DiagnosticCode::RustcHardError("E0605"), + format_ty!(ctx, "non-primitive cast: `{}` as `{}`", d.expr_ty, d.cast_ty), + ), + CastError::UnknownCastPtrKind | CastError::UnknownExprPtrKind => ( + DiagnosticCode::RustcHardError("E0641"), + "cannot cast to a pointer of an unknown kind".to_owned(), + ), + }; + Diagnostic::new(code, message, display_range) +} + +// Diagnostic: cast-to-unsized +// +// This diagnostic is triggered when casting to an unsized type +pub(crate) fn cast_to_unsized(ctx: &DiagnosticsContext<'_>, d: &hir::CastToUnsized) -> Diagnostic { + let display_range = ctx.sema.diagnostics_display_range(d.expr.map(|it| it.into())); + Diagnostic::new( + DiagnosticCode::RustcHardError("E0620"), + format_ty!(ctx, "cast to unsized type: `{}`", d.cast_ty), + display_range, + ) +} + +#[cfg(test)] +mod tests { + use crate::tests::{check_diagnostics, check_diagnostics_with_disabled}; + + #[test] + fn cast_as_bool() { + check_diagnostics( + r#" +//- minicore: sized +fn main() { + let u = 5 as bool; + //^^^^^^^^^ error: cannot cast `i32` as `bool` + + let t = (1 + 2) as bool; + //^^^^^^^^^^^^^^^ error: cannot cast `i32` as `bool` + + let _ = 5_u32 as bool; + //^^^^^^^^^^^^^ error: cannot cast `u32` as `bool` + + let _ = 64.0_f64 as bool; + //^^^^^^^^^^^^^^^^ error: cannot cast `f64` as `bool` + + enum IntEnum { + Zero, + One, + Two + } + let _ = IntEnum::One as bool; + //^^^^^^^^^^^^^^^^^^^^ error: cannot cast `IntEnum` as `bool` + + fn uwu(_: u8) -> i32 { + 5 + } + + unsafe fn owo() {} + + let _ = uwu as bool; + //^^^^^^^^^^^ error: cannot cast `fn uwu(u8) -> i32` as `bool` + let _ = owo as bool; + //^^^^^^^^^^^ error: cannot cast `unsafe fn owo()` as `bool` + + let _ = uwu as fn(u8) -> i32 as bool; + //^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: cannot cast `fn(u8) -> i32` as `bool` + let _ = 'x' as bool; + //^^^^^^^^^^^ error: cannot cast `char` as `bool` + + let ptr = 1 as *const (); + + let _ = ptr as bool; + //^^^^^^^^^^^ error: cannot cast `*const ()` as `bool` + let v = "hello" as bool; + //^^^^^^^^^^^^^^^ error: casting `&str` as `bool` is invalid: needs casting through a raw pointer first +} +"#, + ); + } + + #[test] + fn cast_pointee_projection() { + check_diagnostics( + r#" +//- minicore: sized +trait Tag<'a> { + type Type: ?Sized; +} + +trait IntoRaw: for<'a> Tag<'a> { + fn into_raw(this: *const >::Type) -> *mut >::Type; +} + +impl Tag<'a>> IntoRaw for T { + fn into_raw(this: *const >::Type) -> *mut >::Type { + this as *mut T::Type + } +} + +fn main() {} +"#, + ); + } + + #[test] + fn cast_region_to_int() { + check_diagnostics( + r#" +//- minicore: sized +fn main() { + let x: isize = 3; + let _ = &x as *const isize as usize; +} +"#, + ); + } + + #[test] + fn cast_to_bare_fn() { + check_diagnostics( + r#" +//- minicore: sized +fn foo(_x: isize) { } + +fn main() { + let v: u64 = 5; + let x = foo as extern "C" fn() -> isize; + //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: non-primitive cast: `fn foo(isize)` as `fn() -> isize` + let y = v as extern "Rust" fn(isize) -> (isize, isize); + //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: non-primitive cast: `u64` as `fn(isize) -> (isize, isize)` + y(x()); +} +"#, + ); + } + + #[test] + fn cast_to_unit() { + check_diagnostics( + r#" +//- minicore: sized +fn main() { + let _ = 0u32 as (); + //^^^^^^^^^^ error: non-primitive cast: `u32` as `()` +} +"#, + ); + } + + #[test] + fn cast_to_slice() { + check_diagnostics_with_disabled( + r#" +//- minicore: sized +fn as_bytes(_: &str) -> &[u8] { + loop {} +} + +fn main() { + as_bytes("example") as [char]; + //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: cast to unsized type: `[char]` + + let arr: &[u8] = &[0, 2, 3]; + arr as [char]; + //^^^^^^^^^^^^^ error: cast to unsized type: `[char]` +} +"#, + &["E0308"], + ); + } + + #[test] + fn cast() { + check_diagnostics( + r#" +//- minicore: sized +fn null_mut() -> *mut T { + loop {} +} + +pub fn main() { + let i: isize = 'Q' as isize; + let _u: u32 = i as u32; + + // Test that `_` is correctly inferred. + let x = &"hello"; + let mut y = x as *const _; + y = null_mut(); +} +"#, + ); + } + + #[test] + fn dyn_tail_need_normalization() { + check_diagnostics( + r#" +//- minicore: dispatch_from_dyn +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; +} +"#, + ); + } + + #[test] + fn enum_to_numeric_cast() { + check_diagnostics( + r#" +//- minicore: sized +pub enum UnitOnly { + Foo, + Bar, + Baz, +} + +pub enum Fieldless { + Tuple(), + Struct{}, + Unit, +} + +pub enum NotUnitOnlyOrFieldless { + Foo, + Bar(u8), + Baz +} + +fn main() { + let unit_only = UnitOnly::Foo; + + let _ = unit_only as isize; + let _ = unit_only as i32; + let _ = unit_only as usize; + let _ = unit_only as u32; + + + let fieldless = Fieldless::Struct{}; + + let _ = fieldless as isize; + let _ = fieldless as i32; + let _ = fieldless as usize; + let _ = fieldless as u32; + + + let not_unit_only_or_fieldless = NotUnitOnlyOrFieldless::Foo; + + let _ = not_unit_only_or_fieldless as isize; + //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: non-primitive cast: `NotUnitOnlyOrFieldless` as `isize` + let _ = not_unit_only_or_fieldless as i32; + //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: non-primitive cast: `NotUnitOnlyOrFieldless` as `i32` + let _ = not_unit_only_or_fieldless as usize; + //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: non-primitive cast: `NotUnitOnlyOrFieldless` as `usize` + let _ = not_unit_only_or_fieldless as u32; + //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: non-primitive cast: `NotUnitOnlyOrFieldless` as `u32` +} +"#, + ); + } + + #[test] + fn fat_ptr_cast() { + check_diagnostics_with_disabled( + r#" +//- minicore: sized +trait Foo { + fn foo(&self) {} //~ WARN method `foo` is never used +} + +struct Bar; + +impl Foo for Bar {} + +fn to_raw(_: *mut T) -> *mut () { + loop {} +} + +fn main() { + // Test we can turn a fat pointer to array back into a thin pointer. + let a: *const [i32] = &[1, 2, 3]; + let b = a as *const [i32; 2]; + + // Test conversion to an address (usize). + let a: *const [i32; 3] = &[1, 2, 3]; + let b: *const [i32] = a; + + // And conversion to a void pointer/address for trait objects too. + let a: *mut dyn Foo = &mut Bar; + let b = a as *mut () as usize; + let c = a as *const () as usize; + let d = to_raw(a) as usize; +} +"#, + &["E0308"], + ); + + check_diagnostics_with_disabled( + r#" +//- minicore: sized +trait Trait {} + +struct Box; + +impl Box { + fn new(_: T) -> Self { + loop {} + } +} + +fn as_ptr(_: &[i32]) -> *const i32 { + loop {} +} + +fn main() { + let a: &[i32] = &[1, 2, 3]; + let b: Box<[i32]> = Box::new([1, 2, 3]); + let p = a as *const [i32]; + let q = as_ptr(a); + + a as usize; + //^^^^^^^^^^ error: casting `&[i32]` as `usize` is invalid: needs casting through a raw pointer first + a as isize; + //^^^^^^^^^^ error: casting `&[i32]` as `isize` is invalid: needs casting through a raw pointer first + a as i16; + //^^^^^^^^ error: casting `&[i32]` as `i16` is invalid: needs casting through a raw pointer first + a as u32; + //^^^^^^^^ error: casting `&[i32]` as `u32` is invalid: needs casting through a raw pointer first + b as usize; + //^^^^^^^^^^ error: non-primitive cast: `Box<[i32]>` as `usize` + p as usize; + //^^^^^^^^^^ error: casting `*const [i32]` as `usize` is invalid: needs casting through a thin pointer first + q as *const [i32]; + //^^^^^^^^^^^^^^^^^ error: cannot cast thin pointer `*const i32` to fat pointer `*const [i32]` + + let t: *mut (dyn Trait + 'static) = 0 as *mut _; + //^^^^^^^^^^^ error: cannot cast `usize` to a fat pointer `*mut _` + let mut fail: *const str = 0 as *const str; + //^^^^^^^^^^^^^^^ error: cannot cast `usize` to a fat pointer `*const str` + let mut fail2: *const str = 0isize as *const str; + //^^^^^^^^^^^^^^^^^^^^ error: cannot cast `isize` to a fat pointer `*const str` +} + +fn foo() { + let s = 0 as *const T; + //^^^^^^^^^^^^^ error: cannot cast `usize` to a fat pointer `*const T` +} +"#, + &["E0308", "unused_variables"], + ); + } + + #[test] + fn order_dependent_cast_inference() { + check_diagnostics( + r#" +//- minicore: sized +fn main() { + let x = &"hello"; + let mut y = 0 as *const _; + //^^^^^^^^^^^^^ error: cannot cast to a pointer of an unknown kind + y = x as *const _; +} +"#, + ); + } + + #[test] + fn ptr_to_ptr_different_regions() { + check_diagnostics( + r#" +//- minicore: sized +struct Foo<'a> { a: &'a () } + +fn extend_lifetime_very_very_safely<'a>(v: *const Foo<'a>) -> *const Foo<'static> { + // This should pass because raw pointer casts can do anything they want. + v as *const Foo<'static> +} + +trait Trait {} + +fn assert_static<'a>(ptr: *mut (dyn Trait + 'a)) -> *mut (dyn Trait + 'static) { + ptr as _ +} + +fn main() { + let unit = (); + let foo = Foo { a: &unit }; + let _long: *const Foo<'static> = extend_lifetime_very_very_safely(&foo); +} +"#, + ); + } + + #[test] + fn ptr_to_trait_obj_add_auto() { + check_diagnostics( + r#" +//- minicore: pointee +trait Trait<'a> {} + +fn add_auto<'a>(x: *mut dyn Trait<'a>) -> *mut (dyn Trait<'a> + Send) { + x as _ +} + +// (to test diagnostic list formatting) +fn add_multiple_auto<'a>(x: *mut dyn Trait<'a>) -> *mut (dyn Trait<'a> + Send + Sync + Unpin) { + x as _ +} +"#, + ); + } + + #[test] + fn ptr_to_trait_obj_add_super_auto() { + check_diagnostics( + r#" +//- minicore: pointee +trait Trait: Send {} +impl Trait for () {} + +fn main() { + // This is OK: `Trait` has `Send` super trait. + &() as *const dyn Trait as *const (dyn Trait + Send); +} +"#, + ); + } + + #[test] + fn ptr_to_trait_obj_ok() { + check_diagnostics( + r#" +//- minicore: pointee +trait Trait<'a> {} + +fn remove_auto<'a>(x: *mut (dyn Trait<'a> + Send)) -> *mut dyn Trait<'a> { + x as _ +} + +fn cast_inherent_lt<'a, 'b>(x: *mut (dyn Trait<'static> + 'a)) -> *mut (dyn Trait<'static> + 'b) { + x as _ +} + +fn unprincipled<'a, 'b>(x: *mut (dyn Send + 'a)) -> *mut (dyn Sync + 'b) { + x as _ +} +"#, + ); + } + + #[ignore = "issue #18047"] + #[test] + fn ptr_to_trait_obj_wrap_upcast() { + check_diagnostics( + r#" +//- minicore: sized +trait Super {} +trait Sub: Super {} + +struct Wrapper(T); + +// This cast should not compile. +// Upcasting can't work here, because we are also changing the type (`Wrapper`), +// and reinterpreting would be confusing/surprising. +// See +fn cast(ptr: *const dyn Sub) -> *const Wrapper { + ptr as _ + //^^^^^^^^ error: casting `*const dyn Sub` as `*const Wrapper` is invalid: vtable kinds may not match +} +"#, + ); + } + + #[test] + fn supported_cast() { + check_diagnostics( + r#" +//- minicore: sized +pub fn main() { + struct String; + + let f = 1_usize as *const String; + + let _ = f as isize; + let _ = f as usize; + let _ = f as i8; + let _ = f as i16; + let _ = f as i32; + let _ = f as i64; + let _ = f as u8; + let _ = f as u16; + let _ = f as u32; + let _ = f as u64; + + let _ = 1 as isize; + let _ = 1 as usize; + let _ = 1 as *const String; + let _ = 1 as i8; + let _ = 1 as i16; + let _ = 1 as i32; + let _ = 1 as i64; + let _ = 1 as u8; + let _ = 1 as u16; + let _ = 1 as u32; + let _ = 1 as u64; + let _ = 1 as f32; + let _ = 1 as f64; + + let _ = 1_usize as isize; + let _ = 1_usize as usize; + let _ = 1_usize as *const String; + let _ = 1_usize as i8; + let _ = 1_usize as i16; + let _ = 1_usize as i32; + let _ = 1_usize as i64; + let _ = 1_usize as u8; + let _ = 1_usize as u16; + let _ = 1_usize as u32; + let _ = 1_usize as u64; + let _ = 1_usize as f32; + let _ = 1_usize as f64; + + let _ = 1i8 as isize; + let _ = 1i8 as usize; + let _ = 1i8 as *const String; + let _ = 1i8 as i8; + let _ = 1i8 as i16; + let _ = 1i8 as i32; + let _ = 1i8 as i64; + let _ = 1i8 as u8; + let _ = 1i8 as u16; + let _ = 1i8 as u32; + let _ = 1i8 as u64; + let _ = 1i8 as f32; + let _ = 1i8 as f64; + + let _ = 1u8 as isize; + let _ = 1u8 as usize; + let _ = 1u8 as *const String; + let _ = 1u8 as i8; + let _ = 1u8 as i16; + let _ = 1u8 as i32; + let _ = 1u8 as i64; + let _ = 1u8 as u8; + let _ = 1u8 as u16; + let _ = 1u8 as u32; + let _ = 1u8 as u64; + let _ = 1u8 as f32; + let _ = 1u8 as f64; + + let _ = 1i16 as isize; + let _ = 1i16 as usize; + let _ = 1i16 as *const String; + let _ = 1i16 as i8; + let _ = 1i16 as i16; + let _ = 1i16 as i32; + let _ = 1i16 as i64; + let _ = 1i16 as u8; + let _ = 1i16 as u16; + let _ = 1i16 as u32; + let _ = 1i16 as u64; + let _ = 1i16 as f32; + let _ = 1i16 as f64; + + let _ = 1u16 as isize; + let _ = 1u16 as usize; + let _ = 1u16 as *const String; + let _ = 1u16 as i8; + let _ = 1u16 as i16; + let _ = 1u16 as i32; + let _ = 1u16 as i64; + let _ = 1u16 as u8; + let _ = 1u16 as u16; + let _ = 1u16 as u32; + let _ = 1u16 as u64; + let _ = 1u16 as f32; + let _ = 1u16 as f64; + + let _ = 1i32 as isize; + let _ = 1i32 as usize; + let _ = 1i32 as *const String; + let _ = 1i32 as i8; + let _ = 1i32 as i16; + let _ = 1i32 as i32; + let _ = 1i32 as i64; + let _ = 1i32 as u8; + let _ = 1i32 as u16; + let _ = 1i32 as u32; + let _ = 1i32 as u64; + let _ = 1i32 as f32; + let _ = 1i32 as f64; + + let _ = 1u32 as isize; + let _ = 1u32 as usize; + let _ = 1u32 as *const String; + let _ = 1u32 as i8; + let _ = 1u32 as i16; + let _ = 1u32 as i32; + let _ = 1u32 as i64; + let _ = 1u32 as u8; + let _ = 1u32 as u16; + let _ = 1u32 as u32; + let _ = 1u32 as u64; + let _ = 1u32 as f32; + let _ = 1u32 as f64; + + let _ = 1i64 as isize; + let _ = 1i64 as usize; + let _ = 1i64 as *const String; + let _ = 1i64 as i8; + let _ = 1i64 as i16; + let _ = 1i64 as i32; + let _ = 1i64 as i64; + let _ = 1i64 as u8; + let _ = 1i64 as u16; + let _ = 1i64 as u32; + let _ = 1i64 as u64; + let _ = 1i64 as f32; + let _ = 1i64 as f64; + + let _ = 1u64 as isize; + let _ = 1u64 as usize; + let _ = 1u64 as *const String; + let _ = 1u64 as i8; + let _ = 1u64 as i16; + let _ = 1u64 as i32; + let _ = 1u64 as i64; + let _ = 1u64 as u8; + let _ = 1u64 as u16; + let _ = 1u64 as u32; + let _ = 1u64 as u64; + let _ = 1u64 as f32; + let _ = 1u64 as f64; + + let _ = 1u64 as isize; + let _ = 1u64 as usize; + let _ = 1u64 as *const String; + let _ = 1u64 as i8; + let _ = 1u64 as i16; + let _ = 1u64 as i32; + let _ = 1u64 as i64; + let _ = 1u64 as u8; + let _ = 1u64 as u16; + let _ = 1u64 as u32; + let _ = 1u64 as u64; + let _ = 1u64 as f32; + let _ = 1u64 as f64; + + let _ = true as isize; + let _ = true as usize; + let _ = true as i8; + let _ = true as i16; + let _ = true as i32; + let _ = true as i64; + let _ = true as u8; + let _ = true as u16; + let _ = true as u32; + let _ = true as u64; + + let _ = 1f32 as isize; + let _ = 1f32 as usize; + let _ = 1f32 as i8; + let _ = 1f32 as i16; + let _ = 1f32 as i32; + let _ = 1f32 as i64; + let _ = 1f32 as u8; + let _ = 1f32 as u16; + let _ = 1f32 as u32; + let _ = 1f32 as u64; + let _ = 1f32 as f32; + let _ = 1f32 as f64; + + let _ = 1f64 as isize; + let _ = 1f64 as usize; + let _ = 1f64 as i8; + let _ = 1f64 as i16; + let _ = 1f64 as i32; + let _ = 1f64 as i64; + let _ = 1f64 as u8; + let _ = 1f64 as u16; + let _ = 1f64 as u32; + let _ = 1f64 as u64; + let _ = 1f64 as f32; + let _ = 1f64 as f64; +} +"#, + ); + } + + #[test] + fn unsized_struct_cast() { + check_diagnostics( + r#" +//- minicore: sized +pub struct Data([u8]); + +fn foo(x: &[u8]) { + let _: *const Data = x as *const Data; + //^^^^^^^^^^^^^^^^ error: casting `&[u8]` as `*const Data` is invalid +} +"#, + ); + } + + #[test] + fn unsupported_cast() { + check_diagnostics( + r#" +//- minicore: sized +struct A; + +fn main() { + let _ = 1.0 as *const A; + //^^^^^^^^^^^^^^^ error: casting `f64` as `*const A` is invalid +} +"#, + ); + } + + #[test] + fn issue_17897() { + check_diagnostics( + r#" +//- minicore: sized +fn main() { + _ = ((), ()) as (); + //^^^^^^^^^^^^^^ error: non-primitive cast: `((), ())` as `()` +} +"#, + ); + } + + #[test] + fn rustc_issue_10991() { + check_diagnostics( + r#" +//- minicore: sized +fn main() { + let nil = (); + let _t = nil as usize; + //^^^^^^^^^^^^ error: non-primitive cast: `()` as `usize` +} +"#, + ); + } + + #[test] + fn rustc_issue_17444() { + check_diagnostics( + r#" +//- minicore: sized +enum Test { + Foo = 0 +} + +fn main() { + let _x = Test::Foo as *const isize; + //^^^^^^^^^^^^^^^^^^^^^^^^^ error: casting `Test` as `*const isize` is invalid +} +"#, + ); + } + + #[test] + fn rustc_issue_43825() { + check_diagnostics( + r#" +//- minicore: sized +fn main() { + let error = error; + //^^^^^ error: no such value in this scope + + 0 as f32; + 0.0 as u32; +} +"#, + ); + } + + #[test] + fn rustc_issue_84213() { + check_diagnostics( + r#" +//- minicore: sized +struct Something { + pub field: u32, +} + +fn main() { + let mut something = Something { field: 1337 }; + let _ = something.field; + + let _pointer_to_something = something as *const Something; + //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: non-primitive cast: `Something` as `*const Something` + + let _mut_pointer_to_something = something as *mut Something; + //^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: non-primitive cast: `Something` as `*mut Something` +} +"#, + ); + + // Fixed + check_diagnostics( + r#" +//- minicore: sized +struct Something { + pub field: u32, +} + +fn main() { + let mut something = Something { field: 1337 }; + let _ = something.field; + + let _pointer_to_something = &something as *const Something; + + let _mut_pointer_to_something = &mut something as *mut Something; +} +"#, + ); + } + + #[test] + fn rustc_issue_88621() { + check_diagnostics( + r#" +//- minicore: sized +#[repr(u8)] +enum Kind2 { + Foo() = 1, + Bar{} = 2, + Baz = 3, +} + +fn main() { + let _ = Kind2::Foo() as u8; + //^^^^^^^^^^^^^^^^^^ error: non-primitive cast: `Kind2` as `u8` +} +"#, + ); + } + + #[test] + fn rustc_issue_89497() { + check_diagnostics( + r#" +//- minicore: sized +fn main() { + let pointer: usize = &1_i32 as *const i32 as usize; + let _reference: &'static i32 = unsafe { pointer as *const i32 as &'static i32 }; + //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: non-primitive cast: `*const i32` as `&i32` +} +"#, + ); + + // Fixed + check_diagnostics( + r#" +//- minicore: sized +fn main() { + let pointer: usize = &1_i32 as *const i32 as usize; + let _reference: &'static i32 = unsafe { &*(pointer as *const i32) }; +} +"#, + ); + } + + #[test] + fn rustc_issue_106883() { + check_diagnostics_with_disabled( + r#" +//- minicore: sized, deref +use core::ops::Deref; + +struct Foo; + +impl Deref for Foo { + type Target = [u8]; + + fn deref(&self) -> &Self::Target { + &[] + } +} + +fn main() { + let _ = "foo" as bool; + //^^^^^^^^^^^^^ error: casting `&str` as `bool` is invalid: needs casting through a raw pointer first + + let _ = Foo as bool; + //^^^^^^^^^^^ error: non-primitive cast: `Foo` as `bool` +} + +fn _slice(bar: &[i32]) -> bool { + bar as bool + //^^^^^^^^^^^ error: casting `&[i32]` as `bool` is invalid: needs casting through a raw pointer first +} +"#, + &["E0308"], + ); + } + + #[test] + fn trait_upcasting() { + check_diagnostics( + r#" +//- minicore: coerce_unsized, dispatch_from_dyn +#![feature(trait_upcasting)] +trait Foo {} +trait Bar: Foo {} + +impl dyn Bar { + fn bar(&self) { + _ = self as &dyn Foo; + } +} +"#, + ); + } + + #[test] + fn issue_18047() { + check_diagnostics( + r#" +//- minicore: coerce_unsized, dispatch_from_dyn +trait LocalFrom { + fn from(_: T) -> Self; +} +trait LocalInto { + fn into(self) -> T; +} + +impl LocalInto for T +where + U: LocalFrom, +{ + fn into(self) -> U { + U::from(self) + } +} + +impl LocalFrom for T { + fn from(t: T) -> T { + t + } +} + +trait Foo { + type ErrorType; + type Assoc; +} + +trait Bar { + type ErrorType; +} + +struct ErrorLike; + +impl LocalFrom for ErrorLike +where + E: Trait + 'static, +{ + fn from(_: E) -> Self { + loop {} + } +} + +trait Baz { + type Assoc: Bar; + type Error: LocalInto; +} + +impl Baz for T +where + T: Foo, + T::ErrorType: LocalInto, + U: Bar, + ::ErrorType: LocalInto, +{ + type Assoc = U; + type Error = T::ErrorType; +} +struct S; +trait Trait {} +impl Trait for S {} + +fn test() +where + T: Baz, + T::Assoc: 'static, +{ + let _ = &S as &dyn Trait; +} +"#, + ); + } + + #[test] + fn cast_literal_to_char() { + check_diagnostics( + r#" +fn foo() { + 0 as char; +} + "#, + ); + } +} diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs index af8ac6005d7a..5b43f4b2af3d 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs @@ -11,9 +11,14 @@ use crate::{fix, Diagnostic, DiagnosticCode, DiagnosticsContext}; // // This diagnostic is triggered if an operation marked as `unsafe` is used outside of an `unsafe` function or block. pub(crate) fn missing_unsafe(ctx: &DiagnosticsContext<'_>, d: &hir::MissingUnsafe) -> Diagnostic { + let code = if d.only_lint { + DiagnosticCode::RustcLint("unsafe_op_in_unsafe_fn") + } else { + DiagnosticCode::RustcHardError("E0133") + }; Diagnostic::new_with_syntax_node_ptr( ctx, - DiagnosticCode::RustcHardError("E0133"), + code, "this operation is unsafe and requires an unsafe function or block", d.expr.map(|it| it.into()), ) @@ -99,8 +104,9 @@ mod tests { fn missing_unsafe_diagnostic_with_raw_ptr() { check_diagnostics( r#" +//- minicore: sized fn main() { - let x = &5 as *const usize; + let x = &5_usize as *const usize; unsafe { let _y = *x; } let _z = *x; } //^^💡 error: this operation is unsafe and requires an unsafe function or block @@ -112,17 +118,18 @@ fn main() { fn missing_unsafe_diagnostic_with_unsafe_call() { check_diagnostics( r#" +//- minicore: sized struct HasUnsafe; impl HasUnsafe { unsafe fn unsafe_fn(&self) { - let x = &5 as *const usize; + let x = &5_usize as *const usize; let _y = *x; } } unsafe fn unsafe_fn() { - let x = &5 as *const usize; + let x = &5_usize as *const usize; let _y = *x; } @@ -163,6 +170,56 @@ fn main() { ); } + #[test] + fn missing_unsafe_diagnostic_with_extern_static() { + check_diagnostics( + r#" +//- minicore: copy + +extern "C" { + static EXTERN: i32; + static mut EXTERN_MUT: i32; +} + +fn main() { + let _x = EXTERN; + //^^^^^^💡 error: this operation is unsafe and requires an unsafe function or block + let _x = EXTERN_MUT; + //^^^^^^^^^^💡 error: this operation is unsafe and requires an unsafe function or block + unsafe { + let _x = EXTERN; + let _x = EXTERN_MUT; + } +} +"#, + ); + } + + #[test] + fn no_unsafe_diagnostic_with_addr_of_static() { + check_diagnostics( + r#" +//- minicore: copy, addr_of + +use core::ptr::{addr_of, addr_of_mut}; + +extern "C" { + static EXTERN: i32; + static mut EXTERN_MUT: i32; +} +static mut STATIC_MUT: i32 = 0; + +fn main() { + let _x = addr_of!(EXTERN); + let _x = addr_of!(EXTERN_MUT); + let _x = addr_of!(STATIC_MUT); + let _x = addr_of_mut!(EXTERN_MUT); + let _x = addr_of_mut!(STATIC_MUT); +} +"#, + ); + } + #[test] fn no_missing_unsafe_diagnostic_with_safe_intrinsic() { check_diagnostics( @@ -200,14 +257,15 @@ fn main() { fn add_unsafe_block_when_dereferencing_a_raw_pointer() { check_fix( r#" +//- minicore: sized fn main() { - let x = &5 as *const usize; + let x = &5_usize as *const usize; let _z = *x$0; } "#, r#" fn main() { - let x = &5 as *const usize; + let x = &5_usize as *const usize; let _z = unsafe { *x }; } "#, @@ -218,8 +276,9 @@ fn main() { fn add_unsafe_block_when_calling_unsafe_function() { check_fix( r#" +//- minicore: sized unsafe fn func() { - let x = &5 as *const usize; + let x = &5_usize as *const usize; let z = *x; } fn main() { @@ -228,7 +287,7 @@ fn main() { "#, r#" unsafe fn func() { - let x = &5 as *const usize; + let x = &5_usize as *const usize; let z = *x; } fn main() { @@ -242,6 +301,7 @@ fn main() { fn add_unsafe_block_when_calling_unsafe_method() { check_fix( r#" +//- minicore: sized struct S(usize); impl S { unsafe fn func(&self) { @@ -507,6 +567,30 @@ fn main() { ed2021::safe(); ed2024::not_safe(); //^^^^^^^^^^^^^^^^^^💡 error: this operation is unsafe and requires an unsafe function or block +} + "#, + ) + } + + #[test] + fn unsafe_op_in_unsafe_fn_allowed_by_default() { + check_diagnostics( + r#" +unsafe fn foo(p: *mut i32) { + *p = 123; +} + "#, + ) + } + + #[test] + fn unsafe_op_in_unsafe_fn() { + check_diagnostics( + r#" +#![warn(unsafe_op_in_unsafe_fn)] +unsafe fn foo(p: *mut i32) { + *p = 123; + //^^💡 warn: this operation is unsafe and requires an unsafe function or block } "#, ) diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs index 06c6b0f3e4c3..a9ff06fb0ab1 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs @@ -190,4 +190,16 @@ fn foo(mut slice: &[u32]) -> usize { "#, ); } + + #[test] + fn regression_16564() { + check_diagnostics( + r#" +//- minicore: copy +fn test() { + let _x = (&(&mut (),)).0 as *const (); +} + "#, + ) + } } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs index e4b1f3ca9599..955427939150 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs @@ -824,13 +824,13 @@ fn f() { #[test] fn or_pattern() { + // FIXME: `None` is inferred as unknown here for some reason check_diagnostics( r#" //- minicore: option fn f(_: i32) {} fn main() { let ((Some(mut x), None) | (_, Some(mut x))) = (None, Some(7)) else { return }; - //^^^^^ 💡 warn: variable does not need to be mutable f(x); } "#, diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs index 87932bf989f0..18647206236c 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs @@ -84,7 +84,7 @@ fn foo() { fn replace_filter_map_next_dont_work_for_not_sized_issues_16596() { check_diagnostics( r#" -//- minicore: iterators +//- minicore: iterators, dispatch_from_dyn fn foo() { let mut j = [0].into_iter(); let i: &mut dyn Iterator = &mut j; diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/typed_hole.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/typed_hole.rs index b5c242e1e9f8..6994a7ed1465 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/typed_hole.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/typed_hole.rs @@ -402,4 +402,26 @@ fn f() { ], ); } + + #[test] + fn underscore_in_asm() { + check_diagnostics( + r#" +//- minicore: asm +fn rdtscp() -> u64 { + let hi: u64; + let lo: u64; + unsafe { + core::arch::asm!( + "rdtscp", + out("rdx") hi, + out("rax") lo, + out("rcx") _, + options(nomem, nostack, preserves_flags) + ); + } + (hi << 32) | lo +}"#, + ); + } } 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 9b50a435e4c3..45c723d09d4c 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs @@ -30,6 +30,7 @@ mod handlers { pub(crate) mod inactive_code; pub(crate) mod incoherent_impl; pub(crate) mod incorrect_case; + pub(crate) mod invalid_cast; pub(crate) mod invalid_derive_target; pub(crate) mod macro_error; pub(crate) mod malformed_derive; @@ -75,9 +76,10 @@ mod handlers { #[cfg(test)] mod tests; -use std::sync::LazyLock; +use std::{collections::hash_map, iter, sync::LazyLock}; -use hir::{diagnostics::AnyDiagnostic, InFile, Semantics}; +use either::Either; +use hir::{db::ExpandDatabase, diagnostics::AnyDiagnostic, Crate, HirFileId, InFile, Semantics}; use ide_db::{ assists::{Assist, AssistId, AssistKind, AssistResolveStrategy}, base_db::SourceDatabase, @@ -88,10 +90,10 @@ use ide_db::{ syntax_helpers::node_ext::parse_tt_as_comma_sep_paths, EditionedFileId, FileId, FileRange, FxHashMap, FxHashSet, RootDatabase, SnippetCap, }; -use stdx::never; +use itertools::Itertools; use syntax::{ - ast::{self, AstNode}, - AstPtr, Edition, SyntaxNode, SyntaxNodePtr, TextRange, + ast::{self, AstNode, HasAttrs}, + AstPtr, Edition, NodeOrToken, SmolStr, SyntaxKind, SyntaxNode, SyntaxNodePtr, TextRange, T, }; // FIXME: Make this an enum @@ -390,6 +392,7 @@ pub fn semantic_diagnostics( for diag in diags { let d = match diag { AnyDiagnostic::AwaitOutsideOfAsync(d) => handlers::await_outside_of_async::await_outside_of_async(&ctx, &d), + AnyDiagnostic::CastToUnsized(d) => handlers::invalid_cast::cast_to_unsized(&ctx, &d), AnyDiagnostic::ExpectedFunction(d) => handlers::expected_function::expected_function(&ctx, &d), AnyDiagnostic::InactiveCode(d) => match handlers::inactive_code::inactive_code(&ctx, &d) { Some(it) => it, @@ -397,6 +400,7 @@ pub fn semantic_diagnostics( } AnyDiagnostic::IncoherentImpl(d) => handlers::incoherent_impl::incoherent_impl(&ctx, &d), AnyDiagnostic::IncorrectCase(d) => handlers::incorrect_case::incorrect_case(&ctx, &d), + AnyDiagnostic::InvalidCast(d) => handlers::invalid_cast::invalid_cast(&ctx, &d), AnyDiagnostic::InvalidDeriveTarget(d) => handlers::invalid_derive_target::invalid_derive_target(&ctx, &d), AnyDiagnostic::MacroDefError(d) => handlers::macro_error::macro_def_error(&ctx, &d), AnyDiagnostic::MacroError(d) => handlers::macro_error::macro_error(&ctx, &d), @@ -473,8 +477,9 @@ pub fn semantic_diagnostics( || ctx.config.disable_experimental && d.experimental) }); - let mut diagnostics_of_range = res + let mut lints = res .iter_mut() + .filter(|it| matches!(it.code, DiagnosticCode::Clippy(_) | DiagnosticCode::RustcLint(_))) .filter_map(|it| { Some(( it.main_node.map(|ptr| { @@ -483,27 +488,31 @@ pub fn semantic_diagnostics( it, )) }) - .collect::>(); + .collect::>(); - if diagnostics_of_range.is_empty() { - return res; - } - - let mut rustc_stack: FxHashMap> = FxHashMap::default(); - let mut clippy_stack: FxHashMap> = FxHashMap::default(); - - // FIXME: This becomes quite expensive for big files - handle_lint_attributes( + // The edition isn't accurate (each diagnostics may have its own edition due to macros), + // but it's okay as it's only being used for error recovery. + handle_lints( &ctx.sema, - parse.syntax(), - &mut rustc_stack, - &mut clippy_stack, - &mut diagnostics_of_range, - ctx.edition, + &mut FxHashMap::default(), + &mut lints, + &mut Vec::new(), + file_id.edition(), ); res.retain(|d| d.severity != Severity::Allow); + res.retain_mut(|diag| { + if let Some(node) = diag + .main_node + .map(|ptr| ptr.map(|node| node.to_node(&ctx.sema.parse_or_expand(ptr.file_id)))) + { + handle_diag_from_macros(&ctx.sema, diag, &node) + } else { + true + } + }); + res } @@ -520,6 +529,35 @@ pub fn full_diagnostics( res } +/// Returns whether to keep this diagnostic (or remove it). +fn handle_diag_from_macros( + sema: &Semantics<'_, RootDatabase>, + diag: &mut Diagnostic, + node: &InFile, +) -> bool { + let Some(macro_file) = node.file_id.macro_file() else { return true }; + let span_map = sema.db.expansion_span_map(macro_file); + let mut spans = span_map.spans_for_range(node.text_range()); + if spans.any(|span| { + sema.db.lookup_intern_syntax_context(span.ctx).outer_expn.is_some_and(|expansion| { + let macro_call = + sema.db.lookup_intern_macro_call(expansion.as_macro_file().macro_call_id); + !Crate::from(macro_call.def.krate).origin(sema.db).is_local() + }) + }) { + // Disable suggestions for external macros, they'll change library code and it's just bad. + diag.fixes = None; + + // All Clippy lints report in macros, see https://github.com/rust-lang/rust-clippy/blob/903293b199364/declare_clippy_lint/src/lib.rs#L172. + if let DiagnosticCode::RustcLint(lint) = diag.code { + if !LINTS_TO_REPORT_IN_EXTERNAL_MACROS.contains(lint) { + return false; + } + }; + } + true +} + // `__RA_EVERY_LINT` is a fake lint group to allow every lint in proc macros static RUSTC_LINT_GROUPS_DICT: LazyLock>> = @@ -528,153 +566,347 @@ static RUSTC_LINT_GROUPS_DICT: LazyLock>> = static CLIPPY_LINT_GROUPS_DICT: LazyLock>> = LazyLock::new(|| build_group_dict(CLIPPY_LINT_GROUPS, &["__RA_EVERY_LINT"], "clippy::")); +// FIXME: Autogenerate this instead of enumerating by hand. +static LINTS_TO_REPORT_IN_EXTERNAL_MACROS: LazyLock> = + LazyLock::new(|| FxHashSet::from_iter([])); + fn build_group_dict( lint_group: &'static [LintGroup], all_groups: &'static [&'static str], prefix: &'static str, ) -> FxHashMap<&'static str, Vec<&'static str>> { - let mut r: FxHashMap<&str, Vec<&str>> = FxHashMap::default(); + let mut map_with_prefixes: FxHashMap<&str, Vec<&str>> = FxHashMap::default(); for g in lint_group { - for child in g.children { - r.entry(child.strip_prefix(prefix).unwrap()) - .or_default() - .push(g.lint.label.strip_prefix(prefix).unwrap()); + let mut add_children = |label: &'static str| { + for child in g.children { + map_with_prefixes.entry(child).or_default().push(label); + } + }; + add_children(g.lint.label); + + if g.lint.label == "nonstandard_style" { + // Also add `bad_style`, which for some reason isn't listed in the groups. + add_children("bad_style"); } } - for (lint, groups) in r.iter_mut() { + for (lint, groups) in map_with_prefixes.iter_mut() { groups.push(lint); groups.extend_from_slice(all_groups); } - r + map_with_prefixes.into_iter().map(|(k, v)| (k.strip_prefix(prefix).unwrap(), v)).collect() } -fn handle_lint_attributes( +/// Thd default severity for lints that are not warn by default. +// FIXME: Autogenerate this instead of write manually. +static LINTS_DEFAULT_SEVERITY: LazyLock> = + LazyLock::new(|| FxHashMap::from_iter([("unsafe_op_in_unsafe_fn", Severity::Allow)])); + +fn handle_lints( sema: &Semantics<'_, RootDatabase>, - root: &SyntaxNode, - rustc_stack: &mut FxHashMap>, - clippy_stack: &mut FxHashMap>, - diagnostics_of_range: &mut FxHashMap, &mut Diagnostic>, + cache: &mut FxHashMap>, + diagnostics: &mut [(InFile, &mut Diagnostic)], + cache_stack: &mut Vec, edition: Edition, ) { - let _g = tracing::info_span!("handle_lint_attributes").entered(); - let file_id = sema.hir_file_for(root); - let preorder = root.preorder(); - for ev in preorder { - match ev { - syntax::WalkEvent::Enter(node) => { - for attr in node.children().filter_map(ast::Attr::cast) { - parse_lint_attribute( - attr, - rustc_stack, - clippy_stack, - |stack, severity| { - stack.push(severity); - }, - edition, - ); - } - if let Some(it) = - diagnostics_of_range.get_mut(&InFile { file_id, value: node.clone() }) - { - const EMPTY_LINTS: &[&str] = &[]; - let (names, stack) = match it.code { - DiagnosticCode::RustcLint(name) => ( - RUSTC_LINT_GROUPS_DICT.get(name).map_or(EMPTY_LINTS, |it| &**it), - &mut *rustc_stack, - ), - DiagnosticCode::Clippy(name) => ( - CLIPPY_LINT_GROUPS_DICT.get(name).map_or(EMPTY_LINTS, |it| &**it), - &mut *clippy_stack, - ), - _ => continue, - }; - for &name in names { - if let Some(s) = stack.get(name).and_then(|it| it.last()) { - it.severity = *s; + for (node, diag) in diagnostics { + let lint = match diag.code { + DiagnosticCode::RustcLint(lint) | DiagnosticCode::Clippy(lint) => lint, + _ => panic!("non-lint passed to `handle_lints()`"), + }; + if let Some(&default_severity) = LINTS_DEFAULT_SEVERITY.get(lint) { + diag.severity = default_severity; + } + + let mut diag_severity = fill_lint_attrs(sema, node, cache, cache_stack, diag, edition); + + if let outline_diag_severity @ Some(_) = + find_outline_mod_lint_severity(sema, node, diag, edition) + { + diag_severity = outline_diag_severity; + } + + if let Some(diag_severity) = diag_severity { + diag.severity = diag_severity; + } + } +} + +fn find_outline_mod_lint_severity( + sema: &Semantics<'_, RootDatabase>, + node: &InFile, + diag: &Diagnostic, + edition: Edition, +) -> Option { + let mod_node = node.value.ancestors().find_map(ast::Module::cast)?; + if mod_node.item_list().is_some() { + // Inline modules will be handled by `fill_lint_attrs()`. + return None; + } + + let mod_def = sema.to_module_def(&mod_node)?; + let module_source_file = sema.module_definition_node(mod_def); + let mut result = None; + let lint_groups = lint_groups(&diag.code); + lint_attrs( + sema, + ast::AnyHasAttrs::cast(module_source_file.value).expect("SourceFile always has attrs"), + edition, + ) + .for_each(|(lint, severity)| { + if lint_groups.contains(&&*lint) { + result = Some(severity); + } + }); + result +} + +#[derive(Debug, Clone, Copy)] +struct SeverityAttr { + severity: Severity, + /// This field counts how far we are from the main node. Bigger values mean more far. + /// + /// Note this isn't accurate: there can be gaps between values (created when merging severity maps). + /// The important thing is that if an attr is closer to the main node, it will have smaller value. + /// + /// This is necessary even though we take care to never overwrite a value from deeper nesting + /// because of lint groups. For example, in the following code: + /// ``` + /// #[warn(non_snake_case)] + /// mod foo { + /// #[allow(nonstandard_style)] + /// mod bar; + /// } + /// ``` + /// We want to not warn on non snake case inside `bar`. If we are traversing this for the first + /// time, everything will be fine, because we will set `diag_severity` on the first matching group + /// and never overwrite it since then. But if `bar` is cached, the cache will contain both + /// `#[warn(non_snake_case)]` and `#[allow(nonstandard_style)]`, and without this field, we have + /// no way of differentiating between the two. + depth: u32, +} + +fn fill_lint_attrs( + sema: &Semantics<'_, RootDatabase>, + node: &InFile, + cache: &mut FxHashMap>, + cache_stack: &mut Vec, + diag: &Diagnostic, + edition: Edition, +) -> Option { + let mut collected_lint_attrs = FxHashMap::::default(); + let mut diag_severity = None; + + let mut ancestors = node.value.ancestors().peekable(); + let mut depth = 0; + loop { + let ancestor = ancestors.next().expect("we always return from top-level nodes"); + depth += 1; + + if ancestors.peek().is_none() { + // We don't want to insert too many nodes into cache, but top level nodes (aka. outline modules + // or macro expansions) need to touch the database so they seem like a good fit to cache. + + if let Some(cached) = cache.get_mut(&node.file_id) { + // This node (and everything above it) is already cached; the attribute is either here or nowhere. + + // Workaround for the borrow checker. + let cached = std::mem::take(cached); + + cached.iter().for_each(|(lint, severity)| { + for item in &*cache_stack { + let node_cache_entry = cache + .get_mut(item) + .expect("we always insert cached nodes into the cache map"); + let lint_cache_entry = node_cache_entry.entry(lint.clone()); + if let hash_map::Entry::Vacant(lint_cache_entry) = lint_cache_entry { + // Do not overwrite existing lint attributes, as we go bottom to top and bottom attrs + // overwrite top attrs. + lint_cache_entry.insert(SeverityAttr { + severity: severity.severity, + depth: severity.depth + depth, + }); } } + }); + + let all_matching_groups = lint_groups(&diag.code) + .iter() + .filter_map(|lint_group| cached.get(&**lint_group)); + let cached_severity = + all_matching_groups.min_by_key(|it| it.depth).map(|it| it.severity); + + cache.insert(node.file_id, cached); + + return diag_severity.or(cached_severity); + } + + // Insert this node's descendants' attributes into any outline descendant, but not including this node. + // This must come before inserting this node's own attributes to preserve order. + collected_lint_attrs.drain().for_each(|(lint, severity)| { + if diag_severity.is_none() && lint_groups(&diag.code).contains(&&*lint) { + diag_severity = Some(severity.severity); } - if let Some(item) = ast::Item::cast(node.clone()) { - if let Some(me) = sema.expand_attr_macro(&item) { - for stack in [&mut *rustc_stack, &mut *clippy_stack] { - stack - .entry("__RA_EVERY_LINT".to_owned()) - .or_default() - .push(Severity::Allow); - } - handle_lint_attributes( - sema, - &me, - rustc_stack, - clippy_stack, - diagnostics_of_range, - edition, - ); - for stack in [&mut *rustc_stack, &mut *clippy_stack] { - stack.entry("__RA_EVERY_LINT".to_owned()).or_default().pop(); - } + + for item in &*cache_stack { + let node_cache_entry = cache + .get_mut(item) + .expect("we always insert cached nodes into the cache map"); + let lint_cache_entry = node_cache_entry.entry(lint.clone()); + if let hash_map::Entry::Vacant(lint_cache_entry) = lint_cache_entry { + // Do not overwrite existing lint attributes, as we go bottom to top and bottom attrs + // overwrite top attrs. + lint_cache_entry.insert(severity); } } - if let Some(mc) = ast::MacroCall::cast(node) { - if let Some(me) = sema.expand(&mc) { - handle_lint_attributes( - sema, - &me, - rustc_stack, - clippy_stack, - diagnostics_of_range, - edition, - ); + }); + + cache_stack.push(node.file_id); + cache.insert(node.file_id, FxHashMap::default()); + + if let Some(ancestor) = ast::AnyHasAttrs::cast(ancestor) { + // Insert this node's attributes into any outline descendant, including this node. + lint_attrs(sema, ancestor, edition).for_each(|(lint, severity)| { + if diag_severity.is_none() && lint_groups(&diag.code).contains(&&*lint) { + diag_severity = Some(severity); } - } + + for item in &*cache_stack { + let node_cache_entry = cache + .get_mut(item) + .expect("we always insert cached nodes into the cache map"); + let lint_cache_entry = node_cache_entry.entry(lint.clone()); + if let hash_map::Entry::Vacant(lint_cache_entry) = lint_cache_entry { + // Do not overwrite existing lint attributes, as we go bottom to top and bottom attrs + // overwrite top attrs. + lint_cache_entry.insert(SeverityAttr { severity, depth }); + } + } + }); } - syntax::WalkEvent::Leave(node) => { - for attr in node.children().filter_map(ast::Attr::cast) { - parse_lint_attribute( - attr, - rustc_stack, - clippy_stack, - |stack, severity| { - if stack.pop() != Some(severity) { - never!("Mismatched serevity in walking lint attributes"); - } - }, - edition, - ); + + let parent_node = sema.find_parent_file(node.file_id); + if let Some(parent_node) = parent_node { + let parent_severity = + fill_lint_attrs(sema, &parent_node, cache, cache_stack, diag, edition); + if diag_severity.is_none() { + diag_severity = parent_severity; } } + cache_stack.pop(); + return diag_severity; + } else if let Some(ancestor) = ast::AnyHasAttrs::cast(ancestor) { + lint_attrs(sema, ancestor, edition).for_each(|(lint, severity)| { + if diag_severity.is_none() && lint_groups(&diag.code).contains(&&*lint) { + diag_severity = Some(severity); + } + + let lint_cache_entry = collected_lint_attrs.entry(lint); + if let hash_map::Entry::Vacant(lint_cache_entry) = lint_cache_entry { + // Do not overwrite existing lint attributes, as we go bottom to top and bottom attrs + // overwrite top attrs. + lint_cache_entry.insert(SeverityAttr { severity, depth }); + } + }); } } } -fn parse_lint_attribute( - attr: ast::Attr, - rustc_stack: &mut FxHashMap>, - clippy_stack: &mut FxHashMap>, - job: impl Fn(&mut Vec, Severity), +fn lint_attrs<'a>( + sema: &'a Semantics<'a, RootDatabase>, + ancestor: ast::AnyHasAttrs, edition: Edition, +) -> impl Iterator + 'a { + ancestor + .attrs_including_inner() + .filter_map(|attr| { + attr.as_simple_call().and_then(|(name, value)| match &*name { + "allow" | "expect" => Some(Either::Left(iter::once((Severity::Allow, value)))), + "warn" => Some(Either::Left(iter::once((Severity::Warning, value)))), + "forbid" | "deny" => Some(Either::Left(iter::once((Severity::Error, value)))), + "cfg_attr" => { + let mut lint_attrs = Vec::new(); + cfg_attr_lint_attrs(sema, &value, &mut lint_attrs); + Some(Either::Right(lint_attrs.into_iter())) + } + _ => None, + }) + }) + .flatten() + .flat_map(move |(severity, lints)| { + parse_tt_as_comma_sep_paths(lints, edition).into_iter().flat_map(move |lints| { + // Rejoin the idents with `::`, so we have no spaces in between. + lints.into_iter().map(move |lint| { + ( + lint.segments().filter_map(|segment| segment.name_ref()).join("::").into(), + severity, + ) + }) + }) + }) +} + +fn cfg_attr_lint_attrs( + sema: &Semantics<'_, RootDatabase>, + value: &ast::TokenTree, + lint_attrs: &mut Vec<(Severity, ast::TokenTree)>, ) { - let Some((tag, args_tt)) = attr.as_simple_call() else { - return; - }; - let severity = match tag.as_str() { - "allow" => Severity::Allow, - "warn" => Severity::Warning, - "forbid" | "deny" => Severity::Error, - _ => return, - }; - for lint in parse_tt_as_comma_sep_paths(args_tt, edition).into_iter().flatten() { - if let Some(lint) = lint.as_single_name_ref() { - job(rustc_stack.entry(lint.to_string()).or_default(), severity); + let prev_len = lint_attrs.len(); + + let mut iter = value.token_trees_and_tokens().filter(|it| match it { + NodeOrToken::Node(_) => true, + NodeOrToken::Token(it) => !it.kind().is_trivia(), + }); + + // Skip the condition. + for value in &mut iter { + if value.as_token().is_some_and(|it| it.kind() == T![,]) { + break; } - if let Some(tool) = lint.qualifier().and_then(|it| it.as_single_name_ref()) { - if let Some(name_ref) = &lint.segment().and_then(|it| it.name_ref()) { - if tool.to_string() == "clippy" { - job(clippy_stack.entry(name_ref.to_string()).or_default(), severity); + } + + while let Some(value) = iter.next() { + if let Some(token) = value.as_token() { + if token.kind() == SyntaxKind::IDENT { + let severity = match token.text() { + "allow" | "expect" => Some(Severity::Allow), + "warn" => Some(Severity::Warning), + "forbid" | "deny" => Some(Severity::Error), + "cfg_attr" => { + if let Some(NodeOrToken::Node(value)) = iter.next() { + cfg_attr_lint_attrs(sema, &value, lint_attrs); + } + None + } + _ => None, + }; + if let Some(severity) = severity { + let lints = iter.next(); + if let Some(NodeOrToken::Node(lints)) = lints { + lint_attrs.push((severity, lints)); + } } } } } + + if prev_len != lint_attrs.len() { + if let Some(false) | None = sema.check_cfg_attr(value) { + // Discard the attributes when the condition is false. + lint_attrs.truncate(prev_len); + } + } +} + +fn lint_groups(lint: &DiagnosticCode) -> &'static [&'static str] { + match lint { + DiagnosticCode::RustcLint(name) => { + RUSTC_LINT_GROUPS_DICT.get(name).map(|it| &**it).unwrap_or_default() + } + DiagnosticCode::Clippy(name) => { + CLIPPY_LINT_GROUPS_DICT.get(name).map(|it| &**it).unwrap_or_default() + } + _ => &[], + } } fn fix(id: &'static str, label: &str, source_change: SourceChange, target: TextRange) -> Assist { 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 925ae620231d..ea16a11d56d0 100644 --- a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs +++ b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs @@ -219,7 +219,9 @@ pub(crate) fn resolve_doc_path_for_def( | Definition::Local(_) | Definition::GenericParam(_) | Definition::Label(_) - | Definition::DeriveHelper(_) => None, + | Definition::DeriveHelper(_) + | Definition::InlineAsmRegOrRegClass(_) + | Definition::InlineAsmOperand(_) => None, } .map(Definition::from) } @@ -672,7 +674,9 @@ fn filename_and_frag_for_def( | Definition::BuiltinAttr(_) | Definition::BuiltinLifetime(_) | Definition::ToolModule(_) - | Definition::DeriveHelper(_) => return None, + | Definition::DeriveHelper(_) + | Definition::InlineAsmRegOrRegClass(_) + | Definition::InlineAsmOperand(_) => return None, }; Some((def, res, None)) 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 a939ed214ad8..79fdf75b7f7a 100644 --- a/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs +++ b/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs @@ -1,9 +1,10 @@ +use hir::db::ExpandDatabase; use hir::{InFile, MacroFileIdExt, Semantics}; +use ide_db::base_db::CrateId; use ide_db::{ - helpers::pick_best_token, syntax_helpers::insert_whitespace_into_node::insert_ws_into, FileId, - RootDatabase, + helpers::pick_best_token, syntax_helpers::prettify_macro_expansion, FileId, RootDatabase, }; -use span::Edition; +use span::{Edition, SpanMap, SyntaxContextId, TextRange, TextSize}; use syntax::{ast, ted, AstNode, NodeOrToken, SyntaxKind, SyntaxNode, T}; use crate::FilePosition; @@ -27,6 +28,7 @@ pub struct ExpandedMacro { pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option { let sema = Semantics::new(db); let file = sema.parse_guess_edition(position.file_id); + let krate = sema.file_to_module_def(position.file_id)?.krate().into(); let tok = pick_best_token(file.syntax().token_at_offset(position.offset), |kind| match kind { SyntaxKind::IDENT => 1, @@ -61,8 +63,17 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option< .take_while(|it| it != &token) .filter(|it| it.kind() == T![,]) .count(); - let expansion = - format(db, SyntaxKind::MACRO_ITEMS, position.file_id, expansions.get(idx).cloned()?); + let expansion = expansions.get(idx)?.clone(); + let expansion_file_id = sema.hir_file_for(&expansion).macro_file()?; + let expansion_span_map = db.expansion_span_map(expansion_file_id); + let expansion = format( + db, + SyntaxKind::MACRO_ITEMS, + position.file_id, + expansion, + &expansion_span_map, + krate, + ); Some(ExpandedMacro { name, expansion }) }); @@ -71,6 +82,7 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option< } let mut anc = tok.parent_ancestors(); + let mut span_map = SpanMap::empty(); let (name, expanded, kind) = loop { let node = anc.next()?; @@ -85,7 +97,7 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option< .unwrap_or(Edition::CURRENT), ) .to_string(), - expand_macro_recur(&sema, &item)?, + expand_macro_recur(&sema, &item, &mut span_map, TextSize::new(0))?, SyntaxKind::MACRO_ITEMS, ); } @@ -95,14 +107,23 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option< name.push('!'); let syntax_kind = mac.syntax().parent().map(|it| it.kind()).unwrap_or(SyntaxKind::MACRO_ITEMS); - break (name, expand_macro_recur(&sema, &ast::Item::MacroCall(mac))?, syntax_kind); + break ( + name, + expand_macro_recur( + &sema, + &ast::Item::MacroCall(mac), + &mut span_map, + TextSize::new(0), + )?, + syntax_kind, + ); } }; // FIXME: // macro expansion may lose all white space information // But we hope someday we can use ra_fmt for that - let expansion = format(db, kind, position.file_id, expanded); + let expansion = format(db, kind, position.file_id, expanded, &span_map, krate); Some(ExpandedMacro { name, expansion }) } @@ -110,6 +131,8 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option< fn expand_macro_recur( sema: &Semantics<'_, RootDatabase>, macro_call: &ast::Item, + result_span_map: &mut SpanMap, + offset_in_original_node: TextSize, ) -> Option { let expanded = match macro_call { item @ ast::Item::MacroCall(macro_call) => sema @@ -118,29 +141,60 @@ fn expand_macro_recur( .clone_for_update(), item => sema.expand_attr_macro(item)?.clone_for_update(), }; - expand(sema, expanded) + let file_id = + sema.hir_file_for(&expanded).macro_file().expect("expansion must produce a macro file"); + let expansion_span_map = sema.db.expansion_span_map(file_id); + result_span_map.merge( + TextRange::at(offset_in_original_node, macro_call.syntax().text_range().len()), + expanded.text_range().len(), + &expansion_span_map, + ); + Some(expand(sema, expanded, result_span_map, u32::from(offset_in_original_node) as i32)) } -fn expand(sema: &Semantics<'_, RootDatabase>, expanded: SyntaxNode) -> Option { +fn expand( + sema: &Semantics<'_, RootDatabase>, + expanded: SyntaxNode, + result_span_map: &mut SpanMap, + mut offset_in_original_node: i32, +) -> SyntaxNode { let children = expanded.descendants().filter_map(ast::Item::cast); let mut replacements = Vec::new(); for child in children { - if let Some(new_node) = expand_macro_recur(sema, &child) { + if let Some(new_node) = expand_macro_recur( + sema, + &child, + result_span_map, + TextSize::new( + (offset_in_original_node + (u32::from(child.syntax().text_range().start()) as i32)) + as u32, + ), + ) { + offset_in_original_node = offset_in_original_node + + (u32::from(new_node.text_range().len()) as i32) + - (u32::from(child.syntax().text_range().len()) as i32); // check if the whole original syntax is replaced if expanded == *child.syntax() { - return Some(new_node); + return new_node; } replacements.push((child, new_node)); } } replacements.into_iter().rev().for_each(|(old, new)| ted::replace(old.syntax(), new)); - Some(expanded) + expanded } -fn format(db: &RootDatabase, kind: SyntaxKind, file_id: FileId, expanded: SyntaxNode) -> String { - let expansion = insert_ws_into(expanded).to_string(); +fn format( + db: &RootDatabase, + kind: SyntaxKind, + file_id: FileId, + expanded: SyntaxNode, + span_map: &SpanMap, + krate: CrateId, +) -> String { + let expansion = prettify_macro_expansion(db, expanded, span_map, krate).to_string(); _format(db, kind, file_id, &expansion).unwrap_or(expansion) } @@ -498,7 +552,7 @@ struct Foo {} "#, expect![[r#" Clone - impl < >$crate::clone::Clone for Foo< >where { + impl < >core::clone::Clone for Foo< >where { fn clone(&self) -> Self { match self { Foo{} @@ -524,7 +578,7 @@ struct Foo {} "#, expect![[r#" Copy - impl < >$crate::marker::Copy for Foo< >where{}"#]], + impl < >core::marker::Copy for Foo< >where{}"#]], ); } @@ -539,7 +593,7 @@ struct Foo {} "#, expect![[r#" Copy - impl < >$crate::marker::Copy for Foo< >where{}"#]], + impl < >core::marker::Copy for Foo< >where{}"#]], ); check( r#" @@ -550,7 +604,7 @@ struct Foo {} "#, expect![[r#" Clone - impl < >$crate::clone::Clone for Foo< >where { + impl < >core::clone::Clone for Foo< >where { fn clone(&self) -> Self { match self { Foo{} @@ -563,4 +617,44 @@ struct Foo {} }"#]], ); } + + #[test] + fn dollar_crate() { + check( + r#" +//- /a.rs crate:a +pub struct Foo; +#[macro_export] +macro_rules! m { + ( $i:ident ) => { $crate::Foo; $crate::Foo; $i::Foo; }; +} +//- /b.rs crate:b deps:a +pub struct Foo; +#[macro_export] +macro_rules! m { + () => { a::m!($crate); $crate::Foo; $crate::Foo; }; +} +//- /c.rs crate:c deps:b,a +pub struct Foo; +#[macro_export] +macro_rules! m { + () => { b::m!(); $crate::Foo; $crate::Foo; }; +} +fn bar() { + m$0!(); +} +"#, + expect![[r#" +m! +a::Foo; +a::Foo; +b::Foo; +; +b::Foo; +b::Foo; +; +crate::Foo; +crate::Foo;"#]], + ); + } } 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 971cd3ef585c..8836166d969d 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs @@ -2750,4 +2750,36 @@ fn foo() { "#, ); } + + #[test] + fn issue_18138() { + check( + r#" +mod foo { + macro_rules! x { + () => { + pub struct Foo; + // ^^^ + }; + } + pub(crate) use x as m; +} + +mod bar { + use crate::m; + + m!(); + // ^^^^^ + + fn qux() { + Foo$0; + } +} + +mod m {} + +use foo::m; +"#, + ); + } } 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 5348e855be4b..4c8e3fc3040c 100644 --- a/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs +++ b/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs @@ -2004,6 +2004,36 @@ fn main() { { return; } +"#, + ) + } + + #[test] + fn asm() { + check( + r#" +//- minicore: asm +#[inline] +pub unsafe fn bootstrap() -> ! { + builtin#asm( + "blabla", + "mrs {tmp}, CONTROL", + // ^^^ read + "blabla", + "bics {tmp}, {spsel}", + // ^^^ read + "blabla", + "msr CONTROL, {tmp}", + // ^^^ read + "blabla", + tmp$0 = inout(reg) 0, + // ^^^ + aaa = in(reg) 2, + aaa = in(reg) msp, + aaa = in(reg) rv, + options(noreturn, nomem, nostack), + ); +} "#, ) } diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs index 3e41b42be44b..83adf6548a89 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs @@ -3,8 +3,9 @@ use std::{mem, ops::Not}; use either::Either; use hir::{ - Adt, AsAssocItem, AsExternAssocItem, CaptureKind, HasCrate, HasSource, HirDisplay, Layout, - LayoutError, Name, Semantics, Trait, Type, TypeInfo, + db::ExpandDatabase, Adt, AsAssocItem, AsExternAssocItem, CaptureKind, HasCrate, HasSource, + HirDisplay, Layout, LayoutError, MethodViolationCode, Name, ObjectSafetyViolation, Semantics, + Trait, Type, TypeInfo, }; use ide_db::{ base_db::SourceDatabase, @@ -12,7 +13,7 @@ use ide_db::{ documentation::HasDocs, famous_defs::FamousDefs, generated::lints::{CLIPPY_LINTS, DEFAULT_LINTS, FEATURES}, - syntax_helpers::insert_whitespace_into_node, + syntax_helpers::prettify_macro_expansion, RootDatabase, }; use itertools::Itertools; @@ -328,7 +329,7 @@ pub(super) fn try_for_lint(attr: &ast::Attr, token: &SyntaxToken) -> Option (false, FEATURES), - "allow" | "deny" | "forbid" | "warn" => { + "allow" | "deny" | "expect" | "forbid" | "warn" => { let is_clippy = algo::non_trivia_sibling(token.clone().into(), Direction::Prev) .filter(|t| t.kind() == T![:]) .and_then(|t| algo::non_trivia_sibling(t, Direction::Prev)) @@ -475,8 +476,9 @@ pub(super) fn definition( Err(_) => { let source = it.source(db)?; let mut body = source.value.body()?.syntax().clone(); - if source.file_id.is_macro() { - body = insert_whitespace_into_node::insert_ws_into(body); + if let Some(macro_file) = source.file_id.macro_file() { + let span_map = db.expansion_span_map(macro_file); + body = prettify_macro_expansion(db, body, &span_map, it.krate(db).into()); } Some(body.to_string()) } @@ -485,8 +487,9 @@ pub(super) fn definition( Definition::Static(it) => { let source = it.source(db)?; let mut body = source.value.body()?.syntax().clone(); - if source.file_id.is_macro() { - body = insert_whitespace_into_node::insert_ws_into(body); + if let Some(macro_file) = source.file_id.macro_file() { + let span_map = db.expansion_span_map(macro_file); + body = prettify_macro_expansion(db, body, &span_map, it.krate(db).into()); } Some(body.to_string()) } @@ -526,6 +529,14 @@ pub(super) fn definition( _ => None, }; + let object_safety_info = if let Definition::Trait(it) = def { + let mut object_safety_info = String::new(); + render_object_safety(db, &mut object_safety_info, it.object_safety(db)); + Some(object_safety_info) + } else { + None + }; + let mut desc = String::new(); if let Some(notable_traits) = render_notable_trait_comment(db, notable_traits, edition) { desc.push_str(¬able_traits); @@ -535,6 +546,10 @@ pub(super) fn definition( desc.push_str(&layout_info); desc.push('\n'); } + if let Some(object_safety_info) = object_safety_info { + desc.push_str(&object_safety_info); + desc.push('\n'); + } desc.push_str(&label); if let Some(value) = value { desc.push_str(" = "); @@ -964,3 +979,62 @@ fn keyword_hints( _ => KeywordHint::new(token.text().to_owned(), format!("{}_keyword", token.text())), } } + +fn render_object_safety( + db: &RootDatabase, + buf: &mut String, + safety: Option, +) { + let Some(osv) = safety else { + buf.push_str("// Object Safety: Yes"); + return; + }; + buf.push_str("// Object Safety: No\n// - Reason: "); + match osv { + ObjectSafetyViolation::SizedSelf => { + buf.push_str("has a `Self: Sized` bound"); + } + ObjectSafetyViolation::SelfReferential => { + buf.push_str("has a bound that references `Self`"); + } + ObjectSafetyViolation::Method(func, mvc) => { + let name = hir::Function::from(func).name(db); + format_to!( + buf, + "has a method `{}` that is non dispatchable because of:\n// - ", + name.as_str() + ); + let desc = match mvc { + MethodViolationCode::StaticMethod => "missing a receiver", + MethodViolationCode::ReferencesSelfInput => "a parameter references `Self`", + MethodViolationCode::ReferencesSelfOutput => "the return type references `Self`", + MethodViolationCode::ReferencesImplTraitInTrait => { + "the return type contains `impl Trait`" + } + MethodViolationCode::AsyncFn => "being async", + MethodViolationCode::WhereClauseReferencesSelf => { + "a where clause references `Self`" + } + MethodViolationCode::Generic => "a non-lifetime generic parameter", + MethodViolationCode::UndispatchableReceiver => "a non-dispatchable receiver type", + }; + buf.push_str(desc); + } + ObjectSafetyViolation::AssocConst(const_) => { + let name = hir::Const::from(const_).name(db); + if let Some(name) = name { + format_to!(buf, "has an associated constant `{}`", name.as_str()); + } else { + buf.push_str("has an associated constant"); + } + } + ObjectSafetyViolation::GAT(alias) => { + let name = hir::TypeAlias::from(alias).name(db); + format_to!(buf, "has a generic associated type `{}`", name.as_str()); + } + ObjectSafetyViolation::HasNonSafeSuperTrait(super_trait) => { + let name = hir::Trait::from(super_trait).name(db); + format_to!(buf, "has a object unsafe supertrait `{}`", name.as_str()); + } + } +} 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 9585bdbe4c54..cca62d2181f4 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs @@ -182,13 +182,26 @@ fn check_hover_no_markdown(ra_fixture: &str, expect: Expect) { fn check_actions(ra_fixture: &str, expect: Expect) { let (analysis, file_id, position) = fixture::range_or_position(ra_fixture); - let hover = analysis + let mut hover = analysis .hover( &HoverConfig { links_in_hover: true, ..HOVER_BASE_CONFIG }, FileRange { file_id, range: position.range_or_empty() }, ) .unwrap() .unwrap(); + // stub out ranges into minicore as they can change every now and then + hover.info.actions.iter_mut().for_each(|action| match action { + super::HoverAction::GoToType(act) => act.iter_mut().for_each(|data| { + if data.nav.file_id == file_id { + return; + } + data.nav.full_range = TextRange::empty(span::TextSize::new(!0)); + if let Some(range) = &mut data.nav.focus_range { + *range = TextRange::empty(span::TextSize::new(!0)); + } + }), + _ => (), + }); expect.assert_debug_eq(&hover.info.actions) } @@ -200,10 +213,23 @@ fn check_hover_range(ra_fixture: &str, expect: Expect) { fn check_hover_range_actions(ra_fixture: &str, expect: Expect) { let (analysis, range) = fixture::range(ra_fixture); - let hover = analysis + let mut hover = analysis .hover(&HoverConfig { links_in_hover: true, ..HOVER_BASE_CONFIG }, range) .unwrap() .unwrap(); + // stub out ranges into minicore as they can change every now and then + hover.info.actions.iter_mut().for_each(|action| match action { + super::HoverAction::GoToType(act) => act.iter_mut().for_each(|data| { + if data.nav.file_id == range.file_id { + return; + } + data.nav.full_range = TextRange::empty(span::TextSize::new(!0)); + if let Some(range) = &mut data.nav.focus_range { + *range = TextRange::empty(span::TextSize::new(!0)); + } + }), + _ => (), + }); expect.assert_debug_eq(&hover.info.actions); } @@ -483,8 +509,8 @@ fn main() { file_id: FileId( 1, ), - full_range: 632..867, - focus_range: 693..699, + full_range: 4294967295..4294967295, + focus_range: 4294967295..4294967295, name: "FnOnce", kind: Trait, container_name: "function", @@ -1470,6 +1496,24 @@ const foo$0: u32 = { ); } +#[test] +fn hover_unsigned_max_const() { + check( + r#"const $0A: u128 = -1_i128 as u128;"#, + expect![[r#" + *A* + + ```rust + test + ``` + + ```rust + const A: u128 = 340282366920938463463374607431768211455 (0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) + ``` + "#]], + ); +} + #[test] fn hover_eval_complex_constants() { check( @@ -3104,26 +3148,26 @@ struct S{ f1: u32 } fn main() { let s$0t = S{ f1:0 }; } "#, expect![[r#" - [ - GoToType( - [ - HoverGotoTypeData { - mod_path: "test::S", - nav: NavigationTarget { - file_id: FileId( - 0, - ), - full_range: 0..19, - focus_range: 7..8, - name: "S", - kind: Struct, - description: "struct S", - }, + [ + GoToType( + [ + HoverGotoTypeData { + mod_path: "test::S", + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 0..19, + focus_range: 7..8, + name: "S", + kind: Struct, + description: "struct S", }, - ], - ), - ] - "#]], + }, + ], + ), + ] + "#]], ); } @@ -3616,8 +3660,8 @@ pub mod future { file_id: FileId( 1, ), - full_range: 21..69, - focus_range: 60..66, + full_range: 4294967295..4294967295, + focus_range: 4294967295..4294967295, name: "Future", kind: Trait, container_name: "future", @@ -5442,7 +5486,7 @@ const FOO$0: Option<&i32> = Some(2).as_ref(); fn hover_const_eval_dyn_trait() { check( r#" -//- minicore: fmt, coerce_unsized, builtin_impls +//- minicore: fmt, coerce_unsized, builtin_impls, dispatch_from_dyn use core::fmt::Debug; const FOO$0: &dyn Debug = &2i32; @@ -6292,7 +6336,19 @@ fn hover_lint() { arithmetic operation overflows "#]], - ) + ); + check( + r#"#![expect(arithmetic_overflow$0)]"#, + expect![[r#" + *arithmetic_overflow* + ``` + arithmetic_overflow + ``` + ___ + + arithmetic operation overflows + "#]], + ); } #[test] @@ -6308,7 +6364,19 @@ fn hover_clippy_lint() { Checks for `foo = bar; bar = foo` sequences. "#]], - ) + ); + check( + r#"#![expect(clippy::almost_swapped$0)]"#, + expect![[r#" + *almost_swapped* + ``` + clippy::almost_swapped + ``` + ___ + + Checks for `foo = bar; bar = foo` sequences. + "#]], + ); } #[test] @@ -7107,6 +7175,7 @@ impl T$0 for () {} ``` ```rust + // Object Safety: Yes trait T {} ``` "#]], @@ -7126,6 +7195,7 @@ impl T$0 for () {} ``` ```rust + // Object Safety: Yes trait T {} ``` "#]], @@ -7149,6 +7219,9 @@ impl T$0 for () {} ``` ```rust + // Object Safety: No + // - Reason: has a method `func` that is non dispatchable because of: + // - missing a receiver trait T { /* … */ } ``` "#]], @@ -7172,6 +7245,9 @@ impl T$0 for () {} ``` ```rust + // Object Safety: No + // - Reason: has a method `func` that is non dispatchable because of: + // - missing a receiver trait T { fn func(); const FLAG: i32; @@ -7199,6 +7275,9 @@ impl T$0 for () {} ``` ```rust + // Object Safety: No + // - Reason: has a method `func` that is non dispatchable because of: + // - missing a receiver trait T { fn func(); const FLAG: i32; @@ -7226,6 +7305,9 @@ impl T$0 for () {} ``` ```rust + // Object Safety: No + // - Reason: has a method `func` that is non dispatchable because of: + // - missing a receiver trait T { fn func(); const FLAG: i32; @@ -8465,8 +8547,8 @@ impl Iterator for S { file_id: FileId( 1, ), - full_range: 7800..8042, - focus_range: 7865..7871, + full_range: 4294967295..4294967295, + focus_range: 4294967295..4294967295, name: "Future", kind: Trait, container_name: "future", @@ -8479,8 +8561,8 @@ impl Iterator for S { file_id: FileId( 1, ), - full_range: 8672..9171, - focus_range: 8749..8757, + full_range: 4294967295..4294967295, + focus_range: 4294967295..4294967295, name: "Iterator", kind: Trait, container_name: "iterator", @@ -8702,3 +8784,181 @@ fn foo() { "#]], ); } + +#[test] +fn test_hover_function_with_pat_param() { + check( + r#"fn test_1$0((start_range, end_range): (u32, u32), a: i32) {}"#, + expect![[r#" + *test_1* + + ```rust + test + ``` + + ```rust + fn test_1((start_range, end_range): (u32, u32), a: i32) + ``` + "#]], + ); + + // Test case with tuple pattern and mutable parameters + check( + r#"fn test_2$0((mut x, y): (i32, i32)) {}"#, + expect![[r#" + *test_2* + + ```rust + test + ``` + + ```rust + fn test_2((mut x, y): (i32, i32)) + ``` + "#]], + ); + + // Test case with a pattern in a reference type + check( + r#"fn test_3$0(&(a, b): &(i32, i32)) {}"#, + expect![[r#" + *test_3* + + ```rust + test + ``` + + ```rust + fn test_3(&(a, b): &(i32, i32)) + ``` + "#]], + ); + + // Test case with complex pattern (struct destructuring) + check( + r#"struct Point { x: i32, y: i32 } fn test_4$0(Point { x, y }: Point) {}"#, + expect![[r#" + *test_4* + + ```rust + test + ``` + + ```rust + fn test_4(Point { x, y }: Point) + ``` + "#]], + ); + + // Test case with a nested pattern + check( + r#"fn test_5$0(((a, b), c): ((i32, i32), i32)) {}"#, + expect![[r#" + *test_5* + + ```rust + test + ``` + + ```rust + fn test_5(((a, b), c): ((i32, i32), i32)) + ``` + "#]], + ); + + // Test case with an unused variable in the pattern + check( + r#"fn test_6$0((_, y): (i32, i64)) {}"#, + expect![[r#" + *test_6* + + ```rust + test + ``` + + ```rust + fn test_6((_, y): (i32, i64)) + ``` + "#]], + ); + + // Test case with a complex pattern involving both tuple and struct + check( + r#"struct Foo { a: i32, b: i32 } fn test_7$0((x, Foo { a, b }): (i32, Foo)) {}"#, + expect![[r#" + *test_7* + + ```rust + test + ``` + + ```rust + fn test_7((x, Foo { a, b }): (i32, Foo)) + ``` + "#]], + ); + + // Test case with Enum and Or pattern + check( + r#"enum MyEnum { A(i32), B(i32) } fn test_8$0((MyEnum::A(x) | MyEnum::B(x)): MyEnum) {}"#, + expect![[r#" + *test_8* + + ```rust + test + ``` + + ```rust + fn test_8((MyEnum::A(x) | MyEnum::B(x)): MyEnum) + ``` + "#]], + ); + + // Test case with a pattern as a function parameter + check( + r#"struct Foo { a: i32, b: i32 } fn test_9$0(Foo { a, b }: Foo) {}"#, + expect![[r#" + *test_9* + + ```rust + test + ``` + + ```rust + fn test_9(Foo { a, b }: Foo) + ``` + "#]], + ); + + // Test case with a pattern as a function parameter with a different name + check( + r#"struct Foo { a: i32, b: i32 } fn test_10$0(Foo { a, b: b1 }: Foo) {}"#, + expect![[r#" + *test_10* + + ```rust + test + ``` + + ```rust + fn test_10(Foo { a, b: b1 }: Foo) + ``` + "#]], + ); + + // Test case with a pattern as a function parameter with annotations + check( + r#"struct Foo { a: i32, b: i32 } fn test_10$0(Foo { a, b: mut b }: Foo) {}"#, + expect![[r#" + *test_10* + + ```rust + test + ``` + + ```rust + fn test_10(Foo { a, b: mut b }: Foo) + ``` + "#]], + ); +} 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 6a5d5e26a4f0..97e712356b54 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs @@ -14,8 +14,8 @@ use smallvec::{smallvec, SmallVec}; use span::{Edition, EditionedFileId}; use stdx::never; use syntax::{ - ast::{self, AstNode}, - match_ast, NodeOrToken, SyntaxNode, TextRange, TextSize, + ast::{self, AstNode, HasGenericParams}, + format_smolstr, match_ast, SmolStr, SyntaxNode, TextRange, TextSize, WalkEvent, }; use text_edit::TextEdit; @@ -29,13 +29,230 @@ mod closing_brace; mod closure_captures; mod closure_ret; mod discriminant; -mod fn_lifetime_fn; mod generic_param; mod implicit_drop; mod implicit_static; +mod lifetime; mod param_name; mod range_exclusive; +// Feature: Inlay Hints +// +// rust-analyzer shows additional information inline with the source code. +// Editors usually render this using read-only virtual text snippets interspersed with code. +// +// rust-analyzer by default shows hints for +// +// * types of local variables +// * names of function arguments +// * names of const generic parameters +// * types of chained expressions +// +// Optionally, one can enable additional hints for +// +// * return types of closure expressions +// * elided lifetimes +// * compiler inserted reborrows +// * names of generic type and lifetime parameters +// +// Note: inlay hints for function argument names are heuristically omitted to reduce noise and will not appear if +// any of the +// link:https://github.com/rust-lang/rust-analyzer/blob/6b8b8ff4c56118ddee6c531cde06add1aad4a6af/crates/ide/src/inlay_hints/param_name.rs#L92-L99[following criteria] +// are met: +// +// * the parameter name is a suffix of the function's name +// * the argument is a qualified constructing or call expression where the qualifier is an ADT +// * exact argument<->parameter match(ignoring leading underscore) or parameter is a prefix/suffix +// of argument with _ splitting it off +// * the parameter name starts with `ra_fixture` +// * the parameter name is a +// link:https://github.com/rust-lang/rust-analyzer/blob/6b8b8ff4c56118ddee6c531cde06add1aad4a6af/crates/ide/src/inlay_hints/param_name.rs#L200[well known name] +// in a unary function +// * the parameter name is a +// link:https://github.com/rust-lang/rust-analyzer/blob/6b8b8ff4c56118ddee6c531cde06add1aad4a6af/crates/ide/src/inlay_hints/param_name.rs#L201[single character] +// in a unary function +// +// image::https://user-images.githubusercontent.com/48062697/113020660-b5f98b80-917a-11eb-8d70-3be3fd558cdd.png[] +pub(crate) fn inlay_hints( + db: &RootDatabase, + file_id: FileId, + range_limit: Option, + config: &InlayHintsConfig, +) -> 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(); + + let mut acc = Vec::new(); + + let Some(scope) = sema.scope(file) else { + return acc; + }; + let famous_defs = FamousDefs(&sema, scope.krate()); + + let ctx = &mut InlayHintCtx::default(); + let mut hints = |event| { + if let Some(node) = handle_event(ctx, event) { + hints(&mut acc, ctx, &famous_defs, config, file_id, node); + } + }; + let mut preorder = file.preorder(); + while let Some(event) = preorder.next() { + // FIXME: This can miss some hints that require the parent of the range to calculate + if matches!((&event, range_limit), (WalkEvent::Enter(node), Some(range)) if range.intersect(node.text_range()).is_none()) + { + preorder.skip_subtree(); + continue; + } + hints(event); + } + acc +} + +#[derive(Default)] +struct InlayHintCtx { + lifetime_stacks: Vec>, +} + +pub(crate) fn inlay_hints_resolve( + db: &RootDatabase, + file_id: FileId, + resolve_range: TextRange, + hash: u64, + config: &InlayHintsConfig, + hasher: impl Fn(&InlayHint) -> u64, +) -> 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(); + + let scope = sema.scope(file)?; + let famous_defs = FamousDefs(&sema, scope.krate()); + let mut acc = Vec::new(); + + let ctx = &mut InlayHintCtx::default(); + let mut hints = |event| { + if let Some(node) = handle_event(ctx, event) { + hints(&mut acc, ctx, &famous_defs, config, file_id, node); + } + }; + + let mut preorder = file.preorder(); + while let Some(event) = preorder.next() { + // FIXME: This can miss some hints that require the parent of the range to calculate + if matches!(&event, WalkEvent::Enter(node) if resolve_range.intersect(node.text_range()).is_none()) + { + preorder.skip_subtree(); + continue; + } + hints(event); + } + acc.into_iter().find(|hint| hasher(hint) == hash) +} + +fn handle_event(ctx: &mut InlayHintCtx, node: WalkEvent) -> Option { + match node { + WalkEvent::Enter(node) => { + if let Some(node) = ast::AnyHasGenericParams::cast(node.clone()) { + let params = node + .generic_param_list() + .map(|it| { + it.lifetime_params() + .filter_map(|it| { + it.lifetime().map(|it| format_smolstr!("{}", &it.text()[1..])) + }) + .collect() + }) + .unwrap_or_default(); + ctx.lifetime_stacks.push(params); + } + Some(node) + } + WalkEvent::Leave(n) => { + if ast::AnyHasGenericParams::can_cast(n.kind()) { + ctx.lifetime_stacks.pop(); + } + None + } + } +} + +// FIXME: At some point when our hir infra is fleshed out enough we should flip this and traverse the +// HIR instead of the syntax tree. +fn hints( + hints: &mut Vec, + ctx: &mut InlayHintCtx, + famous_defs @ FamousDefs(sema, _): &FamousDefs<'_, '_>, + config: &InlayHintsConfig, + file_id: EditionedFileId, + node: SyntaxNode, +) { + closing_brace::hints(hints, sema, config, file_id, node.clone()); + if let Some(any_has_generic_args) = ast::AnyHasGenericArgs::cast(node.clone()) { + generic_param::hints(hints, sema, config, any_has_generic_args); + } + + match_ast! { + match node { + ast::Expr(expr) => { + chaining::hints(hints, famous_defs, config, file_id, &expr); + adjustment::hints(hints, famous_defs, config, file_id, &expr); + match expr { + ast::Expr::CallExpr(it) => param_name::hints(hints, famous_defs, config, file_id, ast::Expr::from(it)), + ast::Expr::MethodCallExpr(it) => { + param_name::hints(hints, famous_defs, config, file_id, ast::Expr::from(it)) + } + ast::Expr::ClosureExpr(it) => { + closure_captures::hints(hints, famous_defs, config, file_id, it.clone()); + closure_ret::hints(hints, famous_defs, config, file_id, it) + }, + ast::Expr::RangeExpr(it) => range_exclusive::hints(hints, famous_defs, config, file_id, it), + _ => Some(()), + } + }, + ast::Pat(it) => { + binding_mode::hints(hints, famous_defs, config, file_id, &it); + match it { + ast::Pat::IdentPat(it) => { + bind_pat::hints(hints, famous_defs, config, file_id, &it); + } + ast::Pat::RangePat(it) => { + range_exclusive::hints(hints, famous_defs, config, file_id, it); + } + _ => {} + } + Some(()) + }, + ast::Item(it) => match it { + ast::Item::Fn(it) => { + implicit_drop::hints(hints, famous_defs, config, file_id, &it); + lifetime::fn_hints(hints, ctx, famous_defs, config, file_id, it) + }, + // static type elisions + ast::Item::Static(it) => implicit_static::hints(hints, famous_defs, config, file_id, Either::Left(it)), + ast::Item::Const(it) => implicit_static::hints(hints, famous_defs, config, file_id, Either::Right(it)), + ast::Item::Enum(it) => discriminant::enum_hints(hints, famous_defs, config, file_id, it), + _ => None, + }, + // FIXME: trait object type elisions + ast::Type(ty) => match ty { + ast::Type::FnPtrType(ptr) => lifetime::fn_ptr_hints(hints, ctx, famous_defs, config, file_id, ptr), + ast::Type::PathType(path) => lifetime::fn_path_hints(hints, ctx, famous_defs, config, file_id, path), + _ => Some(()), + }, + _ => Some(()), + } + }; +} + #[derive(Clone, Debug, PartialEq, Eq)] pub struct InlayHintsConfig { pub render_colons: bool, @@ -162,6 +379,9 @@ pub struct InlayHint { pub label: InlayHintLabel, /// Text edit to apply when "accepting" this inlay hint. pub text_edit: Option, + /// Range to recompute inlay hints when trying to resolve for this hint. If this is none, the + /// hint does not support resolving. + pub resolve_parent: Option, } impl std::hash::Hash for InlayHint { @@ -186,6 +406,7 @@ impl InlayHint { position: InlayHintPosition::After, pad_left: false, pad_right: false, + resolve_parent: None, } } @@ -198,11 +419,12 @@ impl InlayHint { position: InlayHintPosition::Before, pad_left: false, pad_right: false, + resolve_parent: None, } } - pub fn needs_resolve(&self) -> bool { - self.text_edit.is_some() || self.label.needs_resolve() + pub fn needs_resolve(&self) -> Option { + self.resolve_parent.filter(|_| self.text_edit.is_some() || self.label.needs_resolve()) } } @@ -355,11 +577,14 @@ impl HirWrite for InlayHintLabelBuilder<'_> { impl InlayHintLabelBuilder<'_> { fn make_new_part(&mut self) { - self.result.parts.push(InlayHintLabelPart { - text: take(&mut self.last_part), - linked_location: self.location.take(), - tooltip: None, - }); + let text = take(&mut self.last_part); + if !text.is_empty() { + self.result.parts.push(InlayHintLabelPart { + text, + linked_location: self.location.take(), + tooltip: None, + }); + } } fn finish(mut self) -> InlayHintLabel { @@ -434,190 +659,6 @@ fn label_of_ty( Some(r) } -fn ty_to_text_edit( - sema: &Semantics<'_, RootDatabase>, - node_for_hint: &SyntaxNode, - ty: &hir::Type, - offset_to_insert: TextSize, - prefix: String, -) -> Option { - let scope = sema.scope(node_for_hint)?; - // FIXME: Limit the length and bail out on excess somehow? - let rendered = ty.display_source_code(scope.db, scope.module().into(), false).ok()?; - - let mut builder = TextEdit::builder(); - builder.insert(offset_to_insert, prefix); - builder.insert(offset_to_insert, rendered); - Some(builder.finish()) -} - -// Feature: Inlay Hints -// -// rust-analyzer shows additional information inline with the source code. -// Editors usually render this using read-only virtual text snippets interspersed with code. -// -// rust-analyzer by default shows hints for -// -// * types of local variables -// * names of function arguments -// * names of const generic parameters -// * types of chained expressions -// -// Optionally, one can enable additional hints for -// -// * return types of closure expressions -// * elided lifetimes -// * compiler inserted reborrows -// * names of generic type and lifetime parameters -// -// Note: inlay hints for function argument names are heuristically omitted to reduce noise and will not appear if -// any of the -// link:https://github.com/rust-lang/rust-analyzer/blob/6b8b8ff4c56118ddee6c531cde06add1aad4a6af/crates/ide/src/inlay_hints/param_name.rs#L92-L99[following criteria] -// are met: -// -// * the parameter name is a suffix of the function's name -// * the argument is a qualified constructing or call expression where the qualifier is an ADT -// * exact argument<->parameter match(ignoring leading underscore) or parameter is a prefix/suffix -// of argument with _ splitting it off -// * the parameter name starts with `ra_fixture` -// * the parameter name is a -// link:https://github.com/rust-lang/rust-analyzer/blob/6b8b8ff4c56118ddee6c531cde06add1aad4a6af/crates/ide/src/inlay_hints/param_name.rs#L200[well known name] -// in a unary function -// * the parameter name is a -// link:https://github.com/rust-lang/rust-analyzer/blob/6b8b8ff4c56118ddee6c531cde06add1aad4a6af/crates/ide/src/inlay_hints/param_name.rs#L201[single character] -// in a unary function -// -// image::https://user-images.githubusercontent.com/48062697/113020660-b5f98b80-917a-11eb-8d70-3be3fd558cdd.png[] -pub(crate) fn inlay_hints( - db: &RootDatabase, - file_id: FileId, - range_limit: Option, - config: &InlayHintsConfig, -) -> 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(); - - let mut acc = Vec::new(); - - if let Some(scope) = sema.scope(file) { - let famous_defs = FamousDefs(&sema, scope.krate()); - - let hints = |node| hints(&mut acc, &famous_defs, config, file_id, node); - match range_limit { - Some(range) => match file.covering_element(range) { - NodeOrToken::Token(_) => return acc, - NodeOrToken::Node(n) => n - .descendants() - .filter(|descendant| range.intersect(descendant.text_range()).is_some()) - .for_each(hints), - }, - None => file.descendants().for_each(hints), - }; - } - - acc -} - -pub(crate) fn inlay_hints_resolve( - db: &RootDatabase, - file_id: FileId, - position: TextSize, - hash: u64, - config: &InlayHintsConfig, - hasher: impl Fn(&InlayHint) -> u64, -) -> 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(); - - let scope = sema.scope(file)?; - let famous_defs = FamousDefs(&sema, scope.krate()); - let mut acc = Vec::new(); - - let hints = |node| hints(&mut acc, &famous_defs, config, file_id, node); - let token = file.token_at_offset(position).left_biased()?; - if let Some(parent_block) = token.parent_ancestors().find_map(ast::BlockExpr::cast) { - parent_block.syntax().descendants().for_each(hints) - } else if let Some(parent_item) = token.parent_ancestors().find_map(ast::Item::cast) { - parent_item.syntax().descendants().for_each(hints) - } else { - return None; - } - - acc.into_iter().find(|hint| hasher(hint) == hash) -} - -fn hints( - hints: &mut Vec, - famous_defs @ FamousDefs(sema, _): &FamousDefs<'_, '_>, - config: &InlayHintsConfig, - file_id: EditionedFileId, - node: SyntaxNode, -) { - closing_brace::hints(hints, sema, config, file_id, node.clone()); - if let Some(any_has_generic_args) = ast::AnyHasGenericArgs::cast(node.clone()) { - generic_param::hints(hints, sema, config, any_has_generic_args); - } - match_ast! { - match node { - ast::Expr(expr) => { - chaining::hints(hints, famous_defs, config, file_id, &expr); - adjustment::hints(hints, sema, config, file_id, &expr); - match expr { - ast::Expr::CallExpr(it) => param_name::hints(hints, sema, config, ast::Expr::from(it)), - ast::Expr::MethodCallExpr(it) => { - param_name::hints(hints, sema, config, ast::Expr::from(it)) - } - ast::Expr::ClosureExpr(it) => { - closure_captures::hints(hints, famous_defs, config, file_id, it.clone()); - closure_ret::hints(hints, famous_defs, config, file_id, it) - }, - ast::Expr::RangeExpr(it) => range_exclusive::hints(hints, config, it), - _ => None, - } - }, - ast::Pat(it) => { - binding_mode::hints(hints, sema, config, &it); - match it { - ast::Pat::IdentPat(it) => { - bind_pat::hints(hints, famous_defs, config, file_id, &it); - } - ast::Pat::RangePat(it) => { - range_exclusive::hints(hints, config, it); - } - _ => {} - } - Some(()) - }, - ast::Item(it) => match it { - // FIXME: record impl lifetimes so they aren't being reused in assoc item lifetime inlay hints - ast::Item::Impl(_) => None, - ast::Item::Fn(it) => { - implicit_drop::hints(hints, sema, config, file_id, &it); - fn_lifetime_fn::hints(hints, config, it) - }, - // static type elisions - ast::Item::Static(it) => implicit_static::hints(hints, config, Either::Left(it)), - ast::Item::Const(it) => implicit_static::hints(hints, config, Either::Right(it)), - ast::Item::Enum(it) => discriminant::enum_hints(hints, famous_defs, config, file_id, it), - _ => None, - }, - // FIXME: fn-ptr type, dyn fn type, and trait object type elisions - ast::Type(_) => None, - _ => None, - } - }; -} - /// Checks if the type is an Iterator from std::iter and returns the iterator trait and the item type of the concrete iterator. fn hint_iterator( sema: &Semantics<'_, RootDatabase>, @@ -653,6 +694,23 @@ fn hint_iterator( None } +fn ty_to_text_edit( + sema: &Semantics<'_, RootDatabase>, + node_for_hint: &SyntaxNode, + ty: &hir::Type, + offset_to_insert: TextSize, + prefix: String, +) -> Option { + let scope = sema.scope(node_for_hint)?; + // FIXME: Limit the length and bail out on excess somehow? + let rendered = ty.display_source_code(scope.db, scope.module().into(), false).ok()?; + + let mut builder = TextEdit::builder(); + builder.insert(offset_to_insert, prefix); + builder.insert(offset_to_insert, rendered); + Some(builder.finish()) +} + fn closure_has_block_body(closure: &ast::ClosureExpr) -> bool { matches!(closure.body(), Some(ast::Expr::BlockExpr(_))) } diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs index 756198d0c011..c37c469dff4e 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs @@ -6,9 +6,8 @@ use either::Either; use hir::{ Adjust, Adjustment, AutoBorrow, HirDisplay, Mutability, OverloadedDeref, PointerCast, Safety, - Semantics, }; -use ide_db::RootDatabase; +use ide_db::famous_defs::FamousDefs; use span::EditionedFileId; use stdx::never; @@ -24,7 +23,7 @@ use crate::{ pub(super) fn hints( acc: &mut Vec, - sema: &Semantics<'_, RootDatabase>, + FamousDefs(sema, _): &FamousDefs<'_, '_>, config: &InlayHintsConfig, file_id: EditionedFileId, expr: &ast::Expr, @@ -156,6 +155,7 @@ pub(super) fn hints( kind: InlayKind::Adjustment, label, text_edit: None, + resolve_parent: Some(expr.syntax().text_range()), }); } if !postfix && needs_inner_parens { @@ -288,7 +288,7 @@ mod tests { check_with_config( InlayHintsConfig { adjustment_hints: AdjustmentHints::Always, ..DISABLED_CONFIG }, r#" -//- minicore: coerce_unsized, fn, eq, index +//- minicore: coerce_unsized, fn, eq, index, dispatch_from_dyn fn main() { let _: u32 = loop {}; //^^^^^^^ @@ -313,6 +313,7 @@ fn main() { //^^^^^^^^^^^^ //^^^^^^^^^^^^( //^^^^^^^^^^^^) + //^^^^ let _: fn() = || {}; //^^^^^ let _: unsafe fn() = || {}; @@ -321,6 +322,8 @@ fn main() { //^^^^^^^^^^^^^^^^^^^^^ //^^^^^^^^^^^^^^^^^^^^^( //^^^^^^^^^^^^^^^^^^^^^) + //^^^^^^^^^&raw mut $ + //^^^^^^^^^* let _: &mut [_] = &mut [0; 0]; //^^^^^^^^^^^ //^^^^^^^^^^^&mut $ @@ -428,7 +431,7 @@ impl core::ops::IndexMut for Struct {} ..DISABLED_CONFIG }, r#" -//- minicore: coerce_unsized, fn, eq, index +//- minicore: coerce_unsized, fn, eq, index, dispatch_from_dyn fn main() { Struct.consume(); 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 82b0a6ffcf13..7a808fb4a929 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 @@ -110,6 +110,7 @@ pub(super) fn hints( position: InlayHintPosition::After, pad_left: !render_colons, pad_right: false, + resolve_parent: Some(pat.syntax().text_range()), }); Some(()) diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/binding_mode.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/binding_mode.rs index f27390ee898b..d1c0677863db 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/binding_mode.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/binding_mode.rs @@ -2,17 +2,19 @@ //! ```no_run //! let /* & */ (/* ref */ x,) = &(0,); //! ``` -use hir::{Mutability, Semantics}; -use ide_db::RootDatabase; +use hir::Mutability; +use ide_db::famous_defs::FamousDefs; +use span::EditionedFileId; use syntax::ast::{self, AstNode}; use crate::{InlayHint, InlayHintPosition, InlayHintsConfig, InlayKind}; pub(super) fn hints( acc: &mut Vec, - sema: &Semantics<'_, RootDatabase>, + FamousDefs(sema, _): &FamousDefs<'_, '_>, config: &InlayHintsConfig, + _file_id: EditionedFileId, pat: &ast::Pat, ) -> Option<()> { if !config.binding_mode_hints { @@ -57,6 +59,7 @@ pub(super) fn hints( position: InlayHintPosition::Before, pad_left: false, pad_right: mut_reference, + resolve_parent: Some(pat.syntax().text_range()), }); }); match pat { @@ -75,6 +78,7 @@ pub(super) fn hints( position: InlayHintPosition::Before, pad_left: false, pad_right: true, + resolve_parent: Some(pat.syntax().text_range()), }); } ast::Pat::OrPat(pat) if !pattern_adjustments.is_empty() && outer_paren_pat.is_none() => { 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 35f4d46e187c..58d8f97a8ced 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 @@ -67,6 +67,7 @@ pub(super) fn hints( position: InlayHintPosition::After, pad_left: true, pad_right: false, + resolve_parent: Some(expr.syntax().text_range()), }); } } @@ -139,7 +140,6 @@ fn main() { ( 147..172, [ - "", InlayHintLabelPart { text: "B", linked_location: Some( @@ -152,13 +152,11 @@ fn main() { ), tooltip: "", }, - "", ], ), ( 147..154, [ - "", InlayHintLabelPart { text: "A", linked_location: Some( @@ -171,7 +169,6 @@ fn main() { ), tooltip: "", }, - "", ], ), ] @@ -222,7 +219,6 @@ fn main() { ( 143..190, [ - "", InlayHintLabelPart { text: "C", linked_location: Some( @@ -235,13 +231,11 @@ fn main() { ), tooltip: "", }, - "", ], ), ( 143..179, [ - "", InlayHintLabelPart { text: "B", linked_location: Some( @@ -254,7 +248,6 @@ fn main() { ), tooltip: "", }, - "", ], ), ] @@ -289,7 +282,6 @@ fn main() { ( 143..190, [ - "", InlayHintLabelPart { text: "C", linked_location: Some( @@ -302,13 +294,11 @@ fn main() { ), tooltip: "", }, - "", ], ), ( 143..179, [ - "", InlayHintLabelPart { text: "B", linked_location: Some( @@ -321,7 +311,6 @@ fn main() { ), tooltip: "", }, - "", ], ), ] @@ -357,7 +346,6 @@ fn main() { ( 246..283, [ - "", InlayHintLabelPart { text: "B", linked_location: Some( @@ -389,7 +377,6 @@ fn main() { ( 246..265, [ - "", InlayHintLabelPart { text: "A", linked_location: Some( @@ -562,7 +549,6 @@ fn main() { ), tooltip: "", }, - "", ], ), ] @@ -597,7 +583,6 @@ fn main() { ( 124..130, [ - "", InlayHintLabelPart { text: "Struct", linked_location: Some( @@ -610,13 +595,11 @@ fn main() { ), tooltip: "", }, - "", ], ), ( 145..185, [ - "", InlayHintLabelPart { text: "Struct", linked_location: Some( @@ -629,13 +612,11 @@ fn main() { ), tooltip: "", }, - "", ], ), ( 145..168, [ - "", InlayHintLabelPart { text: "Struct", linked_location: Some( @@ -648,7 +629,6 @@ fn main() { ), tooltip: "", }, - "", ], ), ( 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 d78fd64bdf4d..90b8be64a46d 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 @@ -18,12 +18,13 @@ pub(super) fn hints( sema: &Semantics<'_, RootDatabase>, config: &InlayHintsConfig, file_id: EditionedFileId, - mut node: SyntaxNode, + original_node: SyntaxNode, ) -> Option<()> { let min_lines = config.closing_brace_hints_min_lines?; let name = |it: ast::Name| it.syntax().text_range(); + let mut node = original_node.clone(); let mut closing_token; let (label, name_range) = if let Some(item_list) = ast::AssocItemList::cast(node.clone()) { closing_token = item_list.r_curly_token()?; @@ -77,7 +78,7 @@ pub(super) fn hints( } closing_token = block.r_curly_token()?; - let lifetime = label.lifetime().map_or_else(String::new, |it| it.to_string()); + let lifetime = label.lifetime()?.to_string(); (lifetime, Some(label.syntax().text_range())) } else if let Some(block) = ast::BlockExpr::cast(node.clone()) { @@ -145,6 +146,7 @@ pub(super) fn hints( position: InlayHintPosition::After, pad_left: true, pad_right: false, + resolve_parent: Some(original_node.text_range()), }); None 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 e87e10d8504e..f399bd01d071 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 @@ -3,7 +3,7 @@ //! Tests live in [`bind_pat`][super::bind_pat] module. use ide_db::famous_defs::FamousDefs; use span::EditionedFileId; -use stdx::TupleExt; +use stdx::{never, TupleExt}; use syntax::ast::{self, AstNode}; use text_edit::{TextRange, TextSize}; @@ -40,6 +40,7 @@ pub(super) fn hints( position: InlayHintPosition::After, pad_left: false, pad_right: false, + resolve_parent: Some(closure.syntax().text_range()), }); range } @@ -52,6 +53,7 @@ pub(super) fn hints( position: InlayHintPosition::After, pad_left: false, pad_right: false, + resolve_parent: None, }); let last = captures.len() - 1; for (idx, capture) in captures.into_iter().enumerate() { @@ -61,17 +63,21 @@ pub(super) fn hints( // force cache the source file, otherwise sema lookup will potentially panic _ = sema.parse_or_expand(source.file()); + let label = format!( + "{}{}", + match capture.kind() { + hir::CaptureKind::SharedRef => "&", + hir::CaptureKind::UniqueSharedRef => "&unique ", + hir::CaptureKind::MutableRef => "&mut ", + hir::CaptureKind::Move => "", + }, + capture.display_place(sema.db) + ); + if never!(label.is_empty()) { + continue; + } let label = InlayHintLabel::simple( - format!( - "{}{}", - match capture.kind() { - hir::CaptureKind::SharedRef => "&", - hir::CaptureKind::UniqueSharedRef => "&unique ", - hir::CaptureKind::MutableRef => "&mut ", - hir::CaptureKind::Move => "", - }, - capture.display_place(sema.db) - ), + label, None, source.name().and_then(|name| { name.syntax().original_file_range_opt(sema.db).map(TupleExt::head).map(Into::into) @@ -85,6 +91,7 @@ pub(super) fn hints( position: InlayHintPosition::After, pad_left: false, pad_right: false, + resolve_parent: Some(closure.syntax().text_range()), }); if idx != last { @@ -96,6 +103,7 @@ pub(super) fn hints( position: InlayHintPosition::After, pad_left: false, pad_right: false, + resolve_parent: None, }); } } @@ -107,6 +115,7 @@ pub(super) fn hints( position: InlayHintPosition::After, pad_left: false, pad_right: true, + resolve_parent: None, }); Some(()) 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 325c2040691b..6827540fa82c 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 @@ -72,6 +72,7 @@ pub(super) fn hints( position: InlayHintPosition::After, pad_left: false, pad_right: false, + resolve_parent: Some(closure.syntax().text_range()), }); Some(()) } 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 eca0ebe629f4..35b62878329f 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 @@ -35,7 +35,7 @@ pub(super) fn enum_hints( return None; } for variant in enum_.variant_list()?.variants() { - variant_hints(acc, sema, &variant); + variant_hints(acc, sema, &enum_, &variant); } Some(()) } @@ -43,6 +43,7 @@ pub(super) fn enum_hints( fn variant_hints( acc: &mut Vec, sema: &Semantics<'_, RootDatabase>, + enum_: &ast::Enum, variant: &ast::Variant, ) -> Option<()> { if variant.expr().is_some() { @@ -90,6 +91,7 @@ fn variant_hints( position: InlayHintPosition::After, pad_left: false, pad_right: false, + resolve_parent: Some(enum_.syntax().text_range()), }); Some(()) diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/fn_lifetime_fn.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/fn_lifetime_fn.rs deleted file mode 100644 index d3666754e2be..000000000000 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/fn_lifetime_fn.rs +++ /dev/null @@ -1,332 +0,0 @@ -//! Implementation of "lifetime elision" inlay hints: -//! ```no_run -//! fn example/* <'0> */(a: &/* '0 */()) {} -//! ``` -use ide_db::{syntax_helpers::node_ext::walk_ty, FxHashMap}; -use itertools::Itertools; -use syntax::{ - ast::{self, AstNode, HasGenericParams, HasName}, - SyntaxToken, -}; -use syntax::{format_smolstr, SmolStr}; - -use crate::{InlayHint, InlayHintPosition, InlayHintsConfig, InlayKind, LifetimeElisionHints}; - -pub(super) fn hints( - acc: &mut Vec, - config: &InlayHintsConfig, - func: ast::Fn, -) -> Option<()> { - if config.lifetime_elision_hints == LifetimeElisionHints::Never { - return None; - } - - let mk_lt_hint = |t: SyntaxToken, label: String| InlayHint { - range: t.text_range(), - kind: InlayKind::Lifetime, - label: label.into(), - text_edit: None, - position: InlayHintPosition::After, - pad_left: false, - pad_right: true, - }; - - let param_list = func.param_list()?; - let generic_param_list = func.generic_param_list(); - let ret_type = func.ret_type(); - let self_param = param_list.self_param().filter(|it| it.amp_token().is_some()); - - let is_elided = |lt: &Option| match lt { - Some(lt) => matches!(lt.text().as_str(), "'_"), - None => true, - }; - - let potential_lt_refs = { - let mut acc: Vec<_> = vec![]; - if let Some(self_param) = &self_param { - let lifetime = self_param.lifetime(); - let is_elided = is_elided(&lifetime); - acc.push((None, self_param.amp_token(), lifetime, is_elided)); - } - param_list.params().filter_map(|it| Some((it.pat(), it.ty()?))).for_each(|(pat, ty)| { - // FIXME: check path types - walk_ty(&ty, &mut |ty| match ty { - ast::Type::RefType(r) => { - let lifetime = r.lifetime(); - let is_elided = is_elided(&lifetime); - acc.push(( - pat.as_ref().and_then(|it| match it { - ast::Pat::IdentPat(p) => p.name(), - _ => None, - }), - r.amp_token(), - lifetime, - is_elided, - )); - false - } - ast::Type::FnPtrType(_) => true, - ast::Type::PathType(t) => { - t.path().and_then(|it| it.segment()).and_then(|it| it.param_list()).is_some() - } - _ => false, - }) - }); - acc - }; - - // allocate names - let mut gen_idx_name = { - let mut gen = (0u8..).map(|idx| match idx { - idx if idx < 10 => SmolStr::from_iter(['\'', (idx + 48) as char]), - idx => format_smolstr!("'{idx}"), - }); - move || gen.next().unwrap_or_default() - }; - let mut allocated_lifetimes = vec![]; - - let mut used_names: FxHashMap = - match config.param_names_for_lifetime_elision_hints { - true => generic_param_list - .iter() - .flat_map(|gpl| gpl.lifetime_params()) - .filter_map(|param| param.lifetime()) - .filter_map(|lt| Some((SmolStr::from(lt.text().as_str().get(1..)?), 0))) - .collect(), - false => Default::default(), - }; - { - let mut potential_lt_refs = potential_lt_refs.iter().filter(|&&(.., is_elided)| is_elided); - if self_param.is_some() && potential_lt_refs.next().is_some() { - allocated_lifetimes.push(if config.param_names_for_lifetime_elision_hints { - // self can't be used as a lifetime, so no need to check for collisions - "'self".into() - } else { - gen_idx_name() - }); - } - potential_lt_refs.for_each(|(name, ..)| { - let name = match name { - Some(it) if config.param_names_for_lifetime_elision_hints => { - if let Some(c) = used_names.get_mut(it.text().as_str()) { - *c += 1; - SmolStr::from(format!("'{text}{c}", text = it.text().as_str())) - } else { - used_names.insert(it.text().as_str().into(), 0); - SmolStr::from_iter(["\'", it.text().as_str()]) - } - } - _ => gen_idx_name(), - }; - allocated_lifetimes.push(name); - }); - } - - // fetch output lifetime if elision rule applies - let output = match potential_lt_refs.as_slice() { - [(_, _, lifetime, _), ..] if self_param.is_some() || potential_lt_refs.len() == 1 => { - match lifetime { - Some(lt) => match lt.text().as_str() { - "'_" => allocated_lifetimes.first().cloned(), - "'static" => None, - name => Some(name.into()), - }, - None => allocated_lifetimes.first().cloned(), - } - } - [..] => None, - }; - - if allocated_lifetimes.is_empty() && output.is_none() { - return None; - } - - // apply hints - // apply output if required - let mut is_trivial = true; - if let (Some(output_lt), Some(r)) = (&output, ret_type) { - if let Some(ty) = r.ty() { - walk_ty(&ty, &mut |ty| match ty { - ast::Type::RefType(ty) if ty.lifetime().is_none() => { - if let Some(amp) = ty.amp_token() { - is_trivial = false; - acc.push(mk_lt_hint(amp, output_lt.to_string())); - } - false - } - ast::Type::FnPtrType(_) => true, - ast::Type::PathType(t) => { - t.path().and_then(|it| it.segment()).and_then(|it| it.param_list()).is_some() - } - _ => false, - }) - } - } - - if config.lifetime_elision_hints == LifetimeElisionHints::SkipTrivial && is_trivial { - return None; - } - - let mut a = allocated_lifetimes.iter(); - for (_, amp_token, _, is_elided) in potential_lt_refs { - if is_elided { - let t = amp_token?; - let lt = a.next()?; - acc.push(mk_lt_hint(t, lt.to_string())); - } - } - - // generate generic param list things - match (generic_param_list, allocated_lifetimes.as_slice()) { - (_, []) => (), - (Some(gpl), allocated_lifetimes) => { - let angle_tok = gpl.l_angle_token()?; - let is_empty = gpl.generic_params().next().is_none(); - acc.push(InlayHint { - range: angle_tok.text_range(), - kind: InlayKind::Lifetime, - label: format!( - "{}{}", - allocated_lifetimes.iter().format(", "), - if is_empty { "" } else { ", " } - ) - .into(), - text_edit: None, - position: InlayHintPosition::After, - pad_left: false, - pad_right: true, - }); - } - (None, allocated_lifetimes) => acc.push(InlayHint { - range: func.name()?.syntax().text_range(), - kind: InlayKind::GenericParamList, - label: format!("<{}>", allocated_lifetimes.iter().format(", "),).into(), - text_edit: None, - position: InlayHintPosition::After, - pad_left: false, - pad_right: false, - }), - } - Some(()) -} - -#[cfg(test)] -mod tests { - use crate::{ - inlay_hints::tests::{check, check_with_config, TEST_CONFIG}, - InlayHintsConfig, LifetimeElisionHints, - }; - - #[test] - fn hints_lifetimes() { - check( - r#" -fn empty() {} - -fn no_gpl(a: &()) {} - //^^^^^^<'0> - // ^'0 -fn empty_gpl<>(a: &()) {} - // ^'0 ^'0 -fn partial<'b>(a: &(), b: &'b ()) {} -// ^'0, $ ^'0 -fn partial<'a>(a: &'a (), b: &()) {} -// ^'0, $ ^'0 - -fn single_ret(a: &()) -> &() {} -// ^^^^^^^^^^<'0> - // ^'0 ^'0 -fn full_mul(a: &(), b: &()) {} -// ^^^^^^^^<'0, '1> - // ^'0 ^'1 - -fn foo<'c>(a: &'c ()) -> &() {} - // ^'c - -fn nested_in(a: & &X< &()>) {} -// ^^^^^^^^^<'0, '1, '2> - //^'0 ^'1 ^'2 -fn nested_out(a: &()) -> & &X< &()>{} -// ^^^^^^^^^^<'0> - //^'0 ^'0 ^'0 ^'0 - -impl () { - fn foo(&self) {} - // ^^^<'0> - // ^'0 - fn foo(&self) -> &() {} - // ^^^<'0> - // ^'0 ^'0 - fn foo(&self, a: &()) -> &() {} - // ^^^<'0, '1> - // ^'0 ^'1 ^'0 -} -"#, - ); - } - - #[test] - fn hints_lifetimes_named() { - check_with_config( - InlayHintsConfig { param_names_for_lifetime_elision_hints: true, ..TEST_CONFIG }, - r#" -fn nested_in<'named>(named: & &X< &()>) {} -// ^'named1, 'named2, 'named3, $ - //^'named1 ^'named2 ^'named3 -"#, - ); - } - - #[test] - fn hints_lifetimes_trivial_skip() { - check_with_config( - InlayHintsConfig { - lifetime_elision_hints: LifetimeElisionHints::SkipTrivial, - ..TEST_CONFIG - }, - r#" -fn no_gpl(a: &()) {} -fn empty_gpl<>(a: &()) {} -fn partial<'b>(a: &(), b: &'b ()) {} -fn partial<'a>(a: &'a (), b: &()) {} - -fn single_ret(a: &()) -> &() {} -// ^^^^^^^^^^<'0> - // ^'0 ^'0 -fn full_mul(a: &(), b: &()) {} - -fn foo<'c>(a: &'c ()) -> &() {} - // ^'c - -fn nested_in(a: & &X< &()>) {} -fn nested_out(a: &()) -> & &X< &()>{} -// ^^^^^^^^^^<'0> - //^'0 ^'0 ^'0 ^'0 - -impl () { - fn foo(&self) {} - fn foo(&self) -> &() {} - // ^^^<'0> - // ^'0 ^'0 - fn foo(&self, a: &()) -> &() {} - // ^^^<'0, '1> - // ^'0 ^'1 ^'0 -} -"#, - ); - } - - #[test] - fn hints_lifetimes_skip_fn_likes() { - check_with_config( - InlayHintsConfig { - lifetime_elision_hints: LifetimeElisionHints::Always, - ..TEST_CONFIG - }, - r#" -fn fn_ptr(a: fn(&()) -> &()) {} -fn fn_trait<>(a: impl Fn(&()) -> &()) {} -"#, - ); - } -} 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 b60a80a8ac6b..ed7ebc3b1e7a 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 @@ -92,6 +92,7 @@ pub(crate) fn hints( kind: InlayKind::GenericParameter, label, text_edit: None, + resolve_parent: Some(node.syntax().text_range()), }) }); 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 b4695a2b3519..dd4b3efeecfe 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 @@ -8,9 +8,9 @@ use hir::{ db::{DefDatabase as _, HirDatabase as _}, mir::{MirSpan, TerminatorKind}, - ChalkTyInterner, DefWithBody, Semantics, + ChalkTyInterner, DefWithBody, }; -use ide_db::{FileRange, RootDatabase}; +use ide_db::{famous_defs::FamousDefs, FileRange}; use span::EditionedFileId; use syntax::{ @@ -22,16 +22,16 @@ use crate::{InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, Inla pub(super) fn hints( acc: &mut Vec, - sema: &Semantics<'_, RootDatabase>, + FamousDefs(sema, _): &FamousDefs<'_, '_>, config: &InlayHintsConfig, file_id: EditionedFileId, - def: &ast::Fn, + node: &ast::Fn, ) -> Option<()> { if !config.implicit_drop_hints { return None; } - let def = sema.to_def(def)?; + let def = sema.to_def(node)?; let def: DefWithBody = def.into(); let (hir, source_map) = sema.db.body_with_source_map(def.into()); @@ -121,6 +121,7 @@ pub(super) fn hints( kind: InlayKind::Drop, label, text_edit: None, + resolve_parent: Some(node.syntax().text_range()), }) } } diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_static.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_static.rs index 42223ddf580e..8d422478cbfc 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_static.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_static.rs @@ -3,6 +3,8 @@ //! static S: &/* 'static */str = ""; //! ``` use either::Either; +use ide_db::famous_defs::FamousDefs; +use span::EditionedFileId; use syntax::{ ast::{self, AstNode}, SyntaxKind, @@ -12,7 +14,9 @@ use crate::{InlayHint, InlayHintPosition, InlayHintsConfig, InlayKind, LifetimeE pub(super) fn hints( acc: &mut Vec, + FamousDefs(_sema, _): &FamousDefs<'_, '_>, config: &InlayHintsConfig, + _file_id: EditionedFileId, statik_or_const: Either, ) -> Option<()> { if config.lifetime_elision_hints != LifetimeElisionHints::Always { @@ -38,6 +42,7 @@ pub(super) fn hints( position: InlayHintPosition::After, pad_left: false, pad_right: true, + resolve_parent: None, }); } } diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/lifetime.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/lifetime.rs new file mode 100644 index 000000000000..2163c959b18a --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/lifetime.rs @@ -0,0 +1,566 @@ +//! Implementation of "lifetime elision" inlay hints: +//! ```no_run +//! fn example/* <'0> */(a: &/* '0 */()) {} +//! ``` +use std::iter; + +use ide_db::{famous_defs::FamousDefs, syntax_helpers::node_ext::walk_ty, FxHashMap}; +use itertools::Itertools; +use span::EditionedFileId; +use syntax::{ + ast::{self, AstNode, HasGenericParams, HasName}, + SyntaxKind, SyntaxToken, +}; +use syntax::{format_smolstr, SmolStr}; + +use crate::{ + inlay_hints::InlayHintCtx, InlayHint, InlayHintPosition, InlayHintsConfig, InlayKind, + LifetimeElisionHints, +}; + +pub(super) fn fn_hints( + acc: &mut Vec, + ctx: &mut InlayHintCtx, + fd: &FamousDefs<'_, '_>, + config: &InlayHintsConfig, + file_id: EditionedFileId, + func: ast::Fn, +) -> Option<()> { + if config.lifetime_elision_hints == LifetimeElisionHints::Never { + return None; + } + + let param_list = func.param_list()?; + let generic_param_list = func.generic_param_list(); + let ret_type = func.ret_type(); + let self_param = param_list.self_param().filter(|it| it.amp_token().is_some()); + let gpl_append_range = func.name()?.syntax().text_range(); + hints_( + acc, + ctx, + fd, + config, + file_id, + param_list, + generic_param_list, + ret_type, + self_param, + |acc, allocated_lifetimes| { + acc.push(InlayHint { + range: gpl_append_range, + kind: InlayKind::GenericParamList, + label: format!("<{}>", allocated_lifetimes.iter().format(", "),).into(), + text_edit: None, + position: InlayHintPosition::After, + pad_left: false, + pad_right: false, + resolve_parent: None, + }) + }, + true, + ) +} + +pub(super) fn fn_ptr_hints( + acc: &mut Vec, + ctx: &mut InlayHintCtx, + fd: &FamousDefs<'_, '_>, + config: &InlayHintsConfig, + file_id: EditionedFileId, + func: ast::FnPtrType, +) -> Option<()> { + if config.lifetime_elision_hints == LifetimeElisionHints::Never { + return None; + } + + let parent_for_type = func + .syntax() + .ancestors() + .skip(1) + .take_while(|it| matches!(it.kind(), SyntaxKind::PAREN_TYPE | SyntaxKind::FOR_TYPE)) + .find_map(ast::ForType::cast); + + let param_list = func.param_list()?; + let generic_param_list = parent_for_type.as_ref().and_then(|it| it.generic_param_list()); + let ret_type = func.ret_type(); + let for_kw = parent_for_type.as_ref().and_then(|it| it.for_token()); + hints_( + acc, + ctx, + fd, + config, + file_id, + param_list, + generic_param_list, + ret_type, + None, + |acc, allocated_lifetimes| { + let has_for = for_kw.is_some(); + let for_ = if has_for { "" } else { "for" }; + acc.push(InlayHint { + range: for_kw.map_or_else( + || func.syntax().first_token().unwrap().text_range(), + |it| it.text_range(), + ), + kind: InlayKind::GenericParamList, + label: format!("{for_}<{}>", allocated_lifetimes.iter().format(", "),).into(), + text_edit: None, + position: if has_for { + InlayHintPosition::After + } else { + InlayHintPosition::Before + }, + pad_left: false, + pad_right: true, + resolve_parent: None, + }); + }, + false, + ) +} + +pub(super) fn fn_path_hints( + acc: &mut Vec, + ctx: &mut InlayHintCtx, + fd: &FamousDefs<'_, '_>, + config: &InlayHintsConfig, + file_id: EditionedFileId, + func: ast::PathType, +) -> Option<()> { + if config.lifetime_elision_hints == LifetimeElisionHints::Never { + return None; + } + + // FIXME: Support general path types + let (param_list, ret_type) = func.path().as_ref().and_then(path_as_fn)?; + let parent_for_type = func + .syntax() + .ancestors() + .skip(1) + .take_while(|it| matches!(it.kind(), SyntaxKind::PAREN_TYPE | SyntaxKind::FOR_TYPE)) + .find_map(ast::ForType::cast); + + let generic_param_list = parent_for_type.as_ref().and_then(|it| it.generic_param_list()); + let for_kw = parent_for_type.as_ref().and_then(|it| it.for_token()); + hints_( + acc, + ctx, + fd, + config, + file_id, + param_list, + generic_param_list, + ret_type, + None, + |acc, allocated_lifetimes| { + let has_for = for_kw.is_some(); + let for_ = if has_for { "" } else { "for" }; + acc.push(InlayHint { + range: for_kw.map_or_else( + || func.syntax().first_token().unwrap().text_range(), + |it| it.text_range(), + ), + kind: InlayKind::GenericParamList, + label: format!("{for_}<{}>", allocated_lifetimes.iter().format(", "),).into(), + text_edit: None, + position: if has_for { + InlayHintPosition::After + } else { + InlayHintPosition::Before + }, + pad_left: false, + pad_right: true, + resolve_parent: None, + }); + }, + false, + ) +} + +fn path_as_fn(path: &ast::Path) -> Option<(ast::ParamList, Option)> { + path.segment().and_then(|it| it.param_list().zip(Some(it.ret_type()))) +} + +fn hints_( + acc: &mut Vec, + ctx: &mut InlayHintCtx, + FamousDefs(_, _): &FamousDefs<'_, '_>, + config: &InlayHintsConfig, + _file_id: EditionedFileId, + param_list: ast::ParamList, + generic_param_list: Option, + ret_type: Option, + self_param: Option, + on_missing_gpl: impl FnOnce(&mut Vec, &[SmolStr]), + mut is_trivial: bool, +) -> Option<()> { + let is_elided = |lt: &Option| match lt { + Some(lt) => matches!(lt.text().as_str(), "'_"), + None => true, + }; + + let mk_lt_hint = |t: SyntaxToken, label: String| InlayHint { + range: t.text_range(), + kind: InlayKind::Lifetime, + label: label.into(), + text_edit: None, + position: InlayHintPosition::After, + pad_left: false, + pad_right: true, + resolve_parent: None, + }; + + let potential_lt_refs = { + let mut acc: Vec<_> = vec![]; + if let Some(self_param) = &self_param { + let lifetime = self_param.lifetime(); + let is_elided = is_elided(&lifetime); + acc.push((None, self_param.amp_token(), lifetime, is_elided)); + } + param_list + .params() + .filter_map(|it| { + Some(( + it.pat().and_then(|it| match it { + ast::Pat::IdentPat(p) => p.name(), + _ => None, + }), + it.ty()?, + )) + }) + .for_each(|(name, ty)| { + // FIXME: check path types + walk_ty(&ty, &mut |ty| match ty { + ast::Type::RefType(r) => { + let lifetime = r.lifetime(); + let is_elided = is_elided(&lifetime); + acc.push((name.clone(), r.amp_token(), lifetime, is_elided)); + false + } + ast::Type::FnPtrType(_) => { + is_trivial = false; + true + } + ast::Type::PathType(t) => { + if t.path() + .and_then(|it| it.segment()) + .and_then(|it| it.param_list()) + .is_some() + { + is_trivial = false; + true + } else { + false + } + } + _ => false, + }) + }); + acc + }; + + let mut used_names: FxHashMap = + ctx.lifetime_stacks.iter().flat_map(|it| it.iter()).cloned().zip(iter::repeat(0)).collect(); + // allocate names + let mut gen_idx_name = { + let mut gen = (0u8..).map(|idx| match idx { + idx if idx < 10 => SmolStr::from_iter(['\'', (idx + 48) as char]), + idx => format_smolstr!("'{idx}"), + }); + let ctx = &*ctx; + move || { + gen.by_ref() + .find(|s| ctx.lifetime_stacks.iter().flat_map(|it| it.iter()).all(|n| n != s)) + .unwrap_or_default() + } + }; + let mut allocated_lifetimes = vec![]; + + { + let mut potential_lt_refs = potential_lt_refs.iter().filter(|&&(.., is_elided)| is_elided); + if self_param.is_some() && potential_lt_refs.next().is_some() { + allocated_lifetimes.push(if config.param_names_for_lifetime_elision_hints { + // self can't be used as a lifetime, so no need to check for collisions + "'self".into() + } else { + gen_idx_name() + }); + } + potential_lt_refs.for_each(|(name, ..)| { + let name = match name { + Some(it) if config.param_names_for_lifetime_elision_hints => { + if let Some(c) = used_names.get_mut(it.text().as_str()) { + *c += 1; + format_smolstr!("'{}{c}", it.text().as_str()) + } else { + used_names.insert(it.text().as_str().into(), 0); + format_smolstr!("'{}", it.text().as_str()) + } + } + _ => gen_idx_name(), + }; + allocated_lifetimes.push(name); + }); + } + + // fetch output lifetime if elision rule applies + let output = match potential_lt_refs.as_slice() { + [(_, _, lifetime, _), ..] if self_param.is_some() || potential_lt_refs.len() == 1 => { + match lifetime { + Some(lt) => match lt.text().as_str() { + "'_" => allocated_lifetimes.first().cloned(), + "'static" => None, + name => Some(name.into()), + }, + None => allocated_lifetimes.first().cloned(), + } + } + [..] => None, + }; + + if allocated_lifetimes.is_empty() && output.is_none() { + return None; + } + + // apply hints + // apply output if required + if let (Some(output_lt), Some(r)) = (&output, ret_type) { + if let Some(ty) = r.ty() { + walk_ty(&ty, &mut |ty| match ty { + ast::Type::RefType(ty) if ty.lifetime().is_none() => { + if let Some(amp) = ty.amp_token() { + is_trivial = false; + acc.push(mk_lt_hint(amp, output_lt.to_string())); + } + false + } + ast::Type::FnPtrType(_) => { + is_trivial = false; + true + } + ast::Type::PathType(t) => { + if t.path().and_then(|it| it.segment()).and_then(|it| it.param_list()).is_some() + { + is_trivial = false; + true + } else { + false + } + } + _ => false, + }) + } + } + + if config.lifetime_elision_hints == LifetimeElisionHints::SkipTrivial && is_trivial { + return None; + } + + let mut a = allocated_lifetimes.iter(); + for (_, amp_token, _, is_elided) in potential_lt_refs { + if is_elided { + let t = amp_token?; + let lt = a.next()?; + acc.push(mk_lt_hint(t, lt.to_string())); + } + } + + // generate generic param list things + match (generic_param_list, allocated_lifetimes.as_slice()) { + (_, []) => (), + (Some(gpl), allocated_lifetimes) => { + let angle_tok = gpl.l_angle_token()?; + let is_empty = gpl.generic_params().next().is_none(); + acc.push(InlayHint { + range: angle_tok.text_range(), + kind: InlayKind::Lifetime, + label: format!( + "{}{}", + allocated_lifetimes.iter().format(", "), + if is_empty { "" } else { ", " } + ) + .into(), + text_edit: None, + position: InlayHintPosition::After, + pad_left: false, + pad_right: true, + resolve_parent: None, + }); + } + (None, allocated_lifetimes) => on_missing_gpl(acc, allocated_lifetimes), + } + if let Some(stack) = ctx.lifetime_stacks.last_mut() { + stack.extend(allocated_lifetimes); + } + Some(()) +} + +#[cfg(test)] +mod tests { + use crate::{ + inlay_hints::tests::{check, check_with_config, TEST_CONFIG}, + InlayHintsConfig, LifetimeElisionHints, + }; + + #[test] + fn hints_lifetimes() { + check( + r#" +fn empty() {} + +fn no_gpl(a: &()) {} + //^^^^^^<'0> + // ^'0 +fn empty_gpl<>(a: &()) {} + // ^'0 ^'0 +fn partial<'b>(a: &(), b: &'b ()) {} +// ^'0, $ ^'0 +fn partial<'a>(a: &'a (), b: &()) {} +// ^'0, $ ^'0 + +fn single_ret(a: &()) -> &() {} +// ^^^^^^^^^^<'0> + // ^'0 ^'0 +fn full_mul(a: &(), b: &()) {} +// ^^^^^^^^<'0, '1> + // ^'0 ^'1 + +fn foo<'c>(a: &'c ()) -> &() {} + // ^'c + +fn nested_in(a: & &X< &()>) {} +// ^^^^^^^^^<'0, '1, '2> + //^'0 ^'1 ^'2 +fn nested_out(a: &()) -> & &X< &()>{} +// ^^^^^^^^^^<'0> + //^'0 ^'0 ^'0 ^'0 + +impl () { + fn foo(&self) {} + // ^^^<'0> + // ^'0 + fn foo(&self) -> &() {} + // ^^^<'0> + // ^'0 ^'0 + fn foo(&self, a: &()) -> &() {} + // ^^^<'0, '1> + // ^'0 ^'1 ^'0 +} +"#, + ); + } + + #[test] + fn hints_lifetimes_named() { + check_with_config( + InlayHintsConfig { param_names_for_lifetime_elision_hints: true, ..TEST_CONFIG }, + r#" +fn nested_in<'named>(named: & &X< &()>) {} +// ^'named1, 'named2, 'named3, $ + //^'named1 ^'named2 ^'named3 +"#, + ); + } + + #[test] + fn hints_lifetimes_trivial_skip() { + check_with_config( + InlayHintsConfig { + lifetime_elision_hints: LifetimeElisionHints::SkipTrivial, + ..TEST_CONFIG + }, + r#" +fn no_gpl(a: &()) {} +fn empty_gpl<>(a: &()) {} +fn partial<'b>(a: &(), b: &'b ()) {} +fn partial<'a>(a: &'a (), b: &()) {} + +fn single_ret(a: &()) -> &() {} +// ^^^^^^^^^^<'0> + // ^'0 ^'0 +fn full_mul(a: &(), b: &()) {} + +fn foo<'c>(a: &'c ()) -> &() {} + // ^'c + +fn nested_in(a: & &X< &()>) {} +fn nested_out(a: &()) -> & &X< &()>{} +// ^^^^^^^^^^<'0> + //^'0 ^'0 ^'0 ^'0 + +impl () { + fn foo(&self) {} + fn foo(&self) -> &() {} + // ^^^<'0> + // ^'0 ^'0 + fn foo(&self, a: &()) -> &() {} + // ^^^<'0, '1> + // ^'0 ^'1 ^'0 +} +"#, + ); + } + + #[test] + fn no_collide() { + check_with_config( + InlayHintsConfig { + lifetime_elision_hints: LifetimeElisionHints::Always, + param_names_for_lifetime_elision_hints: true, + ..TEST_CONFIG + }, + r#" +impl<'foo> { + fn foo(foo: &()) {} + // ^^^ <'foo1> + // ^ 'foo1 +} +"#, + ); + } + + #[test] + fn hints_lifetimes_fn_ptr() { + check_with_config( + InlayHintsConfig { + lifetime_elision_hints: LifetimeElisionHints::Always, + ..TEST_CONFIG + }, + r#" +fn fn_ptr(a: fn(&()) -> &fn(&()) -> &()) {} + //^^ for<'0> + //^'0 + //^'0 + //^^ for<'1> + //^'1 + //^'1 +fn fn_ptr2(a: for<'a> fn(&()) -> &()) {} + //^'0, $ + //^'0 + //^'0 +fn fn_trait(a: &impl Fn(&()) -> &()) {} +// ^^^^^^^^<'0> + // ^'0 + // ^^ for<'1> + //^'1 + // ^'1 +"#, + ); + } + + #[test] + fn hints_in_non_gen_defs() { + check_with_config( + InlayHintsConfig { + lifetime_elision_hints: LifetimeElisionHints::Always, + ..TEST_CONFIG + }, + r#" +const _: fn(&()) -> &(); + //^^ for<'0> + //^'0 + //^'0 +"#, + ); + } +} diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs index 0f3142ef3f88..28b0fa6dd4d1 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs @@ -7,8 +7,9 @@ use std::fmt::Display; use either::Either; use hir::{Callable, Semantics}; -use ide_db::RootDatabase; +use ide_db::{famous_defs::FamousDefs, RootDatabase}; +use span::EditionedFileId; use stdx::to_lower_snake_case; use syntax::{ ast::{self, AstNode, HasArgList, HasName, UnaryOp}, @@ -19,8 +20,9 @@ use crate::{InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, Inla pub(super) fn hints( acc: &mut Vec, - sema: &Semantics<'_, RootDatabase>, + FamousDefs(sema, _): &FamousDefs<'_, '_>, config: &InlayHintsConfig, + _file_id: EditionedFileId, expr: ast::Expr, ) -> Option<()> { if !config.parameter_hints { @@ -60,6 +62,7 @@ pub(super) fn hints( position: InlayHintPosition::Before, pad_left: false, pad_right: true, + resolve_parent: Some(expr.syntax().text_range()), } }); diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/range_exclusive.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/range_exclusive.rs index bfb92838857c..de9b0e98a4be 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/range_exclusive.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/range_exclusive.rs @@ -3,13 +3,17 @@ //! for i in 0../* < */10 {} //! if let ../* < */100 = 50 {} //! ``` +use ide_db::famous_defs::FamousDefs; +use span::EditionedFileId; use syntax::{ast, SyntaxToken, T}; use crate::{InlayHint, InlayHintsConfig}; pub(super) fn hints( acc: &mut Vec, + FamousDefs(_sema, _): &FamousDefs<'_, '_>, config: &InlayHintsConfig, + _file_id: EditionedFileId, range: impl ast::RangeItem, ) -> Option<()> { (config.range_exclusive_hints && range.end().is_some()) @@ -30,6 +34,7 @@ fn inlay_hint(token: SyntaxToken) -> InlayHint { kind: crate::InlayKind::RangeExclusive, label: crate::InlayHintLabel::from("<"), text_edit: None, + resolve_parent: None, } } diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs index ba0aaae19c9f..547286c3f4d6 100644 --- a/src/tools/rust-analyzer/crates/ide/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs @@ -57,7 +57,7 @@ mod view_item_tree; mod view_memory_layout; mod view_mir; -use std::panic::UnwindSafe; +use std::{iter, panic::UnwindSafe}; use cfg::CfgOptions; use fetch_crates::CrateInfo; @@ -65,7 +65,8 @@ use hir::{sym, ChangeWithProcMacros}; use ide_db::{ base_db::{ salsa::{self, ParallelDatabase}, - CrateOrigin, Env, FileLoader, FileSet, SourceDatabase, SourceRootDatabase, VfsPath, + CrateOrigin, CrateWorkspaceData, Env, FileLoader, FileSet, SourceDatabase, + SourceRootDatabase, VfsPath, }, prime_caches, symbol_index, FxHashMap, FxIndexSet, LineIndexDatabase, }; @@ -256,9 +257,16 @@ impl Analysis { CrateOrigin::Local { repo: None, name: None }, ); change.change_file(file_id, Some(text)); - change.set_crate_graph(crate_graph); - change.set_target_data_layouts(vec![Err("fixture has no layout".into())]); - change.set_toolchains(vec![None]); + let ws_data = crate_graph + .iter() + .zip(iter::repeat(Arc::new(CrateWorkspaceData { + proc_macro_cwd: None, + data_layout: Err("fixture has no layout".into()), + toolchain: None, + }))) + .collect(); + change.set_crate_graph(crate_graph, ws_data); + host.apply_change(change); (host.analysis(), file_id) } @@ -439,12 +447,12 @@ impl Analysis { &self, config: &InlayHintsConfig, file_id: FileId, - position: TextSize, + resolve_range: TextRange, hash: u64, hasher: impl Fn(&InlayHint) -> u64 + Send + UnwindSafe, ) -> Cancellable> { self.with_db(|db| { - inlay_hints::inlay_hints_resolve(db, file_id, position, hash, config, hasher) + inlay_hints::inlay_hints_resolve(db, file_id, resolve_range, hash, config, hasher) }) } diff --git a/src/tools/rust-analyzer/crates/ide/src/moniker.rs b/src/tools/rust-analyzer/crates/ide/src/moniker.rs index 4be1b570981f..14781b212969 100644 --- a/src/tools/rust-analyzer/crates/ide/src/moniker.rs +++ b/src/tools/rust-analyzer/crates/ide/src/moniker.rs @@ -223,11 +223,12 @@ pub(crate) fn def_to_kind(db: &RootDatabase, def: Definition) -> SymbolInformati Variable } } - Definition::Label(..) => Variable, // For lack of a better variant + Definition::Label(..) | Definition::InlineAsmOperand(_) => Variable, // For lack of a better variant Definition::DeriveHelper(..) => Attribute, Definition::BuiltinAttr(..) => Attribute, Definition::ToolModule(..) => Module, Definition::ExternCrateDecl(..) => Module, + Definition::InlineAsmRegOrRegClass(..) => Module, } } @@ -320,7 +321,9 @@ pub(crate) fn def_to_moniker( | Definition::DeriveHelper(_) | Definition::BuiltinLifetime(_) | Definition::BuiltinAttr(_) - | Definition::ToolModule(_) => return None, + | Definition::ToolModule(_) + | Definition::InlineAsmRegOrRegClass(_) + | Definition::InlineAsmOperand(_) => return None, Definition::Local(local) => { if !local.is_param(db) { 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 9ace9fda62b9..9bc7bf411f05 100644 --- a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs +++ b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs @@ -237,11 +237,13 @@ impl TryToNav for Definition { Definition::Trait(it) => it.try_to_nav(db), Definition::TraitAlias(it) => it.try_to_nav(db), Definition::TypeAlias(it) => it.try_to_nav(db), - Definition::ExternCrateDecl(it) => Some(it.try_to_nav(db)?), + Definition::ExternCrateDecl(it) => it.try_to_nav(db), + Definition::InlineAsmOperand(it) => it.try_to_nav(db), Definition::BuiltinLifetime(_) | Definition::BuiltinType(_) | Definition::TupleField(_) | Definition::ToolModule(_) + | Definition::InlineAsmRegOrRegClass(_) | Definition::BuiltinAttr(_) => None, // FIXME: The focus range should be set to the helper declaration Definition::DeriveHelper(it) => it.derive().try_to_nav(db), @@ -693,6 +695,31 @@ impl TryToNav for hir::ConstParam { } } +impl TryToNav for hir::InlineAsmOperand { + fn try_to_nav(&self, db: &RootDatabase) -> Option> { + let InFile { file_id, value } = &self.source(db)?; + let file_id = *file_id; + Some(orig_range_with_focus(db, file_id, value.syntax(), value.name()).map( + |(FileRange { file_id, range: full_range }, focus_range)| { + let edition = self.parent(db).module(db).krate().edition(db); + NavigationTarget { + file_id, + name: self + .name(db) + .map_or_else(|| "_".into(), |it| it.display(db, edition).to_smolstr()), + alias: None, + kind: Some(SymbolKind::Local), + full_range, + focus_range, + container_name: None, + description: None, + docs: None, + } + }, + )) + } +} + #[derive(Debug)] pub struct UpmappingResult { /// The macro call site. diff --git a/src/tools/rust-analyzer/crates/ide/src/rename.rs b/src/tools/rust-analyzer/crates/ide/src/rename.rs index 42b7472c645f..e46cb5a781f4 100644 --- a/src/tools/rust-analyzer/crates/ide/src/rename.rs +++ b/src/tools/rust-analyzer/crates/ide/src/rename.rs @@ -2975,6 +2975,62 @@ fn test() { ); } + #[test] + fn asm_operand() { + check( + "bose", + r#" +//- minicore: asm +fn test() { + core::arch::asm!( + "push {base}", + base$0 = const 0 + ); +} +"#, + r#" +fn test() { + core::arch::asm!( + "push {bose}", + bose = const 0 + ); +} +"#, + ); + } + + #[test] + fn asm_operand2() { + check( + "bose", + r#" +//- minicore: asm +fn test() { + core::arch::asm!( + "push {base$0}", + "push {base}", + boo = const 0, + virtual_free = sym VIRTUAL_FREE, + base = const 0, + boo = const 0, + ); +} +"#, + r#" +fn test() { + core::arch::asm!( + "push {bose}", + "push {bose}", + boo = const 0, + virtual_free = sym VIRTUAL_FREE, + bose = const 0, + boo = const 0, + ); +} +"#, + ); + } + #[test] fn rename_path_inside_use_tree() { check( diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/format.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/format.rs index 518e71454798..7234108701a3 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/format.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/format.rs @@ -19,21 +19,21 @@ pub(super) fn highlight_format_string( expanded_string: &ast::String, range: TextRange, ) { - if !is_format_string(expanded_string) { + if is_format_string(expanded_string) { + // FIXME: Replace this with the HIR info we have now. + lex_format_specifiers(string, &mut |piece_range, kind| { + if let Some(highlight) = highlight_format_specifier(kind) { + stack.add(HlRange { + range: piece_range + range.start(), + highlight: highlight.into(), + binding_hash: None, + }); + } + }); + return; } - // FIXME: Replace this with the HIR info we have now. - lex_format_specifiers(string, &mut |piece_range, kind| { - if let Some(highlight) = highlight_format_specifier(kind) { - stack.add(HlRange { - range: piece_range + range.start(), - highlight: highlight.into(), - binding_hash: None, - }); - } - }); - if let Some(parts) = sema.as_format_args_parts(string) { parts.into_iter().for_each(|(range, res)| { if let Some(res) = res { 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 eeba9cf35c99..96375937a12e 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 @@ -534,6 +534,10 @@ pub(super) fn highlight_def( Definition::BuiltinAttr(_) => Highlight::new(HlTag::Symbol(SymbolKind::BuiltinAttr)), Definition::ToolModule(_) => Highlight::new(HlTag::Symbol(SymbolKind::ToolModule)), Definition::DeriveHelper(_) => Highlight::new(HlTag::Symbol(SymbolKind::DeriveHelper)), + Definition::InlineAsmRegOrRegClass(_) => { + Highlight::new(HlTag::Symbol(SymbolKind::InlineAsmRegOrRegClass)) + } + Definition::InlineAsmOperand(_) => Highlight::new(HlTag::Symbol(SymbolKind::Local)), }; let def_crate = def.krate(db); 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 bc1ec530076c..5583f1bc8df9 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 @@ -315,6 +315,8 @@ fn module_def_to_hl_tag(def: Definition) -> HlTag { Definition::BuiltinAttr(_) => SymbolKind::BuiltinAttr, Definition::ToolModule(_) => SymbolKind::ToolModule, Definition::DeriveHelper(_) => SymbolKind::DeriveHelper, + Definition::InlineAsmRegOrRegClass(_) => SymbolKind::InlineAsmRegOrRegClass, + Definition::InlineAsmOperand(_) => SymbolKind::Local, }; HlTag::Symbol(symbol) } diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs index e329023606a1..3b5d1af0ac72 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs @@ -146,6 +146,7 @@ impl HlTag { SymbolKind::Field => "field", SymbolKind::Function => "function", SymbolKind::Impl => "self_type", + SymbolKind::InlineAsmRegOrRegClass => "reg", SymbolKind::Label => "label", SymbolKind::LifetimeParam => "lifetime", SymbolKind::Local => "variable", diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_asm.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_asm.html new file mode 100644 index 000000000000..15a6386aa3c8 --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_asm.html @@ -0,0 +1,119 @@ + + +
fn main() {
+    unsafe {
+        let foo = 1;
+        let mut o = 0;
+        core::arch::asm!(
+            "%input = OpLoad _ {0}",
+            concat!("%result = ", "bar", " _ %input"),
+            "OpStore {1} %result",
+            in(reg) &foo,
+            in(reg) &mut o,
+        );
+
+        let thread_id: usize;
+        core::arch::asm!("
+            mov {0}, gs:[0x30]
+            mov {0}, [{0}+0x48]
+        ", out(reg) thread_id, options(pure, readonly, nostack));
+
+        static UNMAP_BASE: usize;
+        const MEM_RELEASE: usize;
+        static VirtualFree: usize;
+        const OffPtr: usize;
+        const OffFn: usize;
+        core::arch::asm!("
+            push {free_type}
+            push {free_size}
+            push {base}
+
+            mov eax, fs:[30h]
+            mov eax, [eax+8h]
+            add eax, {off_fn}
+            mov [eax-{off_fn}+{off_ptr}], eax
+
+            push eax
+
+            jmp {virtual_free}
+            ",
+            off_ptr = const OffPtr,
+            off_fn  = const OffFn,
+
+            free_size = const 0,
+            free_type = const MEM_RELEASE,
+
+            virtual_free = sym VirtualFree,
+
+            base = sym UNMAP_BASE,
+            options(noreturn),
+        );
+    }
+}
+// taken from https://github.com/rust-embedded/cortex-m/blob/47921b51f8b960344fcfa1255a50a0d19efcde6d/cortex-m/src/asm.rs#L254-L274
+#[inline]
+pub unsafe fn bootstrap(msp: *const u32, rv: *const u32) -> ! {
+    // Ensure thumb mode is set.
+    let rv = (rv as u32) | 1;
+    let msp = msp as u32;
+    core::arch::asm!(
+        "mrs {tmp}, CONTROL",
+        "bics {tmp}, {spsel}",
+        "msr CONTROL, {tmp}",
+        "isb",
+        "msr MSP, {msp}",
+        "bx {rv}",
+        // `out(reg) _` is not permitted in a `noreturn` asm! call,
+        // so instead use `in(reg) 0` and don't restore it afterwards.
+        tmp = in(reg) 0,
+        spsel = in(reg) 2,
+        msp = in(reg) msp,
+        rv = in(reg) rv,
+        options(noreturn, nomem, nostack),
+    );
+}
\ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_issue_18089.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_issue_18089.html new file mode 100644 index 000000000000..d07ba74db249 --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_issue_18089.html @@ -0,0 +1,53 @@ + + +
fn main() {
+    template!(template);
+}
+
+#[proc_macros::issue_18089]
+fn template() {}
\ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2015.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2015.html new file mode 100644 index 000000000000..a790b385786d --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2015.html @@ -0,0 +1,74 @@ + + +
extern crate self;
+
+use crate;
+use self;
+mod __ {
+    use super::*;
+}
+
+macro_rules! void {
+    ($($tt:tt)*) => {}
+}
+
+struct __ where Self:;
+fn __(_: Self) {}
+void!(Self);
+
+// edition dependent
+void!(try async await gen);
+// edition and context dependent
+void!(dyn);
+// builtin custom syntax
+void!(builtin offset_of format_args asm);
+// contextual
+void!(macro_rules, union, default, raw, auto, yeet);
+// reserved
+void!(abstract become box do final macro override priv typeof unsized virtual yield);
+void!('static 'self 'unsafe)
\ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2018.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2018.html new file mode 100644 index 000000000000..6dac066bfaa1 --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2018.html @@ -0,0 +1,74 @@ + + +
extern crate self;
+
+use crate;
+use self;
+mod __ {
+    use super::*;
+}
+
+macro_rules! void {
+    ($($tt:tt)*) => {}
+}
+
+struct __ where Self:;
+fn __(_: Self) {}
+void!(Self);
+
+// edition dependent
+void!(try async await gen);
+// edition and context dependent
+void!(dyn);
+// builtin custom syntax
+void!(builtin offset_of format_args asm);
+// contextual
+void!(macro_rules, union, default, raw, auto, yeet);
+// reserved
+void!(abstract become box do final macro override priv typeof unsized virtual yield);
+void!('static 'self 'unsafe)
\ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2021.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2021.html new file mode 100644 index 000000000000..6dac066bfaa1 --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2021.html @@ -0,0 +1,74 @@ + + +
extern crate self;
+
+use crate;
+use self;
+mod __ {
+    use super::*;
+}
+
+macro_rules! void {
+    ($($tt:tt)*) => {}
+}
+
+struct __ where Self:;
+fn __(_: Self) {}
+void!(Self);
+
+// edition dependent
+void!(try async await gen);
+// edition and context dependent
+void!(dyn);
+// builtin custom syntax
+void!(builtin offset_of format_args asm);
+// contextual
+void!(macro_rules, union, default, raw, auto, yeet);
+// reserved
+void!(abstract become box do final macro override priv typeof unsized virtual yield);
+void!('static 'self 'unsafe)
\ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2024.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2024.html new file mode 100644 index 000000000000..4ccc40799088 --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2024.html @@ -0,0 +1,74 @@ + + +
extern crate self;
+
+use crate;
+use self;
+mod __ {
+    use super::*;
+}
+
+macro_rules! void {
+    ($($tt:tt)*) => {}
+}
+
+struct __ where Self:;
+fn __(_: Self) {}
+void!(Self);
+
+// edition dependent
+void!(try async await gen);
+// edition and context dependent
+void!(dyn);
+// builtin custom syntax
+void!(builtin offset_of format_args asm);
+// contextual
+void!(macro_rules, union, default, raw, auto, yeet);
+// reserved
+void!(abstract become box do final macro override priv typeof unsized virtual yield);
+void!('static 'self 'unsafe)
\ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html index cb47fc68bc1b..5594a36e7318 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html @@ -165,16 +165,16 @@ toho!("{}fmt", 0); let i: u64 = 3; let o: u64; - asm!( - "mov {0}, {1}", - "add {0}, 5", - out(reg) o, - in(reg) i, + core::arch::asm!( + "mov {0}, {1}", + "add {0}, 5", + out(reg) o, + in(reg) i, ); const CONSTANT: () = (): let mut m = (); format_args!(concat!("{}"), "{}"); - format_args!("{} {} {} {} {} {} {backslash} {CONSTANT} {m}", backslash, format_args!("{}", 0), foo, "bar", toho!(), backslash); - reuse_twice!("{backslash}"); + format_args!("{} {} {} {} {} {} {backslash} {CONSTANT} {m}", backslash, format_args!("{}", 0), foo, "bar", toho!(), backslash); + reuse_twice!("{backslash}"); }
\ No newline at end of file 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 2070022d4183..94cee4ef43b1 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 @@ -2,6 +2,7 @@ use std::time::Instant; use expect_test::{expect_file, ExpectFile}; use ide_db::SymbolKind; +use span::Edition; use test_utils::{bench, bench_fixture, skip_slow_tests, AssertLinear}; use crate::{fixture, FileRange, HighlightConfig, HlTag, TextRange}; @@ -383,8 +384,10 @@ where #[test] fn test_keyword_highlighting() { - check_highlighting( - r#" + for edition in Edition::iter() { + check_highlighting( + &(format!("//- /main.rs crate:main edition:{edition}") + + r#" extern crate self; use crate; @@ -396,13 +399,27 @@ mod __ { macro_rules! void { ($($tt:tt)*) => {} } -void!(Self); + struct __ where Self:; fn __(_: Self) {} -"#, - expect_file!["./test_data/highlight_keywords.html"], - false, - ); +void!(Self); + +// edition dependent +void!(try async await gen); +// edition and context dependent +void!(dyn); +// builtin custom syntax +void!(builtin offset_of format_args asm); +// contextual +void!(macro_rules, union, default, raw, auto, yeet); +// reserved +void!(abstract become box do final macro override priv typeof unsized virtual yield); +void!('static 'self 'unsafe) +"#), + expect_file![format!("./test_data/highlight_keywords_{edition}.html")], + false, + ); + } } #[test] @@ -532,7 +549,7 @@ fn main() { toho!("{}fmt", 0); let i: u64 = 3; let o: u64; - asm!( + core::arch::asm!( "mov {0}, {1}", "add {0}, 5", out(reg) o, @@ -554,6 +571,7 @@ fn main() { fn test_unsafe_highlighting() { check_highlighting( r#" +//- minicore: sized macro_rules! id { ($($tt:tt)*) => { $($tt)* @@ -1257,3 +1275,103 @@ fn f<'de, T: Deserialize<'de>>() { ); let _ = analysis.highlight(HL_CONFIG, file_id).unwrap(); } + +#[test] +fn test_asm_highlighting() { + check_highlighting( + r#" +//- minicore: asm, concat +fn main() { + unsafe { + let foo = 1; + let mut o = 0; + core::arch::asm!( + "%input = OpLoad _ {0}", + concat!("%result = ", "bar", " _ %input"), + "OpStore {1} %result", + in(reg) &foo, + in(reg) &mut o, + ); + + let thread_id: usize; + core::arch::asm!(" + mov {0}, gs:[0x30] + mov {0}, [{0}+0x48] + ", out(reg) thread_id, options(pure, readonly, nostack)); + + static UNMAP_BASE: usize; + const MEM_RELEASE: usize; + static VirtualFree: usize; + const OffPtr: usize; + const OffFn: usize; + core::arch::asm!(" + push {free_type} + push {free_size} + push {base} + + mov eax, fs:[30h] + mov eax, [eax+8h] + add eax, {off_fn} + mov [eax-{off_fn}+{off_ptr}], eax + + push eax + + jmp {virtual_free} + ", + off_ptr = const OffPtr, + off_fn = const OffFn, + + free_size = const 0, + free_type = const MEM_RELEASE, + + virtual_free = sym VirtualFree, + + base = sym UNMAP_BASE, + options(noreturn), + ); + } +} +// taken from https://github.com/rust-embedded/cortex-m/blob/47921b51f8b960344fcfa1255a50a0d19efcde6d/cortex-m/src/asm.rs#L254-L274 +#[inline] +pub unsafe fn bootstrap(msp: *const u32, rv: *const u32) -> ! { + // Ensure thumb mode is set. + let rv = (rv as u32) | 1; + let msp = msp as u32; + core::arch::asm!( + "mrs {tmp}, CONTROL", + "bics {tmp}, {spsel}", + "msr CONTROL, {tmp}", + "isb", + "msr MSP, {msp}", + "bx {rv}", + // `out(reg) _` is not permitted in a `noreturn` asm! call, + // so instead use `in(reg) 0` and don't restore it afterwards. + tmp = in(reg) 0, + spsel = in(reg) 2, + msp = in(reg) msp, + rv = in(reg) rv, + options(noreturn, nomem, nostack), + ); +} +"#, + expect_file!["./test_data/highlight_asm.html"], + false, + ); +} + +#[test] +fn issue_18089() { + check_highlighting( + r#" +//- proc_macros: issue_18089 +fn main() { + template!(template); +} + +#[proc_macros::issue_18089] +fn template() {} +"#, + expect_file!["./test_data/highlight_issue_18089.html"], + false, + ); +} diff --git a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs index c066f5048915..a62cbc7fb29f 100644 --- a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs +++ b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs @@ -15,7 +15,7 @@ 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 + // statics we should swap these for `const`s and have 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)) }; @@ -243,6 +243,7 @@ define_symbols! { future_output, Future, ge, + generic_associated_type_extended, get_context, global_allocator, global_asm, 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 abad7e9f7d22..baa45174236c 100644 --- a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs +++ b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs @@ -10,7 +10,7 @@ use hir_expand::proc_macro::{ ProcMacros, }; use ide_db::{ - base_db::{CrateGraph, Env, SourceRoot, SourceRootId}, + base_db::{CrateGraph, CrateWorkspaceData, Env, SourceRoot, SourceRootId}, prime_caches, ChangeWithProcMacros, FxHashMap, RootDatabase, }; use itertools::Itertools; @@ -447,12 +447,16 @@ fn load_crate_graph( let source_roots = source_root_config.partition(vfs); analysis_change.set_roots(source_roots); - let num_crates = crate_graph.len(); - analysis_change.set_crate_graph(crate_graph); + let ws_data = crate_graph + .iter() + .zip(iter::repeat(From::from(CrateWorkspaceData { + proc_macro_cwd: None, + data_layout: target_layout.clone(), + toolchain: toolchain.clone(), + }))) + .collect(); + analysis_change.set_crate_graph(crate_graph, ws_data); analysis_change.set_proc_macros(proc_macros); - analysis_change - .set_target_data_layouts(iter::repeat(target_layout.clone()).take(num_crates).collect()); - analysis_change.set_toolchains(iter::repeat(toolchain.clone()).take(num_crates).collect()); db.apply_change(analysis_change); db @@ -489,8 +493,17 @@ impl ProcMacroExpander for Expander { def_site: Span, call_site: Span, mixed_site: Span, + current_dir: Option, ) -> Result, ProcMacroExpansionError> { - match self.0.expand(subtree, attrs, env.clone(), def_site, call_site, mixed_site) { + match self.0.expand( + subtree, + attrs, + env.clone(), + def_site, + call_site, + mixed_site, + current_dir, + ) { Ok(Ok(subtree)) => Ok(subtree), Ok(Err(err)) => Err(ProcMacroExpansionError::Panic(err.0)), Err(err) => Err(ProcMacroExpansionError::System(err.to_string())), diff --git a/src/tools/rust-analyzer/crates/mbe/Cargo.toml b/src/tools/rust-analyzer/crates/mbe/Cargo.toml index 756d42ef573f..b37d57f28012 100644 --- a/src/tools/rust-analyzer/crates/mbe/Cargo.toml +++ b/src/tools/rust-analyzer/crates/mbe/Cargo.toml @@ -18,6 +18,7 @@ rustc-hash.workspace = true smallvec.workspace = true tracing.workspace = true arrayvec.workspace = true +ra-ap-rustc_lexer.workspace = true # local deps syntax.workspace = true @@ -30,6 +31,7 @@ syntax-bridge.workspace = true [dev-dependencies] test-utils.workspace = true +expect-test.workspace = true [features] in-rust-tree = ["parser/in-rust-tree", "tt/in-rust-tree", "syntax/in-rust-tree"] diff --git a/src/tools/rust-analyzer/crates/mbe/src/benchmark.rs b/src/tools/rust-analyzer/crates/mbe/src/benchmark.rs index 43604eb232dc..9e4ef1407408 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/benchmark.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/benchmark.rs @@ -146,7 +146,7 @@ fn invocation_fixtures( Some(MetaVarKind::Pat) => token_trees.push(make_ident("foo")), Some(MetaVarKind::Path) => token_trees.push(make_ident("foo")), Some(MetaVarKind::Literal) => token_trees.push(make_literal("1")), - Some(MetaVarKind::Expr) => token_trees.push(make_ident("foo")), + Some(MetaVarKind::Expr(_)) => token_trees.push(make_ident("foo")), Some(MetaVarKind::Lifetime) => { token_trees.push(make_punct('\'')); token_trees.push(make_ident("a")); @@ -216,7 +216,11 @@ fn invocation_fixtures( token_trees.push(subtree.into()); } - Op::Ignore { .. } | Op::Index { .. } | Op::Count { .. } | Op::Len { .. } => {} + Op::Ignore { .. } + | Op::Index { .. } + | Op::Count { .. } + | Op::Len { .. } + | Op::Concat { .. } => {} }; // Simple linear congruential generator for deterministic result 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 e69d7d14e253..95641abc0f37 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs @@ -69,7 +69,7 @@ use tt::{iter::TtIter, DelimSpan}; use crate::{ expander::{Binding, Bindings, ExpandResult, Fragment}, expect_fragment, - parser::{MetaVarKind, Op, RepeatKind, Separator}, + parser::{ExprKind, MetaVarKind, Op, RepeatKind, Separator}, ExpandError, ExpandErrorKind, MetaTemplate, ValueResult, }; @@ -584,7 +584,11 @@ fn match_loop_inner<'t>( error_items.push(item); } OpDelimited::Op( - Op::Ignore { .. } | Op::Index { .. } | Op::Count { .. } | Op::Len { .. }, + Op::Ignore { .. } + | Op::Index { .. } + | Op::Count { .. } + | Op::Len { .. } + | Op::Concat { .. }, ) => { stdx::never!("metavariable expression in lhs found"); } @@ -769,23 +773,28 @@ fn match_meta_var( 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 - // two are for [backwards compatibility][0]. + MetaVarKind::Expr(expr) => { + // `expr_2021` should not match underscores, let expressions, or inline const. + // The latter two are for [backwards compatibility][0]. + // And `expr` also should not contain let expressions but may contain the other two + // since `Edition2024`. // HACK: Macro expansion should not be done using "rollback and try another alternative". // rustc [explicitly checks the next token][1]. // [0]: https://github.com/rust-lang/rust/issues/86730 // [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.sym == sym::underscore - || it.sym == sym::let_ - || it.sym == sym::const_ => - { - return ExpandResult::only_err(ExpandError::new( - it.span, - ExpandErrorKind::NoMatchingRule, - )) + Some(tt::TokenTree::Leaf(tt::Leaf::Ident(it))) => { + let is_err = if it.is_raw.no() && matches!(expr, ExprKind::Expr2021) { + it.sym == sym::underscore || it.sym == sym::let_ || it.sym == sym::const_ + } else { + it.sym == sym::let_ + }; + if is_err { + return ExpandResult::only_err(ExpandError::new( + it.span, + ExpandErrorKind::NoMatchingRule, + )); + } } _ => {} }; @@ -874,7 +883,11 @@ fn collect_vars(collector_fun: &mut impl FnMut(Symbol), pattern: &MetaTemplate) Op::Subtree { tokens, .. } => collect_vars(collector_fun, tokens), Op::Repeat { tokens, .. } => collect_vars(collector_fun, tokens), Op::Literal(_) | Op::Ident(_) | Op::Punct(_) => {} - Op::Ignore { .. } | Op::Index { .. } | Op::Count { .. } | Op::Len { .. } => { + Op::Ignore { .. } + | Op::Index { .. } + | Op::Count { .. } + | Op::Len { .. } + | Op::Concat { .. } => { stdx::never!("metavariable expression in lhs found"); } } 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 286bd748cbe7..1db2f35d2623 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/expander/transcriber.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/expander/transcriber.rs @@ -2,12 +2,12 @@ //! `$ident => foo`, interpolates variables in the template, to get `fn foo() {}` use intern::{sym, Symbol}; -use span::Span; +use span::{Edition, Span}; use tt::Delimiter; use crate::{ expander::{Binding, Bindings, Fragment}, - parser::{MetaVarKind, Op, RepeatKind, Separator}, + parser::{ConcatMetaVarExprElem, MetaVarKind, Op, RepeatKind, Separator}, ExpandError, ExpandErrorKind, ExpandResult, MetaTemplate, }; @@ -97,7 +97,7 @@ impl Bindings { | MetaVarKind::Ty | MetaVarKind::Pat | MetaVarKind::PatParam - | MetaVarKind::Expr + | MetaVarKind::Expr(_) | MetaVarKind::Ident => { Fragment::Tokens(tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { sym: sym::missing.clone(), @@ -312,6 +312,82 @@ fn expand_subtree( .into(), ); } + Op::Concat { elements, span: concat_span } => { + let mut concatenated = String::new(); + for element in elements { + match element { + ConcatMetaVarExprElem::Ident(ident) => { + concatenated.push_str(ident.sym.as_str()) + } + ConcatMetaVarExprElem::Literal(lit) => { + // FIXME: This isn't really correct wrt. escaping, but that's what rustc does and anyway + // escaping is used most of the times for characters that are invalid in identifiers. + concatenated.push_str(lit.symbol.as_str()) + } + ConcatMetaVarExprElem::Var(var) => { + // Handling of repetitions in `${concat}` isn't fleshed out in rustc, so we currently + // err at it. + // FIXME: Do what rustc does for repetitions. + let var_value = match ctx.bindings.get_fragment( + &var.sym, + var.span, + &mut ctx.nesting, + marker, + ) { + Ok(var) => var, + Err(e) => { + if err.is_none() { + err = Some(e); + }; + continue; + } + }; + let value = match &var_value { + Fragment::Tokens(tt::TokenTree::Leaf(tt::Leaf::Ident(ident))) => { + ident.sym.as_str() + } + Fragment::Tokens(tt::TokenTree::Leaf(tt::Leaf::Literal(lit))) => { + lit.symbol.as_str() + } + _ => { + if err.is_none() { + err = Some(ExpandError::binding_error(var.span, "metavariables of `${concat(..)}` must be of type `ident`, `literal` or `tt`")) + } + continue; + } + }; + concatenated.push_str(value); + } + } + } + + // `${concat}` span comes from the macro (at least for now). + // See https://github.com/rust-lang/rust/blob/b0af276da341/compiler/rustc_expand/src/mbe/transcribe.rs#L724-L726. + let mut result_span = *concat_span; + marker(&mut result_span); + + // FIXME: NFC normalize the result. + if !rustc_lexer::is_ident(&concatenated) { + if err.is_none() { + err = Some(ExpandError::binding_error( + *concat_span, + "`${concat(..)}` is not generating a valid identifier", + )); + } + // Insert a dummy identifier for better parsing. + concatenated.clear(); + concatenated.push_str("__ra_concat_dummy"); + } + + let needs_raw = + parser::SyntaxKind::from_keyword(&concatenated, Edition::LATEST).is_some(); + let is_raw = if needs_raw { tt::IdentIsRaw::Yes } else { tt::IdentIsRaw::No }; + arena.push(tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { + is_raw, + span: result_span, + sym: Symbol::intern(&concatenated), + }))); + } } } // drain the elements added in this instance of expand_subtree diff --git a/src/tools/rust-analyzer/crates/mbe/src/lib.rs b/src/tools/rust-analyzer/crates/mbe/src/lib.rs index 88785537c7d2..ca10a2be2732 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/lib.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/lib.rs @@ -6,11 +6,20 @@ //! The tests for this functionality live in another crate: //! `hir_def::macro_expansion_tests::mbe`. +#![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; + mod expander; mod parser; #[cfg(test)] mod benchmark; +#[cfg(test)] +mod tests; use span::{Edition, Span, SyntaxContextId}; use syntax_bridge::to_parser_input; diff --git a/src/tools/rust-analyzer/crates/mbe/src/parser.rs b/src/tools/rust-analyzer/crates/mbe/src/parser.rs index 218c04640f12..b55edf4a5e0c 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/parser.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/parser.rs @@ -84,6 +84,10 @@ pub(crate) enum Op { // FIXME: `usize`` once we drop support for 1.76 depth: Option, }, + Concat { + elements: Box<[ConcatMetaVarExprElem]>, + span: Span, + }, Repeat { tokens: MetaTemplate, kind: RepeatKind, @@ -98,6 +102,18 @@ pub(crate) enum Op { Ident(tt::Ident), } +#[derive(Clone, Debug, PartialEq, Eq)] +pub(crate) enum ConcatMetaVarExprElem { + /// There is NO preceding dollar sign, which means that this identifier should be interpreted + /// as a literal. + Ident(tt::Ident), + /// There is a preceding dollar sign, which means that this identifier should be expanded + /// and interpreted as a variable. + Var(tt::Ident), + /// For example, a number or a string. + Literal(tt::Literal), +} + #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub(crate) enum RepeatKind { ZeroOrMore, @@ -105,6 +121,16 @@ pub(crate) enum RepeatKind { ZeroOrOne, } +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub(crate) enum ExprKind { + // Matches expressions using the post-edition 2024. Was written using + // `expr` in edition 2024 or later. + Expr, + // Matches expressions using the pre-edition 2024 rules. + // Either written using `expr` in edition 2021 or earlier or.was written using `expr_2021`. + Expr2021, +} + #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub(crate) enum MetaVarKind { Path, @@ -116,7 +142,7 @@ pub(crate) enum MetaVarKind { Meta, Item, Vis, - Expr, + Expr(ExprKind), Ident, Tt, Lifetime, @@ -277,17 +303,27 @@ fn eat_fragment_kind( let kind = match ident.sym.as_str() { "path" => MetaVarKind::Path, "ty" => MetaVarKind::Ty, - "pat" => match edition(ident.span.ctx) { - Edition::Edition2015 | Edition::Edition2018 => MetaVarKind::PatParam, - Edition::Edition2021 | Edition::Edition2024 => MetaVarKind::Pat, - }, + "pat" => { + if edition(ident.span.ctx).at_least_2021() { + MetaVarKind::Pat + } else { + MetaVarKind::PatParam + } + } "pat_param" => MetaVarKind::PatParam, "stmt" => MetaVarKind::Stmt, "block" => MetaVarKind::Block, "meta" => MetaVarKind::Meta, "item" => MetaVarKind::Item, "vis" => MetaVarKind::Vis, - "expr" => MetaVarKind::Expr, + "expr" => { + if edition(ident.span.ctx).at_least_2024() { + MetaVarKind::Expr(ExprKind::Expr) + } else { + MetaVarKind::Expr(ExprKind::Expr2021) + } + } + "expr_2021" => MetaVarKind::Expr(ExprKind::Expr2021), "ident" => MetaVarKind::Ident, "tt" => MetaVarKind::Tt, "lifetime" => MetaVarKind::Lifetime, @@ -364,6 +400,32 @@ fn parse_metavar_expr(src: &mut TtIter<'_, Span>) -> Result { let depth = if try_eat_comma(&mut args) { Some(parse_depth(&mut args)?) } else { None }; Op::Count { name: ident.sym.clone(), depth } } + s if sym::concat == *s => { + let mut elements = Vec::new(); + while let Some(next) = args.peek_n(0) { + let element = if let tt::TokenTree::Leaf(tt::Leaf::Literal(lit)) = next { + args.next().expect("already peeked"); + ConcatMetaVarExprElem::Literal(lit.clone()) + } else { + let is_var = try_eat_dollar(&mut args); + let ident = args.expect_ident_or_underscore()?.clone(); + + if is_var { + ConcatMetaVarExprElem::Var(ident) + } else { + ConcatMetaVarExprElem::Ident(ident) + } + }; + elements.push(element); + if args.peek_n(0).is_some() { + args.expect_comma()?; + } + } + if elements.len() < 2 { + return Err(()); + } + Op::Concat { elements: elements.into_boxed_slice(), span: func.span } + } _ => return Err(()), }; @@ -394,3 +456,11 @@ fn try_eat_comma(src: &mut TtIter<'_, Span>) -> bool { } false } + +fn try_eat_dollar(src: &mut TtIter<'_, Span>) -> bool { + if let Some(tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { char: '$', .. }))) = src.peek_n(0) { + let _ = src.next(); + return true; + } + false +} diff --git a/src/tools/rust-analyzer/crates/mbe/src/tests.rs b/src/tools/rust-analyzer/crates/mbe/src/tests.rs new file mode 100644 index 000000000000..e63ad113ffd2 --- /dev/null +++ b/src/tools/rust-analyzer/crates/mbe/src/tests.rs @@ -0,0 +1,332 @@ +//! Tests specific to declarative macros, aka macros by example. This covers +//! both stable `macro_rules!` macros as well as unstable `macro` macros. +// FIXME: Move more of the nameres independent tests from +// crates\hir-def\src\macro_expansion_tests\mod.rs to this +use expect_test::expect; +use span::{Edition, EditionedFileId, ErasedFileAstId, FileId, Span, SpanAnchor, SyntaxContextId}; +use stdx::format_to; +use tt::{TextRange, TextSize}; + +use crate::DeclarativeMacro; + +#[expect(deprecated)] +fn check_( + def_edition: Edition, + call_edition: Edition, + macro2: bool, + decl: &str, + arg: &str, + render_debug: bool, + expect: expect_test::Expect, + parse: parser::TopEntryPoint, +) { + let decl_tt = &syntax_bridge::parse_to_token_tree( + def_edition, + SpanAnchor { + file_id: EditionedFileId::new(FileId::from_raw(0), def_edition), + ast_id: ErasedFileAstId::from_raw(0), + }, + SyntaxContextId::ROOT, + decl, + ) + .unwrap(); + let mac = if macro2 { + DeclarativeMacro::parse_macro2(None, decl_tt, |_| def_edition) + } else { + DeclarativeMacro::parse_macro_rules(decl_tt, |_| def_edition) + }; + let call_anchor = SpanAnchor { + file_id: EditionedFileId::new(FileId::from_raw(1), call_edition), + ast_id: ErasedFileAstId::from_raw(0), + }; + let arg_tt = + syntax_bridge::parse_to_token_tree(call_edition, call_anchor, SyntaxContextId::ROOT, arg) + .unwrap(); + let res = mac.expand( + &arg_tt, + |_| (), + Span { + range: TextRange::up_to(TextSize::of(arg)), + anchor: call_anchor, + ctx: SyntaxContextId::ROOT, + }, + def_edition, + ); + let mut expect_res = String::new(); + if let Some(err) = res.err { + format_to!(expect_res, "{err:#?}\n\n",); + } + if render_debug { + format_to!(expect_res, "{:#?}\n\n", res.value.0); + } + let (node, _) = syntax_bridge::token_tree_to_syntax_node(&res.value.0, parse, def_edition); + format_to!( + expect_res, + "{}", + syntax_bridge::prettify_macro_expansion::prettify_macro_expansion( + node.syntax_node(), + &mut |it| it.clone() + ) + ); + expect.assert_eq(&expect_res); +} + +fn check( + def_edition: Edition, + call_edition: Edition, + decl: &str, + arg: &str, + expect: expect_test::Expect, +) { + check_( + def_edition, + call_edition, + false, + decl, + arg, + true, + expect, + parser::TopEntryPoint::SourceFile, + ); +} + +#[test] +fn token_mapping_smoke_test() { + check( + Edition::CURRENT, + Edition::CURRENT, + r#" +( struct $ident:ident ) => { + struct $ident { + map: ::std::collections::HashSet<()>, + } +}; +"#, + r#" +struct MyTraitMap2 +"#, + expect![[r#" + SUBTREE $$ 1:0@0..20#0 1:0@0..20#0 + IDENT struct 0:0@34..40#0 + IDENT MyTraitMap2 1:0@8..19#0 + SUBTREE {} 0:0@48..49#0 0:0@100..101#0 + IDENT map 0:0@58..61#0 + PUNCH : [alone] 0:0@61..62#0 + PUNCH : [joint] 0:0@63..64#0 + PUNCH : [alone] 0:0@64..65#0 + IDENT std 0:0@65..68#0 + PUNCH : [joint] 0:0@68..69#0 + PUNCH : [alone] 0:0@69..70#0 + IDENT collections 0:0@70..81#0 + PUNCH : [joint] 0:0@81..82#0 + PUNCH : [alone] 0:0@82..83#0 + IDENT HashSet 0:0@83..90#0 + PUNCH < [alone] 0:0@90..91#0 + SUBTREE () 0:0@91..92#0 0:0@92..93#0 + PUNCH > [joint] 0:0@93..94#0 + PUNCH , [alone] 0:0@94..95#0 + + struct MyTraitMap2 { + map: ::std::collections::HashSet<()>, + }"#]], + ); +} + +#[test] +fn token_mapping_floats() { + // Regression test for https://github.com/rust-lang/rust-analyzer/issues/12216 + // (and related issues) + check( + Edition::CURRENT, + Edition::CURRENT, + r#" +($($tt:tt)*) => { + $($tt)* +}; +"#, + r#" +fn main() { + 1; + 1.0; + ((1,),).0.0; + let x = 1; +} +"#, + expect![[r#" + SUBTREE $$ 1:0@0..63#0 1:0@0..63#0 + IDENT fn 1:0@1..3#0 + IDENT main 1:0@4..8#0 + SUBTREE () 1:0@8..9#0 1:0@9..10#0 + SUBTREE {} 1:0@11..12#0 1:0@61..62#0 + LITERAL Integer 1 1:0@17..18#0 + PUNCH ; [alone] 1:0@18..19#0 + LITERAL Float 1.0 1:0@24..27#0 + PUNCH ; [alone] 1:0@27..28#0 + SUBTREE () 1:0@33..34#0 1:0@39..40#0 + SUBTREE () 1:0@34..35#0 1:0@37..38#0 + LITERAL Integer 1 1:0@35..36#0 + PUNCH , [alone] 1:0@36..37#0 + PUNCH , [alone] 1:0@38..39#0 + PUNCH . [alone] 1:0@40..41#0 + LITERAL Float 0.0 1:0@41..44#0 + PUNCH ; [alone] 1:0@44..45#0 + IDENT let 1:0@50..53#0 + IDENT x 1:0@54..55#0 + PUNCH = [alone] 1:0@56..57#0 + LITERAL Integer 1 1:0@58..59#0 + PUNCH ; [alone] 1:0@59..60#0 + + fn main(){ + 1; + 1.0; + ((1,),).0.0; + let x = 1; + }"#]], + ); +} + +#[test] +fn expr_2021() { + check( + Edition::Edition2024, + Edition::Edition2024, + r#" +($($e:expr),* $(,)?) => { + $($e);* ; +}; +"#, + r#" + _, + const { 1 }, +"#, + expect![[r#" + SUBTREE $$ 1:0@0..25#0 1:0@0..25#0 + IDENT _ 1:0@5..6#0 + PUNCH ; [joint] 0:0@36..37#0 + SUBTREE () 0:0@34..35#0 0:0@34..35#0 + IDENT const 1:0@12..17#0 + SUBTREE {} 1:0@18..19#0 1:0@22..23#0 + LITERAL Integer 1 1:0@20..21#0 + PUNCH ; [alone] 0:0@39..40#0 + + _; + (const { + 1 + });"#]], + ); + check( + Edition::Edition2021, + Edition::Edition2024, + r#" +($($e:expr),* $(,)?) => { + $($e);* ; +}; +"#, + r#" + _, +"#, + expect![[r#" + ExpandError { + inner: ( + 1:0@5..6#0, + NoMatchingRule, + ), + } + + SUBTREE $$ 1:0@0..8#0 1:0@0..8#0 + PUNCH ; [alone] 0:0@39..40#0 + + ;"#]], + ); + check( + Edition::Edition2021, + Edition::Edition2024, + r#" +($($e:expr),* $(,)?) => { + $($e);* ; +}; +"#, + r#" + const { 1 }, +"#, + expect![[r#" + ExpandError { + inner: ( + 1:0@5..10#0, + NoMatchingRule, + ), + } + + SUBTREE $$ 1:0@0..18#0 1:0@0..18#0 + PUNCH ; [alone] 0:0@39..40#0 + + ;"#]], + ); + check( + Edition::Edition2024, + Edition::Edition2024, + r#" +($($e:expr_2021),* $(,)?) => { + $($e);* ; +}; +"#, + r#" + 4, + "literal", + funcall(), + future.await, + break 'foo bar, +"#, + expect![[r#" + SUBTREE $$ 1:0@0..76#0 1:0@0..76#0 + LITERAL Integer 4 1:0@5..6#0 + PUNCH ; [joint] 0:0@41..42#0 + LITERAL Str literal 1:0@12..21#0 + PUNCH ; [joint] 0:0@41..42#0 + SUBTREE () 0:0@39..40#0 0:0@39..40#0 + IDENT funcall 1:0@27..34#0 + SUBTREE () 1:0@34..35#0 1:0@35..36#0 + PUNCH ; [joint] 0:0@41..42#0 + SUBTREE () 0:0@39..40#0 0:0@39..40#0 + IDENT future 1:0@42..48#0 + PUNCH . [alone] 1:0@48..49#0 + IDENT await 1:0@49..54#0 + PUNCH ; [joint] 0:0@41..42#0 + SUBTREE () 0:0@39..40#0 0:0@39..40#0 + IDENT break 1:0@60..65#0 + PUNCH ' [joint] 1:0@66..67#0 + IDENT foo 1:0@67..70#0 + IDENT bar 1:0@71..74#0 + PUNCH ; [alone] 0:0@44..45#0 + + 4; + "literal"; + (funcall()); + (future.await); + (break 'foo bar);"#]], + ); + check( + Edition::Edition2024, + Edition::Edition2024, + r#" +($($e:expr_2021),* $(,)?) => { + $($e);* ; +}; +"#, + r#" + _, +"#, + expect![[r#" + ExpandError { + inner: ( + 1:0@5..6#0, + NoMatchingRule, + ), + } + + SUBTREE $$ 1:0@0..8#0 1:0@0..8#0 + PUNCH ; [alone] 0:0@44..45#0 + + ;"#]], + ); +} diff --git a/src/tools/rust-analyzer/crates/parser/src/edition.rs b/src/tools/rust-analyzer/crates/parser/src/edition.rs index be0a2c794e58..702b16252d4e 100644 --- a/src/tools/rust-analyzer/crates/parser/src/edition.rs +++ b/src/tools/rust-analyzer/crates/parser/src/edition.rs @@ -30,6 +30,12 @@ impl Edition { pub fn at_least_2018(self) -> bool { self >= Edition::Edition2018 } + + pub fn iter() -> impl Iterator { + [Edition::Edition2015, Edition::Edition2018, Edition::Edition2021, Edition::Edition2024] + .iter() + .copied() + } } #[derive(Debug)] 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 a678c1f3a70e..2333e6c862be 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 @@ -245,7 +245,7 @@ fn tuple_expr(p: &mut Parser<'_>) -> CompletedMarker { // test builtin_expr // fn foo() { -// builtin#asm(0); +// builtin#asm(""); // builtin#format_args("", 0, 1, a = 2 + 3, a + b); // builtin#offset_of(Foo, bar.baz.0); // } @@ -297,18 +297,188 @@ fn builtin_expr(p: &mut Parser<'_>) -> Option { p.expect(T![')']); Some(m.complete(p, FORMAT_ARGS_EXPR)) } else if p.at_contextual_kw(T![asm]) { - p.bump_remap(T![asm]); - p.expect(T!['(']); - // FIXME: We just put expression here so highlighting kind of keeps working - expr(p); - p.expect(T![')']); - Some(m.complete(p, ASM_EXPR)) + parse_asm_expr(p, m) } else { m.abandon(p); None } } +// test asm_expr +// fn foo() { +// builtin#asm( +// "mov {tmp}, {x}", +// "shl {tmp}, 1", +// "shl {x}, 2", +// "add {x}, {tmp}", +// x = inout(reg) x, +// tmp = out(reg) _, +// ); +// } +fn parse_asm_expr(p: &mut Parser<'_>, m: Marker) -> Option { + p.bump_remap(T![asm]); + p.expect(T!['(']); + if expr(p).is_none() { + p.err_and_bump("expected asm template"); + } + let mut allow_templates = true; + while !p.at(EOF) && !p.at(T![')']) { + p.expect(T![,]); + // accept trailing commas + if p.at(T![')']) { + break; + } + + let op_n = p.start(); + // Parse clobber_abi + if p.eat_contextual_kw(T![clobber_abi]) { + parse_clobber_abi(p); + op_n.complete(p, ASM_CLOBBER_ABI); + allow_templates = false; + continue; + } + + // Parse options + if p.eat_contextual_kw(T![options]) { + parse_options(p); + op_n.complete(p, ASM_OPTIONS); + allow_templates = false; + continue; + } + + // Parse operand names + if p.at(T![ident]) && p.nth_at(1, T![=]) { + name(p); + p.bump(T![=]); + allow_templates = false; + true + } else { + false + }; + + let op = p.start(); + let dir_spec = p.start(); + if p.eat(T![in]) || p.eat_contextual_kw(T![out]) || p.eat_contextual_kw(T![lateout]) { + dir_spec.complete(p, ASM_DIR_SPEC); + parse_reg(p); + let op_expr = p.start(); + expr(p); + op_expr.complete(p, ASM_OPERAND_EXPR); + op.complete(p, ASM_REG_OPERAND); + op_n.complete(p, ASM_OPERAND_NAMED); + } else if p.eat_contextual_kw(T![inout]) || p.eat_contextual_kw(T![inlateout]) { + dir_spec.complete(p, ASM_DIR_SPEC); + parse_reg(p); + let op_expr = p.start(); + expr(p); + if p.eat(T![=>]) { + expr(p); + } + op_expr.complete(p, ASM_OPERAND_EXPR); + op.complete(p, ASM_REG_OPERAND); + op_n.complete(p, ASM_OPERAND_NAMED); + } else if p.eat_contextual_kw(T![label]) { + dir_spec.abandon(p); + block_expr(p); + op.complete(p, ASM_OPERAND_NAMED); + op_n.complete(p, ASM_LABEL); + } else if p.eat(T![const]) { + dir_spec.abandon(p); + expr(p); + op.complete(p, ASM_CONST); + op_n.complete(p, ASM_OPERAND_NAMED); + } else if p.eat_contextual_kw(T![sym]) { + dir_spec.abandon(p); + paths::type_path(p); + op.complete(p, ASM_SYM); + op_n.complete(p, ASM_OPERAND_NAMED); + } else if allow_templates { + dir_spec.abandon(p); + op.abandon(p); + op_n.abandon(p); + if expr(p).is_none() { + p.err_and_bump("expected asm template"); + } + continue; + } else { + dir_spec.abandon(p); + op.abandon(p); + op_n.abandon(p); + p.err_and_bump("expected asm operand"); + if p.at(T!['}']) { + break; + } + continue; + }; + allow_templates = false; + } + p.expect(T![')']); + Some(m.complete(p, ASM_EXPR)) +} + +fn parse_options(p: &mut Parser<'_>) { + p.expect(T!['(']); + + while !p.eat(T![')']) && !p.at(EOF) { + const OPTIONS: &[SyntaxKind] = &[ + T![pure], + T![nomem], + T![readonly], + T![preserves_flags], + T![noreturn], + T![nostack], + T![may_unwind], + T![att_syntax], + T![raw], + ]; + let m = p.start(); + if !OPTIONS.iter().any(|&syntax| p.eat_contextual_kw(syntax)) { + p.err_and_bump("expected asm option"); + m.abandon(p); + continue; + } + m.complete(p, ASM_OPTION); + + // Allow trailing commas + if p.eat(T![')']) { + break; + } + p.expect(T![,]); + } +} + +fn parse_clobber_abi(p: &mut Parser<'_>) { + p.expect(T!['(']); + + while !p.eat(T![')']) && !p.at(EOF) { + if !p.expect(T![string]) { + break; + } + + // Allow trailing commas + if p.eat(T![')']) { + break; + } + p.expect(T![,]); + } +} + +fn parse_reg(p: &mut Parser<'_>) { + p.expect(T!['(']); + if p.at(T![ident]) { + let m = p.start(); + name_ref(p); + m.complete(p, ASM_REG_SPEC); + } else if p.at(T![string]) { + let m = p.start(); + p.bump_any(); + m.complete(p, ASM_REG_SPEC); + } else { + p.err_and_bump("expected register name"); + } + p.expect(T![')']); +} + // test array_expr // fn foo() { // []; 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 3590486bd29e..dceac815e0b5 100644 --- a/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs +++ b/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs @@ -202,9 +202,7 @@ impl<'a> Converter<'a> { err = "Unknown lifetime prefix"; LIFETIME_IDENT } - rustc_lexer::TokenKind::RawLifetime => { - LIFETIME_IDENT - } + rustc_lexer::TokenKind::RawLifetime => LIFETIME_IDENT, rustc_lexer::TokenKind::Semi => T![;], rustc_lexer::TokenKind::Comma => T![,], diff --git a/src/tools/rust-analyzer/crates/parser/src/parser.rs b/src/tools/rust-analyzer/crates/parser/src/parser.rs index 7d3eb5de25f9..f6b3783d1cac 100644 --- a/src/tools/rust-analyzer/crates/parser/src/parser.rs +++ b/src/tools/rust-analyzer/crates/parser/src/parser.rs @@ -131,6 +131,14 @@ impl<'t> Parser<'t> { true } + pub(crate) fn eat_contextual_kw(&mut self, kind: SyntaxKind) -> bool { + if !self.at_contextual_kw(kind) { + return false; + } + self.bump_remap(kind); + true + } + fn at_composite2(&self, n: usize, k1: SyntaxKind, k2: SyntaxKind) -> bool { self.inp.kind(self.pos + n) == k1 && self.inp.kind(self.pos + n + 1) == k2 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 00f212487ae6..288a07ef44dc 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 @@ -111,16 +111,32 @@ pub enum SyntaxKind { YIELD_KW, ASM_KW, ASYNC_KW, + ATT_SYNTAX_KW, AUTO_KW, AWAIT_KW, BUILTIN_KW, + CLOBBER_ABI_KW, DEFAULT_KW, DYN_KW, FORMAT_ARGS_KW, GEN_KW, + INLATEOUT_KW, + INOUT_KW, + LABEL_KW, + LATEOUT_KW, MACRO_RULES_KW, + MAY_UNWIND_KW, + NOMEM_KW, + NORETURN_KW, + NOSTACK_KW, OFFSET_OF_KW, + OPTIONS_KW, + OUT_KW, + PRESERVES_FLAGS_KW, + PURE_KW, RAW_KW, + READONLY_KW, + SYM_KW, TRY_KW, UNION_KW, YEET_KW, @@ -146,7 +162,20 @@ pub enum SyntaxKind { ARG_LIST, ARRAY_EXPR, ARRAY_TYPE, + ASM_CLOBBER_ABI, + ASM_CONST, + ASM_DIR_SPEC, ASM_EXPR, + ASM_LABEL, + ASM_OPERAND, + ASM_OPERAND_EXPR, + ASM_OPERAND_NAMED, + ASM_OPTION, + ASM_OPTIONS, + ASM_PIECE, + ASM_REG_OPERAND, + ASM_REG_SPEC, + ASM_SYM, ASSOC_ITEM, ASSOC_ITEM_LIST, ASSOC_TYPE_ARG, @@ -364,14 +393,30 @@ impl SyntaxKind { pub fn is_contextual_keyword(self, edition: Edition) -> bool { match self { ASM_KW => true, + ATT_SYNTAX_KW => true, AUTO_KW => true, BUILTIN_KW => true, + CLOBBER_ABI_KW => true, DEFAULT_KW => true, DYN_KW if edition < Edition::Edition2018 => true, FORMAT_ARGS_KW => true, + INLATEOUT_KW => true, + INOUT_KW => true, + LABEL_KW => true, + LATEOUT_KW => true, MACRO_RULES_KW => true, + MAY_UNWIND_KW => true, + NOMEM_KW => true, + NORETURN_KW => true, + NOSTACK_KW => true, OFFSET_OF_KW => true, + OPTIONS_KW => true, + OUT_KW => true, + PRESERVES_FLAGS_KW => true, + PURE_KW => true, RAW_KW => true, + READONLY_KW => true, + SYM_KW => true, UNION_KW => true, YEET_KW => true, _ => false, @@ -435,14 +480,30 @@ impl SyntaxKind { GEN_KW if Edition::Edition2024 <= edition => true, TRY_KW if Edition::Edition2018 <= edition => true, ASM_KW => true, + ATT_SYNTAX_KW => true, AUTO_KW => true, BUILTIN_KW => true, + CLOBBER_ABI_KW => true, DEFAULT_KW => true, DYN_KW if edition < Edition::Edition2018 => true, FORMAT_ARGS_KW => true, + INLATEOUT_KW => true, + INOUT_KW => true, + LABEL_KW => true, + LATEOUT_KW => true, MACRO_RULES_KW => true, + MAY_UNWIND_KW => true, + NOMEM_KW => true, + NORETURN_KW => true, + NOSTACK_KW => true, OFFSET_OF_KW => true, + OPTIONS_KW => true, + OUT_KW => true, + PRESERVES_FLAGS_KW => true, + PURE_KW => true, RAW_KW => true, + READONLY_KW => true, + SYM_KW => true, UNION_KW => true, YEET_KW => true, _ => false, @@ -580,14 +641,30 @@ impl SyntaxKind { pub fn from_contextual_keyword(ident: &str, edition: Edition) -> Option { let kw = match ident { "asm" => ASM_KW, + "att_syntax" => ATT_SYNTAX_KW, "auto" => AUTO_KW, "builtin" => BUILTIN_KW, + "clobber_abi" => CLOBBER_ABI_KW, "default" => DEFAULT_KW, "dyn" if edition < Edition::Edition2018 => DYN_KW, "format_args" => FORMAT_ARGS_KW, + "inlateout" => INLATEOUT_KW, + "inout" => INOUT_KW, + "label" => LABEL_KW, + "lateout" => LATEOUT_KW, "macro_rules" => MACRO_RULES_KW, + "may_unwind" => MAY_UNWIND_KW, + "nomem" => NOMEM_KW, + "noreturn" => NORETURN_KW, + "nostack" => NOSTACK_KW, "offset_of" => OFFSET_OF_KW, + "options" => OPTIONS_KW, + "out" => OUT_KW, + "preserves_flags" => PRESERVES_FLAGS_KW, + "pure" => PURE_KW, "raw" => RAW_KW, + "readonly" => READONLY_KW, + "sym" => SYM_KW, "union" => UNION_KW, "yeet" => YEET_KW, _ => return None, @@ -630,4 +707,4 @@ impl SyntaxKind { } } #[macro_export] -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 } ; [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 } ; [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 } ; [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 } ; [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 } ; [dyn] => { $ crate :: SyntaxKind :: DYN_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 } ; [async] => { $ crate :: SyntaxKind :: ASYNC_KW } ; [await] => { $ crate :: SyntaxKind :: AWAIT_KW } ; [dyn] => { $ crate :: SyntaxKind :: DYN_KW } ; [gen] => { $ crate :: SyntaxKind :: GEN_KW } ; [try] => { $ crate :: SyntaxKind :: TRY_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 } ; } +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 } ; [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 } ; [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 } ; [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 } ; [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 } ; [att_syntax] => { $ crate :: SyntaxKind :: ATT_SYNTAX_KW } ; [auto] => { $ crate :: SyntaxKind :: AUTO_KW } ; [builtin] => { $ crate :: SyntaxKind :: BUILTIN_KW } ; [clobber_abi] => { $ crate :: SyntaxKind :: CLOBBER_ABI_KW } ; [default] => { $ crate :: SyntaxKind :: DEFAULT_KW } ; [dyn] => { $ crate :: SyntaxKind :: DYN_KW } ; [format_args] => { $ crate :: SyntaxKind :: FORMAT_ARGS_KW } ; [inlateout] => { $ crate :: SyntaxKind :: INLATEOUT_KW } ; [inout] => { $ crate :: SyntaxKind :: INOUT_KW } ; [label] => { $ crate :: SyntaxKind :: LABEL_KW } ; [lateout] => { $ crate :: SyntaxKind :: LATEOUT_KW } ; [macro_rules] => { $ crate :: SyntaxKind :: MACRO_RULES_KW } ; [may_unwind] => { $ crate :: SyntaxKind :: MAY_UNWIND_KW } ; [nomem] => { $ crate :: SyntaxKind :: NOMEM_KW } ; [noreturn] => { $ crate :: SyntaxKind :: NORETURN_KW } ; [nostack] => { $ crate :: SyntaxKind :: NOSTACK_KW } ; [offset_of] => { $ crate :: SyntaxKind :: OFFSET_OF_KW } ; [options] => { $ crate :: SyntaxKind :: OPTIONS_KW } ; [out] => { $ crate :: SyntaxKind :: OUT_KW } ; [preserves_flags] => { $ crate :: SyntaxKind :: PRESERVES_FLAGS_KW } ; [pure] => { $ crate :: SyntaxKind :: PURE_KW } ; [raw] => { $ crate :: SyntaxKind :: RAW_KW } ; [readonly] => { $ crate :: SyntaxKind :: READONLY_KW } ; [sym] => { $ crate :: SyntaxKind :: SYM_KW } ; [union] => { $ crate :: SyntaxKind :: UNION_KW } ; [yeet] => { $ crate :: SyntaxKind :: YEET_KW } ; [async] => { $ crate :: SyntaxKind :: ASYNC_KW } ; [await] => { $ crate :: SyntaxKind :: AWAIT_KW } ; [dyn] => { $ crate :: SyntaxKind :: DYN_KW } ; [gen] => { $ crate :: SyntaxKind :: GEN_KW } ; [try] => { $ crate :: SyntaxKind :: TRY_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/test_data/generated/runner.rs b/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs index 9ce5a2ae748f..164d0f36f1be 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs +++ b/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs @@ -19,6 +19,8 @@ mod ok { #[test] fn as_precedence() { run_and_expect_no_errors("test_data/parser/inline/ok/as_precedence.rs"); } #[test] + fn asm_expr() { run_and_expect_no_errors("test_data/parser/inline/ok/asm_expr.rs"); } + #[test] fn assoc_const_eq() { run_and_expect_no_errors("test_data/parser/inline/ok/assoc_const_eq.rs"); } diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/asm_expr.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/asm_expr.rast new file mode 100644 index 000000000000..f0213d0b5e36 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/asm_expr.rast @@ -0,0 +1,85 @@ +SOURCE_FILE + FN + FN_KW "fn" + WHITESPACE " " + NAME + IDENT "foo" + PARAM_LIST + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE "\n " + EXPR_STMT + ASM_EXPR + BUILTIN_KW "builtin" + POUND "#" + ASM_KW "asm" + L_PAREN "(" + WHITESPACE "\n " + LITERAL + STRING "\"mov {tmp}, {x}\"" + COMMA "," + WHITESPACE "\n " + LITERAL + STRING "\"shl {tmp}, 1\"" + COMMA "," + WHITESPACE "\n " + LITERAL + STRING "\"shl {x}, 2\"" + COMMA "," + WHITESPACE "\n " + LITERAL + STRING "\"add {x}, {tmp}\"" + COMMA "," + WHITESPACE "\n " + ASM_OPERAND_NAMED + NAME + IDENT "x" + WHITESPACE " " + EQ "=" + WHITESPACE " " + ASM_REG_OPERAND + ASM_DIR_SPEC + INOUT_KW "inout" + L_PAREN "(" + ASM_REG_SPEC + NAME_REF + IDENT "reg" + R_PAREN ")" + WHITESPACE " " + ASM_OPERAND_EXPR + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "x" + COMMA "," + WHITESPACE "\n " + ASM_OPERAND_NAMED + NAME + IDENT "tmp" + WHITESPACE " " + EQ "=" + WHITESPACE " " + ASM_REG_OPERAND + ASM_DIR_SPEC + OUT_KW "out" + L_PAREN "(" + ASM_REG_SPEC + NAME_REF + IDENT "reg" + R_PAREN ")" + WHITESPACE " " + ASM_OPERAND_EXPR + UNDERSCORE_EXPR + UNDERSCORE "_" + COMMA "," + WHITESPACE "\n " + R_PAREN ")" + SEMICOLON ";" + WHITESPACE "\n" + R_CURLY "}" + WHITESPACE "\n" diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/asm_expr.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/asm_expr.rs new file mode 100644 index 000000000000..0906cd3e7197 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/asm_expr.rs @@ -0,0 +1,10 @@ +fn foo() { + builtin#asm( + "mov {tmp}, {x}", + "shl {tmp}, 1", + "shl {x}, 2", + "add {x}, {tmp}", + x = inout(reg) x, + tmp = out(reg) _, + ); +} diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/builtin_expr.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/builtin_expr.rast index 361900b6d3e4..19a84ac54096 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/builtin_expr.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/builtin_expr.rast @@ -19,7 +19,7 @@ SOURCE_FILE ASM_KW "asm" L_PAREN "(" LITERAL - INT_NUMBER "0" + STRING "\"\"" R_PAREN ")" SEMICOLON ";" WHITESPACE "\n " diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/builtin_expr.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/builtin_expr.rs index 14431b0210ea..920d0f794f24 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/builtin_expr.rs +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/builtin_expr.rs @@ -1,5 +1,5 @@ fn foo() { - builtin#asm(0); + builtin#asm(""); builtin#format_args("", 0, 1, a = 2 + 3, a + b); builtin#offset_of(Foo, bar.baz.0); } 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 d50a3cdbf72d..011baad65f7d 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 @@ -152,10 +152,9 @@ impl ProcMacro { def_site: Span, call_site: Span, mixed_site: Span, + current_dir: Option, ) -> Result, PanicMessage>, ServerError> { let version = self.process.version(); - let current_dir = - env.get("CARGO_RUSTC_CURRENT_DIR").or_else(|| env.get("CARGO_MANIFEST_DIR")); let mut span_data_table = SpanDataIndexMap::default(); let def_site = span_data_table.insert_full(def_site).0; diff --git a/src/tools/rust-analyzer/crates/project-model/src/lib.rs b/src/tools/rust-analyzer/crates/project-model/src/lib.rs index b8ac55ed0d5d..91bdef4889ca 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/lib.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/lib.rs @@ -52,6 +52,15 @@ pub use crate::{ }; pub use cargo_metadata::Metadata; +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ProjectJsonFromCommand { + /// The data describing this project, such as its dependencies. + pub data: ProjectJsonData, + /// The build system specific file that describes this project, + /// such as a `my-project/BUCK` file. + pub buildfile: AbsPathBuf, +} + #[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)] pub enum ProjectManifest { ProjectJson(ManifestPath), 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 a09c7a77abce..6a88cf022dfb 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 @@ -66,6 +66,8 @@ pub struct ProjectJson { /// e.g. `path/to/sysroot/lib/rustlib/src/rust` pub(crate) sysroot_src: Option, project_root: AbsPathBuf, + /// The path to the rust-project.json file. May be None if this + /// data was generated by the discoverConfig command. manifest: Option, crates: Vec, /// Configuration for CLI commands. 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 f540bb94c196..30d1ddb636ef 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/tests.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/tests.rs @@ -45,39 +45,6 @@ fn load_cargo_with_overrides( to_crate_graph(project_workspace) } -fn load_cargo_with_fake_sysroot( - file_map: &mut FxHashMap, - file: &str, -) -> (CrateGraph, ProcMacroPaths) { - let meta: Metadata = get_test_json_file(file); - let manifest_path = - ManifestPath::try_from(AbsPathBuf::try_from(meta.workspace_root.clone()).unwrap()).unwrap(); - let cargo_workspace = CargoWorkspace::new(meta, manifest_path); - let project_workspace = ProjectWorkspace { - kind: ProjectWorkspaceKind::Cargo { - cargo: cargo_workspace, - build_scripts: WorkspaceBuildScripts::default(), - rustc: Err(None), - cargo_config_extra_env: Default::default(), - error: None, - }, - sysroot: get_fake_sysroot(), - rustc_cfg: Vec::new(), - cfg_overrides: Default::default(), - toolchain: None, - target_layout: Err("target_data_layout not loaded".into()), - }; - project_workspace.to_crate_graph( - &mut { - |path| { - let len = file_map.len(); - Some(*file_map.entry(path.to_path_buf()).or_insert(FileId::from_raw(len as u32))) - } - }, - &Default::default(), - ) -} - fn load_rust_project(file: &str) -> (CrateGraph, ProcMacroPaths) { let data = get_test_json_file(file); let project = rooted_project_json(data); @@ -253,34 +220,6 @@ fn rust_project_is_proc_macro_has_proc_macro_dep() { crate_data.dependencies.iter().find(|&dep| dep.name.deref() == "proc_macro").unwrap(); } -#[test] -fn crate_graph_dedup_identical() { - let (mut crate_graph, proc_macros) = - load_cargo_with_fake_sysroot(&mut Default::default(), "regex-metadata.json"); - crate_graph.sort_deps(); - - let (d_crate_graph, mut d_proc_macros) = (crate_graph.clone(), proc_macros.clone()); - - crate_graph.extend(d_crate_graph.clone(), &mut d_proc_macros, |(_, a), (_, b)| a == b); - assert!(crate_graph.iter().eq(d_crate_graph.iter())); - assert_eq!(proc_macros, d_proc_macros); -} - -#[test] -fn crate_graph_dedup() { - let path_map = &mut Default::default(); - let (mut crate_graph, _proc_macros) = - load_cargo_with_fake_sysroot(path_map, "ripgrep-metadata.json"); - assert_eq!(crate_graph.iter().count(), 81); - crate_graph.sort_deps(); - let (regex_crate_graph, mut regex_proc_macros) = - load_cargo_with_fake_sysroot(path_map, "regex-metadata.json"); - assert_eq!(regex_crate_graph.iter().count(), 60); - - crate_graph.extend(regex_crate_graph, &mut regex_proc_macros, |(_, a), (_, b)| a == b); - assert_eq!(crate_graph.iter().count(), 118); -} - #[test] // FIXME Remove the ignore #[ignore = "requires nightly until the sysroot ships a cargo workspace for library on stable"] 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 7834238acefd..17b40a87cda9 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs @@ -109,7 +109,7 @@ impl fmt::Debug for ProjectWorkspace { ProjectWorkspaceKind::Cargo { cargo, error: _, - build_scripts: _, + build_scripts, rustc, cargo_config_extra_env, } => f @@ -126,6 +126,7 @@ impl fmt::Debug for ProjectWorkspace { .field("toolchain", &toolchain) .field("data_layout", &target_layout) .field("cargo_config_extra_env", &cargo_config_extra_env) + .field("build_scripts", &build_scripts.error().unwrap_or("ok")) .finish(), ProjectWorkspaceKind::Json(project) => { let mut debug_struct = f.debug_struct("Json"); @@ -1456,7 +1457,7 @@ fn sysroot_to_crate_graph( // Remove all crates except the ones we are interested in to keep the sysroot graph small. let removed_mapping = cg.remove_crates_except(&marker_set); - let mapping = crate_graph.extend(cg, &mut pm, |(_, a), (_, b)| a == b); + let mapping = crate_graph.extend(cg, &mut pm); // Map the id through the removal mapping first, then through the crate graph extension mapping. pub_deps.iter_mut().for_each(|(_, cid, _)| { @@ -1554,6 +1555,6 @@ fn add_proc_macro_dep(crate_graph: &mut CrateGraph, from: CrateId, to: CrateId, fn add_dep_inner(graph: &mut CrateGraph, from: CrateId, dep: Dependency) { if let Err(err) = graph.add_dep(from, dep) { - tracing::error!("{}", err) + tracing::warn!("{}", err) } } diff --git a/src/tools/rust-analyzer/crates/project-model/test_data/regex-metadata.json b/src/tools/rust-analyzer/crates/project-model/test_data/regex-metadata.json deleted file mode 100644 index 371464dd20aa..000000000000 --- a/src/tools/rust-analyzer/crates/project-model/test_data/regex-metadata.json +++ /dev/null @@ -1,6420 +0,0 @@ -{ - "packages": [ - { - "name": "aho-corasick", - "version": "0.7.20", - "id": "aho-corasick 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "Unlicense OR MIT", - "license_file": null, - "description": "Fast multiple substring searching.", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "memchr", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^2.4.0", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": false, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "aho_corasick", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/aho-corasick-0.7.20/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - } - ], - "features": { - "default": [ - "std" - ], - "std": [ - "memchr/std" - ] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/aho-corasick-0.7.20/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Andrew Gallant " - ], - "categories": [ - "text-processing" - ], - "keywords": [ - "string", - "search", - "text", - "aho", - "multi" - ], - "readme": "README.md", - "repository": "https://github.com/BurntSushi/aho-corasick", - "homepage": "https://github.com/BurntSushi/aho-corasick", - "documentation": null, - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "cc", - "version": "1.0.79", - "id": "cc 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT OR Apache-2.0", - "license_file": null, - "description": "A build-time dependency for Cargo build scripts to assist in invoking the native\nC compiler to compile native C code into a static archive to be linked into Rust\ncode.\n", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "jobserver", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.1.16", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "tempfile", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^3", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "cc", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "bin" - ], - "crate_types": [ - "bin" - ], - "name": "gcc-shim", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/src/bin/gcc-shim.rs", - "edition": "2018", - "doc": true, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "cc_env", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/tests/cc_env.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "cflags", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/tests/cflags.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "cxxflags", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/tests/cxxflags.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/tests/test.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - } - ], - "features": { - "jobserver": [ - "dep:jobserver" - ], - "parallel": [ - "jobserver" - ] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Alex Crichton " - ], - "categories": [ - "development-tools::build-utils" - ], - "keywords": [ - "build-dependencies" - ], - "readme": "README.md", - "repository": "https://github.com/rust-lang/cc-rs", - "homepage": "https://github.com/rust-lang/cc-rs", - "documentation": "https://docs.rs/cc", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "cfg-if", - "version": "0.1.10", - "id": "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT/Apache-2.0", - "license_file": null, - "description": "A macro to ergonomically define an item depending on a large number of #[cfg]\nparameters. Structured like an if-else chain, the first matching branch is the\nitem that gets emitted.\n", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "compiler_builtins", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.1.2", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "rustc-std-workspace-core", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.0", - "kind": null, - "rename": "core", - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "cfg-if", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cfg-if-0.1.10/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "xcrate", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cfg-if-0.1.10/tests/xcrate.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - } - ], - "features": { - "compiler_builtins": [ - "dep:compiler_builtins" - ], - "core": [ - "dep:core" - ], - "rustc-dep-of-std": [ - "core", - "compiler_builtins" - ] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cfg-if-0.1.10/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Alex Crichton " - ], - "categories": [], - "keywords": [], - "readme": "README.md", - "repository": "https://github.com/alexcrichton/cfg-if", - "homepage": "https://github.com/alexcrichton/cfg-if", - "documentation": "https://docs.rs/cfg-if", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "cfg-if", - "version": "1.0.0", - "id": "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT/Apache-2.0", - "license_file": null, - "description": "A macro to ergonomically define an item depending on a large number of #[cfg]\nparameters. Structured like an if-else chain, the first matching branch is the\nitem that gets emitted.\n", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "compiler_builtins", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.1.2", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "rustc-std-workspace-core", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.0", - "kind": null, - "rename": "core", - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "cfg-if", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cfg-if-1.0.0/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "xcrate", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cfg-if-1.0.0/tests/xcrate.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - } - ], - "features": { - "compiler_builtins": [ - "dep:compiler_builtins" - ], - "core": [ - "dep:core" - ], - "rustc-dep-of-std": [ - "core", - "compiler_builtins" - ] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cfg-if-1.0.0/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Alex Crichton " - ], - "categories": [], - "keywords": [], - "readme": "README.md", - "repository": "https://github.com/alexcrichton/cfg-if", - "homepage": "https://github.com/alexcrichton/cfg-if", - "documentation": "https://docs.rs/cfg-if", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "docopt", - "version": "1.1.1", - "id": "docopt 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "Unlicense/MIT", - "license_file": null, - "description": "Command line argument parsing.", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "lazy_static", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.3", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "regex", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.4.0", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": false, - "features": [ - "std", - "unicode" - ], - "target": null, - "registry": null - }, - { - "name": "serde", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [ - "derive" - ], - "target": null, - "registry": null - }, - { - "name": "strsim", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.10", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "docopt", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/docopt-1.1.1/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "bin" - ], - "crate_types": [ - "bin" - ], - "name": "docopt-wordlist", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/docopt-1.1.1/src/wordlist.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "cargo", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/docopt-1.1.1/examples/cargo.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "cp", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/docopt-1.1.1/examples/cp.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "decode", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/docopt-1.1.1/examples/decode.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "hashmap", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/docopt-1.1.1/examples/hashmap.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "optional_command", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/docopt-1.1.1/examples/optional_command.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "verbose_multiple", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/docopt-1.1.1/examples/verbose_multiple.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": {}, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/docopt-1.1.1/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Andrew Gallant " - ], - "categories": [ - "command-line-interface" - ], - "keywords": [ - "docopt", - "argument", - "command", - "argv" - ], - "readme": "README.md", - "repository": "https://github.com/docopt/docopt.rs", - "homepage": "https://github.com/docopt/docopt.rs", - "documentation": "http://burntsushi.net/rustdoc/docopt/", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "getrandom", - "version": "0.2.9", - "id": "getrandom 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT OR Apache-2.0", - "license_file": null, - "description": "A small cross-platform library for retrieving random data from system source", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "cfg-if", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "compiler_builtins", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.1", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "rustc-std-workspace-core", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": null, - "rename": "core", - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "js-sys", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.3", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": "cfg(all(any(target_arch = \"wasm32\", target_arch = \"wasm64\"), target_os = \"unknown\"))", - "registry": null - }, - { - "name": "wasm-bindgen", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.2.62", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": false, - "features": [], - "target": "cfg(all(any(target_arch = \"wasm32\", target_arch = \"wasm64\"), target_os = \"unknown\"))", - "registry": null - }, - { - "name": "wasm-bindgen-test", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.3.18", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": "cfg(all(any(target_arch = \"wasm32\", target_arch = \"wasm64\"), target_os = \"unknown\"))", - "registry": null - }, - { - "name": "wasi", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.11", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": false, - "features": [], - "target": "cfg(target_os = \"wasi\")", - "registry": null - }, - { - "name": "libc", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.2.139", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": false, - "features": [], - "target": "cfg(unix)", - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "getrandom", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/getrandom-0.2.9/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "custom", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/getrandom-0.2.9/tests/custom.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "normal", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/getrandom-0.2.9/tests/normal.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "rdrand", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/getrandom-0.2.9/tests/rdrand.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "bench" - ], - "crate_types": [ - "bin" - ], - "name": "buffer", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/getrandom-0.2.9/benches/buffer.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": { - "compiler_builtins": [ - "dep:compiler_builtins" - ], - "core": [ - "dep:core" - ], - "custom": [], - "js": [ - "wasm-bindgen", - "js-sys" - ], - "js-sys": [ - "dep:js-sys" - ], - "rdrand": [], - "rustc-dep-of-std": [ - "compiler_builtins", - "core", - "libc/rustc-dep-of-std", - "wasi/rustc-dep-of-std" - ], - "std": [], - "test-in-browser": [], - "wasm-bindgen": [ - "dep:wasm-bindgen" - ] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/getrandom-0.2.9/Cargo.toml", - "metadata": { - "docs": { - "rs": { - "features": [ - "std", - "custom" - ], - "rustdoc-args": [ - "--cfg", - "docsrs" - ] - } - } - }, - "publish": null, - "authors": [ - "The Rand Project Developers" - ], - "categories": [ - "os", - "no-std" - ], - "keywords": [], - "readme": "README.md", - "repository": "https://github.com/rust-random/getrandom", - "homepage": null, - "documentation": "https://docs.rs/getrandom", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "lazy_static", - "version": "1.4.0", - "id": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT/Apache-2.0", - "license_file": null, - "description": "A macro for declaring lazily evaluated statics in Rust.", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "spin", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.5.0", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "doc-comment", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.3.1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "lazy_static", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/lazy_static-1.4.0/src/lib.rs", - "edition": "2015", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "no_std", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/lazy_static-1.4.0/tests/no_std.rs", - "edition": "2015", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/lazy_static-1.4.0/tests/test.rs", - "edition": "2015", - "doc": false, - "doctest": false, - "test": true - } - ], - "features": { - "spin": [ - "dep:spin" - ], - "spin_no_std": [ - "spin" - ] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/lazy_static-1.4.0/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Marvin Löbel " - ], - "categories": [ - "no-std", - "rust-patterns", - "memory-management" - ], - "keywords": [ - "macro", - "lazy", - "static" - ], - "readme": "README.md", - "repository": "https://github.com/rust-lang-nursery/lazy-static.rs", - "homepage": null, - "documentation": "https://docs.rs/lazy_static", - "edition": "2015", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "libc", - "version": "0.2.142", - "id": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT OR Apache-2.0", - "license_file": null, - "description": "Raw FFI bindings to platform libraries like libc.\n", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "rustc-std-workspace-core", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.0", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "libc", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/libc-0.2.142/src/lib.rs", - "edition": "2015", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "const_fn", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/libc-0.2.142/tests/const_fn.rs", - "edition": "2015", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "custom-build" - ], - "crate_types": [ - "bin" - ], - "name": "build-script-build", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/libc-0.2.142/build.rs", - "edition": "2015", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": { - "align": [], - "const-extern-fn": [], - "default": [ - "std" - ], - "extra_traits": [], - "rustc-dep-of-std": [ - "align", - "rustc-std-workspace-core" - ], - "rustc-std-workspace-core": [ - "dep:rustc-std-workspace-core" - ], - "std": [], - "use_std": [ - "std" - ] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/libc-0.2.142/Cargo.toml", - "metadata": { - "docs": { - "rs": { - "features": [ - "const-extern-fn", - "extra_traits" - ] - } - } - }, - "publish": null, - "authors": [ - "The Rust Project Developers" - ], - "categories": [ - "external-ffi-bindings", - "no-std", - "os" - ], - "keywords": [ - "libc", - "ffi", - "bindings", - "operating", - "system" - ], - "readme": "README.md", - "repository": "https://github.com/rust-lang/libc", - "homepage": "https://github.com/rust-lang/libc", - "documentation": "https://docs.rs/libc/", - "edition": "2015", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "memchr", - "version": "2.5.0", - "id": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "Unlicense/MIT", - "license_file": null, - "description": "Safe interface to memchr.", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "compiler_builtins", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.1.2", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "rustc-std-workspace-core", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.0", - "kind": null, - "rename": "core", - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "libc", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.2.18", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": false, - "features": [], - "target": null, - "registry": null - }, - { - "name": "quickcheck", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.3", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": false, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "memchr", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memchr-2.5.0/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "custom-build" - ], - "crate_types": [ - "bin" - ], - "name": "build-script-build", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memchr-2.5.0/build.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": { - "compiler_builtins": [ - "dep:compiler_builtins" - ], - "core": [ - "dep:core" - ], - "default": [ - "std" - ], - "libc": [ - "dep:libc" - ], - "rustc-dep-of-std": [ - "core", - "compiler_builtins" - ], - "std": [], - "use_std": [ - "std" - ] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memchr-2.5.0/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Andrew Gallant ", - "bluss" - ], - "categories": [], - "keywords": [ - "memchr", - "char", - "scan", - "strchr", - "string" - ], - "readme": "README.md", - "repository": "https://github.com/BurntSushi/memchr", - "homepage": "https://github.com/BurntSushi/memchr", - "documentation": "https://docs.rs/memchr/", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "memmap", - "version": "0.6.2", - "id": "memmap 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT/Apache-2.0", - "license_file": null, - "description": "Cross-platform Rust API for memory-mapped file IO", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "tempdir", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.3", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "libc", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.2", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": "cfg(unix)", - "registry": null - }, - { - "name": "winapi", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.3", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [ - "basetsd", - "handleapi", - "memoryapi", - "minwindef", - "std", - "sysinfoapi" - ], - "target": "cfg(windows)", - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "memmap", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memmap-0.6.2/src/lib.rs", - "edition": "2015", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "cat", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memmap-0.6.2/examples/cat.rs", - "edition": "2015", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": {}, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memmap-0.6.2/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Dan Burkert " - ], - "categories": [], - "keywords": [ - "mmap", - "memory-map", - "io", - "file" - ], - "readme": "README.md", - "repository": "https://github.com/danburkert/memmap-rs", - "homepage": null, - "documentation": "https://docs.rs/memmap", - "edition": "2015", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "pkg-config", - "version": "0.3.26", - "id": "pkg-config 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT OR Apache-2.0", - "license_file": null, - "description": "A library to run the pkg-config system tool at build time in order to be used in\nCargo build scripts.\n", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "lazy_static", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "pkg-config", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pkg-config-0.3.26/src/lib.rs", - "edition": "2015", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pkg-config-0.3.26/tests/test.rs", - "edition": "2015", - "doc": false, - "doctest": false, - "test": true - } - ], - "features": {}, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pkg-config-0.3.26/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Alex Crichton " - ], - "categories": [], - "keywords": [ - "build-dependencies" - ], - "readme": "README.md", - "repository": "https://github.com/rust-lang/pkg-config-rs", - "homepage": null, - "documentation": "https://docs.rs/pkg-config", - "edition": "2015", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "proc-macro2", - "version": "1.0.56", - "id": "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT OR Apache-2.0", - "license_file": null, - "description": "A substitute implementation of the compiler's `proc_macro` API to decouple token-based libraries from the procedural macro use case.", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "unicode-ident", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "quote", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": false, - "features": [], - "target": null, - "registry": null - }, - { - "name": "rustversion", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "proc-macro2", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "comments", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/comments.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "features", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/features.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "marker", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/marker.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/test.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_fmt", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/test_fmt.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_size", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/test_size.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "custom-build" - ], - "crate_types": [ - "bin" - ], - "name": "build-script-build", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/build.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": { - "default": [ - "proc-macro" - ], - "nightly": [], - "proc-macro": [], - "span-locations": [] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/Cargo.toml", - "metadata": { - "docs": { - "rs": { - "rustc-args": [ - "--cfg", - "procmacro2_semver_exempt" - ], - "rustdoc-args": [ - "--cfg", - "procmacro2_semver_exempt", - "--cfg", - "doc_cfg" - ], - "targets": [ - "x86_64-unknown-linux-gnu" - ] - } - }, - "playground": { - "features": [ - "span-locations" - ] - } - }, - "publish": null, - "authors": [ - "David Tolnay ", - "Alex Crichton " - ], - "categories": [ - "development-tools::procedural-macro-helpers" - ], - "keywords": [ - "macros", - "syn" - ], - "readme": "README.md", - "repository": "https://github.com/dtolnay/proc-macro2", - "homepage": null, - "documentation": "https://docs.rs/proc-macro2", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": "1.31" - }, - { - "name": "quickcheck", - "version": "1.0.3", - "id": "quickcheck 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "Unlicense/MIT", - "license_file": null, - "description": "Automatic property based testing with shrinking.", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "env_logger", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.8.2", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": false, - "features": [], - "target": null, - "registry": null - }, - { - "name": "log", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.4", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "rand", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.8", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": false, - "features": [ - "getrandom", - "small_rng" - ], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "quickcheck", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quickcheck-1.0.3/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "btree_set_range", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quickcheck-1.0.3/examples/btree_set_range.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "out_of_bounds", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quickcheck-1.0.3/examples/out_of_bounds.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "reverse", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quickcheck-1.0.3/examples/reverse.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "reverse_single", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quickcheck-1.0.3/examples/reverse_single.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "sieve", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quickcheck-1.0.3/examples/sieve.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "sort", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quickcheck-1.0.3/examples/sort.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": { - "default": [ - "regex", - "use_logging" - ], - "env_logger": [ - "dep:env_logger" - ], - "log": [ - "dep:log" - ], - "regex": [ - "env_logger/regex" - ], - "use_logging": [ - "log", - "env_logger" - ] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quickcheck-1.0.3/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Andrew Gallant " - ], - "categories": [ - "development-tools::testing" - ], - "keywords": [ - "testing", - "quickcheck", - "property", - "shrinking", - "fuzz" - ], - "readme": "README.md", - "repository": "https://github.com/BurntSushi/quickcheck", - "homepage": "https://github.com/BurntSushi/quickcheck", - "documentation": "https://docs.rs/quickcheck", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "quote", - "version": "1.0.26", - "id": "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT OR Apache-2.0", - "license_file": null, - "description": "Quasi-quoting macro quote!(...)", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "proc-macro2", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.52", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": false, - "features": [], - "target": null, - "registry": null - }, - { - "name": "rustversion", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "trybuild", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.66", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [ - "diff" - ], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "quote", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quote-1.0.26/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "compiletest", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quote-1.0.26/tests/compiletest.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quote-1.0.26/tests/test.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "custom-build" - ], - "crate_types": [ - "bin" - ], - "name": "build-script-build", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quote-1.0.26/build.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": { - "default": [ - "proc-macro" - ], - "proc-macro": [ - "proc-macro2/proc-macro" - ] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quote-1.0.26/Cargo.toml", - "metadata": { - "docs": { - "rs": { - "targets": [ - "x86_64-unknown-linux-gnu" - ] - } - } - }, - "publish": null, - "authors": [ - "David Tolnay " - ], - "categories": [ - "development-tools::procedural-macro-helpers" - ], - "keywords": [ - "macros", - "syn" - ], - "readme": "README.md", - "repository": "https://github.com/dtolnay/quote", - "homepage": null, - "documentation": "https://docs.rs/quote/", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": "1.31" - }, - { - "name": "rand", - "version": "0.8.5", - "id": "rand 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT OR Apache-2.0", - "license_file": null, - "description": "Random number generators and other randomness functionality.\n", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "log", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.4.4", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "packed_simd_2", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.3.7", - "kind": null, - "rename": "packed_simd", - "optional": true, - "uses_default_features": true, - "features": [ - "into_bits" - ], - "target": null, - "registry": null - }, - { - "name": "rand_chacha", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.3.0", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": false, - "features": [], - "target": null, - "registry": null - }, - { - "name": "rand_core", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.6.0", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "serde", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.103", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [ - "derive" - ], - "target": null, - "registry": null - }, - { - "name": "bincode", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.2.1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "rand_pcg", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.3.0", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "libc", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.2.22", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": false, - "features": [], - "target": "cfg(unix)", - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "rand", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/rand-0.8.5/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - } - ], - "features": { - "alloc": [ - "rand_core/alloc" - ], - "default": [ - "std", - "std_rng" - ], - "getrandom": [ - "rand_core/getrandom" - ], - "libc": [ - "dep:libc" - ], - "log": [ - "dep:log" - ], - "min_const_gen": [], - "nightly": [], - "packed_simd": [ - "dep:packed_simd" - ], - "rand_chacha": [ - "dep:rand_chacha" - ], - "serde": [ - "dep:serde" - ], - "serde1": [ - "serde", - "rand_core/serde1" - ], - "simd_support": [ - "packed_simd" - ], - "small_rng": [], - "std": [ - "rand_core/std", - "rand_chacha/std", - "alloc", - "getrandom", - "libc" - ], - "std_rng": [ - "rand_chacha" - ] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/rand-0.8.5/Cargo.toml", - "metadata": { - "docs": { - "rs": { - "all-features": true, - "rustdoc-args": [ - "--cfg", - "doc_cfg" - ] - } - }, - "playground": { - "features": [ - "small_rng", - "serde1" - ] - } - }, - "publish": null, - "authors": [ - "The Rand Project Developers", - "The Rust Project Developers" - ], - "categories": [ - "algorithms", - "no-std" - ], - "keywords": [ - "random", - "rng" - ], - "readme": "README.md", - "repository": "https://github.com/rust-random/rand", - "homepage": "https://rust-random.github.io/book", - "documentation": "https://docs.rs/rand", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "rand_core", - "version": "0.6.4", - "id": "rand_core 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT OR Apache-2.0", - "license_file": null, - "description": "Core random number generator traits and tools for implementation.\n", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "getrandom", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.2", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "serde", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [ - "derive" - ], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "rand_core", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/rand_core-0.6.4/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - } - ], - "features": { - "alloc": [], - "getrandom": [ - "dep:getrandom" - ], - "serde": [ - "dep:serde" - ], - "serde1": [ - "serde" - ], - "std": [ - "alloc", - "getrandom", - "getrandom/std" - ] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/rand_core-0.6.4/Cargo.toml", - "metadata": { - "docs": { - "rs": { - "all-features": true, - "rustdoc-args": [ - "--cfg", - "doc_cfg" - ] - } - }, - "playground": { - "all-features": true - } - }, - "publish": null, - "authors": [ - "The Rand Project Developers", - "The Rust Project Developers" - ], - "categories": [ - "algorithms", - "no-std" - ], - "keywords": [ - "random", - "rng" - ], - "readme": "README.md", - "repository": "https://github.com/rust-random/rand", - "homepage": "https://rust-random.github.io/book", - "documentation": "https://docs.rs/rand_core", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "regex", - "version": "1.7.1", - "id": "regex 1.7.1 (path+file:///$ROOT$regex)", - "license": "MIT OR Apache-2.0", - "license_file": null, - "description": "An implementation of regular expressions for Rust. This implementation uses\nfinite automata and guarantees linear time matching on all inputs.\n", - "source": null, - "dependencies": [ - { - "name": "aho-corasick", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.7.18", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "memchr", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^2.4.0", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "regex-syntax", - "source": null, - "req": "^0.6.27", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": false, - "features": [], - "target": null, - "registry": null, - "path": "$ROOT$regex/regex-syntax" - }, - { - "name": "lazy_static", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "quickcheck", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.3", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": false, - "features": [], - "target": null, - "registry": null - }, - { - "name": "rand", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.8.3", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": false, - "features": [ - "getrandom", - "small_rng" - ], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "regex", - "src_path": "$ROOT$regex/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": false, - "test": true - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "shootout-regex-dna-bytes", - "src_path": "$ROOT$regex/examples/shootout-regex-dna-bytes.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "shootout-regex-dna-cheat", - "src_path": "$ROOT$regex/examples/shootout-regex-dna-cheat.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "shootout-regex-dna-replace", - "src_path": "$ROOT$regex/examples/shootout-regex-dna-replace.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "shootout-regex-dna-single-cheat", - "src_path": "$ROOT$regex/examples/shootout-regex-dna-single-cheat.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "shootout-regex-dna-single", - "src_path": "$ROOT$regex/examples/shootout-regex-dna-single.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "shootout-regex-dna", - "src_path": "$ROOT$regex/examples/shootout-regex-dna.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "default", - "src_path": "$ROOT$regex/tests/test_default.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "default-bytes", - "src_path": "$ROOT$regex/tests/test_default_bytes.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "nfa", - "src_path": "$ROOT$regex/tests/test_nfa.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "nfa-utf8bytes", - "src_path": "$ROOT$regex/tests/test_nfa_utf8bytes.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "nfa-bytes", - "src_path": "$ROOT$regex/tests/test_nfa_bytes.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "backtrack", - "src_path": "$ROOT$regex/tests/test_backtrack.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "backtrack-utf8bytes", - "src_path": "$ROOT$regex/tests/test_backtrack_utf8bytes.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "backtrack-bytes", - "src_path": "$ROOT$regex/tests/test_backtrack_bytes.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "crates-regex", - "src_path": "$ROOT$regex/tests/test_crates_regex.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - } - ], - "features": { - "aho-corasick": [ - "dep:aho-corasick" - ], - "default": [ - "std", - "perf", - "unicode", - "regex-syntax/default" - ], - "memchr": [ - "dep:memchr" - ], - "pattern": [], - "perf": [ - "perf-cache", - "perf-dfa", - "perf-inline", - "perf-literal" - ], - "perf-cache": [], - "perf-dfa": [], - "perf-inline": [], - "perf-literal": [ - "aho-corasick", - "memchr" - ], - "std": [], - "unicode": [ - "unicode-age", - "unicode-bool", - "unicode-case", - "unicode-gencat", - "unicode-perl", - "unicode-script", - "unicode-segment", - "regex-syntax/unicode" - ], - "unicode-age": [ - "regex-syntax/unicode-age" - ], - "unicode-bool": [ - "regex-syntax/unicode-bool" - ], - "unicode-case": [ - "regex-syntax/unicode-case" - ], - "unicode-gencat": [ - "regex-syntax/unicode-gencat" - ], - "unicode-perl": [ - "regex-syntax/unicode-perl" - ], - "unicode-script": [ - "regex-syntax/unicode-script" - ], - "unicode-segment": [ - "regex-syntax/unicode-segment" - ], - "unstable": [ - "pattern" - ], - "use_std": [ - "std" - ] - }, - "manifest_path": "$ROOT$regex/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "The Rust Project Developers" - ], - "categories": [ - "text-processing" - ], - "keywords": [], - "readme": "README.md", - "repository": "https://github.com/rust-lang/regex", - "homepage": "https://github.com/rust-lang/regex", - "documentation": "https://docs.rs/regex", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "regex", - "version": "1.8.1", - "id": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT OR Apache-2.0", - "license_file": null, - "description": "An implementation of regular expressions for Rust. This implementation uses\nfinite automata and guarantees linear time matching on all inputs.\n", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "aho-corasick", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.0", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "memchr", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^2.5.0", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "regex-syntax", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.7.1", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": false, - "features": [], - "target": null, - "registry": null - }, - { - "name": "lazy_static", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "quickcheck", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.3", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": false, - "features": [], - "target": null, - "registry": null - }, - { - "name": "rand", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.8.3", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": false, - "features": [ - "getrandom", - "small_rng" - ], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "regex", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/src/lib.rs", - "edition": "2021", - "doc": true, - "doctest": false, - "test": true - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "shootout-regex-dna-bytes", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna-bytes.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "shootout-regex-dna-cheat", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna-cheat.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "shootout-regex-dna-replace", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna-replace.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "shootout-regex-dna-single-cheat", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna-single-cheat.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "shootout-regex-dna-single", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna-single.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "shootout-regex-dna", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "default", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_default.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "default-bytes", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_default_bytes.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "nfa", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_nfa.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "nfa-utf8bytes", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_nfa_utf8bytes.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "nfa-bytes", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_nfa_bytes.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "backtrack", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_backtrack.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "backtrack-utf8bytes", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_backtrack_utf8bytes.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "backtrack-bytes", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_backtrack_bytes.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "crates-regex", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_crates_regex.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - } - ], - "features": { - "aho-corasick": [ - "dep:aho-corasick" - ], - "default": [ - "std", - "perf", - "unicode", - "regex-syntax/default" - ], - "memchr": [ - "dep:memchr" - ], - "pattern": [], - "perf": [ - "perf-cache", - "perf-dfa", - "perf-inline", - "perf-literal" - ], - "perf-cache": [], - "perf-dfa": [], - "perf-inline": [], - "perf-literal": [ - "aho-corasick", - "memchr" - ], - "std": [], - "unicode": [ - "unicode-age", - "unicode-bool", - "unicode-case", - "unicode-gencat", - "unicode-perl", - "unicode-script", - "unicode-segment", - "regex-syntax/unicode" - ], - "unicode-age": [ - "regex-syntax/unicode-age" - ], - "unicode-bool": [ - "regex-syntax/unicode-bool" - ], - "unicode-case": [ - "regex-syntax/unicode-case" - ], - "unicode-gencat": [ - "regex-syntax/unicode-gencat" - ], - "unicode-perl": [ - "regex-syntax/unicode-perl" - ], - "unicode-script": [ - "regex-syntax/unicode-script" - ], - "unicode-segment": [ - "regex-syntax/unicode-segment" - ], - "unstable": [ - "pattern" - ], - "use_std": [ - "std" - ] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "The Rust Project Developers" - ], - "categories": [ - "text-processing" - ], - "keywords": [], - "readme": "README.md", - "repository": "https://github.com/rust-lang/regex", - "homepage": "https://github.com/rust-lang/regex", - "documentation": "https://docs.rs/regex", - "edition": "2021", - "links": null, - "default_run": null, - "rust_version": "1.60.0" - }, - { - "name": "regex-benchmark", - "version": "0.1.0", - "id": "regex-benchmark 0.1.0 (path+file:///$ROOT$regex/bench)", - "license": "MIT OR Apache-2.0", - "license_file": null, - "description": "Regex benchmarks for Rust's and other engines.", - "source": null, - "dependencies": [ - { - "name": "cfg-if", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.1", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "docopt", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "lazy_static", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "libc", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.2", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "libpcre-sys", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.2", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "memmap", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.6.2", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "onig", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^3", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "regex", - "source": null, - "req": "^1", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null, - "path": "$ROOT$regex" - }, - { - "name": "regex-syntax", - "source": null, - "req": "^0.6", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null, - "path": "$ROOT$regex/regex-syntax" - }, - { - "name": "serde", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [ - "derive" - ], - "target": null, - "registry": null - }, - { - "name": "cc", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": "build", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "pkg-config", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.3.9", - "kind": "build", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "bin" - ], - "crate_types": [ - "bin" - ], - "name": "regex-run-one", - "src_path": "$ROOT$regex/bench/src/main.rs", - "edition": "2018", - "doc": true, - "doctest": false, - "test": true - }, - { - "kind": [ - "bench" - ], - "crate_types": [ - "bin" - ], - "name": "bench", - "src_path": "$ROOT$regex/bench/src/bench.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "custom-build" - ], - "crate_types": [ - "bin" - ], - "name": "build-script-build", - "src_path": "$ROOT$regex/bench/build.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": { - "libpcre-sys": [ - "dep:libpcre-sys" - ], - "onig": [ - "dep:onig" - ], - "re-onig": [ - "onig" - ], - "re-pcre1": [ - "libpcre-sys" - ], - "re-pcre2": [], - "re-re2": [], - "re-rust": [], - "re-rust-bytes": [], - "re-tcl": [] - }, - "manifest_path": "$ROOT$regex/bench/Cargo.toml", - "metadata": null, - "publish": [], - "authors": [ - "The Rust Project Developers" - ], - "categories": [], - "keywords": [], - "readme": null, - "repository": "https://github.com/rust-lang/regex", - "homepage": "https://github.com/rust-lang/regex", - "documentation": "https://docs.rs/regex", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "regex-debug", - "version": "0.1.0", - "id": "regex-debug 0.1.0 (path+file:///$ROOT$regex/regex-debug)", - "license": "MIT OR Apache-2.0", - "license_file": null, - "description": "A tool useful for debugging regular expressions.", - "source": null, - "dependencies": [ - { - "name": "docopt", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "regex", - "source": null, - "req": "^1.1", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null, - "path": "$ROOT$regex" - }, - { - "name": "regex-syntax", - "source": null, - "req": "^0.6", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null, - "path": "$ROOT$regex/regex-syntax" - }, - { - "name": "serde", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [ - "derive" - ], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "bin" - ], - "crate_types": [ - "bin" - ], - "name": "regex-debug", - "src_path": "$ROOT$regex/regex-debug/src/main.rs", - "edition": "2018", - "doc": true, - "doctest": false, - "test": true - } - ], - "features": {}, - "manifest_path": "$ROOT$regex/regex-debug/Cargo.toml", - "metadata": null, - "publish": [], - "authors": [ - "The Rust Project Developers" - ], - "categories": [], - "keywords": [], - "readme": null, - "repository": "https://github.com/rust-lang/regex", - "homepage": "https://github.com/rust-lang/regex", - "documentation": "https://docs.rs/regex", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "regex-syntax", - "version": "0.6.28", - "id": "regex-syntax 0.6.28 (path+file:///$ROOT$regex/regex-syntax)", - "license": "MIT OR Apache-2.0", - "license_file": null, - "description": "A regular expression parser.", - "source": null, - "dependencies": [], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "regex-syntax", - "src_path": "$ROOT$regex/regex-syntax/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "bench" - ], - "crate_types": [ - "bin" - ], - "name": "bench", - "src_path": "$ROOT$regex/regex-syntax/benches/bench.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": { - "default": [ - "unicode" - ], - "unicode": [ - "unicode-age", - "unicode-bool", - "unicode-case", - "unicode-gencat", - "unicode-perl", - "unicode-script", - "unicode-segment" - ], - "unicode-age": [], - "unicode-bool": [], - "unicode-case": [], - "unicode-gencat": [], - "unicode-perl": [], - "unicode-script": [], - "unicode-segment": [] - }, - "manifest_path": "$ROOT$regex/regex-syntax/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "The Rust Project Developers" - ], - "categories": [], - "keywords": [], - "readme": "README.md", - "repository": "https://github.com/rust-lang/regex", - "homepage": "https://github.com/rust-lang/regex", - "documentation": "https://docs.rs/regex-syntax", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "regex-syntax", - "version": "0.7.1", - "id": "regex-syntax 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT OR Apache-2.0", - "license_file": null, - "description": "A regular expression parser.", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "regex-syntax", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-syntax-0.7.1/src/lib.rs", - "edition": "2021", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "bench" - ], - "crate_types": [ - "bin" - ], - "name": "bench", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-syntax-0.7.1/benches/bench.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": { - "default": [ - "std", - "unicode" - ], - "std": [], - "unicode": [ - "unicode-age", - "unicode-bool", - "unicode-case", - "unicode-gencat", - "unicode-perl", - "unicode-script", - "unicode-segment" - ], - "unicode-age": [], - "unicode-bool": [], - "unicode-case": [], - "unicode-gencat": [], - "unicode-perl": [], - "unicode-script": [], - "unicode-segment": [] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-syntax-0.7.1/Cargo.toml", - "metadata": { - "docs": { - "rs": { - "all-features": true, - "rustdoc-args": [ - "--cfg", - "docsrs" - ] - } - } - }, - "publish": null, - "authors": [ - "The Rust Project Developers" - ], - "categories": [], - "keywords": [], - "readme": "README.md", - "repository": "https://github.com/rust-lang/regex", - "homepage": "https://github.com/rust-lang/regex", - "documentation": "https://docs.rs/regex-syntax", - "edition": "2021", - "links": null, - "default_run": null, - "rust_version": "1.60.0" - }, - { - "name": "rure", - "version": "0.2.2", - "id": "rure 0.2.2 (path+file:///$ROOT$regex/regex-capi)", - "license": "MIT OR Apache-2.0", - "license_file": null, - "description": "A C API for Rust's regular expression library.\n", - "source": null, - "dependencies": [ - { - "name": "libc", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.2", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "regex", - "source": null, - "req": "^1", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null, - "path": "$ROOT$regex" - } - ], - "targets": [ - { - "kind": [ - "staticlib", - "cdylib", - "rlib" - ], - "crate_types": [ - "staticlib", - "cdylib", - "rlib" - ], - "name": "rure", - "src_path": "$ROOT$regex/regex-capi/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - } - ], - "features": {}, - "manifest_path": "$ROOT$regex/regex-capi/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "The Rust Project Developers" - ], - "categories": [], - "keywords": [], - "readme": "README.md", - "repository": "https://github.com/rust-lang/regex", - "homepage": "https://github.com/rust-lang/regex", - "documentation": "https://github.com/rust-lang/regex/tree/master/regex-capi", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "serde", - "version": "1.0.160", - "id": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT OR Apache-2.0", - "license_file": null, - "description": "A generic serialization/deserialization framework", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "serde_derive", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "=1.0.160", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "serde_derive", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "serde", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde-1.0.160/src/lib.rs", - "edition": "2015", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "custom-build" - ], - "crate_types": [ - "bin" - ], - "name": "build-script-build", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde-1.0.160/build.rs", - "edition": "2015", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": { - "alloc": [], - "default": [ - "std" - ], - "derive": [ - "serde_derive" - ], - "rc": [], - "serde_derive": [ - "dep:serde_derive" - ], - "std": [], - "unstable": [] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde-1.0.160/Cargo.toml", - "metadata": { - "docs": { - "rs": { - "features": [ - "derive" - ], - "targets": [ - "x86_64-unknown-linux-gnu" - ] - } - }, - "playground": { - "features": [ - "derive", - "rc" - ] - } - }, - "publish": null, - "authors": [ - "Erick Tryzelaar ", - "David Tolnay " - ], - "categories": [ - "encoding", - "no-std" - ], - "keywords": [ - "serde", - "serialization", - "no_std" - ], - "readme": "crates-io.md", - "repository": "https://github.com/serde-rs/serde", - "homepage": "https://serde.rs", - "documentation": "https://docs.rs/serde", - "edition": "2015", - "links": null, - "default_run": null, - "rust_version": "1.19" - }, - { - "name": "serde_derive", - "version": "1.0.160", - "id": "serde_derive 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT OR Apache-2.0", - "license_file": null, - "description": "Macros 1.1 implementation of #[derive(Serialize, Deserialize)]", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "proc-macro2", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "quote", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "syn", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^2.0.3", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "serde", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "proc-macro" - ], - "crate_types": [ - "proc-macro" - ], - "name": "serde_derive", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_derive-1.0.160/src/lib.rs", - "edition": "2015", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "custom-build" - ], - "crate_types": [ - "bin" - ], - "name": "build-script-build", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_derive-1.0.160/build.rs", - "edition": "2015", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": { - "default": [], - "deserialize_in_place": [] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_derive-1.0.160/Cargo.toml", - "metadata": { - "docs": { - "rs": { - "targets": [ - "x86_64-unknown-linux-gnu" - ] - } - } - }, - "publish": null, - "authors": [ - "Erick Tryzelaar ", - "David Tolnay " - ], - "categories": [ - "no-std" - ], - "keywords": [ - "serde", - "serialization", - "no_std", - "derive" - ], - "readme": "crates-io.md", - "repository": "https://github.com/serde-rs/serde", - "homepage": "https://serde.rs", - "documentation": "https://serde.rs/derive.html", - "edition": "2015", - "links": null, - "default_run": null, - "rust_version": "1.56" - }, - { - "name": "strsim", - "version": "0.10.0", - "id": "strsim 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT", - "license_file": null, - "description": "Implementations of string similarity metrics. Includes Hamming, Levenshtein,\nOSA, Damerau-Levenshtein, Jaro, Jaro-Winkler, and Sørensen-Dice.\n", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "strsim", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/strsim-0.10.0/src/lib.rs", - "edition": "2015", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "lib", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/strsim-0.10.0/tests/lib.rs", - "edition": "2015", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "bench" - ], - "crate_types": [ - "bin" - ], - "name": "benches", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/strsim-0.10.0/benches/benches.rs", - "edition": "2015", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": {}, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/strsim-0.10.0/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Danny Guo " - ], - "categories": [], - "keywords": [ - "string", - "similarity", - "Hamming", - "Levenshtein", - "Jaro" - ], - "readme": "README.md", - "repository": "https://github.com/dguo/strsim-rs", - "homepage": "https://github.com/dguo/strsim-rs", - "documentation": "https://docs.rs/strsim/", - "edition": "2015", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "syn", - "version": "2.0.15", - "id": "syn 2.0.15 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT OR Apache-2.0", - "license_file": null, - "description": "Parser for Rust source code", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "proc-macro2", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.55", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": false, - "features": [], - "target": null, - "registry": null - }, - { - "name": "quote", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.25", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": false, - "features": [], - "target": null, - "registry": null - }, - { - "name": "unicode-ident", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "anyhow", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "automod", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "flate2", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "insta", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "rayon", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "ref-cast", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "regex", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "reqwest", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.11", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [ - "blocking" - ], - "target": null, - "registry": null - }, - { - "name": "rustversion", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "syn-test-suite", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "tar", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.4.16", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "termcolor", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "walkdir", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^2.3.2", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "syn", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/src/lib.rs", - "edition": "2021", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "regression", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/regression.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_asyncness", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_asyncness.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_attribute", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_attribute.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_derive_input", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_derive_input.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_expr", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_expr.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_generics", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_generics.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_grouping", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_grouping.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_ident", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_ident.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_item", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_item.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_iterators", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_iterators.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_lit", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_lit.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_meta", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_meta.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_parse_buffer", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_parse_buffer.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_parse_stream", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_parse_stream.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_pat", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_pat.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_path", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_path.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_precedence", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_precedence.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_receiver", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_receiver.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_round_trip", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_round_trip.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_shebang", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_shebang.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_should_parse", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_should_parse.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_size", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_size.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_stmt", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_stmt.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_token_trees", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_token_trees.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_ty", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_ty.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_visibility", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_visibility.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "zzz_stable", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/zzz_stable.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "bench" - ], - "crate_types": [ - "bin" - ], - "name": "rust", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/benches/rust.rs", - "edition": "2021", - "required-features": [ - "full", - "parsing" - ], - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "bench" - ], - "crate_types": [ - "bin" - ], - "name": "file", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/benches/file.rs", - "edition": "2021", - "required-features": [ - "full", - "parsing" - ], - "doc": false, - "doctest": false, - "test": false - } - ], - "features": { - "clone-impls": [], - "default": [ - "derive", - "parsing", - "printing", - "clone-impls", - "proc-macro" - ], - "derive": [], - "extra-traits": [], - "fold": [], - "full": [], - "parsing": [], - "printing": [ - "quote" - ], - "proc-macro": [ - "proc-macro2/proc-macro", - "quote/proc-macro" - ], - "quote": [ - "dep:quote" - ], - "test": [ - "syn-test-suite/all-features" - ], - "visit": [], - "visit-mut": [] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/Cargo.toml", - "metadata": { - "docs": { - "rs": { - "all-features": true, - "rustdoc-args": [ - "--cfg", - "doc_cfg" - ], - "targets": [ - "x86_64-unknown-linux-gnu" - ] - } - }, - "playground": { - "features": [ - "full", - "visit", - "visit-mut", - "fold", - "extra-traits" - ] - } - }, - "publish": null, - "authors": [ - "David Tolnay " - ], - "categories": [ - "development-tools::procedural-macro-helpers", - "parser-implementations" - ], - "keywords": [ - "macros", - "syn" - ], - "readme": "README.md", - "repository": "https://github.com/dtolnay/syn", - "homepage": null, - "documentation": "https://docs.rs/syn", - "edition": "2021", - "links": null, - "default_run": null, - "rust_version": "1.56" - }, - { - "name": "unicode-ident", - "version": "1.0.8", - "id": "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "(MIT OR Apache-2.0) AND Unicode-DFS-2016", - "license_file": null, - "description": "Determine whether characters have the XID_Start or XID_Continue properties according to Unicode Standard Annex #31", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "criterion", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.4", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": false, - "features": [], - "target": null, - "registry": null - }, - { - "name": "fst", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.4", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "rand", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.8", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [ - "small_rng" - ], - "target": null, - "registry": null - }, - { - "name": "roaring", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.10", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "ucd-trie", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": false, - "features": [], - "target": null, - "registry": null - }, - { - "name": "unicode-xid", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.2.4", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "unicode-ident", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-ident-1.0.8/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "compare", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-ident-1.0.8/tests/compare.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "static_size", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-ident-1.0.8/tests/static_size.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "bench" - ], - "crate_types": [ - "bin" - ], - "name": "xid", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-ident-1.0.8/benches/xid.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": {}, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-ident-1.0.8/Cargo.toml", - "metadata": { - "docs": { - "rs": { - "targets": [ - "x86_64-unknown-linux-gnu" - ] - } - } - }, - "publish": null, - "authors": [ - "David Tolnay " - ], - "categories": [ - "development-tools::procedural-macro-helpers", - "no-std" - ], - "keywords": [ - "unicode", - "xid" - ], - "readme": "README.md", - "repository": "https://github.com/dtolnay/unicode-ident", - "homepage": null, - "documentation": "https://docs.rs/unicode-ident", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": "1.31" - }, - { - "name": "wasi", - "version": "0.11.0+wasi-snapshot-preview1", - "id": "wasi 0.11.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT", - "license_file": null, - "description": "Experimental WASI API bindings for Rust", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "compiler_builtins", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.1", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "rustc-std-workspace-core", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": null, - "rename": "core", - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "rustc-std-workspace-alloc", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "wasi", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/wasi-0.11.0+wasi-snapshot-preview1/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - } - ], - "features": { - "compiler_builtins": [ - "dep:compiler_builtins" - ], - "core": [ - "dep:core" - ], - "default": [ - "std" - ], - "rustc-dep-of-std": [ - "compiler_builtins", - "core", - "rustc-std-workspace-alloc" - ], - "rustc-std-workspace-alloc": [ - "dep:rustc-std-workspace-alloc" - ], - "std": [] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/wasi-0.11.0+wasi-snapshot-preview1/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "The Cranelift Project Developers" - ], - "categories": [ - "no-std", - "wasm" - ], - "keywords": [ - "webassembly", - "wasm" - ], - "readme": "README.md", - "repository": "https://github.com/bytecodealliance/wasi", - "homepage": null, - "documentation": "https://docs.rs/wasi", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "winapi", - "version": "0.3.9", - "id": "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT/Apache-2.0", - "license_file": null, - "description": "Raw FFI bindings for all of Windows API.", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "winapi-i686-pc-windows-gnu", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.4", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": "i686-pc-windows-gnu", - "registry": null - }, - { - "name": "winapi-x86_64-pc-windows-gnu", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.4", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": "x86_64-pc-windows-gnu", - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "winapi", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-0.3.9/src/lib.rs", - "edition": "2015", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "custom-build" - ], - "crate_types": [ - "bin" - ], - "name": "build-script-build", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-0.3.9/build.rs", - "edition": "2015", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": { - "accctrl": [], - "aclapi": [], - "activation": [], - "adhoc": [], - "appmgmt": [], - "audioclient": [], - "audiosessiontypes": [], - "avrt": [], - "basetsd": [], - "bcrypt": [], - "bits": [], - "bits10_1": [], - "bits1_5": [], - "bits2_0": [], - "bits2_5": [], - "bits3_0": [], - "bits4_0": [], - "bits5_0": [], - "bitscfg": [], - "bitsmsg": [], - "bluetoothapis": [], - "bluetoothleapis": [], - "bthdef": [], - "bthioctl": [], - "bthledef": [], - "bthsdpdef": [], - "bugcodes": [], - "cderr": [], - "cfg": [], - "cfgmgr32": [], - "cguid": [], - "combaseapi": [], - "coml2api": [], - "commapi": [], - "commctrl": [], - "commdlg": [], - "commoncontrols": [], - "consoleapi": [], - "corecrt": [], - "corsym": [], - "d2d1": [], - "d2d1_1": [], - "d2d1_2": [], - "d2d1_3": [], - "d2d1effectauthor": [], - "d2d1effects": [], - "d2d1effects_1": [], - "d2d1effects_2": [], - "d2d1svg": [], - "d2dbasetypes": [], - "d3d": [], - "d3d10": [], - "d3d10_1": [], - "d3d10_1shader": [], - "d3d10effect": [], - "d3d10misc": [], - "d3d10sdklayers": [], - "d3d10shader": [], - "d3d11": [], - "d3d11_1": [], - "d3d11_2": [], - "d3d11_3": [], - "d3d11_4": [], - "d3d11on12": [], - "d3d11sdklayers": [], - "d3d11shader": [], - "d3d11tokenizedprogramformat": [], - "d3d12": [], - "d3d12sdklayers": [], - "d3d12shader": [], - "d3d9": [], - "d3d9caps": [], - "d3d9types": [], - "d3dcommon": [], - "d3dcompiler": [], - "d3dcsx": [], - "d3dkmdt": [], - "d3dkmthk": [], - "d3dukmdt": [], - "d3dx10core": [], - "d3dx10math": [], - "d3dx10mesh": [], - "datetimeapi": [], - "davclnt": [], - "dbghelp": [], - "dbt": [], - "dcommon": [], - "dcomp": [], - "dcompanimation": [], - "dcomptypes": [], - "dde": [], - "ddraw": [], - "ddrawi": [], - "ddrawint": [], - "debug": [ - "impl-debug" - ], - "debugapi": [], - "devguid": [], - "devicetopology": [], - "devpkey": [], - "devpropdef": [], - "dinput": [], - "dinputd": [], - "dispex": [], - "dmksctl": [], - "dmusicc": [], - "docobj": [], - "documenttarget": [], - "dot1x": [], - "dpa_dsa": [], - "dpapi": [], - "dsgetdc": [], - "dsound": [], - "dsrole": [], - "dvp": [], - "dwmapi": [], - "dwrite": [], - "dwrite_1": [], - "dwrite_2": [], - "dwrite_3": [], - "dxdiag": [], - "dxfile": [], - "dxgi": [], - "dxgi1_2": [], - "dxgi1_3": [], - "dxgi1_4": [], - "dxgi1_5": [], - "dxgi1_6": [], - "dxgidebug": [], - "dxgiformat": [], - "dxgitype": [], - "dxva2api": [], - "dxvahd": [], - "eaptypes": [], - "enclaveapi": [], - "endpointvolume": [], - "errhandlingapi": [], - "everything": [], - "evntcons": [], - "evntprov": [], - "evntrace": [], - "excpt": [], - "exdisp": [], - "fibersapi": [], - "fileapi": [], - "functiondiscoverykeys_devpkey": [], - "gl-gl": [], - "guiddef": [], - "handleapi": [], - "heapapi": [], - "hidclass": [], - "hidpi": [], - "hidsdi": [], - "hidusage": [], - "highlevelmonitorconfigurationapi": [], - "hstring": [], - "http": [], - "ifdef": [], - "ifmib": [], - "imm": [], - "impl-debug": [], - "impl-default": [], - "in6addr": [], - "inaddr": [], - "inspectable": [], - "interlockedapi": [], - "intsafe": [], - "ioapiset": [], - "ipexport": [], - "iphlpapi": [], - "ipifcons": [], - "ipmib": [], - "iprtrmib": [], - "iptypes": [], - "jobapi": [], - "jobapi2": [], - "knownfolders": [], - "ks": [], - "ksmedia": [], - "ktmtypes": [], - "ktmw32": [], - "l2cmn": [], - "libloaderapi": [], - "limits": [], - "lmaccess": [], - "lmalert": [], - "lmapibuf": [], - "lmat": [], - "lmcons": [], - "lmdfs": [], - "lmerrlog": [], - "lmjoin": [], - "lmmsg": [], - "lmremutl": [], - "lmrepl": [], - "lmserver": [], - "lmshare": [], - "lmstats": [], - "lmsvc": [], - "lmuse": [], - "lmwksta": [], - "lowlevelmonitorconfigurationapi": [], - "lsalookup": [], - "memoryapi": [], - "minschannel": [], - "minwinbase": [], - "minwindef": [], - "mmdeviceapi": [], - "mmeapi": [], - "mmreg": [], - "mmsystem": [], - "mprapidef": [], - "msaatext": [], - "mscat": [], - "mschapp": [], - "mssip": [], - "mstcpip": [], - "mswsock": [], - "mswsockdef": [], - "namedpipeapi": [], - "namespaceapi": [], - "nb30": [], - "ncrypt": [], - "netioapi": [], - "nldef": [], - "ntddndis": [], - "ntddscsi": [], - "ntddser": [], - "ntdef": [], - "ntlsa": [], - "ntsecapi": [], - "ntstatus": [], - "oaidl": [], - "objbase": [], - "objidl": [], - "objidlbase": [], - "ocidl": [], - "ole2": [], - "oleauto": [], - "olectl": [], - "oleidl": [], - "opmapi": [], - "pdh": [], - "perflib": [], - "physicalmonitorenumerationapi": [], - "playsoundapi": [], - "portabledevice": [], - "portabledeviceapi": [], - "portabledevicetypes": [], - "powerbase": [], - "powersetting": [], - "powrprof": [], - "processenv": [], - "processsnapshot": [], - "processthreadsapi": [], - "processtopologyapi": [], - "profileapi": [], - "propidl": [], - "propkey": [], - "propkeydef": [], - "propsys": [], - "prsht": [], - "psapi": [], - "qos": [], - "realtimeapiset": [], - "reason": [], - "restartmanager": [], - "restrictederrorinfo": [], - "rmxfguid": [], - "roapi": [], - "robuffer": [], - "roerrorapi": [], - "rpc": [], - "rpcdce": [], - "rpcndr": [], - "rtinfo": [], - "sapi": [], - "sapi51": [], - "sapi53": [], - "sapiddk": [], - "sapiddk51": [], - "schannel": [], - "sddl": [], - "securityappcontainer": [], - "securitybaseapi": [], - "servprov": [], - "setupapi": [], - "shellapi": [], - "shellscalingapi": [], - "shlobj": [], - "shobjidl": [], - "shobjidl_core": [], - "shtypes": [], - "softpub": [], - "spapidef": [], - "spellcheck": [], - "sporder": [], - "sql": [], - "sqlext": [], - "sqltypes": [], - "sqlucode": [], - "sspi": [], - "std": [], - "stralign": [], - "stringapiset": [], - "strmif": [], - "subauth": [], - "synchapi": [], - "sysinfoapi": [], - "systemtopologyapi": [], - "taskschd": [], - "tcpestats": [], - "tcpmib": [], - "textstor": [], - "threadpoolapiset": [], - "threadpoollegacyapiset": [], - "timeapi": [], - "timezoneapi": [], - "tlhelp32": [], - "transportsettingcommon": [], - "tvout": [], - "udpmib": [], - "unknwnbase": [], - "urlhist": [], - "urlmon": [], - "usb": [], - "usbioctl": [], - "usbiodef": [], - "usbscan": [], - "usbspec": [], - "userenv": [], - "usp10": [], - "utilapiset": [], - "uxtheme": [], - "vadefs": [], - "vcruntime": [], - "vsbackup": [], - "vss": [], - "vsserror": [], - "vswriter": [], - "wbemads": [], - "wbemcli": [], - "wbemdisp": [], - "wbemprov": [], - "wbemtran": [], - "wct": [], - "werapi": [], - "winbase": [], - "wincodec": [], - "wincodecsdk": [], - "wincon": [], - "wincontypes": [], - "wincred": [], - "wincrypt": [], - "windef": [], - "windot11": [], - "windowsceip": [], - "windowsx": [], - "winefs": [], - "winerror": [], - "winevt": [], - "wingdi": [], - "winhttp": [], - "wininet": [], - "winineti": [], - "winioctl": [], - "winnetwk": [], - "winnls": [], - "winnt": [], - "winreg": [], - "winsafer": [], - "winscard": [], - "winsmcrd": [], - "winsock2": [], - "winspool": [], - "winstring": [], - "winsvc": [], - "wintrust": [], - "winusb": [], - "winusbio": [], - "winuser": [], - "winver": [], - "wlanapi": [], - "wlanihv": [], - "wlanihvtypes": [], - "wlantypes": [], - "wlclient": [], - "wmistr": [], - "wnnc": [], - "wow64apiset": [], - "wpdmtpextensions": [], - "ws2bth": [], - "ws2def": [], - "ws2ipdef": [], - "ws2spi": [], - "ws2tcpip": [], - "wtsapi32": [], - "wtypes": [], - "wtypesbase": [], - "xinput": [] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-0.3.9/Cargo.toml", - "metadata": { - "docs": { - "rs": { - "default-target": "x86_64-pc-windows-msvc", - "features": [ - "everything", - "impl-debug", - "impl-default" - ], - "targets": [ - "aarch64-pc-windows-msvc", - "i686-pc-windows-msvc", - "x86_64-pc-windows-msvc" - ] - } - } - }, - "publish": null, - "authors": [ - "Peter Atashian " - ], - "categories": [ - "external-ffi-bindings", - "no-std", - "os::windows-apis" - ], - "keywords": [ - "windows", - "ffi", - "win32", - "com", - "directx" - ], - "readme": "README.md", - "repository": "https://github.com/retep998/winapi-rs", - "homepage": null, - "documentation": "https://docs.rs/winapi/", - "edition": "2015", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "winapi-i686-pc-windows-gnu", - "version": "0.4.0", - "id": "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT/Apache-2.0", - "license_file": null, - "description": "Import libraries for the i686-pc-windows-gnu target. Please don't use this crate directly, depend on winapi instead.", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "winapi-i686-pc-windows-gnu", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-i686-pc-windows-gnu-0.4.0/src/lib.rs", - "edition": "2015", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "custom-build" - ], - "crate_types": [ - "bin" - ], - "name": "build-script-build", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-i686-pc-windows-gnu-0.4.0/build.rs", - "edition": "2015", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": {}, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-i686-pc-windows-gnu-0.4.0/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Peter Atashian " - ], - "categories": [], - "keywords": [ - "windows" - ], - "readme": null, - "repository": "https://github.com/retep998/winapi-rs", - "homepage": null, - "documentation": null, - "edition": "2015", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "winapi-x86_64-pc-windows-gnu", - "version": "0.4.0", - "id": "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT/Apache-2.0", - "license_file": null, - "description": "Import libraries for the x86_64-pc-windows-gnu target. Please don't use this crate directly, depend on winapi instead.", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "winapi-x86_64-pc-windows-gnu", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-x86_64-pc-windows-gnu-0.4.0/src/lib.rs", - "edition": "2015", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "custom-build" - ], - "crate_types": [ - "bin" - ], - "name": "build-script-build", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-x86_64-pc-windows-gnu-0.4.0/build.rs", - "edition": "2015", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": {}, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-x86_64-pc-windows-gnu-0.4.0/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Peter Atashian " - ], - "categories": [], - "keywords": [ - "windows" - ], - "readme": null, - "repository": "https://github.com/retep998/winapi-rs", - "homepage": null, - "documentation": null, - "edition": "2015", - "links": null, - "default_run": null, - "rust_version": null - } - ], - "workspace_members": [ - "regex-benchmark 0.1.0 (path+file:///$ROOT$regex/bench)", - "regex 1.7.1 (path+file:///$ROOT$regex)", - "regex-syntax 0.6.28 (path+file:///$ROOT$regex/regex-syntax)", - "rure 0.2.2 (path+file:///$ROOT$regex/regex-capi)", - "regex-debug 0.1.0 (path+file:///$ROOT$regex/regex-debug)" - ], - "resolve": { - "nodes": [ - { - "id": "aho-corasick 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "memchr", - "pkg": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - } - ], - "features": [ - "default", - "std" - ] - }, - { - "id": "cc 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [], - "deps": [], - "features": [] - }, - { - "id": "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [], - "deps": [], - "features": [] - }, - { - "id": "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [], - "deps": [], - "features": [] - }, - { - "id": "docopt 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", - "strsim 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "lazy_static", - "pkg": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "regex", - "pkg": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "serde", - "pkg": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "strsim", - "pkg": "strsim 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - } - ], - "features": [] - }, - { - "id": "getrandom 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", - "wasi 0.11.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "cfg_if", - "pkg": "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "libc", - "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": "cfg(unix)" - } - ] - }, - { - "name": "wasi", - "pkg": "wasi 0.11.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": "cfg(target_os = \"wasi\")" - } - ] - } - ], - "features": [] - }, - { - "id": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [], - "deps": [], - "features": [] - }, - { - "id": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [], - "deps": [], - "features": [ - "default", - "std" - ] - }, - { - "id": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [], - "deps": [], - "features": [ - "default", - "std" - ] - }, - { - "id": "memmap 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "libc", - "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": "cfg(unix)" - } - ] - }, - { - "name": "winapi", - "pkg": "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": "cfg(windows)" - } - ] - } - ], - "features": [] - }, - { - "id": "pkg-config 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [], - "deps": [], - "features": [] - }, - { - "id": "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "unicode_ident", - "pkg": "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - } - ], - "features": [ - "default", - "proc-macro" - ] - }, - { - "id": "quickcheck 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "rand 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "rand", - "pkg": "rand 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - } - ], - "features": [] - }, - { - "id": "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "proc_macro2", - "pkg": "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - } - ], - "features": [ - "default", - "proc-macro" - ] - }, - { - "id": "rand 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "rand_core 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "rand_core", - "pkg": "rand_core 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - } - ], - "features": [ - "getrandom", - "small_rng" - ] - }, - { - "id": "rand_core 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "getrandom 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "getrandom", - "pkg": "getrandom 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - } - ], - "features": [ - "getrandom" - ] - }, - { - "id": "regex 1.7.1 (path+file:///$ROOT$regex)", - "dependencies": [ - "aho-corasick 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "quickcheck 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)", - "regex-syntax 0.6.28 (path+file:///$ROOT$regex/regex-syntax)" - ], - "deps": [ - { - "name": "aho_corasick", - "pkg": "aho-corasick 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "lazy_static", - "pkg": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": "dev", - "target": null - } - ] - }, - { - "name": "memchr", - "pkg": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "quickcheck", - "pkg": "quickcheck 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": "dev", - "target": null - } - ] - }, - { - "name": "rand", - "pkg": "rand 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": "dev", - "target": null - } - ] - }, - { - "name": "regex_syntax", - "pkg": "regex-syntax 0.6.28 (path+file:///$ROOT$regex/regex-syntax)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - } - ], - "features": [ - "aho-corasick", - "default", - "memchr", - "perf", - "perf-cache", - "perf-dfa", - "perf-inline", - "perf-literal", - "std", - "unicode", - "unicode-age", - "unicode-bool", - "unicode-case", - "unicode-gencat", - "unicode-perl", - "unicode-script", - "unicode-segment" - ] - }, - { - "id": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "regex-syntax 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "regex_syntax", - "pkg": "regex-syntax 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - } - ], - "features": [ - "std", - "unicode", - "unicode-age", - "unicode-bool", - "unicode-case", - "unicode-gencat", - "unicode-perl", - "unicode-script", - "unicode-segment" - ] - }, - { - "id": "regex-benchmark 0.1.0 (path+file:///$ROOT$regex/bench)", - "dependencies": [ - "cc 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "docopt 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", - "memmap 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "pkg-config 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.7.1 (path+file:///$ROOT$regex)", - "regex-syntax 0.6.28 (path+file:///$ROOT$regex/regex-syntax)", - "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "cc", - "pkg": "cc 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": "build", - "target": null - } - ] - }, - { - "name": "cfg_if", - "pkg": "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "docopt", - "pkg": "docopt 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "lazy_static", - "pkg": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "libc", - "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "memmap", - "pkg": "memmap 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "pkg_config", - "pkg": "pkg-config 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": "build", - "target": null - } - ] - }, - { - "name": "regex", - "pkg": "regex 1.7.1 (path+file:///$ROOT$regex)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "regex_syntax", - "pkg": "regex-syntax 0.6.28 (path+file:///$ROOT$regex/regex-syntax)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "serde", - "pkg": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - } - ], - "features": [] - }, - { - "id": "regex-debug 0.1.0 (path+file:///$ROOT$regex/regex-debug)", - "dependencies": [ - "docopt 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.7.1 (path+file:///$ROOT$regex)", - "regex-syntax 0.6.28 (path+file:///$ROOT$regex/regex-syntax)", - "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "docopt", - "pkg": "docopt 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "regex", - "pkg": "regex 1.7.1 (path+file:///$ROOT$regex)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "regex_syntax", - "pkg": "regex-syntax 0.6.28 (path+file:///$ROOT$regex/regex-syntax)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "serde", - "pkg": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - } - ], - "features": [] - }, - { - "id": "regex-syntax 0.6.28 (path+file:///$ROOT$regex/regex-syntax)", - "dependencies": [], - "deps": [], - "features": [ - "default", - "unicode", - "unicode-age", - "unicode-bool", - "unicode-case", - "unicode-gencat", - "unicode-perl", - "unicode-script", - "unicode-segment" - ] - }, - { - "id": "regex-syntax 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [], - "deps": [], - "features": [ - "unicode", - "unicode-age", - "unicode-bool", - "unicode-case", - "unicode-gencat", - "unicode-perl", - "unicode-script", - "unicode-segment" - ] - }, - { - "id": "rure 0.2.2 (path+file:///$ROOT$regex/regex-capi)", - "dependencies": [ - "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.7.1 (path+file:///$ROOT$regex)" - ], - "deps": [ - { - "name": "libc", - "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "regex", - "pkg": "regex 1.7.1 (path+file:///$ROOT$regex)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - } - ], - "features": [] - }, - { - "id": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "serde_derive 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "serde_derive", - "pkg": "serde_derive 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - } - ], - "features": [ - "default", - "derive", - "serde_derive", - "std" - ] - }, - { - "id": "serde_derive 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 2.0.15 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "proc_macro2", - "pkg": "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "quote", - "pkg": "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "syn", - "pkg": "syn 2.0.15 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - } - ], - "features": [ - "default" - ] - }, - { - "id": "strsim 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [], - "deps": [], - "features": [] - }, - { - "id": "syn 2.0.15 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "proc_macro2", - "pkg": "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "quote", - "pkg": "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "unicode_ident", - "pkg": "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - } - ], - "features": [ - "clone-impls", - "default", - "derive", - "parsing", - "printing", - "proc-macro", - "quote" - ] - }, - { - "id": "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [], - "deps": [], - "features": [] - }, - { - "id": "wasi 0.11.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [], - "deps": [], - "features": [] - }, - { - "id": "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "winapi_i686_pc_windows_gnu", - "pkg": "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": "i686-pc-windows-gnu" - } - ] - }, - { - "name": "winapi_x86_64_pc_windows_gnu", - "pkg": "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": "x86_64-pc-windows-gnu" - } - ] - } - ], - "features": [ - "basetsd", - "handleapi", - "memoryapi", - "minwindef", - "std", - "sysinfoapi" - ] - }, - { - "id": "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [], - "deps": [], - "features": [] - }, - { - "id": "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [], - "deps": [], - "features": [] - } - ], - "root": "regex 1.7.1 (path+file:///$ROOT$regex)" - }, - "target_directory": "$ROOT$regex/target", - "version": 1, - "workspace_root": "$ROOT$regex", - "metadata": null -} diff --git a/src/tools/rust-analyzer/crates/project-model/test_data/ripgrep-metadata.json b/src/tools/rust-analyzer/crates/project-model/test_data/ripgrep-metadata.json deleted file mode 100644 index 131ff5dd7157..000000000000 --- a/src/tools/rust-analyzer/crates/project-model/test_data/ripgrep-metadata.json +++ /dev/null @@ -1,12816 +0,0 @@ -{ - "packages": [ - { - "name": "aho-corasick", - "version": "0.7.20", - "id": "aho-corasick 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "Unlicense OR MIT", - "license_file": null, - "description": "Fast multiple substring searching.", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "memchr", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^2.4.0", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": false, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "aho_corasick", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/aho-corasick-0.7.20/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - } - ], - "features": { - "default": [ - "std" - ], - "std": [ - "memchr/std" - ] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/aho-corasick-0.7.20/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Andrew Gallant " - ], - "categories": [ - "text-processing" - ], - "keywords": [ - "string", - "search", - "text", - "aho", - "multi" - ], - "readme": "README.md", - "repository": "https://github.com/BurntSushi/aho-corasick", - "homepage": "https://github.com/BurntSushi/aho-corasick", - "documentation": null, - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "aho-corasick", - "version": "1.0.1", - "id": "aho-corasick 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "Unlicense OR MIT", - "license_file": null, - "description": "Fast multiple substring searching.", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "log", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.4.17", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "memchr", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^2.4.0", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": false, - "features": [], - "target": null, - "registry": null - }, - { - "name": "doc-comment", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.3.3", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "aho_corasick", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/aho-corasick-1.0.1/src/lib.rs", - "edition": "2021", - "doc": true, - "doctest": true, - "test": true - } - ], - "features": { - "default": [ - "std", - "perf-literal" - ], - "logging": [ - "dep:log" - ], - "perf-literal": [ - "dep:memchr" - ], - "std": [ - "memchr?/std" - ] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/aho-corasick-1.0.1/Cargo.toml", - "metadata": { - "docs": { - "rs": { - "all-features": true, - "rustdoc-args": [ - "--cfg", - "docsrs" - ] - } - } - }, - "publish": null, - "authors": [ - "Andrew Gallant " - ], - "categories": [ - "text-processing" - ], - "keywords": [ - "string", - "search", - "text", - "pattern", - "multi" - ], - "readme": "README.md", - "repository": "https://github.com/BurntSushi/aho-corasick", - "homepage": "https://github.com/BurntSushi/aho-corasick", - "documentation": null, - "edition": "2021", - "links": null, - "default_run": null, - "rust_version": "1.60.0" - }, - { - "name": "atty", - "version": "0.2.14", - "id": "atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT", - "license_file": null, - "description": "A simple interface for querying atty", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "hermit-abi", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.1.6", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": "cfg(target_os = \"hermit\")", - "registry": null - }, - { - "name": "libc", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.2", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": false, - "features": [], - "target": "cfg(unix)", - "registry": null - }, - { - "name": "winapi", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.3", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [ - "consoleapi", - "processenv", - "minwinbase", - "minwindef", - "winbase" - ], - "target": "cfg(windows)", - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "atty", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/atty-0.2.14/src/lib.rs", - "edition": "2015", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "atty", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/atty-0.2.14/examples/atty.rs", - "edition": "2015", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": {}, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/atty-0.2.14/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "softprops " - ], - "categories": [], - "keywords": [ - "terminal", - "tty", - "isatty" - ], - "readme": "README.md", - "repository": "https://github.com/softprops/atty", - "homepage": "https://github.com/softprops/atty", - "documentation": "http://softprops.github.io/atty", - "edition": "2015", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "base64", - "version": "0.20.0", - "id": "base64 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT OR Apache-2.0", - "license_file": null, - "description": "encodes and decodes base64 as bytes or utf8", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "criterion", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.4.0", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "rand", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.8.5", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [ - "small_rng" - ], - "target": null, - "registry": null - }, - { - "name": "rstest", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.12.0", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "rstest_reuse", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.3.0", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "structopt", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.3.26", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "base64", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/base64-0.20.0/src/lib.rs", - "edition": "2021", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "base64", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/base64-0.20.0/examples/base64.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "encode", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/base64-0.20.0/tests/encode.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "tests", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/base64-0.20.0/tests/tests.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "bench" - ], - "crate_types": [ - "bin" - ], - "name": "benchmarks", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/base64-0.20.0/benches/benchmarks.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": { - "alloc": [], - "default": [ - "std" - ], - "std": [] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/base64-0.20.0/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Alice Maz ", - "Marshall Pierce " - ], - "categories": [ - "encoding" - ], - "keywords": [ - "base64", - "utf8", - "encode", - "decode", - "no_std" - ], - "readme": "README.md", - "repository": "https://github.com/marshallpierce/rust-base64", - "homepage": null, - "documentation": "https://docs.rs/base64", - "edition": "2021", - "links": null, - "default_run": null, - "rust_version": "1.57.0" - }, - { - "name": "bitflags", - "version": "1.3.2", - "id": "bitflags 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT/Apache-2.0", - "license_file": null, - "description": "A macro to generate structures which behave like bitflags.\n", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "compiler_builtins", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.1.2", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "rustc-std-workspace-core", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.0", - "kind": null, - "rename": "core", - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "rustversion", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "serde", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "serde_derive", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "serde_json", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "trybuild", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "walkdir", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^2.3", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "bitflags", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bitflags-1.3.2/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "basic", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bitflags-1.3.2/tests/basic.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "compile", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bitflags-1.3.2/tests/compile.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - } - ], - "features": { - "compiler_builtins": [ - "dep:compiler_builtins" - ], - "core": [ - "dep:core" - ], - "default": [], - "example_generated": [], - "rustc-dep-of-std": [ - "core", - "compiler_builtins" - ] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bitflags-1.3.2/Cargo.toml", - "metadata": { - "docs": { - "rs": { - "features": [ - "example_generated" - ] - } - } - }, - "publish": null, - "authors": [ - "The Rust Project Developers" - ], - "categories": [ - "no-std" - ], - "keywords": [ - "bit", - "bitmask", - "bitflags", - "flags" - ], - "readme": "README.md", - "repository": "https://github.com/bitflags/bitflags", - "homepage": "https://github.com/bitflags/bitflags", - "documentation": "https://docs.rs/bitflags", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "bstr", - "version": "1.4.0", - "id": "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT OR Apache-2.0", - "license_file": null, - "description": "A string type that is not required to be valid UTF-8.", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "memchr", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^2.4.0", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": false, - "features": [], - "target": null, - "registry": null - }, - { - "name": "once_cell", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.14.0", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "regex-automata", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.1.5", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": false, - "features": [], - "target": null, - "registry": null - }, - { - "name": "serde", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.85", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": false, - "features": [], - "target": null, - "registry": null - }, - { - "name": "quickcheck", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": false, - "features": [], - "target": null, - "registry": null - }, - { - "name": "ucd-parse", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.1.3", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "unicode-segmentation", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.2.1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "bstr", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bstr-1.4.0/src/lib.rs", - "edition": "2021", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "graphemes", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bstr-1.4.0/examples/graphemes.rs", - "edition": "2021", - "required-features": [ - "std", - "unicode" - ], - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "lines", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bstr-1.4.0/examples/lines.rs", - "edition": "2021", - "required-features": [ - "std" - ], - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "uppercase", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bstr-1.4.0/examples/uppercase.rs", - "edition": "2021", - "required-features": [ - "std", - "unicode" - ], - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "words", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bstr-1.4.0/examples/words.rs", - "edition": "2021", - "required-features": [ - "std", - "unicode" - ], - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "graphemes-std", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bstr-1.4.0/examples/graphemes-std.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "lines-std", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bstr-1.4.0/examples/lines-std.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "uppercase-std", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bstr-1.4.0/examples/uppercase-std.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "words-std", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bstr-1.4.0/examples/words-std.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": { - "alloc": [ - "serde?/alloc" - ], - "default": [ - "std", - "unicode" - ], - "serde": [ - "dep:serde" - ], - "std": [ - "alloc", - "memchr/std", - "serde?/std" - ], - "unicode": [ - "dep:once_cell", - "dep:regex-automata" - ] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bstr-1.4.0/Cargo.toml", - "metadata": { - "docs": { - "rs": { - "all-features": true, - "rustdoc-args": [ - "--cfg", - "docsrs" - ] - } - } - }, - "publish": null, - "authors": [ - "Andrew Gallant " - ], - "categories": [ - "text-processing", - "encoding" - ], - "keywords": [ - "string", - "str", - "byte", - "bytes", - "text" - ], - "readme": "README.md", - "repository": "https://github.com/BurntSushi/bstr", - "homepage": "https://github.com/BurntSushi/bstr", - "documentation": "https://docs.rs/bstr", - "edition": "2021", - "links": null, - "default_run": null, - "rust_version": "1.60" - }, - { - "name": "bytecount", - "version": "0.6.3", - "id": "bytecount 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "Apache-2.0/MIT", - "license_file": null, - "description": "count occurrences of a given byte, or the number of UTF-8 code points, in a byte slice, fast", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "packed_simd_2", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.3.8", - "kind": null, - "rename": "packed_simd", - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "criterion", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.3", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": false, - "features": [], - "target": null, - "registry": null - }, - { - "name": "quickcheck", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "rand", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.8", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "bytecount", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bytecount-0.6.3/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "check", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bytecount-0.6.3/tests/check.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "bench" - ], - "crate_types": [ - "bin" - ], - "name": "bench", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bytecount-0.6.3/benches/bench.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": { - "generic-simd": [ - "packed_simd" - ], - "html_report": [], - "packed_simd": [ - "dep:packed_simd" - ], - "runtime-dispatch-simd": [] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bytecount-0.6.3/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Andre Bogus ", - "Joshua Landau " - ], - "categories": [ - "algorithms", - "no-std" - ], - "keywords": [], - "readme": "README.md", - "repository": "https://github.com/llogiq/bytecount", - "homepage": null, - "documentation": null, - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "cc", - "version": "1.0.79", - "id": "cc 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT OR Apache-2.0", - "license_file": null, - "description": "A build-time dependency for Cargo build scripts to assist in invoking the native\nC compiler to compile native C code into a static archive to be linked into Rust\ncode.\n", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "jobserver", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.1.16", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "tempfile", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^3", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "cc", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "bin" - ], - "crate_types": [ - "bin" - ], - "name": "gcc-shim", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/src/bin/gcc-shim.rs", - "edition": "2018", - "doc": true, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "cc_env", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/tests/cc_env.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "cflags", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/tests/cflags.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "cxxflags", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/tests/cxxflags.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/tests/test.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - } - ], - "features": { - "jobserver": [ - "dep:jobserver" - ], - "parallel": [ - "jobserver" - ] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Alex Crichton " - ], - "categories": [ - "development-tools::build-utils" - ], - "keywords": [ - "build-dependencies" - ], - "readme": "README.md", - "repository": "https://github.com/rust-lang/cc-rs", - "homepage": "https://github.com/rust-lang/cc-rs", - "documentation": "https://docs.rs/cc", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "cfg-if", - "version": "1.0.0", - "id": "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT/Apache-2.0", - "license_file": null, - "description": "A macro to ergonomically define an item depending on a large number of #[cfg]\nparameters. Structured like an if-else chain, the first matching branch is the\nitem that gets emitted.\n", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "compiler_builtins", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.1.2", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "rustc-std-workspace-core", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.0", - "kind": null, - "rename": "core", - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "cfg-if", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cfg-if-1.0.0/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "xcrate", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cfg-if-1.0.0/tests/xcrate.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - } - ], - "features": { - "compiler_builtins": [ - "dep:compiler_builtins" - ], - "core": [ - "dep:core" - ], - "rustc-dep-of-std": [ - "core", - "compiler_builtins" - ] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cfg-if-1.0.0/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Alex Crichton " - ], - "categories": [], - "keywords": [], - "readme": "README.md", - "repository": "https://github.com/alexcrichton/cfg-if", - "homepage": "https://github.com/alexcrichton/cfg-if", - "documentation": "https://docs.rs/cfg-if", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "clap", - "version": "2.34.0", - "id": "clap 2.34.0 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT", - "license_file": null, - "description": "A simple to use, efficient, and full-featured Command Line Argument Parser\n", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "atty", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.2.2", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "bitflags", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "clippy", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "~0.0.166", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "strsim", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.8", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "term_size", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.3.0", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "textwrap", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.11.0", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "unicode-width", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.1.4", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "vec_map", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.8", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "yaml-rust", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.3.5", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "lazy_static", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.3", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "regex", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "version-sync", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.8", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "ansi_term", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.12", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": "cfg(not(windows))", - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "clap", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/clap-2.34.0/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - } - ], - "features": { - "ansi_term": [ - "dep:ansi_term" - ], - "atty": [ - "dep:atty" - ], - "clippy": [ - "dep:clippy" - ], - "color": [ - "ansi_term", - "atty" - ], - "debug": [], - "default": [ - "suggestions", - "color", - "vec_map" - ], - "doc": [ - "yaml" - ], - "nightly": [], - "no_cargo": [], - "strsim": [ - "dep:strsim" - ], - "suggestions": [ - "strsim" - ], - "term_size": [ - "dep:term_size" - ], - "unstable": [], - "vec_map": [ - "dep:vec_map" - ], - "wrap_help": [ - "term_size", - "textwrap/term_size" - ], - "yaml": [ - "yaml-rust" - ], - "yaml-rust": [ - "dep:yaml-rust" - ] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/clap-2.34.0/Cargo.toml", - "metadata": { - "docs": { - "rs": { - "features": [ - "doc" - ] - } - } - }, - "publish": null, - "authors": [ - "Kevin K. " - ], - "categories": [ - "command-line-interface" - ], - "keywords": [ - "argument", - "cli", - "arg", - "parser", - "parse" - ], - "readme": "README.md", - "repository": "https://github.com/clap-rs/clap", - "homepage": "https://clap.rs/", - "documentation": "https://docs.rs/clap/", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "crossbeam-channel", - "version": "0.5.8", - "id": "crossbeam-channel 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT OR Apache-2.0", - "license_file": null, - "description": "Multi-producer multi-consumer channels for message passing", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "cfg-if", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "crossbeam-utils", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.8", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": false, - "features": [], - "target": null, - "registry": null - }, - { - "name": "num_cpus", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.13.0", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "rand", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.8", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "signal-hook", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.3", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "crossbeam-channel", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "fibonacci", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/examples/fibonacci.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "matching", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/examples/matching.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "stopwatch", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/examples/stopwatch.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "after", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/after.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "array", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/array.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "golang", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/golang.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "iter", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/iter.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "list", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/list.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "mpsc", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/mpsc.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "never", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/never.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "ready", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/ready.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "same_channel", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/same_channel.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "select", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/select.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "select_macro", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/select_macro.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "thread_locals", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/thread_locals.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "tick", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/tick.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "zero", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/zero.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "bench" - ], - "crate_types": [ - "bin" - ], - "name": "crossbeam", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/benches/crossbeam.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": { - "crossbeam-utils": [ - "dep:crossbeam-utils" - ], - "default": [ - "std" - ], - "std": [ - "crossbeam-utils/std" - ] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [], - "categories": [ - "algorithms", - "concurrency", - "data-structures" - ], - "keywords": [ - "channel", - "mpmc", - "select", - "golang", - "message" - ], - "readme": "README.md", - "repository": "https://github.com/crossbeam-rs/crossbeam", - "homepage": "https://github.com/crossbeam-rs/crossbeam/tree/master/crossbeam-channel", - "documentation": null, - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": "1.38" - }, - { - "name": "crossbeam-utils", - "version": "0.8.15", - "id": "crossbeam-utils 0.8.15 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT OR Apache-2.0", - "license_file": null, - "description": "Utilities for concurrent programming", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "cfg-if", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "rand", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.8", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "rustversion", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "loom", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.5", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": "cfg(crossbeam_loom)", - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "crossbeam-utils", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-utils-0.8.15/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "atomic_cell", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-utils-0.8.15/tests/atomic_cell.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "cache_padded", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-utils-0.8.15/tests/cache_padded.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "parker", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-utils-0.8.15/tests/parker.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "sharded_lock", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-utils-0.8.15/tests/sharded_lock.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "thread", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-utils-0.8.15/tests/thread.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "wait_group", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-utils-0.8.15/tests/wait_group.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "bench" - ], - "crate_types": [ - "bin" - ], - "name": "atomic_cell", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-utils-0.8.15/benches/atomic_cell.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "custom-build" - ], - "crate_types": [ - "bin" - ], - "name": "build-script-build", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-utils-0.8.15/build.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": { - "default": [ - "std" - ], - "loom": [ - "dep:loom" - ], - "nightly": [], - "std": [] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-utils-0.8.15/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [], - "categories": [ - "algorithms", - "concurrency", - "data-structures", - "no-std" - ], - "keywords": [ - "scoped", - "thread", - "atomic", - "cache" - ], - "readme": "README.md", - "repository": "https://github.com/crossbeam-rs/crossbeam", - "homepage": "https://github.com/crossbeam-rs/crossbeam/tree/master/crossbeam-utils", - "documentation": null, - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": "1.38" - }, - { - "name": "encoding_rs", - "version": "0.8.32", - "id": "encoding_rs 0.8.32 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "(Apache-2.0 OR MIT) AND BSD-3-Clause", - "license_file": null, - "description": "A Gecko-oriented implementation of the Encoding Standard", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "cfg-if", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "packed_simd_2", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.3.4", - "kind": null, - "rename": "packed_simd", - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "serde", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "bincode", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "serde_derive", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "serde_json", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "encoding_rs", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/encoding_rs-0.8.32/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - } - ], - "features": { - "alloc": [], - "default": [ - "alloc" - ], - "fast-big5-hanzi-encode": [], - "fast-gb-hanzi-encode": [], - "fast-hangul-encode": [], - "fast-hanja-encode": [], - "fast-kanji-encode": [], - "fast-legacy-encode": [ - "fast-hangul-encode", - "fast-hanja-encode", - "fast-kanji-encode", - "fast-gb-hanzi-encode", - "fast-big5-hanzi-encode" - ], - "less-slow-big5-hanzi-encode": [], - "less-slow-gb-hanzi-encode": [], - "less-slow-kanji-encode": [], - "packed_simd": [ - "dep:packed_simd" - ], - "serde": [ - "dep:serde" - ], - "simd-accel": [ - "packed_simd", - "packed_simd/into_bits" - ] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/encoding_rs-0.8.32/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Henri Sivonen " - ], - "categories": [ - "text-processing", - "encoding", - "web-programming", - "internationalization" - ], - "keywords": [ - "encoding", - "web", - "unicode", - "charset" - ], - "readme": "README.md", - "repository": "https://github.com/hsivonen/encoding_rs", - "homepage": "https://docs.rs/encoding_rs/", - "documentation": "https://docs.rs/encoding_rs/", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "encoding_rs_io", - "version": "0.1.7", - "id": "encoding_rs_io 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT OR Apache-2.0", - "license_file": null, - "description": "Streaming transcoding for encoding_rs", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "encoding_rs", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.8", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "encoding_rs_io", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/encoding_rs_io-0.1.7/src/lib.rs", - "edition": "2015", - "doc": true, - "doctest": true, - "test": true - } - ], - "features": {}, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/encoding_rs_io-0.1.7/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Andrew Gallant " - ], - "categories": [ - "text-processing", - "encoding", - "web-programming", - "email" - ], - "keywords": [ - "encoding", - "transcoding", - "stream", - "io", - "read" - ], - "readme": "README.md", - "repository": "https://github.com/BurntSushi/encoding_rs_io", - "homepage": null, - "documentation": "https://docs.rs/encoding_rs_io", - "edition": "2015", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "fnv", - "version": "1.0.7", - "id": "fnv 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "Apache-2.0 / MIT", - "license_file": null, - "description": "Fowler–Noll–Vo hash function", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "fnv", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/fnv-1.0.7/lib.rs", - "edition": "2015", - "doc": true, - "doctest": true, - "test": true - } - ], - "features": { - "default": [ - "std" - ], - "std": [] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/fnv-1.0.7/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Alex Crichton " - ], - "categories": [], - "keywords": [], - "readme": "README.md", - "repository": "https://github.com/servo/rust-fnv", - "homepage": null, - "documentation": "https://doc.servo.org/fnv/", - "edition": "2015", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "glob", - "version": "0.3.1", - "id": "glob 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT OR Apache-2.0", - "license_file": null, - "description": "Support for matching file paths against Unix shell style patterns.\n", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "doc-comment", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.3", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "tempdir", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.3", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "glob", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/glob-0.3.1/src/lib.rs", - "edition": "2015", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "glob-std", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/glob-0.3.1/tests/glob-std.rs", - "edition": "2015", - "doc": false, - "doctest": false, - "test": true - } - ], - "features": {}, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/glob-0.3.1/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "The Rust Project Developers" - ], - "categories": [ - "filesystem" - ], - "keywords": [], - "readme": "README.md", - "repository": "https://github.com/rust-lang/glob", - "homepage": "https://github.com/rust-lang/glob", - "documentation": "https://docs.rs/glob/0.3.1", - "edition": "2015", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "globset", - "version": "0.4.10", - "id": "globset 0.4.10 (path+file:///$ROOT$ripgrep/crates/globset)", - "license": "Unlicense OR MIT", - "license_file": null, - "description": "Cross platform single glob and glob set matching. Glob set matching is the\nprocess of matching one or more glob patterns against a single candidate path\nsimultaneously, and returning all of the globs that matched.\n", - "source": null, - "dependencies": [ - { - "name": "aho-corasick", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.7.3", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "bstr", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.1.0", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": false, - "features": [ - "std" - ], - "target": null, - "registry": null - }, - { - "name": "fnv", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.6", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "log", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.4.5", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "regex", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.1.5", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": false, - "features": [ - "perf", - "std" - ], - "target": null, - "registry": null - }, - { - "name": "serde", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.104", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "glob", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.3.0", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "lazy_static", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "serde_json", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.45", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "globset", - "src_path": "$ROOT$ripgrep/crates/globset/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "bench" - ], - "crate_types": [ - "bin" - ], - "name": "bench", - "src_path": "$ROOT$ripgrep/crates/globset/benches/bench.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": { - "default": [ - "log" - ], - "log": [ - "dep:log" - ], - "serde": [ - "dep:serde" - ], - "serde1": [ - "serde" - ], - "simd-accel": [] - }, - "manifest_path": "$ROOT$ripgrep/crates/globset/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Andrew Gallant " - ], - "categories": [], - "keywords": [ - "regex", - "glob", - "multiple", - "set", - "pattern" - ], - "readme": "README.md", - "repository": "https://github.com/BurntSushi/ripgrep/tree/master/crates/globset", - "homepage": "https://github.com/BurntSushi/ripgrep/tree/master/crates/globset", - "documentation": "https://docs.rs/globset", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "grep", - "version": "0.2.11", - "id": "grep 0.2.11 (path+file:///$ROOT$ripgrep/crates/grep)", - "license": "Unlicense OR MIT", - "license_file": null, - "description": "Fast line oriented regex searching as a library.\n", - "source": null, - "dependencies": [ - { - "name": "grep-cli", - "source": null, - "req": "^0.1.7", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null, - "path": "$ROOT$ripgrep/crates/cli" - }, - { - "name": "grep-matcher", - "source": null, - "req": "^0.1.6", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null, - "path": "$ROOT$ripgrep/crates/matcher" - }, - { - "name": "grep-pcre2", - "source": null, - "req": "^0.1.6", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null, - "path": "$ROOT$ripgrep/crates/pcre2" - }, - { - "name": "grep-printer", - "source": null, - "req": "^0.1.7", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null, - "path": "$ROOT$ripgrep/crates/printer" - }, - { - "name": "grep-regex", - "source": null, - "req": "^0.1.11", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null, - "path": "$ROOT$ripgrep/crates/regex" - }, - { - "name": "grep-searcher", - "source": null, - "req": "^0.1.11", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null, - "path": "$ROOT$ripgrep/crates/searcher" - }, - { - "name": "termcolor", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.4", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "walkdir", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^2.2.7", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "grep", - "src_path": "$ROOT$ripgrep/crates/grep/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "simplegrep", - "src_path": "$ROOT$ripgrep/crates/grep/examples/simplegrep.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": { - "avx-accel": [], - "grep-pcre2": [ - "dep:grep-pcre2" - ], - "pcre2": [ - "grep-pcre2" - ], - "simd-accel": [ - "grep-searcher/simd-accel" - ] - }, - "manifest_path": "$ROOT$ripgrep/crates/grep/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Andrew Gallant " - ], - "categories": [], - "keywords": [ - "regex", - "grep", - "egrep", - "search", - "pattern" - ], - "readme": "README.md", - "repository": "https://github.com/BurntSushi/ripgrep/tree/master/crates/grep", - "homepage": "https://github.com/BurntSushi/ripgrep/tree/master/crates/grep", - "documentation": "https://docs.rs/grep", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "grep-cli", - "version": "0.1.7", - "id": "grep-cli 0.1.7 (path+file:///$ROOT$ripgrep/crates/cli)", - "license": "Unlicense OR MIT", - "license_file": null, - "description": "Utilities for search oriented command line applications.\n", - "source": null, - "dependencies": [ - { - "name": "atty", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.2.11", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "bstr", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.1.0", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "globset", - "source": null, - "req": "^0.4.10", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null, - "path": "$ROOT$ripgrep/crates/globset" - }, - { - "name": "lazy_static", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.1.0", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "log", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.4.5", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "regex", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.1", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "same-file", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.4", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "termcolor", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.4", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "winapi-util", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.1.1", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": "cfg(windows)", - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "grep-cli", - "src_path": "$ROOT$ripgrep/crates/cli/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - } - ], - "features": {}, - "manifest_path": "$ROOT$ripgrep/crates/cli/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Andrew Gallant " - ], - "categories": [], - "keywords": [ - "regex", - "grep", - "cli", - "utility", - "util" - ], - "readme": "README.md", - "repository": "https://github.com/BurntSushi/ripgrep/tree/master/crates/cli", - "homepage": "https://github.com/BurntSushi/ripgrep/tree/master/crates/cli", - "documentation": "https://docs.rs/grep-cli", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "grep-matcher", - "version": "0.1.6", - "id": "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)", - "license": "Unlicense OR MIT", - "license_file": null, - "description": "A trait for regular expressions, with a focus on line oriented search.\n", - "source": null, - "dependencies": [ - { - "name": "memchr", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^2.1", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "regex", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "grep-matcher", - "src_path": "$ROOT$ripgrep/crates/matcher/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "integration", - "src_path": "$ROOT$ripgrep/crates/matcher/tests/tests.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - } - ], - "features": {}, - "manifest_path": "$ROOT$ripgrep/crates/matcher/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Andrew Gallant " - ], - "categories": [], - "keywords": [ - "regex", - "pattern", - "trait" - ], - "readme": "README.md", - "repository": "https://github.com/BurntSushi/ripgrep/tree/master/crates/matcher", - "homepage": "https://github.com/BurntSushi/ripgrep/tree/master/crates/matcher", - "documentation": "https://docs.rs/grep-matcher", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "grep-pcre2", - "version": "0.1.6", - "id": "grep-pcre2 0.1.6 (path+file:///$ROOT$ripgrep/crates/pcre2)", - "license": "Unlicense OR MIT", - "license_file": null, - "description": "Use PCRE2 with the 'grep' crate.\n", - "source": null, - "dependencies": [ - { - "name": "grep-matcher", - "source": null, - "req": "^0.1.6", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null, - "path": "$ROOT$ripgrep/crates/matcher" - }, - { - "name": "pcre2", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.2.3", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "grep-pcre2", - "src_path": "$ROOT$ripgrep/crates/pcre2/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - } - ], - "features": {}, - "manifest_path": "$ROOT$ripgrep/crates/pcre2/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Andrew Gallant " - ], - "categories": [], - "keywords": [ - "regex", - "grep", - "pcre", - "backreference", - "look" - ], - "readme": "README.md", - "repository": "https://github.com/BurntSushi/ripgrep/tree/master/crates/pcre2", - "homepage": "https://github.com/BurntSushi/ripgrep/tree/master/crates/pcre2", - "documentation": "https://docs.rs/grep-pcre2", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "grep-printer", - "version": "0.1.7", - "id": "grep-printer 0.1.7 (path+file:///$ROOT$ripgrep/crates/printer)", - "license": "Unlicense OR MIT", - "license_file": null, - "description": "An implementation of the grep crate's Sink trait that provides standard\nprinting of search results, similar to grep itself.\n", - "source": null, - "dependencies": [ - { - "name": "base64", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.20.0", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "bstr", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.1.0", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "grep-matcher", - "source": null, - "req": "^0.1.6", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null, - "path": "$ROOT$ripgrep/crates/matcher" - }, - { - "name": "grep-searcher", - "source": null, - "req": "^0.1.11", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null, - "path": "$ROOT$ripgrep/crates/searcher" - }, - { - "name": "serde", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.77", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [ - "derive" - ], - "target": null, - "registry": null - }, - { - "name": "serde_json", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.27", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "termcolor", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.4", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "grep-regex", - "source": null, - "req": "^0.1.11", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null, - "path": "$ROOT$ripgrep/crates/regex" - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "grep-printer", - "src_path": "$ROOT$ripgrep/crates/printer/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - } - ], - "features": { - "base64": [ - "dep:base64" - ], - "default": [ - "serde1" - ], - "serde": [ - "dep:serde" - ], - "serde1": [ - "base64", - "serde", - "serde_json" - ], - "serde_json": [ - "dep:serde_json" - ] - }, - "manifest_path": "$ROOT$ripgrep/crates/printer/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Andrew Gallant " - ], - "categories": [], - "keywords": [ - "grep", - "pattern", - "print", - "printer", - "sink" - ], - "readme": "README.md", - "repository": "https://github.com/BurntSushi/ripgrep/tree/master/crates/printer", - "homepage": "https://github.com/BurntSushi/ripgrep/tree/master/crates/printer", - "documentation": "https://docs.rs/grep-printer", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "grep-regex", - "version": "0.1.11", - "id": "grep-regex 0.1.11 (path+file:///$ROOT$ripgrep/crates/regex)", - "license": "Unlicense OR MIT", - "license_file": null, - "description": "Use Rust's regex library with the 'grep' crate.\n", - "source": null, - "dependencies": [ - { - "name": "aho-corasick", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.7.3", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "bstr", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.1.0", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "grep-matcher", - "source": null, - "req": "^0.1.6", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null, - "path": "$ROOT$ripgrep/crates/matcher" - }, - { - "name": "log", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.4.5", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "regex", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.1", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "regex-syntax", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.6.5", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "thread_local", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.1.2", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "grep-regex", - "src_path": "$ROOT$ripgrep/crates/regex/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - } - ], - "features": {}, - "manifest_path": "$ROOT$ripgrep/crates/regex/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Andrew Gallant " - ], - "categories": [], - "keywords": [ - "regex", - "grep", - "search", - "pattern", - "line" - ], - "readme": "README.md", - "repository": "https://github.com/BurntSushi/ripgrep/tree/master/crates/regex", - "homepage": "https://github.com/BurntSushi/ripgrep/tree/master/crates/regex", - "documentation": "https://docs.rs/grep-regex", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "grep-searcher", - "version": "0.1.11", - "id": "grep-searcher 0.1.11 (path+file:///$ROOT$ripgrep/crates/searcher)", - "license": "Unlicense OR MIT", - "license_file": null, - "description": "Fast line oriented regex searching as a library.\n", - "source": null, - "dependencies": [ - { - "name": "bstr", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.1.0", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": false, - "features": [ - "std" - ], - "target": null, - "registry": null - }, - { - "name": "bytecount", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.6", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "encoding_rs", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.8.14", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "encoding_rs_io", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.1.6", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "grep-matcher", - "source": null, - "req": "^0.1.6", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null, - "path": "$ROOT$ripgrep/crates/matcher" - }, - { - "name": "log", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.4.5", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "memmap2", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.5.3", - "kind": null, - "rename": "memmap", - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "grep-regex", - "source": null, - "req": "^0.1.11", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null, - "path": "$ROOT$ripgrep/crates/regex" - }, - { - "name": "regex", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "grep-searcher", - "src_path": "$ROOT$ripgrep/crates/searcher/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "search-stdin", - "src_path": "$ROOT$ripgrep/crates/searcher/examples/search-stdin.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": { - "avx-accel": [], - "default": [ - "bytecount/runtime-dispatch-simd" - ], - "simd-accel": [ - "encoding_rs/simd-accel" - ] - }, - "manifest_path": "$ROOT$ripgrep/crates/searcher/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Andrew Gallant " - ], - "categories": [], - "keywords": [ - "regex", - "grep", - "egrep", - "search", - "pattern" - ], - "readme": "README.md", - "repository": "https://github.com/BurntSushi/ripgrep/tree/master/crates/searcher", - "homepage": "https://github.com/BurntSushi/ripgrep/tree/master/crates/searcher", - "documentation": "https://docs.rs/grep-searcher", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "hermit-abi", - "version": "0.1.19", - "id": "hermit-abi 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT/Apache-2.0", - "license_file": null, - "description": "hermit-abi is small interface to call functions from the unikernel RustyHermit.\nIt is used to build the target `x86_64-unknown-hermit`.\n", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "compiler_builtins", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.1", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "rustc-std-workspace-core", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.0", - "kind": null, - "rename": "core", - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "libc", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.2.51", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": false, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "hermit-abi", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/hermit-abi-0.1.19/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - } - ], - "features": { - "compiler_builtins": [ - "dep:compiler_builtins" - ], - "core": [ - "dep:core" - ], - "default": [], - "docs": [], - "rustc-dep-of-std": [ - "core", - "compiler_builtins/rustc-dep-of-std", - "libc/rustc-dep-of-std" - ] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/hermit-abi-0.1.19/Cargo.toml", - "metadata": { - "docs": { - "rs": { - "default-target": "x86_64-unknown-hermit", - "features": [ - "docs" - ] - } - } - }, - "publish": null, - "authors": [ - "Stefan Lankes" - ], - "categories": [ - "os" - ], - "keywords": [ - "unikernel", - "libos" - ], - "readme": "README.md", - "repository": "https://github.com/hermitcore/libhermit-rs", - "homepage": null, - "documentation": "https://hermitcore.github.io/rusty-hermit/hermit_abi", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "ignore", - "version": "0.4.20", - "id": "ignore 0.4.20 (path+file:///$ROOT$ripgrep/crates/ignore)", - "license": "Unlicense OR MIT", - "license_file": null, - "description": "A fast library for efficiently matching ignore files such as `.gitignore`\nagainst file paths.\n", - "source": null, - "dependencies": [ - { - "name": "globset", - "source": null, - "req": "^0.4.10", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null, - "path": "$ROOT$ripgrep/crates/globset" - }, - { - "name": "lazy_static", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.1", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "log", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.4.5", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "memchr", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^2.1", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "regex", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.1", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "same-file", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.4", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "thread_local", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "walkdir", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^2.2.7", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "crossbeam-channel", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.5.0", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "winapi-util", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.1.2", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": "cfg(windows)", - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "ignore", - "src_path": "$ROOT$ripgrep/crates/ignore/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "walk", - "src_path": "$ROOT$ripgrep/crates/ignore/examples/walk.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "gitignore_matched_path_or_any_parents_tests", - "src_path": "$ROOT$ripgrep/crates/ignore/tests/gitignore_matched_path_or_any_parents_tests.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - } - ], - "features": { - "simd-accel": [ - "globset/simd-accel" - ] - }, - "manifest_path": "$ROOT$ripgrep/crates/ignore/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Andrew Gallant " - ], - "categories": [], - "keywords": [ - "glob", - "ignore", - "gitignore", - "pattern", - "file" - ], - "readme": "README.md", - "repository": "https://github.com/BurntSushi/ripgrep/tree/master/crates/ignore", - "homepage": "https://github.com/BurntSushi/ripgrep/tree/master/crates/ignore", - "documentation": "https://docs.rs/ignore", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "itoa", - "version": "1.0.6", - "id": "itoa 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT OR Apache-2.0", - "license_file": null, - "description": "Fast integer primitive to string conversion", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "no-panic", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.1", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "itoa", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/itoa-1.0.6/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/itoa-1.0.6/tests/test.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "bench" - ], - "crate_types": [ - "bin" - ], - "name": "bench", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/itoa-1.0.6/benches/bench.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": { - "no-panic": [ - "dep:no-panic" - ] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/itoa-1.0.6/Cargo.toml", - "metadata": { - "docs": { - "rs": { - "targets": [ - "x86_64-unknown-linux-gnu" - ] - } - } - }, - "publish": null, - "authors": [ - "David Tolnay " - ], - "categories": [ - "value-formatting", - "no-std" - ], - "keywords": [ - "integer" - ], - "readme": "README.md", - "repository": "https://github.com/dtolnay/itoa", - "homepage": null, - "documentation": "https://docs.rs/itoa", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": "1.36" - }, - { - "name": "jemalloc-sys", - "version": "0.5.3+5.3.0-patched", - "id": "jemalloc-sys 0.5.3+5.3.0-patched (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT/Apache-2.0", - "license_file": null, - "description": "Rust FFI bindings to jemalloc\n", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "libc", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.2.8", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": false, - "features": [], - "target": null, - "registry": null - }, - { - "name": "cc", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.13", - "kind": "build", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "jemalloc-sys", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemalloc-sys-0.5.3+5.3.0-patched/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "malloc_conf_empty", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemalloc-sys-0.5.3+5.3.0-patched/tests/malloc_conf_empty.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "malloc_conf_set", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemalloc-sys-0.5.3+5.3.0-patched/tests/malloc_conf_set.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "unprefixed_malloc", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemalloc-sys-0.5.3+5.3.0-patched/tests/unprefixed_malloc.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "custom-build" - ], - "crate_types": [ - "bin" - ], - "name": "build-script-build", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemalloc-sys-0.5.3+5.3.0-patched/build.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": { - "background_threads": [ - "background_threads_runtime_support" - ], - "background_threads_runtime_support": [], - "debug": [], - "default": [ - "background_threads_runtime_support" - ], - "disable_initial_exec_tls": [], - "profiling": [], - "stats": [], - "unprefixed_malloc_on_supported_platforms": [] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemalloc-sys-0.5.3+5.3.0-patched/Cargo.toml", - "metadata": { - "docs": { - "rs": { - "rustdoc-args": [ - "--cfg", - "jemallocator_docs" - ] - } - } - }, - "publish": null, - "authors": [ - "Alex Crichton ", - "Gonzalo Brito Gadeschi ", - "The TiKV Project Developers" - ], - "categories": [], - "keywords": [ - "allocator", - "jemalloc" - ], - "readme": "README.md", - "repository": "https://github.com/tikv/jemallocator", - "homepage": "https://github.com/tikv/jemallocator", - "documentation": "https://docs.rs/jemallocator-sys", - "edition": "2018", - "links": "jemalloc", - "default_run": null, - "rust_version": null - }, - { - "name": "jemallocator", - "version": "0.5.0", - "id": "jemallocator 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT/Apache-2.0", - "license_file": null, - "description": "A Rust allocator backed by jemalloc\n", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "jemalloc-sys", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.5.0", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": false, - "features": [], - "target": null, - "registry": null - }, - { - "name": "libc", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.2.8", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": false, - "features": [], - "target": null, - "registry": null - }, - { - "name": "paste", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "jemallocator", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": false - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "background_thread_defaults", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/tests/background_thread_defaults.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "background_thread_enabled", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/tests/background_thread_enabled.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "ffi", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/tests/ffi.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "grow_in_place", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/tests/grow_in_place.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "malloctl", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/tests/malloctl.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "shrink_in_place", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/tests/shrink_in_place.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "smoke", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/tests/smoke.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "smoke_ffi", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/tests/smoke_ffi.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "usable_size", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/tests/usable_size.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "bench" - ], - "crate_types": [ - "bin" - ], - "name": "roundtrip", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/benches/roundtrip.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": { - "alloc_trait": [], - "background_threads": [ - "jemalloc-sys/background_threads" - ], - "background_threads_runtime_support": [ - "jemalloc-sys/background_threads_runtime_support" - ], - "debug": [ - "jemalloc-sys/debug" - ], - "default": [ - "background_threads_runtime_support" - ], - "disable_initial_exec_tls": [ - "jemalloc-sys/disable_initial_exec_tls" - ], - "profiling": [ - "jemalloc-sys/profiling" - ], - "stats": [ - "jemalloc-sys/stats" - ], - "unprefixed_malloc_on_supported_platforms": [ - "jemalloc-sys/unprefixed_malloc_on_supported_platforms" - ] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/Cargo.toml", - "metadata": { - "docs": { - "rs": { - "features": [], - "rustdoc-args": [ - "--cfg", - "jemallocator_docs" - ] - } - } - }, - "publish": null, - "authors": [ - "Alex Crichton ", - "Gonzalo Brito Gadeschi ", - "Simon Sapin ", - "Steven Fackler ", - "The TiKV Project Developers" - ], - "categories": [ - "memory-management", - "api-bindings" - ], - "keywords": [ - "allocator", - "jemalloc" - ], - "readme": "README.md", - "repository": "https://github.com/tikv/jemallocator", - "homepage": "https://github.com/tikv/jemallocator", - "documentation": "https://docs.rs/jemallocator", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "jobserver", - "version": "0.1.26", - "id": "jobserver 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT/Apache-2.0", - "license_file": null, - "description": "An implementation of the GNU make jobserver for Rust\n", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "futures", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "num_cpus", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "tempfile", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^3", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "tokio-core", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "tokio-process", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.2", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "libc", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.2.50", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": "cfg(unix)", - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "jobserver", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jobserver-0.1.26/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "client", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jobserver-0.1.26/tests/client.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "server", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jobserver-0.1.26/tests/server.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "client-of-myself", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jobserver-0.1.26/tests/client-of-myself.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "make-as-a-client", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jobserver-0.1.26/tests/make-as-a-client.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "helper", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jobserver-0.1.26/tests/helper.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - } - ], - "features": {}, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jobserver-0.1.26/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Alex Crichton " - ], - "categories": [], - "keywords": [], - "readme": "README.md", - "repository": "https://github.com/alexcrichton/jobserver-rs", - "homepage": "https://github.com/alexcrichton/jobserver-rs", - "documentation": "https://docs.rs/jobserver", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "lazy_static", - "version": "1.4.0", - "id": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT/Apache-2.0", - "license_file": null, - "description": "A macro for declaring lazily evaluated statics in Rust.", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "spin", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.5.0", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "doc-comment", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.3.1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "lazy_static", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/lazy_static-1.4.0/src/lib.rs", - "edition": "2015", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "no_std", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/lazy_static-1.4.0/tests/no_std.rs", - "edition": "2015", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/lazy_static-1.4.0/tests/test.rs", - "edition": "2015", - "doc": false, - "doctest": false, - "test": true - } - ], - "features": { - "spin": [ - "dep:spin" - ], - "spin_no_std": [ - "spin" - ] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/lazy_static-1.4.0/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Marvin Löbel " - ], - "categories": [ - "no-std", - "rust-patterns", - "memory-management" - ], - "keywords": [ - "macro", - "lazy", - "static" - ], - "readme": "README.md", - "repository": "https://github.com/rust-lang-nursery/lazy-static.rs", - "homepage": null, - "documentation": "https://docs.rs/lazy_static", - "edition": "2015", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "libc", - "version": "0.2.142", - "id": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT OR Apache-2.0", - "license_file": null, - "description": "Raw FFI bindings to platform libraries like libc.\n", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "rustc-std-workspace-core", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.0", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "libc", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/libc-0.2.142/src/lib.rs", - "edition": "2015", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "const_fn", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/libc-0.2.142/tests/const_fn.rs", - "edition": "2015", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "custom-build" - ], - "crate_types": [ - "bin" - ], - "name": "build-script-build", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/libc-0.2.142/build.rs", - "edition": "2015", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": { - "align": [], - "const-extern-fn": [], - "default": [ - "std" - ], - "extra_traits": [], - "rustc-dep-of-std": [ - "align", - "rustc-std-workspace-core" - ], - "rustc-std-workspace-core": [ - "dep:rustc-std-workspace-core" - ], - "std": [], - "use_std": [ - "std" - ] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/libc-0.2.142/Cargo.toml", - "metadata": { - "docs": { - "rs": { - "features": [ - "const-extern-fn", - "extra_traits" - ] - } - } - }, - "publish": null, - "authors": [ - "The Rust Project Developers" - ], - "categories": [ - "external-ffi-bindings", - "no-std", - "os" - ], - "keywords": [ - "libc", - "ffi", - "bindings", - "operating", - "system" - ], - "readme": "README.md", - "repository": "https://github.com/rust-lang/libc", - "homepage": "https://github.com/rust-lang/libc", - "documentation": "https://docs.rs/libc/", - "edition": "2015", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "log", - "version": "0.4.17", - "id": "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT OR Apache-2.0", - "license_file": null, - "description": "A lightweight logging facade for Rust\n", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "cfg-if", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "serde", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": false, - "features": [], - "target": null, - "registry": null - }, - { - "name": "sval", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "=1.0.0-alpha.5", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": false, - "features": [], - "target": null, - "registry": null - }, - { - "name": "value-bag", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "=1.0.0-alpha.9", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": false, - "features": [], - "target": null, - "registry": null - }, - { - "name": "rustversion", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "serde", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [ - "derive" - ], - "target": null, - "registry": null - }, - { - "name": "serde_test", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "sval", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "=1.0.0-alpha.5", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [ - "derive" - ], - "target": null, - "registry": null - }, - { - "name": "value-bag", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "=1.0.0-alpha.9", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [ - "test" - ], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "log", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/log-0.4.17/src/lib.rs", - "edition": "2015", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "filters", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/log-0.4.17/tests/filters.rs", - "edition": "2015", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "macros", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/log-0.4.17/tests/macros.rs", - "edition": "2015", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "bench" - ], - "crate_types": [ - "bin" - ], - "name": "value", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/log-0.4.17/benches/value.rs", - "edition": "2015", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "custom-build" - ], - "crate_types": [ - "bin" - ], - "name": "build-script-build", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/log-0.4.17/build.rs", - "edition": "2015", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": { - "kv_unstable": [ - "value-bag" - ], - "kv_unstable_serde": [ - "kv_unstable_std", - "value-bag/serde", - "serde" - ], - "kv_unstable_std": [ - "std", - "kv_unstable", - "value-bag/error" - ], - "kv_unstable_sval": [ - "kv_unstable", - "value-bag/sval", - "sval" - ], - "max_level_debug": [], - "max_level_error": [], - "max_level_info": [], - "max_level_off": [], - "max_level_trace": [], - "max_level_warn": [], - "release_max_level_debug": [], - "release_max_level_error": [], - "release_max_level_info": [], - "release_max_level_off": [], - "release_max_level_trace": [], - "release_max_level_warn": [], - "serde": [ - "dep:serde" - ], - "std": [], - "sval": [ - "dep:sval" - ], - "value-bag": [ - "dep:value-bag" - ] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/log-0.4.17/Cargo.toml", - "metadata": { - "docs": { - "rs": { - "features": [ - "std", - "serde", - "kv_unstable_std", - "kv_unstable_sval", - "kv_unstable_serde" - ] - } - } - }, - "publish": null, - "authors": [ - "The Rust Project Developers" - ], - "categories": [ - "development-tools::debugging" - ], - "keywords": [ - "logging" - ], - "readme": "README.md", - "repository": "https://github.com/rust-lang/log", - "homepage": null, - "documentation": "https://docs.rs/log", - "edition": "2015", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "memchr", - "version": "2.5.0", - "id": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "Unlicense/MIT", - "license_file": null, - "description": "Safe interface to memchr.", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "compiler_builtins", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.1.2", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "rustc-std-workspace-core", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.0", - "kind": null, - "rename": "core", - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "libc", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.2.18", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": false, - "features": [], - "target": null, - "registry": null - }, - { - "name": "quickcheck", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.3", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": false, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "memchr", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memchr-2.5.0/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "custom-build" - ], - "crate_types": [ - "bin" - ], - "name": "build-script-build", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memchr-2.5.0/build.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": { - "compiler_builtins": [ - "dep:compiler_builtins" - ], - "core": [ - "dep:core" - ], - "default": [ - "std" - ], - "libc": [ - "dep:libc" - ], - "rustc-dep-of-std": [ - "core", - "compiler_builtins" - ], - "std": [], - "use_std": [ - "std" - ] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memchr-2.5.0/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Andrew Gallant ", - "bluss" - ], - "categories": [], - "keywords": [ - "memchr", - "char", - "scan", - "strchr", - "string" - ], - "readme": "README.md", - "repository": "https://github.com/BurntSushi/memchr", - "homepage": "https://github.com/BurntSushi/memchr", - "documentation": "https://docs.rs/memchr/", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "memmap2", - "version": "0.5.10", - "id": "memmap2 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT OR Apache-2.0", - "license_file": null, - "description": "Cross-platform Rust API for memory-mapped file IO", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "stable_deref_trait", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "owning_ref", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.4.1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "tempfile", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^3", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "libc", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.2", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": "cfg(unix)", - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "memmap2", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memmap2-0.5.10/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "cat", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memmap2-0.5.10/examples/cat.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": { - "stable_deref_trait": [ - "dep:stable_deref_trait" - ] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memmap2-0.5.10/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Dan Burkert ", - "Yevhenii Reizner " - ], - "categories": [], - "keywords": [ - "mmap", - "memory-map", - "io", - "file" - ], - "readme": "README.md", - "repository": "https://github.com/RazrFalcon/memmap2-rs", - "homepage": null, - "documentation": "https://docs.rs/memmap2", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "once_cell", - "version": "1.17.1", - "id": "once_cell 1.17.1 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT OR Apache-2.0", - "license_file": null, - "description": "Single assignment cells and lazy values.", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "atomic-polyfill", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": null, - "rename": "atomic_polyfill", - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "critical-section", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": null, - "rename": "critical_section", - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "parking_lot_core", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.9.3", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": false, - "features": [], - "target": null, - "registry": null - }, - { - "name": "critical-section", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.1.1", - "kind": "dev", - "rename": "critical_section", - "optional": false, - "uses_default_features": true, - "features": [ - "std" - ], - "target": null, - "registry": null - }, - { - "name": "crossbeam-utils", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.8.7", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "lazy_static", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.0", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "regex", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.2.0", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "once_cell", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/once_cell-1.17.1/src/lib.rs", - "edition": "2021", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "bench", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/once_cell-1.17.1/examples/bench.rs", - "edition": "2021", - "required-features": [ - "std" - ], - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "bench_acquire", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/once_cell-1.17.1/examples/bench_acquire.rs", - "edition": "2021", - "required-features": [ - "std" - ], - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "bench_vs_lazy_static", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/once_cell-1.17.1/examples/bench_vs_lazy_static.rs", - "edition": "2021", - "required-features": [ - "std" - ], - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "lazy_static", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/once_cell-1.17.1/examples/lazy_static.rs", - "edition": "2021", - "required-features": [ - "std" - ], - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "reentrant_init_deadlocks", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/once_cell-1.17.1/examples/reentrant_init_deadlocks.rs", - "edition": "2021", - "required-features": [ - "std" - ], - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "regex", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/once_cell-1.17.1/examples/regex.rs", - "edition": "2021", - "required-features": [ - "std" - ], - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "test_synchronization", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/once_cell-1.17.1/examples/test_synchronization.rs", - "edition": "2021", - "required-features": [ - "std" - ], - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "it", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/once_cell-1.17.1/tests/it.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - } - ], - "features": { - "alloc": [ - "race" - ], - "atomic-polyfill": [ - "critical-section" - ], - "atomic_polyfill": [ - "dep:atomic_polyfill" - ], - "critical-section": [ - "critical_section", - "atomic_polyfill" - ], - "critical_section": [ - "dep:critical_section" - ], - "default": [ - "std" - ], - "parking_lot": [ - "parking_lot_core" - ], - "parking_lot_core": [ - "dep:parking_lot_core" - ], - "race": [], - "std": [ - "alloc" - ], - "unstable": [] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/once_cell-1.17.1/Cargo.toml", - "metadata": { - "docs": { - "rs": { - "all-features": true - } - } - }, - "publish": null, - "authors": [ - "Aleksey Kladov " - ], - "categories": [ - "rust-patterns", - "memory-management" - ], - "keywords": [ - "lazy", - "static" - ], - "readme": "README.md", - "repository": "https://github.com/matklad/once_cell", - "homepage": null, - "documentation": "https://docs.rs/once_cell", - "edition": "2021", - "links": null, - "default_run": null, - "rust_version": "1.56" - }, - { - "name": "pcre2", - "version": "0.2.3", - "id": "pcre2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "Unlicense/MIT", - "license_file": null, - "description": "High level wrapper library for PCRE2.", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "libc", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.2.46", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "log", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.4.5", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "pcre2-sys", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.2.0", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "thread_local", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "pcre2", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pcre2-0.2.3/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - } - ], - "features": {}, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pcre2-0.2.3/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Andrew Gallant " - ], - "categories": [ - "text-processing" - ], - "keywords": [ - "pcre", - "pcre2", - "regex", - "jit", - "perl" - ], - "readme": "README.md", - "repository": "https://github.com/BurntSushi/rust-pcre2", - "homepage": "https://github.com/BurntSushi/rust-pcre2", - "documentation": "https://docs.rs/pcre2", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "pcre2-sys", - "version": "0.2.5", - "id": "pcre2-sys 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "Unlicense/MIT", - "license_file": null, - "description": "Low level bindings to PCRE2.", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "libc", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.2", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "cc", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": "build", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [ - "parallel" - ], - "target": null, - "registry": null - }, - { - "name": "pkg-config", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.3.13", - "kind": "build", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "pcre2-sys", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pcre2-sys-0.2.5/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "custom-build" - ], - "crate_types": [ - "bin" - ], - "name": "build-script-build", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pcre2-sys-0.2.5/build.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": {}, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pcre2-sys-0.2.5/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Andrew Gallant " - ], - "categories": [ - "external-ffi-bindings" - ], - "keywords": [ - "pcre", - "pcre2", - "regex", - "jit" - ], - "readme": "README.md", - "repository": "https://github.com/BurntSushi/rust-pcre2", - "homepage": "https://github.com/BurntSushi/rust-pcre2", - "documentation": "https://docs.rs/pcre2-sys", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "pkg-config", - "version": "0.3.26", - "id": "pkg-config 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT OR Apache-2.0", - "license_file": null, - "description": "A library to run the pkg-config system tool at build time in order to be used in\nCargo build scripts.\n", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "lazy_static", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "pkg-config", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pkg-config-0.3.26/src/lib.rs", - "edition": "2015", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pkg-config-0.3.26/tests/test.rs", - "edition": "2015", - "doc": false, - "doctest": false, - "test": true - } - ], - "features": {}, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pkg-config-0.3.26/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Alex Crichton " - ], - "categories": [], - "keywords": [ - "build-dependencies" - ], - "readme": "README.md", - "repository": "https://github.com/rust-lang/pkg-config-rs", - "homepage": null, - "documentation": "https://docs.rs/pkg-config", - "edition": "2015", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "proc-macro2", - "version": "1.0.56", - "id": "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT OR Apache-2.0", - "license_file": null, - "description": "A substitute implementation of the compiler's `proc_macro` API to decouple token-based libraries from the procedural macro use case.", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "unicode-ident", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "quote", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": false, - "features": [], - "target": null, - "registry": null - }, - { - "name": "rustversion", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "proc-macro2", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "comments", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/comments.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "features", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/features.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "marker", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/marker.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/test.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_fmt", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/test_fmt.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_size", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/test_size.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "custom-build" - ], - "crate_types": [ - "bin" - ], - "name": "build-script-build", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/build.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": { - "default": [ - "proc-macro" - ], - "nightly": [], - "proc-macro": [], - "span-locations": [] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/Cargo.toml", - "metadata": { - "docs": { - "rs": { - "rustc-args": [ - "--cfg", - "procmacro2_semver_exempt" - ], - "rustdoc-args": [ - "--cfg", - "procmacro2_semver_exempt", - "--cfg", - "doc_cfg" - ], - "targets": [ - "x86_64-unknown-linux-gnu" - ] - } - }, - "playground": { - "features": [ - "span-locations" - ] - } - }, - "publish": null, - "authors": [ - "David Tolnay ", - "Alex Crichton " - ], - "categories": [ - "development-tools::procedural-macro-helpers" - ], - "keywords": [ - "macros", - "syn" - ], - "readme": "README.md", - "repository": "https://github.com/dtolnay/proc-macro2", - "homepage": null, - "documentation": "https://docs.rs/proc-macro2", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": "1.31" - }, - { - "name": "quote", - "version": "1.0.26", - "id": "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT OR Apache-2.0", - "license_file": null, - "description": "Quasi-quoting macro quote!(...)", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "proc-macro2", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.52", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": false, - "features": [], - "target": null, - "registry": null - }, - { - "name": "rustversion", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "trybuild", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.66", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [ - "diff" - ], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "quote", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quote-1.0.26/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "compiletest", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quote-1.0.26/tests/compiletest.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quote-1.0.26/tests/test.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "custom-build" - ], - "crate_types": [ - "bin" - ], - "name": "build-script-build", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quote-1.0.26/build.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": { - "default": [ - "proc-macro" - ], - "proc-macro": [ - "proc-macro2/proc-macro" - ] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quote-1.0.26/Cargo.toml", - "metadata": { - "docs": { - "rs": { - "targets": [ - "x86_64-unknown-linux-gnu" - ] - } - } - }, - "publish": null, - "authors": [ - "David Tolnay " - ], - "categories": [ - "development-tools::procedural-macro-helpers" - ], - "keywords": [ - "macros", - "syn" - ], - "readme": "README.md", - "repository": "https://github.com/dtolnay/quote", - "homepage": null, - "documentation": "https://docs.rs/quote/", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": "1.31" - }, - { - "name": "regex", - "version": "1.8.1", - "id": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT OR Apache-2.0", - "license_file": null, - "description": "An implementation of regular expressions for Rust. This implementation uses\nfinite automata and guarantees linear time matching on all inputs.\n", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "aho-corasick", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.0", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "memchr", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^2.5.0", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "regex-syntax", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.7.1", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": false, - "features": [], - "target": null, - "registry": null - }, - { - "name": "lazy_static", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "quickcheck", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.3", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": false, - "features": [], - "target": null, - "registry": null - }, - { - "name": "rand", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.8.3", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": false, - "features": [ - "getrandom", - "small_rng" - ], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "regex", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/src/lib.rs", - "edition": "2021", - "doc": true, - "doctest": false, - "test": true - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "shootout-regex-dna-bytes", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna-bytes.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "shootout-regex-dna-cheat", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna-cheat.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "shootout-regex-dna-replace", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna-replace.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "shootout-regex-dna-single-cheat", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna-single-cheat.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "shootout-regex-dna-single", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna-single.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "shootout-regex-dna", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "default", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_default.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "default-bytes", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_default_bytes.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "nfa", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_nfa.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "nfa-utf8bytes", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_nfa_utf8bytes.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "nfa-bytes", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_nfa_bytes.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "backtrack", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_backtrack.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "backtrack-utf8bytes", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_backtrack_utf8bytes.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "backtrack-bytes", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_backtrack_bytes.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "crates-regex", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_crates_regex.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - } - ], - "features": { - "aho-corasick": [ - "dep:aho-corasick" - ], - "default": [ - "std", - "perf", - "unicode", - "regex-syntax/default" - ], - "memchr": [ - "dep:memchr" - ], - "pattern": [], - "perf": [ - "perf-cache", - "perf-dfa", - "perf-inline", - "perf-literal" - ], - "perf-cache": [], - "perf-dfa": [], - "perf-inline": [], - "perf-literal": [ - "aho-corasick", - "memchr" - ], - "std": [], - "unicode": [ - "unicode-age", - "unicode-bool", - "unicode-case", - "unicode-gencat", - "unicode-perl", - "unicode-script", - "unicode-segment", - "regex-syntax/unicode" - ], - "unicode-age": [ - "regex-syntax/unicode-age" - ], - "unicode-bool": [ - "regex-syntax/unicode-bool" - ], - "unicode-case": [ - "regex-syntax/unicode-case" - ], - "unicode-gencat": [ - "regex-syntax/unicode-gencat" - ], - "unicode-perl": [ - "regex-syntax/unicode-perl" - ], - "unicode-script": [ - "regex-syntax/unicode-script" - ], - "unicode-segment": [ - "regex-syntax/unicode-segment" - ], - "unstable": [ - "pattern" - ], - "use_std": [ - "std" - ] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "The Rust Project Developers" - ], - "categories": [ - "text-processing" - ], - "keywords": [], - "readme": "README.md", - "repository": "https://github.com/rust-lang/regex", - "homepage": "https://github.com/rust-lang/regex", - "documentation": "https://docs.rs/regex", - "edition": "2021", - "links": null, - "default_run": null, - "rust_version": "1.60.0" - }, - { - "name": "regex-automata", - "version": "0.1.10", - "id": "regex-automata 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "Unlicense/MIT", - "license_file": null, - "description": "Automata construction and matching using regular expressions.", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "fst", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.4.0", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "regex-syntax", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.6.16", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "bstr", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.2", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": false, - "features": [ - "std" - ], - "target": null, - "registry": null - }, - { - "name": "lazy_static", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.2.0", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "regex", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "serde", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.82", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "serde_bytes", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.11", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "serde_derive", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.82", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "toml", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.4.10", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "regex-automata", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-automata-0.1.10/src/lib.rs", - "edition": "2015", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "default", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-automata-0.1.10/tests/tests.rs", - "edition": "2015", - "doc": false, - "doctest": false, - "test": true - } - ], - "features": { - "default": [ - "std" - ], - "fst": [ - "dep:fst" - ], - "regex-syntax": [ - "dep:regex-syntax" - ], - "std": [ - "regex-syntax" - ], - "transducer": [ - "std", - "fst" - ] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-automata-0.1.10/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Andrew Gallant " - ], - "categories": [ - "text-processing" - ], - "keywords": [ - "regex", - "dfa", - "automata", - "automaton", - "nfa" - ], - "readme": "README.md", - "repository": "https://github.com/BurntSushi/regex-automata", - "homepage": "https://github.com/BurntSushi/regex-automata", - "documentation": "https://docs.rs/regex-automata", - "edition": "2015", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "regex-syntax", - "version": "0.6.29", - "id": "regex-syntax 0.6.29 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT OR Apache-2.0", - "license_file": null, - "description": "A regular expression parser.", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "regex-syntax", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-syntax-0.6.29/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "bench" - ], - "crate_types": [ - "bin" - ], - "name": "bench", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-syntax-0.6.29/benches/bench.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": { - "default": [ - "unicode" - ], - "unicode": [ - "unicode-age", - "unicode-bool", - "unicode-case", - "unicode-gencat", - "unicode-perl", - "unicode-script", - "unicode-segment" - ], - "unicode-age": [], - "unicode-bool": [], - "unicode-case": [], - "unicode-gencat": [], - "unicode-perl": [], - "unicode-script": [], - "unicode-segment": [] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-syntax-0.6.29/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "The Rust Project Developers" - ], - "categories": [], - "keywords": [], - "readme": "README.md", - "repository": "https://github.com/rust-lang/regex", - "homepage": "https://github.com/rust-lang/regex", - "documentation": "https://docs.rs/regex-syntax", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "regex-syntax", - "version": "0.7.1", - "id": "regex-syntax 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT OR Apache-2.0", - "license_file": null, - "description": "A regular expression parser.", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "regex-syntax", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-syntax-0.7.1/src/lib.rs", - "edition": "2021", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "bench" - ], - "crate_types": [ - "bin" - ], - "name": "bench", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-syntax-0.7.1/benches/bench.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": { - "default": [ - "std", - "unicode" - ], - "std": [], - "unicode": [ - "unicode-age", - "unicode-bool", - "unicode-case", - "unicode-gencat", - "unicode-perl", - "unicode-script", - "unicode-segment" - ], - "unicode-age": [], - "unicode-bool": [], - "unicode-case": [], - "unicode-gencat": [], - "unicode-perl": [], - "unicode-script": [], - "unicode-segment": [] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-syntax-0.7.1/Cargo.toml", - "metadata": { - "docs": { - "rs": { - "all-features": true, - "rustdoc-args": [ - "--cfg", - "docsrs" - ] - } - } - }, - "publish": null, - "authors": [ - "The Rust Project Developers" - ], - "categories": [], - "keywords": [], - "readme": "README.md", - "repository": "https://github.com/rust-lang/regex", - "homepage": "https://github.com/rust-lang/regex", - "documentation": "https://docs.rs/regex-syntax", - "edition": "2021", - "links": null, - "default_run": null, - "rust_version": "1.60.0" - }, - { - "name": "ripgrep", - "version": "13.0.0", - "id": "ripgrep 13.0.0 (path+file:///$ROOT$ripgrep)", - "license": "Unlicense OR MIT", - "license_file": null, - "description": "ripgrep is a line-oriented search tool that recursively searches the current\ndirectory for a regex pattern while respecting gitignore rules. ripgrep has\nfirst class support on Windows, macOS and Linux.\n", - "source": null, - "dependencies": [ - { - "name": "bstr", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.1.0", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "clap", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^2.33.0", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": false, - "features": [ - "suggestions" - ], - "target": null, - "registry": null - }, - { - "name": "grep", - "source": null, - "req": "^0.2.11", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null, - "path": "$ROOT$ripgrep/crates/grep" - }, - { - "name": "ignore", - "source": null, - "req": "^0.4.19", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null, - "path": "$ROOT$ripgrep/crates/ignore" - }, - { - "name": "lazy_static", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.1.0", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "log", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.4.5", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "regex", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.3.5", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "serde_json", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.23", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "termcolor", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.1.0", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "serde", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.77", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "serde_derive", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.77", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "walkdir", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^2", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "clap", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^2.33.0", - "kind": "build", - "rename": null, - "optional": false, - "uses_default_features": false, - "features": [ - "suggestions" - ], - "target": null, - "registry": null - }, - { - "name": "lazy_static", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.1.0", - "kind": "build", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "jemallocator", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.5.0", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": "cfg(all(target_env = \"musl\", target_pointer_width = \"64\"))", - "registry": null - } - ], - "targets": [ - { - "kind": [ - "bin" - ], - "crate_types": [ - "bin" - ], - "name": "rg", - "src_path": "$ROOT$ripgrep/crates/core/main.rs", - "edition": "2018", - "doc": true, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "integration", - "src_path": "$ROOT$ripgrep/tests/tests.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "custom-build" - ], - "crate_types": [ - "bin" - ], - "name": "build-script-build", - "src_path": "$ROOT$ripgrep/build.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": { - "pcre2": [ - "grep/pcre2" - ], - "simd-accel": [ - "grep/simd-accel" - ] - }, - "manifest_path": "$ROOT$ripgrep/Cargo.toml", - "metadata": { - "deb": { - "assets": [ - [ - "target/release/rg", - "usr/bin/", - "755" - ], - [ - "COPYING", - "usr/share/doc/ripgrep/", - "644" - ], - [ - "LICENSE-MIT", - "usr/share/doc/ripgrep/", - "644" - ], - [ - "UNLICENSE", - "usr/share/doc/ripgrep/", - "644" - ], - [ - "CHANGELOG.md", - "usr/share/doc/ripgrep/CHANGELOG", - "644" - ], - [ - "README.md", - "usr/share/doc/ripgrep/README", - "644" - ], - [ - "FAQ.md", - "usr/share/doc/ripgrep/FAQ", - "644" - ], - [ - "deployment/deb/rg.1", - "usr/share/man/man1/rg.1", - "644" - ], - [ - "deployment/deb/rg.bash", - "usr/share/bash-completion/completions/rg", - "644" - ], - [ - "deployment/deb/rg.fish", - "usr/share/fish/vendor_completions.d/rg.fish", - "644" - ], - [ - "deployment/deb/_rg", - "usr/share/zsh/vendor-completions/", - "644" - ] - ], - "extended-description": "ripgrep (rg) recursively searches your current directory for a regex pattern.\nBy default, ripgrep will respect your .gitignore and automatically skip hidden\nfiles/directories and binary files.\n", - "features": [ - "pcre2" - ], - "section": "utils" - } - }, - "publish": null, - "authors": [ - "Andrew Gallant " - ], - "categories": [ - "command-line-utilities", - "text-processing" - ], - "keywords": [ - "regex", - "grep", - "egrep", - "search", - "pattern" - ], - "readme": "README.md", - "repository": "https://github.com/BurntSushi/ripgrep", - "homepage": "https://github.com/BurntSushi/ripgrep", - "documentation": "https://github.com/BurntSushi/ripgrep", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": "1.65" - }, - { - "name": "ryu", - "version": "1.0.13", - "id": "ryu 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "Apache-2.0 OR BSL-1.0", - "license_file": null, - "description": "Fast floating point to string conversion", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "no-panic", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.1", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "num_cpus", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.8", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "rand", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.8", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "rand_xorshift", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.3", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "ryu", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "upstream_benchmark", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/examples/upstream_benchmark.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "common_test", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/tests/common_test.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "d2s_table_test", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/tests/d2s_table_test.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "d2s_test", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/tests/d2s_test.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "exhaustive", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/tests/exhaustive.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "f2s_test", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/tests/f2s_test.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "s2d_test", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/tests/s2d_test.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "s2f_test", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/tests/s2f_test.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "bench" - ], - "crate_types": [ - "bin" - ], - "name": "bench", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/benches/bench.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": { - "no-panic": [ - "dep:no-panic" - ], - "small": [] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/Cargo.toml", - "metadata": { - "docs": { - "rs": { - "targets": [ - "x86_64-unknown-linux-gnu" - ] - } - } - }, - "publish": null, - "authors": [ - "David Tolnay " - ], - "categories": [ - "value-formatting", - "no-std" - ], - "keywords": [ - "float" - ], - "readme": "README.md", - "repository": "https://github.com/dtolnay/ryu", - "homepage": null, - "documentation": "https://docs.rs/ryu", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": "1.36" - }, - { - "name": "same-file", - "version": "1.0.6", - "id": "same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "Unlicense/MIT", - "license_file": null, - "description": "A simple crate for determining whether two file paths point to the same file.\n", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "doc-comment", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.3", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "winapi-util", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.1.1", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": "cfg(windows)", - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "same-file", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/same-file-1.0.6/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "is_same_file", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/same-file-1.0.6/examples/is_same_file.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "is_stderr", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/same-file-1.0.6/examples/is_stderr.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": {}, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/same-file-1.0.6/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Andrew Gallant " - ], - "categories": [], - "keywords": [ - "same", - "file", - "equal", - "inode" - ], - "readme": "README.md", - "repository": "https://github.com/BurntSushi/same-file", - "homepage": "https://github.com/BurntSushi/same-file", - "documentation": "https://docs.rs/same-file", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "serde", - "version": "1.0.160", - "id": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT OR Apache-2.0", - "license_file": null, - "description": "A generic serialization/deserialization framework", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "serde_derive", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "=1.0.160", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "serde_derive", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "serde", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde-1.0.160/src/lib.rs", - "edition": "2015", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "custom-build" - ], - "crate_types": [ - "bin" - ], - "name": "build-script-build", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde-1.0.160/build.rs", - "edition": "2015", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": { - "alloc": [], - "default": [ - "std" - ], - "derive": [ - "serde_derive" - ], - "rc": [], - "serde_derive": [ - "dep:serde_derive" - ], - "std": [], - "unstable": [] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde-1.0.160/Cargo.toml", - "metadata": { - "docs": { - "rs": { - "features": [ - "derive" - ], - "targets": [ - "x86_64-unknown-linux-gnu" - ] - } - }, - "playground": { - "features": [ - "derive", - "rc" - ] - } - }, - "publish": null, - "authors": [ - "Erick Tryzelaar ", - "David Tolnay " - ], - "categories": [ - "encoding", - "no-std" - ], - "keywords": [ - "serde", - "serialization", - "no_std" - ], - "readme": "crates-io.md", - "repository": "https://github.com/serde-rs/serde", - "homepage": "https://serde.rs", - "documentation": "https://docs.rs/serde", - "edition": "2015", - "links": null, - "default_run": null, - "rust_version": "1.19" - }, - { - "name": "serde_derive", - "version": "1.0.160", - "id": "serde_derive 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT OR Apache-2.0", - "license_file": null, - "description": "Macros 1.1 implementation of #[derive(Serialize, Deserialize)]", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "proc-macro2", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "quote", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "syn", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^2.0.3", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "serde", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "proc-macro" - ], - "crate_types": [ - "proc-macro" - ], - "name": "serde_derive", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_derive-1.0.160/src/lib.rs", - "edition": "2015", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "custom-build" - ], - "crate_types": [ - "bin" - ], - "name": "build-script-build", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_derive-1.0.160/build.rs", - "edition": "2015", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": { - "default": [], - "deserialize_in_place": [] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_derive-1.0.160/Cargo.toml", - "metadata": { - "docs": { - "rs": { - "targets": [ - "x86_64-unknown-linux-gnu" - ] - } - } - }, - "publish": null, - "authors": [ - "Erick Tryzelaar ", - "David Tolnay " - ], - "categories": [ - "no-std" - ], - "keywords": [ - "serde", - "serialization", - "no_std", - "derive" - ], - "readme": "crates-io.md", - "repository": "https://github.com/serde-rs/serde", - "homepage": "https://serde.rs", - "documentation": "https://serde.rs/derive.html", - "edition": "2015", - "links": null, - "default_run": null, - "rust_version": "1.56" - }, - { - "name": "serde_json", - "version": "1.0.96", - "id": "serde_json 1.0.96 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT OR Apache-2.0", - "license_file": null, - "description": "A JSON serialization file format", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "indexmap", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.5.2", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [ - "std" - ], - "target": null, - "registry": null - }, - { - "name": "itoa", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "ryu", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "serde", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.100", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": false, - "features": [], - "target": null, - "registry": null - }, - { - "name": "automod", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "indoc", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^2.0", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "ref-cast", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "rustversion", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "serde", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.100", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [ - "derive" - ], - "target": null, - "registry": null - }, - { - "name": "serde_bytes", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.11", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "serde_derive", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "serde_stacker", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "trybuild", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.49", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [ - "diff" - ], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "serde_json", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.96/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "compiletest", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.96/tests/compiletest.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "debug", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.96/tests/debug.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "lexical", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.96/tests/lexical.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "map", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.96/tests/map.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "regression", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.96/tests/regression.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "stream", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.96/tests/stream.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.96/tests/test.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "custom-build" - ], - "crate_types": [ - "bin" - ], - "name": "build-script-build", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.96/build.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": { - "alloc": [ - "serde/alloc" - ], - "arbitrary_precision": [], - "default": [ - "std" - ], - "float_roundtrip": [], - "indexmap": [ - "dep:indexmap" - ], - "preserve_order": [ - "indexmap", - "std" - ], - "raw_value": [], - "std": [ - "serde/std" - ], - "unbounded_depth": [] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.96/Cargo.toml", - "metadata": { - "docs": { - "rs": { - "features": [ - "raw_value", - "unbounded_depth" - ], - "rustdoc-args": [ - "--cfg", - "docsrs" - ], - "targets": [ - "x86_64-unknown-linux-gnu" - ] - } - }, - "playground": { - "features": [ - "raw_value" - ] - } - }, - "publish": null, - "authors": [ - "Erick Tryzelaar ", - "David Tolnay " - ], - "categories": [ - "encoding", - "parser-implementations", - "no-std" - ], - "keywords": [ - "json", - "serde", - "serialization" - ], - "readme": "README.md", - "repository": "https://github.com/serde-rs/json", - "homepage": null, - "documentation": "https://docs.rs/serde_json", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": "1.36" - }, - { - "name": "strsim", - "version": "0.8.0", - "id": "strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT", - "license_file": null, - "description": "Implementations of string similarity metrics.\nIncludes Hamming, Levenshtein, OSA, Damerau-Levenshtein, Jaro, and Jaro-Winkler.\n", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "strsim", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/strsim-0.8.0/src/lib.rs", - "edition": "2015", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "lib", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/strsim-0.8.0/tests/lib.rs", - "edition": "2015", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "bench" - ], - "crate_types": [ - "bin" - ], - "name": "benches", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/strsim-0.8.0/benches/benches.rs", - "edition": "2015", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": {}, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/strsim-0.8.0/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Danny Guo " - ], - "categories": [], - "keywords": [ - "string", - "similarity", - "Hamming", - "Levenshtein", - "Jaro" - ], - "readme": "README.md", - "repository": "https://github.com/dguo/strsim-rs", - "homepage": "https://github.com/dguo/strsim-rs", - "documentation": "https://docs.rs/strsim/", - "edition": "2015", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "syn", - "version": "2.0.15", - "id": "syn 2.0.15 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT OR Apache-2.0", - "license_file": null, - "description": "Parser for Rust source code", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "proc-macro2", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.55", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": false, - "features": [], - "target": null, - "registry": null - }, - { - "name": "quote", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.25", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": false, - "features": [], - "target": null, - "registry": null - }, - { - "name": "unicode-ident", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "anyhow", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "automod", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "flate2", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "insta", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "rayon", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "ref-cast", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "regex", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "reqwest", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.11", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [ - "blocking" - ], - "target": null, - "registry": null - }, - { - "name": "rustversion", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "syn-test-suite", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "tar", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.4.16", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "termcolor", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "walkdir", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^2.3.2", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "syn", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/src/lib.rs", - "edition": "2021", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "regression", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/regression.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_asyncness", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_asyncness.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_attribute", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_attribute.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_derive_input", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_derive_input.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_expr", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_expr.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_generics", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_generics.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_grouping", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_grouping.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_ident", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_ident.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_item", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_item.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_iterators", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_iterators.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_lit", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_lit.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_meta", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_meta.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_parse_buffer", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_parse_buffer.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_parse_stream", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_parse_stream.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_pat", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_pat.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_path", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_path.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_precedence", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_precedence.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_receiver", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_receiver.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_round_trip", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_round_trip.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_shebang", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_shebang.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_should_parse", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_should_parse.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_size", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_size.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_stmt", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_stmt.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_token_trees", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_token_trees.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_ty", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_ty.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_visibility", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_visibility.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "zzz_stable", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/zzz_stable.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "bench" - ], - "crate_types": [ - "bin" - ], - "name": "rust", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/benches/rust.rs", - "edition": "2021", - "required-features": [ - "full", - "parsing" - ], - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "bench" - ], - "crate_types": [ - "bin" - ], - "name": "file", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/benches/file.rs", - "edition": "2021", - "required-features": [ - "full", - "parsing" - ], - "doc": false, - "doctest": false, - "test": false - } - ], - "features": { - "clone-impls": [], - "default": [ - "derive", - "parsing", - "printing", - "clone-impls", - "proc-macro" - ], - "derive": [], - "extra-traits": [], - "fold": [], - "full": [], - "parsing": [], - "printing": [ - "quote" - ], - "proc-macro": [ - "proc-macro2/proc-macro", - "quote/proc-macro" - ], - "quote": [ - "dep:quote" - ], - "test": [ - "syn-test-suite/all-features" - ], - "visit": [], - "visit-mut": [] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/Cargo.toml", - "metadata": { - "docs": { - "rs": { - "all-features": true, - "rustdoc-args": [ - "--cfg", - "doc_cfg" - ], - "targets": [ - "x86_64-unknown-linux-gnu" - ] - } - }, - "playground": { - "features": [ - "full", - "visit", - "visit-mut", - "fold", - "extra-traits" - ] - } - }, - "publish": null, - "authors": [ - "David Tolnay " - ], - "categories": [ - "development-tools::procedural-macro-helpers", - "parser-implementations" - ], - "keywords": [ - "macros", - "syn" - ], - "readme": "README.md", - "repository": "https://github.com/dtolnay/syn", - "homepage": null, - "documentation": "https://docs.rs/syn", - "edition": "2021", - "links": null, - "default_run": null, - "rust_version": "1.56" - }, - { - "name": "termcolor", - "version": "1.2.0", - "id": "termcolor 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "Unlicense OR MIT", - "license_file": null, - "description": "A simple cross platform library for writing colored text to a terminal.\n", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "winapi-util", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.1.3", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": "cfg(windows)", - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "termcolor", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/termcolor-1.2.0/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - } - ], - "features": {}, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/termcolor-1.2.0/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Andrew Gallant " - ], - "categories": [], - "keywords": [ - "windows", - "win", - "color", - "ansi", - "console" - ], - "readme": "README.md", - "repository": "https://github.com/BurntSushi/termcolor", - "homepage": "https://github.com/BurntSushi/termcolor", - "documentation": "https://docs.rs/termcolor", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "textwrap", - "version": "0.11.0", - "id": "textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT", - "license_file": null, - "description": "Textwrap is a small library for word wrapping, indenting, and\ndedenting strings.\n\nYou can use it to format strings (such as help and error messages) for\ndisplay in commandline applications. It is designed to be efficient\nand handle Unicode characters correctly.\n", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "hyphenation", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.7.1", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [ - "embed_all" - ], - "target": null, - "registry": null - }, - { - "name": "term_size", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.3.0", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "unicode-width", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.1.3", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "lipsum", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.6", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "rand", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.6", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "rand_xorshift", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "version-sync", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.6", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "textwrap", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/textwrap-0.11.0/src/lib.rs", - "edition": "2015", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "layout", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/textwrap-0.11.0/examples/layout.rs", - "edition": "2015", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "termwidth", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/textwrap-0.11.0/examples/termwidth.rs", - "edition": "2015", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "version-numbers", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/textwrap-0.11.0/tests/version-numbers.rs", - "edition": "2015", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "bench" - ], - "crate_types": [ - "bin" - ], - "name": "linear", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/textwrap-0.11.0/benches/linear.rs", - "edition": "2015", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": { - "hyphenation": [ - "dep:hyphenation" - ], - "term_size": [ - "dep:term_size" - ] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/textwrap-0.11.0/Cargo.toml", - "metadata": { - "docs": { - "rs": { - "all-features": true - } - } - }, - "publish": null, - "authors": [ - "Martin Geisler " - ], - "categories": [ - "text-processing", - "command-line-interface" - ], - "keywords": [ - "text", - "formatting", - "wrap", - "typesetting", - "hyphenation" - ], - "readme": "README.md", - "repository": "https://github.com/mgeisler/textwrap", - "homepage": null, - "documentation": "https://docs.rs/textwrap/", - "edition": "2015", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "thread_local", - "version": "1.1.7", - "id": "thread_local 1.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT OR Apache-2.0", - "license_file": null, - "description": "Per-object thread-local storage", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "cfg-if", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.0", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "once_cell", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.5.2", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "criterion", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.4.0", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "thread_local", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/thread_local-1.1.7/src/lib.rs", - "edition": "2021", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "bench" - ], - "crate_types": [ - "bin" - ], - "name": "thread_local", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/thread_local-1.1.7/benches/thread_local.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": { - "nightly": [] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/thread_local-1.1.7/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Amanieu d'Antras " - ], - "categories": [], - "keywords": [ - "thread_local", - "concurrent", - "thread" - ], - "readme": "README.md", - "repository": "https://github.com/Amanieu/thread_local-rs", - "homepage": null, - "documentation": "https://docs.rs/thread_local/", - "edition": "2021", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "unicode-ident", - "version": "1.0.8", - "id": "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "(MIT OR Apache-2.0) AND Unicode-DFS-2016", - "license_file": null, - "description": "Determine whether characters have the XID_Start or XID_Continue properties according to Unicode Standard Annex #31", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "criterion", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.4", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": false, - "features": [], - "target": null, - "registry": null - }, - { - "name": "fst", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.4", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "rand", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.8", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [ - "small_rng" - ], - "target": null, - "registry": null - }, - { - "name": "roaring", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.10", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "ucd-trie", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": false, - "features": [], - "target": null, - "registry": null - }, - { - "name": "unicode-xid", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.2.4", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "unicode-ident", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-ident-1.0.8/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "compare", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-ident-1.0.8/tests/compare.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "static_size", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-ident-1.0.8/tests/static_size.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "bench" - ], - "crate_types": [ - "bin" - ], - "name": "xid", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-ident-1.0.8/benches/xid.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": {}, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-ident-1.0.8/Cargo.toml", - "metadata": { - "docs": { - "rs": { - "targets": [ - "x86_64-unknown-linux-gnu" - ] - } - } - }, - "publish": null, - "authors": [ - "David Tolnay " - ], - "categories": [ - "development-tools::procedural-macro-helpers", - "no-std" - ], - "keywords": [ - "unicode", - "xid" - ], - "readme": "README.md", - "repository": "https://github.com/dtolnay/unicode-ident", - "homepage": null, - "documentation": "https://docs.rs/unicode-ident", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": "1.31" - }, - { - "name": "unicode-width", - "version": "0.1.10", - "id": "unicode-width 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT/Apache-2.0", - "license_file": null, - "description": "Determine displayed width of `char` and `str` types\naccording to Unicode Standard Annex #11 rules.\n", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "compiler_builtins", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.1", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "rustc-std-workspace-core", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": null, - "rename": "core", - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "rustc-std-workspace-std", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": null, - "rename": "std", - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "unicode-width", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-width-0.1.10/src/lib.rs", - "edition": "2015", - "doc": true, - "doctest": true, - "test": true - } - ], - "features": { - "bench": [], - "compiler_builtins": [ - "dep:compiler_builtins" - ], - "core": [ - "dep:core" - ], - "default": [], - "no_std": [], - "rustc-dep-of-std": [ - "std", - "core", - "compiler_builtins" - ], - "std": [ - "dep:std" - ] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-width-0.1.10/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "kwantam ", - "Manish Goregaokar " - ], - "categories": [], - "keywords": [ - "text", - "width", - "unicode" - ], - "readme": "README.md", - "repository": "https://github.com/unicode-rs/unicode-width", - "homepage": "https://github.com/unicode-rs/unicode-width", - "documentation": "https://unicode-rs.github.io/unicode-width", - "edition": "2015", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "walkdir", - "version": "2.3.3", - "id": "walkdir 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "Unlicense/MIT", - "license_file": null, - "description": "Recursively walk a directory.", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "same-file", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.1", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "doc-comment", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.3", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "winapi-util", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.1.1", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": "cfg(windows)", - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "walkdir", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/walkdir-2.3.3/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - } - ], - "features": {}, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/walkdir-2.3.3/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Andrew Gallant " - ], - "categories": [ - "filesystem" - ], - "keywords": [ - "directory", - "recursive", - "walk", - "iterator" - ], - "readme": "README.md", - "repository": "https://github.com/BurntSushi/walkdir", - "homepage": "https://github.com/BurntSushi/walkdir", - "documentation": "https://docs.rs/walkdir/", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "winapi", - "version": "0.3.9", - "id": "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT/Apache-2.0", - "license_file": null, - "description": "Raw FFI bindings for all of Windows API.", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "winapi-i686-pc-windows-gnu", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.4", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": "i686-pc-windows-gnu", - "registry": null - }, - { - "name": "winapi-x86_64-pc-windows-gnu", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.4", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": "x86_64-pc-windows-gnu", - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "winapi", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-0.3.9/src/lib.rs", - "edition": "2015", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "custom-build" - ], - "crate_types": [ - "bin" - ], - "name": "build-script-build", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-0.3.9/build.rs", - "edition": "2015", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": { - "accctrl": [], - "aclapi": [], - "activation": [], - "adhoc": [], - "appmgmt": [], - "audioclient": [], - "audiosessiontypes": [], - "avrt": [], - "basetsd": [], - "bcrypt": [], - "bits": [], - "bits10_1": [], - "bits1_5": [], - "bits2_0": [], - "bits2_5": [], - "bits3_0": [], - "bits4_0": [], - "bits5_0": [], - "bitscfg": [], - "bitsmsg": [], - "bluetoothapis": [], - "bluetoothleapis": [], - "bthdef": [], - "bthioctl": [], - "bthledef": [], - "bthsdpdef": [], - "bugcodes": [], - "cderr": [], - "cfg": [], - "cfgmgr32": [], - "cguid": [], - "combaseapi": [], - "coml2api": [], - "commapi": [], - "commctrl": [], - "commdlg": [], - "commoncontrols": [], - "consoleapi": [], - "corecrt": [], - "corsym": [], - "d2d1": [], - "d2d1_1": [], - "d2d1_2": [], - "d2d1_3": [], - "d2d1effectauthor": [], - "d2d1effects": [], - "d2d1effects_1": [], - "d2d1effects_2": [], - "d2d1svg": [], - "d2dbasetypes": [], - "d3d": [], - "d3d10": [], - "d3d10_1": [], - "d3d10_1shader": [], - "d3d10effect": [], - "d3d10misc": [], - "d3d10sdklayers": [], - "d3d10shader": [], - "d3d11": [], - "d3d11_1": [], - "d3d11_2": [], - "d3d11_3": [], - "d3d11_4": [], - "d3d11on12": [], - "d3d11sdklayers": [], - "d3d11shader": [], - "d3d11tokenizedprogramformat": [], - "d3d12": [], - "d3d12sdklayers": [], - "d3d12shader": [], - "d3d9": [], - "d3d9caps": [], - "d3d9types": [], - "d3dcommon": [], - "d3dcompiler": [], - "d3dcsx": [], - "d3dkmdt": [], - "d3dkmthk": [], - "d3dukmdt": [], - "d3dx10core": [], - "d3dx10math": [], - "d3dx10mesh": [], - "datetimeapi": [], - "davclnt": [], - "dbghelp": [], - "dbt": [], - "dcommon": [], - "dcomp": [], - "dcompanimation": [], - "dcomptypes": [], - "dde": [], - "ddraw": [], - "ddrawi": [], - "ddrawint": [], - "debug": [ - "impl-debug" - ], - "debugapi": [], - "devguid": [], - "devicetopology": [], - "devpkey": [], - "devpropdef": [], - "dinput": [], - "dinputd": [], - "dispex": [], - "dmksctl": [], - "dmusicc": [], - "docobj": [], - "documenttarget": [], - "dot1x": [], - "dpa_dsa": [], - "dpapi": [], - "dsgetdc": [], - "dsound": [], - "dsrole": [], - "dvp": [], - "dwmapi": [], - "dwrite": [], - "dwrite_1": [], - "dwrite_2": [], - "dwrite_3": [], - "dxdiag": [], - "dxfile": [], - "dxgi": [], - "dxgi1_2": [], - "dxgi1_3": [], - "dxgi1_4": [], - "dxgi1_5": [], - "dxgi1_6": [], - "dxgidebug": [], - "dxgiformat": [], - "dxgitype": [], - "dxva2api": [], - "dxvahd": [], - "eaptypes": [], - "enclaveapi": [], - "endpointvolume": [], - "errhandlingapi": [], - "everything": [], - "evntcons": [], - "evntprov": [], - "evntrace": [], - "excpt": [], - "exdisp": [], - "fibersapi": [], - "fileapi": [], - "functiondiscoverykeys_devpkey": [], - "gl-gl": [], - "guiddef": [], - "handleapi": [], - "heapapi": [], - "hidclass": [], - "hidpi": [], - "hidsdi": [], - "hidusage": [], - "highlevelmonitorconfigurationapi": [], - "hstring": [], - "http": [], - "ifdef": [], - "ifmib": [], - "imm": [], - "impl-debug": [], - "impl-default": [], - "in6addr": [], - "inaddr": [], - "inspectable": [], - "interlockedapi": [], - "intsafe": [], - "ioapiset": [], - "ipexport": [], - "iphlpapi": [], - "ipifcons": [], - "ipmib": [], - "iprtrmib": [], - "iptypes": [], - "jobapi": [], - "jobapi2": [], - "knownfolders": [], - "ks": [], - "ksmedia": [], - "ktmtypes": [], - "ktmw32": [], - "l2cmn": [], - "libloaderapi": [], - "limits": [], - "lmaccess": [], - "lmalert": [], - "lmapibuf": [], - "lmat": [], - "lmcons": [], - "lmdfs": [], - "lmerrlog": [], - "lmjoin": [], - "lmmsg": [], - "lmremutl": [], - "lmrepl": [], - "lmserver": [], - "lmshare": [], - "lmstats": [], - "lmsvc": [], - "lmuse": [], - "lmwksta": [], - "lowlevelmonitorconfigurationapi": [], - "lsalookup": [], - "memoryapi": [], - "minschannel": [], - "minwinbase": [], - "minwindef": [], - "mmdeviceapi": [], - "mmeapi": [], - "mmreg": [], - "mmsystem": [], - "mprapidef": [], - "msaatext": [], - "mscat": [], - "mschapp": [], - "mssip": [], - "mstcpip": [], - "mswsock": [], - "mswsockdef": [], - "namedpipeapi": [], - "namespaceapi": [], - "nb30": [], - "ncrypt": [], - "netioapi": [], - "nldef": [], - "ntddndis": [], - "ntddscsi": [], - "ntddser": [], - "ntdef": [], - "ntlsa": [], - "ntsecapi": [], - "ntstatus": [], - "oaidl": [], - "objbase": [], - "objidl": [], - "objidlbase": [], - "ocidl": [], - "ole2": [], - "oleauto": [], - "olectl": [], - "oleidl": [], - "opmapi": [], - "pdh": [], - "perflib": [], - "physicalmonitorenumerationapi": [], - "playsoundapi": [], - "portabledevice": [], - "portabledeviceapi": [], - "portabledevicetypes": [], - "powerbase": [], - "powersetting": [], - "powrprof": [], - "processenv": [], - "processsnapshot": [], - "processthreadsapi": [], - "processtopologyapi": [], - "profileapi": [], - "propidl": [], - "propkey": [], - "propkeydef": [], - "propsys": [], - "prsht": [], - "psapi": [], - "qos": [], - "realtimeapiset": [], - "reason": [], - "restartmanager": [], - "restrictederrorinfo": [], - "rmxfguid": [], - "roapi": [], - "robuffer": [], - "roerrorapi": [], - "rpc": [], - "rpcdce": [], - "rpcndr": [], - "rtinfo": [], - "sapi": [], - "sapi51": [], - "sapi53": [], - "sapiddk": [], - "sapiddk51": [], - "schannel": [], - "sddl": [], - "securityappcontainer": [], - "securitybaseapi": [], - "servprov": [], - "setupapi": [], - "shellapi": [], - "shellscalingapi": [], - "shlobj": [], - "shobjidl": [], - "shobjidl_core": [], - "shtypes": [], - "softpub": [], - "spapidef": [], - "spellcheck": [], - "sporder": [], - "sql": [], - "sqlext": [], - "sqltypes": [], - "sqlucode": [], - "sspi": [], - "std": [], - "stralign": [], - "stringapiset": [], - "strmif": [], - "subauth": [], - "synchapi": [], - "sysinfoapi": [], - "systemtopologyapi": [], - "taskschd": [], - "tcpestats": [], - "tcpmib": [], - "textstor": [], - "threadpoolapiset": [], - "threadpoollegacyapiset": [], - "timeapi": [], - "timezoneapi": [], - "tlhelp32": [], - "transportsettingcommon": [], - "tvout": [], - "udpmib": [], - "unknwnbase": [], - "urlhist": [], - "urlmon": [], - "usb": [], - "usbioctl": [], - "usbiodef": [], - "usbscan": [], - "usbspec": [], - "userenv": [], - "usp10": [], - "utilapiset": [], - "uxtheme": [], - "vadefs": [], - "vcruntime": [], - "vsbackup": [], - "vss": [], - "vsserror": [], - "vswriter": [], - "wbemads": [], - "wbemcli": [], - "wbemdisp": [], - "wbemprov": [], - "wbemtran": [], - "wct": [], - "werapi": [], - "winbase": [], - "wincodec": [], - "wincodecsdk": [], - "wincon": [], - "wincontypes": [], - "wincred": [], - "wincrypt": [], - "windef": [], - "windot11": [], - "windowsceip": [], - "windowsx": [], - "winefs": [], - "winerror": [], - "winevt": [], - "wingdi": [], - "winhttp": [], - "wininet": [], - "winineti": [], - "winioctl": [], - "winnetwk": [], - "winnls": [], - "winnt": [], - "winreg": [], - "winsafer": [], - "winscard": [], - "winsmcrd": [], - "winsock2": [], - "winspool": [], - "winstring": [], - "winsvc": [], - "wintrust": [], - "winusb": [], - "winusbio": [], - "winuser": [], - "winver": [], - "wlanapi": [], - "wlanihv": [], - "wlanihvtypes": [], - "wlantypes": [], - "wlclient": [], - "wmistr": [], - "wnnc": [], - "wow64apiset": [], - "wpdmtpextensions": [], - "ws2bth": [], - "ws2def": [], - "ws2ipdef": [], - "ws2spi": [], - "ws2tcpip": [], - "wtsapi32": [], - "wtypes": [], - "wtypesbase": [], - "xinput": [] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-0.3.9/Cargo.toml", - "metadata": { - "docs": { - "rs": { - "default-target": "x86_64-pc-windows-msvc", - "features": [ - "everything", - "impl-debug", - "impl-default" - ], - "targets": [ - "aarch64-pc-windows-msvc", - "i686-pc-windows-msvc", - "x86_64-pc-windows-msvc" - ] - } - } - }, - "publish": null, - "authors": [ - "Peter Atashian " - ], - "categories": [ - "external-ffi-bindings", - "no-std", - "os::windows-apis" - ], - "keywords": [ - "windows", - "ffi", - "win32", - "com", - "directx" - ], - "readme": "README.md", - "repository": "https://github.com/retep998/winapi-rs", - "homepage": null, - "documentation": "https://docs.rs/winapi/", - "edition": "2015", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "winapi-i686-pc-windows-gnu", - "version": "0.4.0", - "id": "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT/Apache-2.0", - "license_file": null, - "description": "Import libraries for the i686-pc-windows-gnu target. Please don't use this crate directly, depend on winapi instead.", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "winapi-i686-pc-windows-gnu", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-i686-pc-windows-gnu-0.4.0/src/lib.rs", - "edition": "2015", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "custom-build" - ], - "crate_types": [ - "bin" - ], - "name": "build-script-build", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-i686-pc-windows-gnu-0.4.0/build.rs", - "edition": "2015", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": {}, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-i686-pc-windows-gnu-0.4.0/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Peter Atashian " - ], - "categories": [], - "keywords": [ - "windows" - ], - "readme": null, - "repository": "https://github.com/retep998/winapi-rs", - "homepage": null, - "documentation": null, - "edition": "2015", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "winapi-util", - "version": "0.1.5", - "id": "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "Unlicense/MIT", - "license_file": null, - "description": "A dumping ground for high level safe wrappers over winapi.", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "winapi", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.3", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [ - "std", - "consoleapi", - "errhandlingapi", - "fileapi", - "minwindef", - "processenv", - "winbase", - "wincon", - "winerror", - "winnt" - ], - "target": "cfg(windows)", - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "winapi-util", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-util-0.1.5/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - } - ], - "features": {}, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-util-0.1.5/Cargo.toml", - "metadata": { - "docs": { - "rs": { - "targets": [ - "x86_64-pc-windows-msvc" - ] - } - } - }, - "publish": null, - "authors": [ - "Andrew Gallant " - ], - "categories": [ - "os::windows-apis", - "external-ffi-bindings" - ], - "keywords": [ - "windows", - "winapi", - "util", - "win" - ], - "readme": "README.md", - "repository": "https://github.com/BurntSushi/winapi-util", - "homepage": "https://github.com/BurntSushi/winapi-util", - "documentation": "https://docs.rs/winapi-util", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "winapi-x86_64-pc-windows-gnu", - "version": "0.4.0", - "id": "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT/Apache-2.0", - "license_file": null, - "description": "Import libraries for the x86_64-pc-windows-gnu target. Please don't use this crate directly, depend on winapi instead.", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "winapi-x86_64-pc-windows-gnu", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-x86_64-pc-windows-gnu-0.4.0/src/lib.rs", - "edition": "2015", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "custom-build" - ], - "crate_types": [ - "bin" - ], - "name": "build-script-build", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-x86_64-pc-windows-gnu-0.4.0/build.rs", - "edition": "2015", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": {}, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-x86_64-pc-windows-gnu-0.4.0/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Peter Atashian " - ], - "categories": [], - "keywords": [ - "windows" - ], - "readme": null, - "repository": "https://github.com/retep998/winapi-rs", - "homepage": null, - "documentation": null, - "edition": "2015", - "links": null, - "default_run": null, - "rust_version": null - } - ], - "workspace_members": [ - "globset 0.4.10 (path+file:///$ROOT$ripgrep/crates/globset)", - "grep 0.2.11 (path+file:///$ROOT$ripgrep/crates/grep)", - "grep-cli 0.1.7 (path+file:///$ROOT$ripgrep/crates/cli)", - "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)", - "grep-pcre2 0.1.6 (path+file:///$ROOT$ripgrep/crates/pcre2)", - "grep-printer 0.1.7 (path+file:///$ROOT$ripgrep/crates/printer)", - "grep-searcher 0.1.11 (path+file:///$ROOT$ripgrep/crates/searcher)", - "grep-regex 0.1.11 (path+file:///$ROOT$ripgrep/crates/regex)", - "ignore 0.4.20 (path+file:///$ROOT$ripgrep/crates/ignore)", - "ripgrep 13.0.0 (path+file:///$ROOT$ripgrep)" - ], - "resolve": { - "nodes": [ - { - "id": "aho-corasick 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "memchr", - "pkg": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - } - ], - "features": [ - "default", - "std" - ] - }, - { - "id": "aho-corasick 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "memchr", - "pkg": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - } - ], - "features": [ - "default", - "perf-literal", - "std" - ] - }, - { - "id": "atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "hermit-abi 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "hermit_abi", - "pkg": "hermit-abi 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": "cfg(target_os = \"hermit\")" - } - ] - }, - { - "name": "libc", - "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": "cfg(unix)" - } - ] - }, - { - "name": "winapi", - "pkg": "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": "cfg(windows)" - } - ] - } - ], - "features": [] - }, - { - "id": "base64 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [], - "deps": [], - "features": [ - "default", - "std" - ] - }, - { - "id": "bitflags 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [], - "deps": [], - "features": [ - "default" - ] - }, - { - "id": "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "once_cell 1.17.1 (registry+https://github.com/rust-lang/crates.io-index)", - "regex-automata 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "memchr", - "pkg": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "once_cell", - "pkg": "once_cell 1.17.1 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "regex_automata", - "pkg": "regex-automata 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "serde", - "pkg": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - } - ], - "features": [ - "alloc", - "default", - "std", - "unicode" - ] - }, - { - "id": "bytecount 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [], - "deps": [], - "features": [ - "runtime-dispatch-simd" - ] - }, - { - "id": "cc 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "jobserver 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "jobserver", - "pkg": "jobserver 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - } - ], - "features": [ - "jobserver", - "parallel" - ] - }, - { - "id": "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [], - "deps": [], - "features": [] - }, - { - "id": "clap 2.34.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "bitflags 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-width 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "bitflags", - "pkg": "bitflags 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "strsim", - "pkg": "strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "textwrap", - "pkg": "textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "unicode_width", - "pkg": "unicode-width 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - } - ], - "features": [ - "strsim", - "suggestions" - ] - }, - { - "id": "crossbeam-channel 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.8.15 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "cfg_if", - "pkg": "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "crossbeam_utils", - "pkg": "crossbeam-utils 0.8.15 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - } - ], - "features": [ - "crossbeam-utils", - "default", - "std" - ] - }, - { - "id": "crossbeam-utils 0.8.15 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "cfg_if", - "pkg": "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - } - ], - "features": [ - "std" - ] - }, - { - "id": "encoding_rs 0.8.32 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "cfg_if", - "pkg": "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - } - ], - "features": [ - "alloc", - "default" - ] - }, - { - "id": "encoding_rs_io 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "encoding_rs 0.8.32 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "encoding_rs", - "pkg": "encoding_rs 0.8.32 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - } - ], - "features": [] - }, - { - "id": "fnv 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [], - "deps": [], - "features": [ - "default", - "std" - ] - }, - { - "id": "glob 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [], - "deps": [], - "features": [] - }, - { - "id": "globset 0.4.10 (path+file:///$ROOT$ripgrep/crates/globset)", - "dependencies": [ - "aho-corasick 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)", - "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "fnv 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "glob 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.96 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "aho_corasick", - "pkg": "aho-corasick 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "bstr", - "pkg": "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "fnv", - "pkg": "fnv 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "glob", - "pkg": "glob 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": "dev", - "target": null - } - ] - }, - { - "name": "lazy_static", - "pkg": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": "dev", - "target": null - } - ] - }, - { - "name": "log", - "pkg": "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "regex", - "pkg": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "serde_json", - "pkg": "serde_json 1.0.96 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": "dev", - "target": null - } - ] - } - ], - "features": [ - "default", - "log" - ] - }, - { - "id": "grep 0.2.11 (path+file:///$ROOT$ripgrep/crates/grep)", - "dependencies": [ - "grep-cli 0.1.7 (path+file:///$ROOT$ripgrep/crates/cli)", - "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)", - "grep-printer 0.1.7 (path+file:///$ROOT$ripgrep/crates/printer)", - "grep-regex 0.1.11 (path+file:///$ROOT$ripgrep/crates/regex)", - "grep-searcher 0.1.11 (path+file:///$ROOT$ripgrep/crates/searcher)", - "termcolor 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "walkdir 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "grep_cli", - "pkg": "grep-cli 0.1.7 (path+file:///$ROOT$ripgrep/crates/cli)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "grep_matcher", - "pkg": "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "grep_printer", - "pkg": "grep-printer 0.1.7 (path+file:///$ROOT$ripgrep/crates/printer)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "grep_regex", - "pkg": "grep-regex 0.1.11 (path+file:///$ROOT$ripgrep/crates/regex)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "grep_searcher", - "pkg": "grep-searcher 0.1.11 (path+file:///$ROOT$ripgrep/crates/searcher)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "termcolor", - "pkg": "termcolor 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": "dev", - "target": null - } - ] - }, - { - "name": "walkdir", - "pkg": "walkdir 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": "dev", - "target": null - } - ] - } - ], - "features": [] - }, - { - "id": "grep-cli 0.1.7 (path+file:///$ROOT$ripgrep/crates/cli)", - "dependencies": [ - "atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", - "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "globset 0.4.10 (path+file:///$ROOT$ripgrep/crates/globset)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "termcolor 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "atty", - "pkg": "atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "bstr", - "pkg": "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "globset", - "pkg": "globset 0.4.10 (path+file:///$ROOT$ripgrep/crates/globset)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "lazy_static", - "pkg": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "log", - "pkg": "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "regex", - "pkg": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "same_file", - "pkg": "same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "termcolor", - "pkg": "termcolor 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "winapi_util", - "pkg": "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": "cfg(windows)" - } - ] - } - ], - "features": [] - }, - { - "id": "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)", - "dependencies": [ - "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "memchr", - "pkg": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "regex", - "pkg": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": "dev", - "target": null - } - ] - } - ], - "features": [] - }, - { - "id": "grep-pcre2 0.1.6 (path+file:///$ROOT$ripgrep/crates/pcre2)", - "dependencies": [ - "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)", - "pcre2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "grep_matcher", - "pkg": "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "pcre2", - "pkg": "pcre2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - } - ], - "features": [] - }, - { - "id": "grep-printer 0.1.7 (path+file:///$ROOT$ripgrep/crates/printer)", - "dependencies": [ - "base64 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", - "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)", - "grep-regex 0.1.11 (path+file:///$ROOT$ripgrep/crates/regex)", - "grep-searcher 0.1.11 (path+file:///$ROOT$ripgrep/crates/searcher)", - "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.96 (registry+https://github.com/rust-lang/crates.io-index)", - "termcolor 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "base64", - "pkg": "base64 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "bstr", - "pkg": "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "grep_matcher", - "pkg": "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "grep_regex", - "pkg": "grep-regex 0.1.11 (path+file:///$ROOT$ripgrep/crates/regex)", - "dep_kinds": [ - { - "kind": "dev", - "target": null - } - ] - }, - { - "name": "grep_searcher", - "pkg": "grep-searcher 0.1.11 (path+file:///$ROOT$ripgrep/crates/searcher)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "serde", - "pkg": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "serde_json", - "pkg": "serde_json 1.0.96 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "termcolor", - "pkg": "termcolor 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - } - ], - "features": [ - "base64", - "default", - "serde", - "serde1", - "serde_json" - ] - }, - { - "id": "grep-regex 0.1.11 (path+file:///$ROOT$ripgrep/crates/regex)", - "dependencies": [ - "aho-corasick 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)", - "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)", - "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "regex-syntax 0.6.29 (registry+https://github.com/rust-lang/crates.io-index)", - "thread_local 1.1.7 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "aho_corasick", - "pkg": "aho-corasick 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "bstr", - "pkg": "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "grep_matcher", - "pkg": "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "log", - "pkg": "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "regex", - "pkg": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "regex_syntax", - "pkg": "regex-syntax 0.6.29 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "thread_local", - "pkg": "thread_local 1.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - } - ], - "features": [] - }, - { - "id": "grep-searcher 0.1.11 (path+file:///$ROOT$ripgrep/crates/searcher)", - "dependencies": [ - "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "bytecount 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", - "encoding_rs 0.8.32 (registry+https://github.com/rust-lang/crates.io-index)", - "encoding_rs_io 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)", - "grep-regex 0.1.11 (path+file:///$ROOT$ripgrep/crates/regex)", - "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)", - "memmap2 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "bstr", - "pkg": "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "bytecount", - "pkg": "bytecount 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "encoding_rs", - "pkg": "encoding_rs 0.8.32 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "encoding_rs_io", - "pkg": "encoding_rs_io 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "grep_matcher", - "pkg": "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "grep_regex", - "pkg": "grep-regex 0.1.11 (path+file:///$ROOT$ripgrep/crates/regex)", - "dep_kinds": [ - { - "kind": "dev", - "target": null - } - ] - }, - { - "name": "log", - "pkg": "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "memmap", - "pkg": "memmap2 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "regex", - "pkg": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": "dev", - "target": null - } - ] - } - ], - "features": [ - "default" - ] - }, - { - "id": "hermit-abi 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "libc", - "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - } - ], - "features": [ - "default" - ] - }, - { - "id": "ignore 0.4.20 (path+file:///$ROOT$ripgrep/crates/ignore)", - "dependencies": [ - "crossbeam-channel 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", - "globset 0.4.10 (path+file:///$ROOT$ripgrep/crates/globset)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "thread_local 1.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "walkdir 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "crossbeam_channel", - "pkg": "crossbeam-channel 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": "dev", - "target": null - } - ] - }, - { - "name": "globset", - "pkg": "globset 0.4.10 (path+file:///$ROOT$ripgrep/crates/globset)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "lazy_static", - "pkg": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "log", - "pkg": "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "memchr", - "pkg": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "regex", - "pkg": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "same_file", - "pkg": "same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "thread_local", - "pkg": "thread_local 1.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "walkdir", - "pkg": "walkdir 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "winapi_util", - "pkg": "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": "cfg(windows)" - } - ] - } - ], - "features": [] - }, - { - "id": "itoa 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [], - "deps": [], - "features": [] - }, - { - "id": "jemalloc-sys 0.5.3+5.3.0-patched (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "cc 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "cc", - "pkg": "cc 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": "build", - "target": null - } - ] - }, - { - "name": "libc", - "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - } - ], - "features": [ - "background_threads_runtime_support" - ] - }, - { - "id": "jemallocator 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "jemalloc-sys 0.5.3+5.3.0-patched (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "jemalloc_sys", - "pkg": "jemalloc-sys 0.5.3+5.3.0-patched (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "libc", - "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - } - ], - "features": [ - "background_threads_runtime_support", - "default" - ] - }, - { - "id": "jobserver 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "libc", - "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": "cfg(unix)" - } - ] - } - ], - "features": [] - }, - { - "id": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [], - "deps": [], - "features": [] - }, - { - "id": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [], - "deps": [], - "features": [ - "default", - "std" - ] - }, - { - "id": "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "cfg_if", - "pkg": "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - } - ], - "features": [] - }, - { - "id": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [], - "deps": [], - "features": [ - "default", - "std" - ] - }, - { - "id": "memmap2 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "libc", - "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": "cfg(unix)" - } - ] - } - ], - "features": [] - }, - { - "id": "once_cell 1.17.1 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [], - "deps": [], - "features": [ - "alloc", - "default", - "race", - "std" - ] - }, - { - "id": "pcre2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)", - "pcre2-sys 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", - "thread_local 1.1.7 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "libc", - "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "log", - "pkg": "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "pcre2_sys", - "pkg": "pcre2-sys 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "thread_local", - "pkg": "thread_local 1.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - } - ], - "features": [] - }, - { - "id": "pcre2-sys 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "cc 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", - "pkg-config 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "cc", - "pkg": "cc 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": "build", - "target": null - } - ] - }, - { - "name": "libc", - "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "pkg_config", - "pkg": "pkg-config 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": "build", - "target": null - } - ] - } - ], - "features": [] - }, - { - "id": "pkg-config 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [], - "deps": [], - "features": [] - }, - { - "id": "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "unicode_ident", - "pkg": "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - } - ], - "features": [ - "default", - "proc-macro" - ] - }, - { - "id": "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "proc_macro2", - "pkg": "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - } - ], - "features": [ - "default", - "proc-macro" - ] - }, - { - "id": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "aho-corasick 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "regex-syntax 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "aho_corasick", - "pkg": "aho-corasick 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "memchr", - "pkg": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "regex_syntax", - "pkg": "regex-syntax 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - } - ], - "features": [ - "aho-corasick", - "default", - "memchr", - "perf", - "perf-cache", - "perf-dfa", - "perf-inline", - "perf-literal", - "std", - "unicode", - "unicode-age", - "unicode-bool", - "unicode-case", - "unicode-gencat", - "unicode-perl", - "unicode-script", - "unicode-segment" - ] - }, - { - "id": "regex-automata 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [], - "deps": [], - "features": [] - }, - { - "id": "regex-syntax 0.6.29 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [], - "deps": [], - "features": [ - "default", - "unicode", - "unicode-age", - "unicode-bool", - "unicode-case", - "unicode-gencat", - "unicode-perl", - "unicode-script", - "unicode-segment" - ] - }, - { - "id": "regex-syntax 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [], - "deps": [], - "features": [ - "default", - "std", - "unicode", - "unicode-age", - "unicode-bool", - "unicode-case", - "unicode-gencat", - "unicode-perl", - "unicode-script", - "unicode-segment" - ] - }, - { - "id": "ripgrep 13.0.0 (path+file:///$ROOT$ripgrep)", - "dependencies": [ - "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "clap 2.34.0 (registry+https://github.com/rust-lang/crates.io-index)", - "grep 0.2.11 (path+file:///$ROOT$ripgrep/crates/grep)", - "ignore 0.4.20 (path+file:///$ROOT$ripgrep/crates/ignore)", - "jemallocator 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.96 (registry+https://github.com/rust-lang/crates.io-index)", - "termcolor 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "walkdir 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "bstr", - "pkg": "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "clap", - "pkg": "clap 2.34.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - }, - { - "kind": "build", - "target": null - } - ] - }, - { - "name": "grep", - "pkg": "grep 0.2.11 (path+file:///$ROOT$ripgrep/crates/grep)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "ignore", - "pkg": "ignore 0.4.20 (path+file:///$ROOT$ripgrep/crates/ignore)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "jemallocator", - "pkg": "jemallocator 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": "cfg(all(target_env = \"musl\", target_pointer_width = \"64\"))" - } - ] - }, - { - "name": "lazy_static", - "pkg": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - }, - { - "kind": "build", - "target": null - } - ] - }, - { - "name": "log", - "pkg": "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "regex", - "pkg": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "serde", - "pkg": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": "dev", - "target": null - } - ] - }, - { - "name": "serde_derive", - "pkg": "serde_derive 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": "dev", - "target": null - } - ] - }, - { - "name": "serde_json", - "pkg": "serde_json 1.0.96 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "termcolor", - "pkg": "termcolor 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "walkdir", - "pkg": "walkdir 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": "dev", - "target": null - } - ] - } - ], - "features": [] - }, - { - "id": "ryu 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [], - "deps": [], - "features": [] - }, - { - "id": "same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "winapi_util", - "pkg": "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": "cfg(windows)" - } - ] - } - ], - "features": [] - }, - { - "id": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "serde_derive 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "serde_derive", - "pkg": "serde_derive 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - } - ], - "features": [ - "alloc", - "default", - "derive", - "serde_derive", - "std" - ] - }, - { - "id": "serde_derive 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 2.0.15 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "proc_macro2", - "pkg": "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "quote", - "pkg": "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "syn", - "pkg": "syn 2.0.15 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - } - ], - "features": [ - "default" - ] - }, - { - "id": "serde_json 1.0.96 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "itoa 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "ryu 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "itoa", - "pkg": "itoa 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "ryu", - "pkg": "ryu 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "serde", - "pkg": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - } - ], - "features": [ - "default", - "std" - ] - }, - { - "id": "strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [], - "deps": [], - "features": [] - }, - { - "id": "syn 2.0.15 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "proc_macro2", - "pkg": "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "quote", - "pkg": "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "unicode_ident", - "pkg": "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - } - ], - "features": [ - "clone-impls", - "default", - "derive", - "parsing", - "printing", - "proc-macro", - "quote" - ] - }, - { - "id": "termcolor 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "winapi_util", - "pkg": "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": "cfg(windows)" - } - ] - } - ], - "features": [] - }, - { - "id": "textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "unicode-width 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "unicode_width", - "pkg": "unicode-width 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - } - ], - "features": [] - }, - { - "id": "thread_local 1.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "once_cell 1.17.1 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "cfg_if", - "pkg": "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "once_cell", - "pkg": "once_cell 1.17.1 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - } - ], - "features": [] - }, - { - "id": "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [], - "deps": [], - "features": [] - }, - { - "id": "unicode-width 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [], - "deps": [], - "features": [ - "default" - ] - }, - { - "id": "walkdir 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "same_file", - "pkg": "same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "winapi_util", - "pkg": "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": "cfg(windows)" - } - ] - } - ], - "features": [] - }, - { - "id": "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "winapi_i686_pc_windows_gnu", - "pkg": "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": "i686-pc-windows-gnu" - } - ] - }, - { - "name": "winapi_x86_64_pc_windows_gnu", - "pkg": "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": "x86_64-pc-windows-gnu" - } - ] - } - ], - "features": [ - "consoleapi", - "errhandlingapi", - "fileapi", - "minwinbase", - "minwindef", - "processenv", - "std", - "winbase", - "wincon", - "winerror", - "winnt" - ] - }, - { - "id": "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [], - "deps": [], - "features": [] - }, - { - "id": "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "winapi", - "pkg": "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": "cfg(windows)" - } - ] - } - ], - "features": [] - }, - { - "id": "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [], - "deps": [], - "features": [] - } - ], - "root": "ripgrep 13.0.0 (path+file:///$ROOT$ripgrep)" - }, - "target_directory": "$ROOT$ripgrep/target", - "version": 1, - "workspace_root": "$ROOT$ripgrep", - "metadata": null -} 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 21b481c1fa29..41b42573f082 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 @@ -82,6 +82,7 @@ fn actual_main() -> anyhow::Result { flags::RustAnalyzerCmd::Highlight(cmd) => cmd.run()?, flags::RustAnalyzerCmd::AnalysisStats(cmd) => cmd.run(verbosity)?, flags::RustAnalyzerCmd::Diagnostics(cmd) => cmd.run()?, + flags::RustAnalyzerCmd::UnresolvedReferences(cmd) => cmd.run()?, flags::RustAnalyzerCmd::Ssr(cmd) => cmd.run()?, flags::RustAnalyzerCmd::Search(cmd) => cmd.run()?, flags::RustAnalyzerCmd::Lsif(cmd) => cmd.run()?, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli.rs index 5eb6ff664f66..a7ec5af89fcb 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli.rs @@ -13,6 +13,7 @@ mod rustc_tests; mod scip; mod ssr; mod symbols; +mod unresolved_references; mod progress_report; 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 44e56645ba3c..e899e0e8eea5 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 @@ -64,6 +64,7 @@ impl flags::AnalysisStats { true => None, false => Some(RustLibSource::Discover), }, + all_targets: true, ..Default::default() }; let no_progress = &|_| (); @@ -77,7 +78,11 @@ impl flags::AnalysisStats { let metadata_time = db_load_sw.elapsed(); let load_cargo_config = LoadCargoConfig { load_out_dirs_from_check: !self.disable_build_scripts, - with_proc_macro_server: ProcMacroServerChoice::Sysroot, + with_proc_macro_server: if self.disable_proc_macros { + ProcMacroServerChoice::None + } else { + ProcMacroServerChoice::Sysroot + }, prefill_caches: false, }; @@ -339,6 +344,7 @@ impl flags::AnalysisStats { true => None, false => Some(RustLibSource::Discover), }, + all_targets: true, ..Default::default() }; 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 cdac0e5ef5cc..28f25975d64a 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 @@ -24,8 +24,11 @@ impl flags::Diagnostics { handle.join() } fn run_(self) -> anyhow::Result<()> { - let cargo_config = - CargoConfig { sysroot: Some(RustLibSource::Discover), ..Default::default() }; + let cargo_config = CargoConfig { + sysroot: Some(RustLibSource::Discover), + all_targets: true, + ..Default::default() + }; let with_proc_macro_server = if let Some(p) = &self.proc_macro_srv { let path = vfs::AbsPathBuf::assert_utf8(std::env::current_dir()?.join(p)); ProcMacroServerChoice::Explicit(path) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs index 16d90de661ad..73e71658d17c 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs @@ -124,6 +124,19 @@ xflags::xflags! { optional --proc-macro-srv path: PathBuf } + /// Report unresolved references + cmd unresolved-references { + /// Directory with Cargo.toml. + required path: PathBuf + + /// Don't run build scripts or load `OUT_DIR` values by running `cargo check` before analysis. + optional --disable-build-scripts + /// Don't use expand proc macros. + optional --disable-proc-macros + /// Run a custom proc-macro-srv binary. + optional --proc-macro-srv path: PathBuf + } + cmd ssr { /// A structured search replace rule (`$a.foo($b) ==>> bar($a, $b)`) repeated rule: SsrRule @@ -181,6 +194,7 @@ pub enum RustAnalyzerCmd { RunTests(RunTests), RustcTests(RustcTests), Diagnostics(Diagnostics), + UnresolvedReferences(UnresolvedReferences), Ssr(Ssr), Search(Search), Lsif(Lsif), @@ -250,6 +264,15 @@ pub struct Diagnostics { pub proc_macro_srv: Option, } +#[derive(Debug)] +pub struct UnresolvedReferences { + pub path: PathBuf, + + pub disable_build_scripts: bool, + pub disable_proc_macros: bool, + pub proc_macro_srv: Option, +} + #[derive(Debug)] pub struct Ssr { pub rule: Vec, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs index 89fe712ced02..e4263a3f667c 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs @@ -273,10 +273,12 @@ impl LsifManager<'_> { impl flags::Lsif { pub fn run(self) -> anyhow::Result<()> { - eprintln!("Generating LSIF started..."); let now = Instant::now(); - let cargo_config = - &CargoConfig { sysroot: Some(RustLibSource::Discover), ..Default::default() }; + let cargo_config = &CargoConfig { + sysroot: Some(RustLibSource::Discover), + all_targets: true, + ..Default::default() + }; let no_progress = &|_| (); let load_cargo_config = LoadCargoConfig { load_out_dirs_from_check: true, @@ -285,6 +287,7 @@ impl flags::Lsif { }; let path = AbsPathBuf::assert_utf8(env::current_dir()?.join(self.path)); let root = ProjectManifest::discover_single(&path)?; + eprintln!("Generating LSIF for project at {root}"); let mut workspace = ProjectWorkspace::load(root, cargo_config, no_progress)?; let build_scripts = workspace.run_build_scripts(cargo_config, no_progress)?; diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/run_tests.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/run_tests.rs index 157ef43dd0a2..f90ebcfdb2e3 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/run_tests.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/run_tests.rs @@ -13,8 +13,11 @@ use crate::cli::{flags, full_name_of_item, Result}; impl flags::RunTests { pub fn run(self) -> Result<()> { - let cargo_config = - CargoConfig { sysroot: Some(RustLibSource::Discover), ..Default::default() }; + let cargo_config = CargoConfig { + sysroot: Some(RustLibSource::Discover), + all_targets: true, + ..Default::default() + }; let load_cargo_config = LoadCargoConfig { load_out_dirs_from_check: true, with_proc_macro_server: ProcMacroServerChoice::Sysroot, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs index 75efdfd7dd8f..730f3c08abb5 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs @@ -67,8 +67,11 @@ impl Tester { path.push("ra-rustc-test.rs"); let tmp_file = AbsPathBuf::try_from(Utf8PathBuf::from_path_buf(path).unwrap()).unwrap(); std::fs::write(&tmp_file, "")?; - let cargo_config = - CargoConfig { sysroot: Some(RustLibSource::Discover), ..Default::default() }; + let cargo_config = CargoConfig { + sysroot: Some(RustLibSource::Discover), + all_targets: true, + ..Default::default() + }; let sysroot = Sysroot::discover(tmp_file.parent().unwrap(), &cargo_config.extra_env); let data_layout = target_data_layout::get( 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 ceb8534fdf55..e9198977dea5 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 @@ -51,7 +51,7 @@ impl flags::Scip { // FIXME @alibektas : What happens to errors without logging? error!(?error_sink, "Config Error(s)"); } - let cargo_config = config.cargo(); + let cargo_config = config.cargo(None); let (db, vfs, _) = load_workspace_at( root.as_path().as_ref(), &cargo_config, @@ -142,7 +142,7 @@ impl flags::Scip { let mut symbol_roles = Default::default(); if let Some(def) = token.definition { - // if the the range of the def and the range of the token are the same, this must be the definition. + // if the range of the def and the range of the token are the same, this must be the definition. // they also must be in the same file. See https://github.com/rust-lang/rust-analyzer/pull/17988 if def.file_id == file_id && def.range == text_range { symbol_roles |= scip_types::SymbolRole::Definition as i32; 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 3caa48798874..bdca800a0d68 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 @@ -10,8 +10,11 @@ use crate::cli::flags; impl flags::Ssr { pub fn run(self) -> anyhow::Result<()> { - let cargo_config = - CargoConfig { sysroot: Some(RustLibSource::Discover), ..Default::default() }; + let cargo_config = CargoConfig { + sysroot: Some(RustLibSource::Discover), + all_targets: true, + ..Default::default() + }; let load_cargo_config = LoadCargoConfig { load_out_dirs_from_check: true, with_proc_macro_server: ProcMacroServerChoice::Sysroot, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/unresolved_references.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/unresolved_references.rs new file mode 100644 index 000000000000..986bd018b424 --- /dev/null +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/unresolved_references.rs @@ -0,0 +1,175 @@ +//! Reports references in code that the IDE layer cannot resolve. +use hir::{db::HirDatabase, AnyDiagnostic, Crate, HirFileIdExt as _, Module, Semantics}; +use ide::{AnalysisHost, RootDatabase, TextRange}; +use ide_db::{ + base_db::{SourceDatabase, SourceRootDatabase}, + defs::NameRefClass, + EditionedFileId, FxHashSet, LineIndexDatabase as _, +}; +use load_cargo::{load_workspace_at, LoadCargoConfig, ProcMacroServerChoice}; +use parser::SyntaxKind; +use syntax::{ast, AstNode, WalkEvent}; +use vfs::FileId; + +use crate::cli::flags; + +impl flags::UnresolvedReferences { + pub fn run(self) -> anyhow::Result<()> { + const STACK_SIZE: usize = 1024 * 1024 * 8; + + let handle = stdx::thread::Builder::new(stdx::thread::ThreadIntent::LatencySensitive) + .name("BIG_STACK_THREAD".into()) + .stack_size(STACK_SIZE) + .spawn(|| self.run_()) + .unwrap(); + + handle.join() + } + + fn run_(self) -> anyhow::Result<()> { + let root = + vfs::AbsPathBuf::assert_utf8(std::env::current_dir()?.join(&self.path)).normalize(); + let config = crate::config::Config::new( + root.clone(), + lsp_types::ClientCapabilities::default(), + vec![], + None, + ); + let cargo_config = config.cargo(None); + let with_proc_macro_server = if let Some(p) = &self.proc_macro_srv { + let path = vfs::AbsPathBuf::assert_utf8(std::env::current_dir()?.join(p)); + ProcMacroServerChoice::Explicit(path) + } else { + ProcMacroServerChoice::Sysroot + }; + let load_cargo_config = LoadCargoConfig { + load_out_dirs_from_check: !self.disable_build_scripts, + with_proc_macro_server, + prefill_caches: false, + }; + let (db, vfs, _proc_macro) = + load_workspace_at(&self.path, &cargo_config, &load_cargo_config, &|_| {})?; + let host = AnalysisHost::with_database(db); + let db = host.raw_database(); + let sema = Semantics::new(db); + + let mut visited_files = FxHashSet::default(); + + 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.into()); + let source_root = db.source_root(source_root); + !source_root.is_library + }); + + for module in work { + let file_id = module.definition_source_file_id(db).original_file(db); + if !visited_files.contains(&file_id) { + let crate_name = + module.krate().display_name(db).as_deref().unwrap_or("unknown").to_owned(); + let file_path = vfs.file_path(file_id.into()); + eprintln!("processing crate: {crate_name}, module: {file_path}",); + + let line_index = db.line_index(file_id.into()); + let file_text = db.file_text(file_id.into()); + + for range in find_unresolved_references(db, &sema, file_id.into(), &module) { + let line_col = line_index.line_col(range.start()); + let line = line_col.line + 1; + let col = line_col.col + 1; + let text = &file_text[range]; + println!("{file_path}:{line}:{col}: {text}"); + } + + visited_files.insert(file_id); + } + } + + eprintln!(); + eprintln!("scan complete"); + + Ok(()) + } +} + +fn all_modules(db: &dyn HirDatabase) -> Vec { + let mut worklist: Vec<_> = + Crate::all(db).into_iter().map(|krate| krate.root_module()).collect(); + let mut modules = Vec::new(); + + while let Some(module) = worklist.pop() { + modules.push(module); + worklist.extend(module.children(db)); + } + + modules +} + +fn find_unresolved_references( + db: &RootDatabase, + sema: &Semantics<'_, RootDatabase>, + file_id: FileId, + module: &Module, +) -> Vec { + let mut unresolved_references = all_unresolved_references(sema, file_id); + + // remove unresolved references which are within inactive code + let mut diagnostics = Vec::new(); + module.diagnostics(db, &mut diagnostics, false); + for diagnostic in diagnostics { + let AnyDiagnostic::InactiveCode(inactive_code) = diagnostic else { + continue; + }; + + let node = inactive_code.node; + let range = node.map(|it| it.text_range()).original_node_file_range_rooted(db); + + if range.file_id != file_id { + continue; + } + + unresolved_references.retain(|r| !range.range.contains_range(*r)); + } + + unresolved_references +} + +fn all_unresolved_references( + sema: &Semantics<'_, RootDatabase>, + file_id: FileId, +) -> Vec { + let file_id = sema + .attach_first_edition(file_id) + .unwrap_or_else(|| EditionedFileId::current_edition(file_id)); + let file = sema.parse(file_id); + let root = file.syntax(); + + let mut unresolved_references = Vec::new(); + for event in root.preorder() { + let WalkEvent::Enter(syntax) = event else { + continue; + }; + let Some(name_ref) = ast::NameRef::cast(syntax) else { + continue; + }; + let Some(descended_name_ref) = name_ref.syntax().first_token().and_then(|tok| { + sema.descend_into_macros_single_exact(tok).parent().and_then(ast::NameRef::cast) + }) else { + continue; + }; + + // if we can classify the name_ref, it's not unresolved + if NameRefClass::classify(sema, &descended_name_ref).is_some() { + continue; + } + + // if we couldn't classify it, but it's in an attr, ignore it. See #10935 + if descended_name_ref.syntax().ancestors().any(|it| it.kind() == SyntaxKind::ATTR) { + continue; + } + + // otherwise, it's unresolved + unresolved_references.push(name_ref.syntax().text_range()); + } + unresolved_references +} 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 2889af844b14..4cc60695fe66 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -24,7 +24,8 @@ use ide_db::{ use itertools::Itertools; use paths::{Utf8Path, Utf8PathBuf}; use project_model::{ - CargoConfig, CargoFeatures, ProjectJson, ProjectJsonData, ProjectManifest, RustLibSource, + CargoConfig, CargoFeatures, ProjectJson, ProjectJsonData, ProjectJsonFromCommand, + ProjectManifest, RustLibSource, }; use rustc_hash::{FxHashMap, FxHashSet}; use semver::Version; @@ -59,15 +60,13 @@ mod patch_old_style; // However, editor specific config, which the server doesn't know about, should // be specified directly in `package.json`. // -// To deprecate an option by replacing it with another name use `new_name | `old_name` so that we keep +// To deprecate an option by replacing it with another name use `new_name` | `old_name` so that we keep // parsing the old name. config_data! { - /// Configs that apply on a workspace-wide scope. There are 3 levels on which a global configuration can be configured - // FIXME: 1. and 3. should be split, some configs do not make sense per project + /// Configs that apply on a workspace-wide scope. There are 2 levels on which a global configuration can be configured /// - /// 1. `rust-analyzer.toml` file under user's config directory (e.g ~/.config/rust-analyzer.toml) + /// 1. `rust-analyzer.toml` file under user's config directory (e.g ~/.config/rust-analyzer/rust-analyzer.toml) /// 2. Client's own configurations (e.g `settings.json` on VS Code) - /// 3. `rust-analyzer.toml` file located at the workspace root /// /// A config is searched for by traversing a "config tree" in a bottom up fashion. It is chosen by the nearest first principle. global: struct GlobalDefaultConfigData <- GlobalConfigInput -> { @@ -76,178 +75,9 @@ config_data! { /// How many worker threads to handle priming caches. The default `0` means to pick automatically. cachePriming_numThreads: NumThreads = NumThreads::Physical, - /// Pass `--all-targets` to cargo invocation. - cargo_allTargets: bool = true, - /// Automatically refresh project info via `cargo metadata` on - /// `Cargo.toml` or `.cargo/config.toml` changes. - pub(crate) cargo_autoreload: bool = true, - /// Run build scripts (`build.rs`) for more precise code analysis. - cargo_buildScripts_enable: bool = true, - /// Specifies the invocation strategy to use when running the build scripts command. - /// If `per_workspace` is set, the command will be executed for each Rust workspace with the - /// workspace as the working directory. - /// If `once` is set, the command will be executed once with the opened project as the - /// working directory. - /// This config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#` - /// is set. - cargo_buildScripts_invocationStrategy: InvocationStrategy = InvocationStrategy::PerWorkspace, - /// Override the command rust-analyzer uses to run build scripts and - /// build procedural macros. The command is required to output json - /// and should therefore include `--message-format=json` or a similar - /// option. - /// - /// If there are multiple linked projects/workspaces, this command is invoked for - /// each of them, with the working directory being the workspace root - /// (i.e., the folder containing the `Cargo.toml`). This can be overwritten - /// by changing `#rust-analyzer.cargo.buildScripts.invocationStrategy#`. - /// - /// By default, a cargo invocation will be constructed for the configured - /// targets and features, with the following base command line: - /// - /// ```bash - /// cargo check --quiet --workspace --message-format=json --all-targets --keep-going - /// ``` - /// . - cargo_buildScripts_overrideCommand: Option> = None, - /// Rerun proc-macros building/build-scripts running when proc-macro - /// or build-script sources change and are saved. - cargo_buildScripts_rebuildOnSave: bool = true, - /// Use `RUSTC_WRAPPER=rust-analyzer` when running build scripts to - /// avoid checking unnecessary things. - cargo_buildScripts_useRustcWrapper: bool = true, - /// List of cfg options to enable with the given values. - cargo_cfgs: FxHashMap> = { - let mut m = FxHashMap::default(); - m.insert("debug_assertions".to_owned(), None); - m.insert("miri".to_owned(), None); - m - }, - /// Extra arguments that are passed to every cargo invocation. - cargo_extraArgs: Vec = vec![], - /// Extra environment variables that will be set when running cargo, rustc - /// or other commands within the workspace. Useful for setting RUSTFLAGS. - cargo_extraEnv: FxHashMap = FxHashMap::default(), - /// List of features to activate. - /// - /// Set this to `"all"` to pass `--all-features` to cargo. - cargo_features: CargoFeaturesDef = CargoFeaturesDef::Selected(vec![]), - /// Whether to pass `--no-default-features` to cargo. - cargo_noDefaultFeatures: bool = false, - /// Relative path to the sysroot, or "discover" to try to automatically find it via - /// "rustc --print sysroot". - /// - /// Unsetting this disables sysroot loading. - /// - /// This option does not take effect until rust-analyzer is restarted. - cargo_sysroot: Option = Some("discover".to_owned()), - /// Relative path to the sysroot library sources. If left unset, this will default to - /// `{cargo.sysroot}/lib/rustlib/src/rust/library`. - /// - /// This option does not take effect until rust-analyzer is restarted. - cargo_sysrootSrc: Option = None, - /// Compilation target override (target triple). - // FIXME(@poliorcetics): move to multiple targets here too, but this will need more work - // than `checkOnSave_target` - cargo_target: Option = None, - /// Optional path to a rust-analyzer specific target directory. - /// This prevents rust-analyzer's `cargo check` and initial build-script and proc-macro - /// building from locking the `Cargo.lock` at the expense of duplicating build artifacts. - /// - /// Set to `true` to use a subdirectory of the existing target directory or - /// set to a path relative to the workspace to use that path. - cargo_targetDir | rust_analyzerTargetDir: Option = None, - - /// Run the check command for diagnostics on save. - checkOnSave | checkOnSave_enable: bool = true, - - /// Check all targets and tests (`--all-targets`). Defaults to - /// `#rust-analyzer.cargo.allTargets#`. - check_allTargets | checkOnSave_allTargets: Option = None, - /// Cargo command to use for `cargo check`. - check_command | checkOnSave_command: String = "check".to_owned(), - /// Extra arguments for `cargo check`. - check_extraArgs | checkOnSave_extraArgs: Vec = vec![], - /// Extra environment variables that will be set when running `cargo check`. - /// Extends `#rust-analyzer.cargo.extraEnv#`. - check_extraEnv | checkOnSave_extraEnv: FxHashMap = FxHashMap::default(), - /// List of features to activate. Defaults to - /// `#rust-analyzer.cargo.features#`. - /// - /// Set to `"all"` to pass `--all-features` to Cargo. - check_features | checkOnSave_features: Option = None, - /// List of `cargo check` (or other command specified in `check.command`) diagnostics to ignore. - /// - /// For example for `cargo check`: `dead_code`, `unused_imports`, `unused_variables`,... - check_ignore: FxHashSet = FxHashSet::default(), - /// Specifies the invocation strategy to use when running the check command. - /// If `per_workspace` is set, the command will be executed for each workspace. - /// If `once` is set, the command will be executed once. - /// This config only has an effect when `#rust-analyzer.check.overrideCommand#` - /// is set. - check_invocationStrategy | checkOnSave_invocationStrategy: InvocationStrategy = InvocationStrategy::PerWorkspace, - /// Whether to pass `--no-default-features` to Cargo. Defaults to - /// `#rust-analyzer.cargo.noDefaultFeatures#`. - check_noDefaultFeatures | checkOnSave_noDefaultFeatures: Option = None, - /// Override the command rust-analyzer uses instead of `cargo check` for - /// diagnostics on save. The command is required to output json and - /// should therefore include `--message-format=json` or a similar option - /// (if your client supports the `colorDiagnosticOutput` experimental - /// capability, you can use `--message-format=json-diagnostic-rendered-ansi`). - /// - /// If you're changing this because you're using some tool wrapping - /// Cargo, you might also want to change - /// `#rust-analyzer.cargo.buildScripts.overrideCommand#`. - /// - /// If there are multiple linked projects/workspaces, this command is invoked for - /// each of them, with the working directory being the workspace root - /// (i.e., the folder containing the `Cargo.toml`). This can be overwritten - /// by changing `#rust-analyzer.check.invocationStrategy#`. - /// - /// If `$saved_file` is part of the command, rust-analyzer will pass - /// the absolute path of the saved file to the provided command. This is - /// intended to be used with non-Cargo build systems. - /// Note that `$saved_file` is experimental and may be removed in the future. - /// - /// An example command would be: - /// - /// ```bash - /// cargo check --workspace --message-format=json --all-targets - /// ``` - /// . - check_overrideCommand | checkOnSave_overrideCommand: Option> = None, - /// Check for specific targets. Defaults to `#rust-analyzer.cargo.target#` if empty. - /// - /// Can be a single target, e.g. `"x86_64-unknown-linux-gnu"` or a list of targets, e.g. - /// `["aarch64-apple-darwin", "x86_64-apple-darwin"]`. - /// - /// Aliased as `"checkOnSave.targets"`. - check_targets | checkOnSave_targets | checkOnSave_target: Option = None, - /// Whether `--workspace` should be passed to `cargo check`. - /// If false, `-p ` will be passed instead. - check_workspace: bool = true, + /// Custom completion snippets. + completion_snippets_custom: FxHashMap = Config::completion_snippets_default(), - /// List of rust-analyzer diagnostics to disable. - diagnostics_disabled: FxHashSet = FxHashSet::default(), - /// Whether to show native rust-analyzer diagnostics. - diagnostics_enable: bool = true, - /// Whether to show experimental rust-analyzer diagnostics that might - /// have more false positives than usual. - diagnostics_experimental_enable: bool = false, - /// Map of prefixes to be substituted when parsing diagnostic file paths. - /// This should be the reverse mapping of what is passed to `rustc` as `--remap-path-prefix`. - diagnostics_remapPrefix: FxHashMap = FxHashMap::default(), - /// Whether to run additional style lints. - diagnostics_styleLints_enable: bool = false, - /// List of warnings that should be displayed with hint severity. - /// - /// The warnings will be indicated by faded text or three dots in code - /// and will not show up in the `Problems Panel`. - diagnostics_warningsAsHint: Vec = vec![], - /// List of warnings that should be displayed with info severity. - /// - /// The warnings will be indicated by a blue squiggly underline in code - /// and a blue icon in the `Problems Panel`. - diagnostics_warningsAsInfo: Vec = vec![], /// These directories will be ignored by rust-analyzer. They are /// relative to the workspace root, and globs are not supported. You may @@ -255,51 +85,234 @@ config_data! { files_excludeDirs: Vec = vec![], - /// Disable project auto-discovery in favor of explicitly specified set - /// of projects. - /// - /// Elements must be paths pointing to `Cargo.toml`, - /// `rust-project.json`, `.rs` files (which will be treated as standalone files) or JSON - /// objects in `rust-project.json` format. - linkedProjects: Vec = vec![], - - /// Number of syntax trees rust-analyzer keeps in memory. Defaults to 128. - lru_capacity: Option = None, - /// Sets the LRU capacity of the specified queries. - lru_query_capacities: FxHashMap, u16> = FxHashMap::default(), - - /// These proc-macros will be ignored when trying to expand them. - /// - /// This config takes a map of crate names with the exported proc-macro names to ignore as values. - procMacro_ignored: FxHashMap, Box<[Box]>> = FxHashMap::default(), - /// Command to be executed instead of 'cargo' for runnables. - runnables_command: Option = None, - /// Additional arguments to be passed to cargo for runnables such as - /// tests or binaries. For example, it may be `--release`. - runnables_extraArgs: Vec = vec![], - /// Additional arguments to be passed through Cargo to launched tests, benchmarks, or - /// doc-tests. - /// - /// Unless the launched target uses a - /// [custom test harness](https://doc.rust-lang.org/cargo/reference/cargo-targets.html#the-harness-field), - /// they will end up being interpreted as options to - /// [`rustc`’s built-in test harness (“libtest”)](https://doc.rust-lang.org/rustc/tests/index.html#cli-arguments). - runnables_extraTestBinaryArgs: Vec = vec!["--show-output".to_owned()], + /// Enables highlighting of related references while the cursor is on `break`, `loop`, `while`, or `for` keywords. + highlightRelated_breakPoints_enable: bool = true, + /// Enables highlighting of all captures of a closure while the cursor is on the `|` or move keyword of a closure. + highlightRelated_closureCaptures_enable: bool = true, + /// Enables highlighting of all exit points while the cursor is on any `return`, `?`, `fn`, or return type arrow (`->`). + highlightRelated_exitPoints_enable: bool = true, + /// Enables highlighting of related references while the cursor is on any identifier. + highlightRelated_references_enable: bool = true, + /// Enables highlighting of all break points for a loop or block context while the cursor is on any `async` or `await` keywords. + highlightRelated_yieldPoints_enable: bool = true, - /// Path to the Cargo.toml of the rust compiler workspace, for usage in rustc_private - /// projects, or "discover" to try to automatically find it if the `rustc-dev` component - /// is installed. - /// - /// Any project which uses rust-analyzer with the rustcPrivate - /// crates must set `[package.metadata.rust-analyzer] rustc_private=true` to use it. - /// - /// This option does not take effect until rust-analyzer is restarted. - rustc_source: Option = None, + /// Whether to show `Debug` action. Only applies when + /// `#rust-analyzer.hover.actions.enable#` is set. + hover_actions_debug_enable: bool = true, + /// Whether to show HoverActions in Rust files. + hover_actions_enable: bool = true, + /// Whether to show `Go to Type Definition` action. Only applies when + /// `#rust-analyzer.hover.actions.enable#` is set. + hover_actions_gotoTypeDef_enable: bool = true, + /// Whether to show `Implementations` action. Only applies when + /// `#rust-analyzer.hover.actions.enable#` is set. + hover_actions_implementations_enable: bool = true, + /// Whether to show `References` action. Only applies when + /// `#rust-analyzer.hover.actions.enable#` is set. + hover_actions_references_enable: bool = false, + /// Whether to show `Run` action. Only applies when + /// `#rust-analyzer.hover.actions.enable#` is set. + hover_actions_run_enable: bool = true, + /// Whether to show documentation on hover. + hover_documentation_enable: bool = true, + /// Whether to show keyword hover popups. Only applies when + /// `#rust-analyzer.hover.documentation.enable#` is set. + hover_documentation_keywords_enable: bool = true, + /// Use markdown syntax for links on hover. + hover_links_enable: bool = true, + /// How to render the align information in a memory layout hover. + hover_memoryLayout_alignment: Option = Some(MemoryLayoutHoverRenderKindDef::Hexadecimal), + /// Whether to show memory layout data on hover. + hover_memoryLayout_enable: bool = true, + /// How to render the niche information in a memory layout hover. + hover_memoryLayout_niches: Option = Some(false), + /// How to render the offset information in a memory layout hover. + hover_memoryLayout_offset: Option = Some(MemoryLayoutHoverRenderKindDef::Hexadecimal), + /// How to render the size information in a memory layout hover. + hover_memoryLayout_size: Option = Some(MemoryLayoutHoverRenderKindDef::Both), - /// Enables automatic discovery of projects using [`DiscoverWorkspaceConfig::command`]. - /// + /// How many variants of an enum to display when hovering on. Show none if empty. + hover_show_enumVariants: Option = Some(5), + /// How many fields of a struct, variant or union to display when hovering on. Show none if empty. + hover_show_fields: Option = Some(5), + /// How many associated items of a trait to display when hovering a trait. + hover_show_traitAssocItems: Option = None, + + /// Whether to show inlay type hints for binding modes. + inlayHints_bindingModeHints_enable: bool = false, + /// Whether to show inlay type hints for method chains. + inlayHints_chainingHints_enable: bool = true, + /// Whether to show inlay hints after a closing `}` to indicate what item it belongs to. + inlayHints_closingBraceHints_enable: bool = true, + /// Minimum number of lines required before the `}` until the hint is shown (set to 0 or 1 + /// to always show them). + inlayHints_closingBraceHints_minLines: usize = 25, + /// Whether to show inlay hints for closure captures. + inlayHints_closureCaptureHints_enable: bool = false, + /// Whether to show inlay type hints for return types of closures. + inlayHints_closureReturnTypeHints_enable: ClosureReturnTypeHintsDef = ClosureReturnTypeHintsDef::Never, + /// Closure notation in type and chaining inlay hints. + inlayHints_closureStyle: ClosureStyle = ClosureStyle::ImplFn, + /// Whether to show enum variant discriminant hints. + inlayHints_discriminantHints_enable: DiscriminantHintsDef = DiscriminantHintsDef::Never, + /// Whether to show inlay hints for type adjustments. + inlayHints_expressionAdjustmentHints_enable: AdjustmentHintsDef = AdjustmentHintsDef::Never, + /// Whether to hide inlay hints for type adjustments outside of `unsafe` blocks. + inlayHints_expressionAdjustmentHints_hideOutsideUnsafe: bool = false, + /// 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= true, + /// Whether to show generic lifetime parameter name inlay hints. + 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. + inlayHints_implicitDrops_enable: bool = false, + /// Whether to show inlay type hints for elided lifetimes in function signatures. + inlayHints_lifetimeElisionHints_enable: LifetimeElisionDef = LifetimeElisionDef::Never, + /// Whether to prefer using parameter names as the name for elided lifetime hints if possible. + inlayHints_lifetimeElisionHints_useParameterNames: bool = false, + /// Maximum length for inlay hints. Set to null to have an unlimited length. + inlayHints_maxLength: Option = Some(25), + /// Whether to show function parameter name inlay hints at the call + /// site. + inlayHints_parameterHints_enable: bool = true, + /// Whether to show exclusive range inlay hints. + inlayHints_rangeExclusiveHints_enable: bool = false, + /// Whether to show inlay hints for compiler inserted reborrows. + /// This setting is deprecated in favor of #rust-analyzer.inlayHints.expressionAdjustmentHints.enable#. + inlayHints_reborrowHints_enable: ReborrowHintsDef = ReborrowHintsDef::Never, + /// Whether to render leading colons for type hints, and trailing colons for parameter hints. + inlayHints_renderColons: bool = true, + /// Whether to show inlay type hints for variables. + inlayHints_typeHints_enable: bool = true, + /// Whether to hide inlay type hints for `let` statements that initialize to a closure. + /// Only applies to closures with blocks, same as `#rust-analyzer.inlayHints.closureReturnTypeHints.enable#`. + inlayHints_typeHints_hideClosureInitialization: bool = false, + /// Whether to hide inlay type hints for constructors. + inlayHints_typeHints_hideNamedConstructor: bool = false, + + /// Enables the experimental support for interpreting tests. + interpret_tests: bool = false, + + /// Join lines merges consecutive declaration and initialization of an assignment. + joinLines_joinAssignments: bool = true, + /// Join lines inserts else between consecutive ifs. + joinLines_joinElseIf: bool = true, + /// Join lines removes trailing commas. + joinLines_removeTrailingComma: bool = true, + /// Join lines unwraps trivial blocks. + joinLines_unwrapTrivialBlock: bool = true, + + /// Whether to show `Debug` lens. Only applies when + /// `#rust-analyzer.lens.enable#` is set. + lens_debug_enable: bool = true, + /// Whether to show CodeLens in Rust files. + lens_enable: bool = true, + /// Whether to show `Implementations` lens. Only applies when + /// `#rust-analyzer.lens.enable#` is set. + lens_implementations_enable: bool = true, + /// Where to render annotations. + lens_location: AnnotationLocation = AnnotationLocation::AboveName, + /// Whether to show `References` lens for Struct, Enum, and Union. + /// Only applies when `#rust-analyzer.lens.enable#` is set. + lens_references_adt_enable: bool = false, + /// Whether to show `References` lens for Enum Variants. + /// Only applies when `#rust-analyzer.lens.enable#` is set. + lens_references_enumVariant_enable: bool = false, + /// Whether to show `Method References` lens. Only applies when + /// `#rust-analyzer.lens.enable#` is set. + lens_references_method_enable: bool = false, + /// Whether to show `References` lens for Trait. + /// Only applies when `#rust-analyzer.lens.enable#` is set. + lens_references_trait_enable: bool = false, + /// Whether to show `Run` lens. Only applies when + /// `#rust-analyzer.lens.enable#` is set. + lens_run_enable: bool = true, + + /// Disable project auto-discovery in favor of explicitly specified set + /// of projects. + /// + /// Elements must be paths pointing to `Cargo.toml`, + /// `rust-project.json`, `.rs` files (which will be treated as standalone files) or JSON + /// objects in `rust-project.json` format. + linkedProjects: Vec = vec![], + + /// Number of syntax trees rust-analyzer keeps in memory. Defaults to 128. + lru_capacity: Option = None, + /// Sets the LRU capacity of the specified queries. + lru_query_capacities: FxHashMap, u16> = FxHashMap::default(), + + /// Whether to show `can't find Cargo.toml` error message. + notifications_cargoTomlNotFound: bool = true, + + /// How many worker threads in the main loop. The default `null` means to pick automatically. + numThreads: Option = None, + + /// Expand attribute macros. Requires `#rust-analyzer.procMacro.enable#` to be set. + procMacro_attributes_enable: bool = true, + /// Enable support for procedural macros, implies `#rust-analyzer.cargo.buildScripts.enable#`. + procMacro_enable: bool = true, + /// Internal config, path to proc-macro server executable. + procMacro_server: Option = None, + + /// Exclude imports from find-all-references. + references_excludeImports: bool = false, + + /// Exclude tests from find-all-references. + references_excludeTests: bool = false, + + /// Inject additional highlighting into doc comments. + /// + /// When enabled, rust-analyzer will highlight rust source in doc comments as well as intra + /// doc links. + semanticHighlighting_doc_comment_inject_enable: bool = true, + /// Whether the server is allowed to emit non-standard tokens and modifiers. + semanticHighlighting_nonStandardTokens: bool = true, + /// Use semantic tokens for operators. + /// + /// When disabled, rust-analyzer will emit semantic tokens only for operator tokens when + /// they are tagged with modifiers. + semanticHighlighting_operator_enable: bool = true, + /// Use specialized semantic tokens for operators. + /// + /// When enabled, rust-analyzer will emit special token types for operator tokens instead + /// of the generic `operator` token type. + semanticHighlighting_operator_specialization_enable: bool = false, + /// Use semantic tokens for punctuation. + /// + /// When disabled, rust-analyzer will emit semantic tokens only for punctuation tokens when + /// they are tagged with modifiers or have a special role. + semanticHighlighting_punctuation_enable: bool = false, + /// When enabled, rust-analyzer will emit a punctuation semantic token for the `!` of macro + /// calls. + semanticHighlighting_punctuation_separate_macro_bang: bool = false, + /// Use specialized semantic tokens for punctuation. + /// + /// When enabled, rust-analyzer will emit special token types for punctuation tokens instead + /// of the generic `punctuation` token type. + semanticHighlighting_punctuation_specialization_enable: bool = false, + /// Use semantic tokens for strings. + /// + /// In some editors (e.g. vscode) semantic tokens override other highlighting grammars. + /// By disabling semantic tokens for strings, other grammars can be used to highlight + /// their contents. + semanticHighlighting_strings_enable: bool = true, + + /// Show full signature of the callable. Only shows parameters if disabled. + signatureInfo_detail: SignatureDetail = SignatureDetail::Full, + /// Show documentation. + signatureInfo_documentation_enable: bool = true, + + /// Whether to insert closing angle brackets when typing an opening angle bracket of a generic argument list. + typing_autoClosingAngleBrackets_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 @@ -408,6 +421,57 @@ config_data! { /// Term search fuel in "units of work" for assists (Defaults to 1800). assist_termSearch_fuel: usize = 1800, + + /// Whether to automatically add a semicolon when completing unit-returning functions. + /// + /// In `match` arms it completes a comma instead. + completion_addSemicolonToUnit: bool = true, + /// Toggles the additional completions that automatically add imports when completed. + /// Note that your client must specify the `additionalTextEdits` LSP client capability to truly have this feature enabled. + completion_autoimport_enable: bool = true, + /// Toggles the additional completions that automatically show method calls and field accesses + /// with `self` prefixed to them when inside a method. + completion_autoself_enable: bool = true, + /// Whether to add parenthesis and argument snippets when completing function. + completion_callable_snippets: CallableCompletionDef = CallableCompletionDef::FillArguments, + /// Whether to show full function/method signatures in completion docs. + completion_fullFunctionSignatures_enable: bool = false, + /// Whether to omit deprecated items from autocompletion. By default they are marked as deprecated but not hidden. + completion_hideDeprecated: bool = false, + /// Maximum number of completions to return. If `None`, the limit is infinite. + completion_limit: Option = None, + /// Whether to show postfix snippets like `dbg`, `if`, `not`, etc. + completion_postfix_enable: bool = true, + /// Enables completions of private items and fields that are defined in the current workspace even if they are not visible at the current position. + completion_privateEditable_enable: bool = false, + /// Whether to enable term search based snippets like `Some(foo.bar().baz())`. + completion_termSearch_enable: bool = false, + /// Term search fuel in "units of work" for autocompletion (Defaults to 1000). + completion_termSearch_fuel: usize = 1000, + + /// List of rust-analyzer diagnostics to disable. + diagnostics_disabled: FxHashSet = FxHashSet::default(), + /// Whether to show native rust-analyzer diagnostics. + diagnostics_enable: bool = true, + /// Whether to show experimental rust-analyzer diagnostics that might + /// have more false positives than usual. + diagnostics_experimental_enable: bool = false, + /// Map of prefixes to be substituted when parsing diagnostic file paths. + /// This should be the reverse mapping of what is passed to `rustc` as `--remap-path-prefix`. + diagnostics_remapPrefix: FxHashMap = FxHashMap::default(), + /// Whether to run additional style lints. + diagnostics_styleLints_enable: bool = false, + /// List of warnings that should be displayed with hint severity. + /// + /// The warnings will be indicated by faded text or three dots in code + /// and will not show up in the `Problems Panel`. + diagnostics_warningsAsHint: Vec = vec![], + /// List of warnings that should be displayed with info severity. + /// + /// The warnings will be indicated by a blue squiggly underline in code + /// and a blue icon in the `Problems Panel`. + diagnostics_warningsAsInfo: Vec = vec![], + /// Whether to enforce the import granularity setting for all files. If set to false rust-analyzer will try to keep import styles consistent per file. imports_granularity_enforce: bool = false, /// How imports should be grouped into use statements. @@ -429,303 +493,199 @@ config_data! { config_data! { workspace: struct WorkspaceDefaultConfigData <- WorkspaceConfigInput -> { + /// Pass `--all-targets` to cargo invocation. + cargo_allTargets: bool = true, + /// Automatically refresh project info via `cargo metadata` on + /// `Cargo.toml` or `.cargo/config.toml` changes. + cargo_autoreload: bool = true, + /// Run build scripts (`build.rs`) for more precise code analysis. + cargo_buildScripts_enable: bool = true, + /// Specifies the invocation strategy to use when running the build scripts command. + /// If `per_workspace` is set, the command will be executed for each Rust workspace with the + /// workspace as the working directory. + /// If `once` is set, the command will be executed once with the opened project as the + /// working directory. + /// This config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#` + /// is set. + cargo_buildScripts_invocationStrategy: InvocationStrategy = InvocationStrategy::PerWorkspace, + /// Override the command rust-analyzer uses to run build scripts and + /// build procedural macros. The command is required to output json + /// and should therefore include `--message-format=json` or a similar + /// option. + /// + /// If there are multiple linked projects/workspaces, this command is invoked for + /// each of them, with the working directory being the workspace root + /// (i.e., the folder containing the `Cargo.toml`). This can be overwritten + /// by changing `#rust-analyzer.cargo.buildScripts.invocationStrategy#`. + /// + /// By default, a cargo invocation will be constructed for the configured + /// targets and features, with the following base command line: + /// + /// ```bash + /// cargo check --quiet --workspace --message-format=json --all-targets --keep-going + /// ``` + /// . + cargo_buildScripts_overrideCommand: Option> = None, + /// Rerun proc-macros building/build-scripts running when proc-macro + /// or build-script sources change and are saved. + cargo_buildScripts_rebuildOnSave: bool = true, + /// Use `RUSTC_WRAPPER=rust-analyzer` when running build scripts to + /// avoid checking unnecessary things. + cargo_buildScripts_useRustcWrapper: bool = true, + /// List of cfg options to enable with the given values. + cargo_cfgs: FxHashMap> = { + let mut m = FxHashMap::default(); + m.insert("debug_assertions".to_owned(), None); + m.insert("miri".to_owned(), None); + m + }, + /// Extra arguments that are passed to every cargo invocation. + cargo_extraArgs: Vec = vec![], + /// Extra environment variables that will be set when running cargo, rustc + /// or other commands within the workspace. Useful for setting RUSTFLAGS. + cargo_extraEnv: FxHashMap = FxHashMap::default(), + /// List of features to activate. + /// + /// Set this to `"all"` to pass `--all-features` to cargo. + cargo_features: CargoFeaturesDef = CargoFeaturesDef::Selected(vec![]), + /// Whether to pass `--no-default-features` to cargo. + cargo_noDefaultFeatures: bool = false, + /// Relative path to the sysroot, or "discover" to try to automatically find it via + /// "rustc --print sysroot". + /// + /// Unsetting this disables sysroot loading. + /// + /// This option does not take effect until rust-analyzer is restarted. + cargo_sysroot: Option = Some("discover".to_owned()), + /// Relative path to the sysroot library sources. If left unset, this will default to + /// `{cargo.sysroot}/lib/rustlib/src/rust/library`. + /// + /// This option does not take effect until rust-analyzer is restarted. + cargo_sysrootSrc: Option = None, + /// Compilation target override (target triple). + // FIXME(@poliorcetics): move to multiple targets here too, but this will need more work + // than `checkOnSave_target` + cargo_target: Option = None, + /// Optional path to a rust-analyzer specific target directory. + /// This prevents rust-analyzer's `cargo check` and initial build-script and proc-macro + /// building from locking the `Cargo.lock` at the expense of duplicating build artifacts. + /// + /// Set to `true` to use a subdirectory of the existing target directory or + /// set to a path relative to the workspace to use that path. + cargo_targetDir | rust_analyzerTargetDir: Option = None, - /// Additional arguments to `rustfmt`. - rustfmt_extraArgs: Vec = vec![], - /// Advanced option, fully override the command rust-analyzer uses for - /// formatting. This should be the equivalent of `rustfmt` here, and - /// not that of `cargo fmt`. The file contents will be passed on the - /// standard input and the formatted result will be read from the - /// standard output. - rustfmt_overrideCommand: Option> = None, - /// Enables the use of rustfmt's unstable range formatting command for the - /// `textDocument/rangeFormatting` request. The rustfmt option is unstable and only - /// available on a nightly build. - rustfmt_rangeFormatting_enable: bool = false, - - } -} - -config_data! { - /// Configs that only make sense when they are set by a client. As such they can only be defined - /// by setting them using client's settings (e.g `settings.json` on VS Code). - client: struct ClientDefaultConfigData <- ClientConfigInput -> { - /// Toggles the additional completions that automatically add imports when completed. - /// Note that your client must specify the `additionalTextEdits` LSP client capability to truly have this feature enabled. - completion_autoimport_enable: bool = true, - /// Toggles the additional completions that automatically show method calls and field accesses - /// with `self` prefixed to them when inside a method. - completion_autoself_enable: bool = true, - /// Whether to add parenthesis and argument snippets when completing function. - completion_callable_snippets: CallableCompletionDef = CallableCompletionDef::FillArguments, - /// Whether to show full function/method signatures in completion docs. - completion_fullFunctionSignatures_enable: bool = false, - /// Maximum number of completions to return. If `None`, the limit is infinite. - completion_limit: Option = None, - /// Whether to show postfix snippets like `dbg`, `if`, `not`, etc. - completion_postfix_enable: bool = true, - /// Enables completions of private items and fields that are defined in the current workspace even if they are not visible at the current position. - completion_privateEditable_enable: bool = false, - /// Custom completion snippets. - completion_snippets_custom: FxHashMap = serde_json::from_str(r#"{ - "Arc::new": { - "postfix": "arc", - "body": "Arc::new(${receiver})", - "requires": "std::sync::Arc", - "description": "Put the expression into an `Arc`", - "scope": "expr" - }, - "Rc::new": { - "postfix": "rc", - "body": "Rc::new(${receiver})", - "requires": "std::rc::Rc", - "description": "Put the expression into an `Rc`", - "scope": "expr" - }, - "Box::pin": { - "postfix": "pinbox", - "body": "Box::pin(${receiver})", - "requires": "std::boxed::Box", - "description": "Put the expression into a pinned `Box`", - "scope": "expr" - }, - "Ok": { - "postfix": "ok", - "body": "Ok(${receiver})", - "description": "Wrap the expression in a `Result::Ok`", - "scope": "expr" - }, - "Err": { - "postfix": "err", - "body": "Err(${receiver})", - "description": "Wrap the expression in a `Result::Err`", - "scope": "expr" - }, - "Some": { - "postfix": "some", - "body": "Some(${receiver})", - "description": "Wrap the expression in an `Option::Some`", - "scope": "expr" - } - }"#).unwrap(), - /// Whether to enable term search based snippets like `Some(foo.bar().baz())`. - completion_termSearch_enable: bool = false, - /// Term search fuel in "units of work" for autocompletion (Defaults to 1000). - completion_termSearch_fuel: usize = 1000, - - /// Controls file watching implementation. - files_watcher: FilesWatcherDef = FilesWatcherDef::Client, - - /// Enables highlighting of related references while the cursor is on `break`, `loop`, `while`, or `for` keywords. - highlightRelated_breakPoints_enable: bool = true, - /// Enables highlighting of all captures of a closure while the cursor is on the `|` or move keyword of a closure. - highlightRelated_closureCaptures_enable: bool = true, - /// Enables highlighting of all exit points while the cursor is on any `return`, `?`, `fn`, or return type arrow (`->`). - highlightRelated_exitPoints_enable: bool = true, - /// Enables highlighting of related references while the cursor is on any identifier. - highlightRelated_references_enable: bool = true, - /// Enables highlighting of all break points for a loop or block context while the cursor is on any `async` or `await` keywords. - highlightRelated_yieldPoints_enable: bool = true, - - /// Whether to show `Debug` action. Only applies when - /// `#rust-analyzer.hover.actions.enable#` is set. - hover_actions_debug_enable: bool = true, - /// Whether to show HoverActions in Rust files. - hover_actions_enable: bool = true, - /// Whether to show `Go to Type Definition` action. Only applies when - /// `#rust-analyzer.hover.actions.enable#` is set. - hover_actions_gotoTypeDef_enable: bool = true, - /// Whether to show `Implementations` action. Only applies when - /// `#rust-analyzer.hover.actions.enable#` is set. - hover_actions_implementations_enable: bool = true, - /// Whether to show `References` action. Only applies when - /// `#rust-analyzer.hover.actions.enable#` is set. - hover_actions_references_enable: bool = false, - /// Whether to show `Run` action. Only applies when - /// `#rust-analyzer.hover.actions.enable#` is set. - hover_actions_run_enable: bool = true, - - /// Whether to show documentation on hover. - hover_documentation_enable: bool = true, - /// Whether to show keyword hover popups. Only applies when - /// `#rust-analyzer.hover.documentation.enable#` is set. - hover_documentation_keywords_enable: bool = true, - /// Use markdown syntax for links on hover. - hover_links_enable: bool = true, - /// How to render the align information in a memory layout hover. - hover_memoryLayout_alignment: Option = Some(MemoryLayoutHoverRenderKindDef::Hexadecimal), - /// Whether to show memory layout data on hover. - hover_memoryLayout_enable: bool = true, - /// How to render the niche information in a memory layout hover. - hover_memoryLayout_niches: Option = Some(false), - /// How to render the offset information in a memory layout hover. - hover_memoryLayout_offset: Option = Some(MemoryLayoutHoverRenderKindDef::Hexadecimal), - /// How to render the size information in a memory layout hover. - hover_memoryLayout_size: Option = Some(MemoryLayoutHoverRenderKindDef::Both), - - /// How many variants of an enum to display when hovering on. Show none if empty. - hover_show_enumVariants: Option = Some(5), - /// How many fields of a struct, variant or union to display when hovering on. Show none if empty. - hover_show_fields: Option = Some(5), - /// How many associated items of a trait to display when hovering a trait. - hover_show_traitAssocItems: Option = None, - - /// Whether to show inlay type hints for binding modes. - inlayHints_bindingModeHints_enable: bool = false, - /// Whether to show inlay type hints for method chains. - inlayHints_chainingHints_enable: bool = true, - /// Whether to show inlay hints after a closing `}` to indicate what item it belongs to. - inlayHints_closingBraceHints_enable: bool = true, - /// Minimum number of lines required before the `}` until the hint is shown (set to 0 or 1 - /// to always show them). - inlayHints_closingBraceHints_minLines: usize = 25, - /// Whether to show inlay hints for closure captures. - inlayHints_closureCaptureHints_enable: bool = false, - /// Whether to show inlay type hints for return types of closures. - inlayHints_closureReturnTypeHints_enable: ClosureReturnTypeHintsDef = ClosureReturnTypeHintsDef::Never, - /// Closure notation in type and chaining inlay hints. - inlayHints_closureStyle: ClosureStyle = ClosureStyle::ImplFn, - /// Whether to show enum variant discriminant hints. - inlayHints_discriminantHints_enable: DiscriminantHintsDef = DiscriminantHintsDef::Never, - /// Whether to show inlay hints for type adjustments. - inlayHints_expressionAdjustmentHints_enable: AdjustmentHintsDef = AdjustmentHintsDef::Never, - /// Whether to hide inlay hints for type adjustments outside of `unsafe` blocks. - inlayHints_expressionAdjustmentHints_hideOutsideUnsafe: bool = false, - /// 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= true, - /// Whether to show generic lifetime parameter name inlay hints. - 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. - inlayHints_implicitDrops_enable: bool = false, - /// Whether to show inlay type hints for elided lifetimes in function signatures. - inlayHints_lifetimeElisionHints_enable: LifetimeElisionDef = LifetimeElisionDef::Never, - /// Whether to prefer using parameter names as the name for elided lifetime hints if possible. - inlayHints_lifetimeElisionHints_useParameterNames: bool = false, - /// Maximum length for inlay hints. Set to null to have an unlimited length. - inlayHints_maxLength: Option = Some(25), - /// Whether to show function parameter name inlay hints at the call - /// site. - inlayHints_parameterHints_enable: bool = true, - /// Whether to show exclusive range inlay hints. - inlayHints_rangeExclusiveHints_enable: bool = false, - /// Whether to show inlay hints for compiler inserted reborrows. - /// This setting is deprecated in favor of #rust-analyzer.inlayHints.expressionAdjustmentHints.enable#. - inlayHints_reborrowHints_enable: ReborrowHintsDef = ReborrowHintsDef::Never, - /// Whether to render leading colons for type hints, and trailing colons for parameter hints. - inlayHints_renderColons: bool = true, - /// Whether to show inlay type hints for variables. - inlayHints_typeHints_enable: bool = true, - /// Whether to hide inlay type hints for `let` statements that initialize to a closure. - /// Only applies to closures with blocks, same as `#rust-analyzer.inlayHints.closureReturnTypeHints.enable#`. - inlayHints_typeHints_hideClosureInitialization: bool = false, - /// Whether to hide inlay type hints for constructors. - inlayHints_typeHints_hideNamedConstructor: bool = false, - - /// Enables the experimental support for interpreting tests. - interpret_tests: bool = false, - - /// Join lines merges consecutive declaration and initialization of an assignment. - joinLines_joinAssignments: bool = true, - /// Join lines inserts else between consecutive ifs. - joinLines_joinElseIf: bool = true, - /// Join lines removes trailing commas. - joinLines_removeTrailingComma: bool = true, - /// Join lines unwraps trivial blocks. - joinLines_unwrapTrivialBlock: bool = true, - - /// Whether to show `Debug` lens. Only applies when - /// `#rust-analyzer.lens.enable#` is set. - lens_debug_enable: bool = true, - /// Whether to show CodeLens in Rust files. - lens_enable: bool = true, - /// Whether to show `Implementations` lens. Only applies when - /// `#rust-analyzer.lens.enable#` is set. - lens_implementations_enable: bool = true, - /// Where to render annotations. - lens_location: AnnotationLocation = AnnotationLocation::AboveName, - /// Whether to show `References` lens for Struct, Enum, and Union. - /// Only applies when `#rust-analyzer.lens.enable#` is set. - lens_references_adt_enable: bool = false, - /// Whether to show `References` lens for Enum Variants. - /// Only applies when `#rust-analyzer.lens.enable#` is set. - lens_references_enumVariant_enable: bool = false, - /// Whether to show `Method References` lens. Only applies when - /// `#rust-analyzer.lens.enable#` is set. - lens_references_method_enable: bool = false, - /// Whether to show `References` lens for Trait. - /// Only applies when `#rust-analyzer.lens.enable#` is set. - lens_references_trait_enable: bool = false, - /// Whether to show `Run` lens. Only applies when - /// `#rust-analyzer.lens.enable#` is set. - lens_run_enable: bool = true, - - /// Whether to show `can't find Cargo.toml` error message. - notifications_cargoTomlNotFound: bool = true, - - /// How many worker threads in the main loop. The default `null` means to pick automatically. - numThreads: Option = None, - - /// Expand attribute macros. Requires `#rust-analyzer.procMacro.enable#` to be set. - procMacro_attributes_enable: bool = true, - /// Enable support for procedural macros, implies `#rust-analyzer.cargo.buildScripts.enable#`. - procMacro_enable: bool = true, - /// Internal config, path to proc-macro server executable. - procMacro_server: Option = None, - - /// Exclude imports from find-all-references. - references_excludeImports: bool = false, + /// Run the check command for diagnostics on save. + checkOnSave | checkOnSave_enable: bool = true, - /// Exclude tests from find-all-references. - references_excludeTests: bool = false, - /// Inject additional highlighting into doc comments. + /// Check all targets and tests (`--all-targets`). Defaults to + /// `#rust-analyzer.cargo.allTargets#`. + check_allTargets | checkOnSave_allTargets: Option = None, + /// Cargo command to use for `cargo check`. + check_command | checkOnSave_command: String = "check".to_owned(), + /// Extra arguments for `cargo check`. + check_extraArgs | checkOnSave_extraArgs: Vec = vec![], + /// Extra environment variables that will be set when running `cargo check`. + /// Extends `#rust-analyzer.cargo.extraEnv#`. + check_extraEnv | checkOnSave_extraEnv: FxHashMap = FxHashMap::default(), + /// List of features to activate. Defaults to + /// `#rust-analyzer.cargo.features#`. /// - /// When enabled, rust-analyzer will highlight rust source in doc comments as well as intra - /// doc links. - semanticHighlighting_doc_comment_inject_enable: bool = true, - /// Whether the server is allowed to emit non-standard tokens and modifiers. - semanticHighlighting_nonStandardTokens: bool = true, - /// Use semantic tokens for operators. + /// Set to `"all"` to pass `--all-features` to Cargo. + check_features | checkOnSave_features: Option = None, + /// List of `cargo check` (or other command specified in `check.command`) diagnostics to ignore. /// - /// When disabled, rust-analyzer will emit semantic tokens only for operator tokens when - /// they are tagged with modifiers. - semanticHighlighting_operator_enable: bool = true, - /// Use specialized semantic tokens for operators. + /// For example for `cargo check`: `dead_code`, `unused_imports`, `unused_variables`,... + check_ignore: FxHashSet = FxHashSet::default(), + /// Specifies the invocation strategy to use when running the check command. + /// If `per_workspace` is set, the command will be executed for each workspace. + /// If `once` is set, the command will be executed once. + /// This config only has an effect when `#rust-analyzer.check.overrideCommand#` + /// is set. + check_invocationStrategy | checkOnSave_invocationStrategy: InvocationStrategy = InvocationStrategy::PerWorkspace, + /// Whether to pass `--no-default-features` to Cargo. Defaults to + /// `#rust-analyzer.cargo.noDefaultFeatures#`. + check_noDefaultFeatures | checkOnSave_noDefaultFeatures: Option = None, + /// Override the command rust-analyzer uses instead of `cargo check` for + /// diagnostics on save. The command is required to output json and + /// should therefore include `--message-format=json` or a similar option + /// (if your client supports the `colorDiagnosticOutput` experimental + /// capability, you can use `--message-format=json-diagnostic-rendered-ansi`). /// - /// When enabled, rust-analyzer will emit special token types for operator tokens instead - /// of the generic `operator` token type. - semanticHighlighting_operator_specialization_enable: bool = false, - /// Use semantic tokens for punctuation. + /// If you're changing this because you're using some tool wrapping + /// Cargo, you might also want to change + /// `#rust-analyzer.cargo.buildScripts.overrideCommand#`. /// - /// When disabled, rust-analyzer will emit semantic tokens only for punctuation tokens when - /// they are tagged with modifiers or have a special role. - semanticHighlighting_punctuation_enable: bool = false, - /// When enabled, rust-analyzer will emit a punctuation semantic token for the `!` of macro - /// calls. - semanticHighlighting_punctuation_separate_macro_bang: bool = false, - /// Use specialized semantic tokens for punctuation. + /// If there are multiple linked projects/workspaces, this command is invoked for + /// each of them, with the working directory being the workspace root + /// (i.e., the folder containing the `Cargo.toml`). This can be overwritten + /// by changing `#rust-analyzer.check.invocationStrategy#`. /// - /// When enabled, rust-analyzer will emit special token types for punctuation tokens instead - /// of the generic `punctuation` token type. - semanticHighlighting_punctuation_specialization_enable: bool = false, - /// Use semantic tokens for strings. + /// If `$saved_file` is part of the command, rust-analyzer will pass + /// the absolute path of the saved file to the provided command. This is + /// intended to be used with non-Cargo build systems. + /// Note that `$saved_file` is experimental and may be removed in the future. /// - /// In some editors (e.g. vscode) semantic tokens override other highlighting grammars. - /// By disabling semantic tokens for strings, other grammars can be used to highlight - /// their contents. - semanticHighlighting_strings_enable: bool = true, + /// An example command would be: + /// + /// ```bash + /// cargo check --workspace --message-format=json --all-targets + /// ``` + /// . + check_overrideCommand | checkOnSave_overrideCommand: Option> = None, + /// Check for specific targets. Defaults to `#rust-analyzer.cargo.target#` if empty. + /// + /// Can be a single target, e.g. `"x86_64-unknown-linux-gnu"` or a list of targets, e.g. + /// `["aarch64-apple-darwin", "x86_64-apple-darwin"]`. + /// + /// Aliased as `"checkOnSave.targets"`. + check_targets | checkOnSave_targets | checkOnSave_target: Option = None, + /// Whether `--workspace` should be passed to `cargo check`. + /// If false, `-p ` will be passed instead. + check_workspace: bool = true, - /// Show full signature of the callable. Only shows parameters if disabled. - signatureInfo_detail: SignatureDetail = SignatureDetail::Full, - /// Show documentation. - signatureInfo_documentation_enable: bool = true, + /// These proc-macros will be ignored when trying to expand them. + /// + /// This config takes a map of crate names with the exported proc-macro names to ignore as values. + procMacro_ignored: FxHashMap, Box<[Box]>> = FxHashMap::default(), + + /// Command to be executed instead of 'cargo' for runnables. + runnables_command: Option = None, + /// Additional arguments to be passed to cargo for runnables such as + /// tests or binaries. For example, it may be `--release`. + runnables_extraArgs: Vec = vec![], + /// Additional arguments to be passed through Cargo to launched tests, benchmarks, or + /// doc-tests. + /// + /// Unless the launched target uses a + /// [custom test harness](https://doc.rust-lang.org/cargo/reference/cargo-targets.html#the-harness-field), + /// they will end up being interpreted as options to + /// [`rustc`’s built-in test harness (“libtest”)](https://doc.rust-lang.org/rustc/tests/index.html#cli-arguments). + runnables_extraTestBinaryArgs: Vec = vec!["--show-output".to_owned()], + + /// Path to the Cargo.toml of the rust compiler workspace, for usage in rustc_private + /// projects, or "discover" to try to automatically find it if the `rustc-dev` component + /// is installed. + /// + /// Any project which uses rust-analyzer with the rustcPrivate + /// crates must set `[package.metadata.rust-analyzer] rustc_private=true` to use it. + /// + /// This option does not take effect until rust-analyzer is restarted. + rustc_source: Option = None, + + /// Additional arguments to `rustfmt`. + rustfmt_extraArgs: Vec = vec![], + /// Advanced option, fully override the command rust-analyzer uses for + /// formatting. This should be the equivalent of `rustfmt` here, and + /// not that of `cargo fmt`. The file contents will be passed on the + /// standard input and the formatted result will be read from the + /// standard output. + rustfmt_overrideCommand: Option> = None, + /// Enables the use of rustfmt's unstable range formatting command for the + /// `textDocument/rangeFormatting` request. The rustfmt option is unstable and only + /// available on a nightly build. + rustfmt_rangeFormatting_enable: bool = false, - /// Whether to insert closing angle brackets when typing an opening angle bracket of a generic argument list. - typing_autoClosingAngleBrackets_enable: bool = false, /// Workspace symbol search kind. workspace_symbol_search_kind: WorkspaceSymbolSearchKindDef = WorkspaceSymbolSearchKindDef::OnlyTypes, @@ -735,6 +695,19 @@ config_data! { workspace_symbol_search_limit: usize = 128, /// Workspace symbol search scope. workspace_symbol_search_scope: WorkspaceSymbolSearchScopeDef = WorkspaceSymbolSearchScopeDef::Workspace, + + } +} + +config_data! { + /// Configs that only make sense when they are set by a client. As such they can only be defined + /// by setting them using client's settings (e.g `settings.json` on VS Code). + client: struct ClientDefaultConfigData <- ClientConfigInput -> { + + /// Controls file watching implementation. + files_watcher: FilesWatcherDef = FilesWatcherDef::Client, + + } } @@ -753,7 +726,13 @@ enum RatomlFile { #[derive(Debug, Clone)] pub struct Config { - discovered_projects: Vec, + /// Projects that have a Cargo.toml or a rust-project.json in a + /// parent directory, so we can discover them by walking the + /// file system. + discovered_projects_from_filesystem: Vec, + /// Projects whose configuration was generated by a command + /// configured in discoverConfig. + discovered_projects_from_command: Vec, /// The workspace roots as registered by the LSP client workspace_roots: Vec, caps: ClientCapabilities, @@ -1016,7 +995,7 @@ impl Config { config.source_root_parent_map = source_root_map; } - if config.check_command().is_empty() { + 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"), @@ -1046,19 +1025,19 @@ impl Config { (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), - } + pub fn add_discovered_project_from_command( + &mut self, + data: ProjectJsonData, + buildfile: AbsPathBuf, + ) { + for proj in self.discovered_projects_from_command.iter_mut() { + if proj.buildfile == buildfile { + proj.data = data; + return; } - None => *linked_projects = Some(vec![new_project]), } + + self.discovered_projects_from_command.push(ProjectJsonFromCommand { data, buildfile }); } } @@ -1251,7 +1230,7 @@ pub struct NotificationsConfig { pub cargo_toml_not_found: bool, } -#[derive(Debug, Clone)] +#[derive(Deserialize, Serialize, Debug, Clone)] pub enum RustfmtConfig { Rustfmt { extra_args: Vec, enable_range_formatting: bool }, CustomCommand { command: String, args: Vec }, @@ -1336,7 +1315,8 @@ impl Config { Config { caps: ClientCapabilities::new(caps), - discovered_projects: Vec::new(), + discovered_projects_from_filesystem: Vec::new(), + discovered_projects_from_command: Vec::new(), root_path, snippets: Default::default(), workspace_roots, @@ -1357,7 +1337,7 @@ impl Config { if discovered.is_empty() { tracing::error!("failed to find any projects in {:?}", &self.workspace_roots); } - self.discovered_projects = discovered; + self.discovered_projects_from_filesystem = discovered; } pub fn remove_workspace(&mut self, path: &AbsPath) { @@ -1412,29 +1392,36 @@ impl Config { pub fn completion(&self, source_root: Option) -> CompletionConfig { CompletionConfig { - enable_postfix_completions: self.completion_postfix_enable().to_owned(), - enable_imports_on_the_fly: self.completion_autoimport_enable().to_owned() + enable_postfix_completions: self.completion_postfix_enable(source_root).to_owned(), + enable_imports_on_the_fly: self.completion_autoimport_enable(source_root).to_owned() && self.caps.completion_item_edit_resolve(), - enable_self_on_the_fly: self.completion_autoself_enable().to_owned(), - enable_private_editable: self.completion_privateEditable_enable().to_owned(), - full_function_signatures: self.completion_fullFunctionSignatures_enable().to_owned(), - callable: match self.completion_callable_snippets() { + enable_self_on_the_fly: self.completion_autoself_enable(source_root).to_owned(), + enable_private_editable: self.completion_privateEditable_enable(source_root).to_owned(), + full_function_signatures: self + .completion_fullFunctionSignatures_enable(source_root) + .to_owned(), + callable: match self.completion_callable_snippets(source_root) { CallableCompletionDef::FillArguments => Some(CallableSnippets::FillArguments), CallableCompletionDef::AddParentheses => Some(CallableSnippets::AddParentheses), CallableCompletionDef::None => None, }, + add_semicolon_to_unit: *self.completion_addSemicolonToUnit(source_root), snippet_cap: SnippetCap::new(self.completion_snippet()), insert_use: self.insert_use_config(source_root), 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(), snippets: self.snippets.clone().to_vec(), - limit: self.completion_limit().to_owned(), - enable_term_search: self.completion_termSearch_enable().to_owned(), - term_search_fuel: self.completion_termSearch_fuel().to_owned() as u64, + limit: self.completion_limit(source_root).to_owned(), + enable_term_search: self.completion_termSearch_enable(source_root).to_owned(), + term_search_fuel: self.completion_termSearch_fuel(source_root).to_owned() as u64, } } + pub fn completion_hide_deprecated(&self) -> bool { + *self.completion_hideDeprecated(None) + } + pub fn detached_files(&self) -> &Vec { // FIXME @alibektas : This is the only config that is confusing. If it's a proper configuration // why is it not among the others? If it's client only which I doubt it is current state should be alright @@ -1443,11 +1430,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, @@ -1457,7 +1444,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(), } @@ -1673,78 +1660,95 @@ impl Config { self.workspace_discoverConfig().as_ref() } - pub fn linked_or_discovered_projects(&self) -> Vec { - match self.linkedProjects().as_slice() { - [] => { - let exclude_dirs: Vec<_> = - self.files_excludeDirs().iter().map(|p| self.root_path.join(p)).collect(); - self.discovered_projects - .iter() - .filter(|project| { - !exclude_dirs.iter().any(|p| project.manifest_path().starts_with(p)) - }) - .cloned() - .map(LinkedProject::from) - .collect() + fn discovered_projects(&self) -> Vec { + let exclude_dirs: Vec<_> = + self.files_excludeDirs().iter().map(|p| self.root_path.join(p)).collect(); + + let mut projects = vec![]; + for fs_proj in &self.discovered_projects_from_filesystem { + let manifest_path = fs_proj.manifest_path(); + if exclude_dirs.iter().any(|p| manifest_path.starts_with(p)) { + continue; } - linked_projects => linked_projects - .iter() - .filter_map(|linked_project| match linked_project { - ManifestOrProjectJson::Manifest(it) => { - let path = self.root_path.join(it); - ProjectManifest::from_manifest_file(path) - .map_err(|e| tracing::error!("failed to load linked project: {}", e)) - .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()) - } - }) - .collect(), + let buf: Utf8PathBuf = manifest_path.to_path_buf().into(); + projects.push(ManifestOrProjectJson::Manifest(buf)); + } + + for dis_proj in &self.discovered_projects_from_command { + projects.push(ManifestOrProjectJson::DiscoveredProjectJson { + data: dis_proj.data.clone(), + buildfile: dis_proj.buildfile.clone(), + }); } + + projects + } + + pub fn linked_or_discovered_projects(&self) -> Vec { + let linked_projects = self.linkedProjects(); + let projects = if linked_projects.is_empty() { + self.discovered_projects() + } else { + linked_projects.clone() + }; + + projects + .iter() + .filter_map(|linked_project| match linked_project { + ManifestOrProjectJson::Manifest(it) => { + let path = self.root_path.join(it); + ProjectManifest::from_manifest_file(path) + .map_err(|e| tracing::error!("failed to load linked project: {}", e)) + .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()) + } + }) + .collect() } pub fn prefill_caches(&self) -> bool { self.cachePriming_enable().to_owned() } - pub fn publish_diagnostics(&self) -> bool { - self.diagnostics_enable().to_owned() + pub fn publish_diagnostics(&self, source_root: Option) -> bool { + self.diagnostics_enable(source_root).to_owned() } - pub fn diagnostics_map(&self) -> DiagnosticsMapConfig { + pub fn diagnostics_map(&self, source_root: Option) -> 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(source_root).clone(), + warnings_as_info: self.diagnostics_warningsAsInfo(source_root).clone(), + warnings_as_hint: self.diagnostics_warningsAsHint(source_root).clone(), + check_ignore: self.check_ignore(source_root).clone(), } } - pub fn extra_args(&self) -> &Vec { - self.cargo_extraArgs() + pub fn extra_args(&self, source_root: Option) -> &Vec { + self.cargo_extraArgs(source_root) } - pub fn extra_env(&self) -> &FxHashMap { - self.cargo_extraEnv() + pub fn extra_env(&self, source_root: Option) -> &FxHashMap { + self.cargo_extraEnv(source_root) } - pub fn check_extra_args(&self) -> Vec { - let mut extra_args = self.extra_args().clone(); - extra_args.extend_from_slice(self.check_extraArgs()); + pub fn check_extra_args(&self, source_root: Option) -> Vec { + let mut extra_args = self.extra_args(source_root).clone(); + extra_args.extend_from_slice(self.check_extraArgs(source_root)); extra_args } - pub fn check_extra_env(&self) -> FxHashMap { - let mut extra_env = self.cargo_extraEnv().clone(); - extra_env.extend(self.check_extraEnv().clone()); + pub fn check_extra_env(&self, source_root: Option) -> FxHashMap { + let mut extra_env = self.cargo_extraEnv(source_root).clone(); + extra_env.extend(self.check_extraEnv(source_root).clone()); extra_env } @@ -1761,8 +1765,11 @@ impl Config { Some(AbsPathBuf::try_from(path).unwrap_or_else(|path| self.root_path.join(path))) } - pub fn ignored_proc_macros(&self) -> &FxHashMap, Box<[Box]>> { - self.procMacro_ignored() + pub fn ignored_proc_macros( + &self, + source_root: Option, + ) -> &FxHashMap, Box<[Box]>> { + self.procMacro_ignored(source_root) } pub fn expand_proc_macros(&self) -> bool { @@ -1787,23 +1794,23 @@ impl Config { } } - pub fn cargo_autoreload_config(&self) -> bool { - self.cargo_autoreload().to_owned() + pub fn cargo_autoreload_config(&self, source_root: Option) -> bool { + self.cargo_autoreload(source_root).to_owned() } - pub fn run_build_scripts(&self) -> bool { - self.cargo_buildScripts_enable().to_owned() || self.procMacro_enable().to_owned() + pub fn run_build_scripts(&self, source_root: Option) -> bool { + self.cargo_buildScripts_enable(source_root).to_owned() || self.procMacro_enable().to_owned() } - pub fn cargo(&self) -> CargoConfig { - let rustc_source = self.rustc_source().as_ref().map(|rustc_src| { + pub fn cargo(&self, source_root: Option) -> CargoConfig { + let rustc_source = self.rustc_source(source_root).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(source_root).as_ref().map(|sysroot| { if sysroot == "discover" { RustLibSource::Discover } else { @@ -1811,24 +1818,24 @@ impl Config { } }); let sysroot_src = - self.cargo_sysrootSrc().as_ref().map(|sysroot| self.root_path.join(sysroot)); + self.cargo_sysrootSrc(source_root).as_ref().map(|sysroot| self.root_path.join(sysroot)); CargoConfig { - all_targets: *self.cargo_allTargets(), - features: match &self.cargo_features() { + all_targets: *self.cargo_allTargets(source_root), + features: match &self.cargo_features(source_root) { 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(source_root).to_owned(), }, }, - target: self.cargo_target().clone(), + target: self.cargo_target(source_root).clone(), sysroot, sysroot_src, rustc_source, cfg_overrides: project_model::CfgOverrides { global: CfgDiff::new( - self.cargo_cfgs() + self.cargo_cfgs(source_root) .iter() .map(|(key, val)| match val { Some(val) => CfgAtom::KeyValue { @@ -1843,18 +1850,65 @@ 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(source_root), + invocation_strategy: match self.cargo_buildScripts_invocationStrategy(source_root) { InvocationStrategy::Once => project_model::InvocationStrategy::Once, InvocationStrategy::PerWorkspace => project_model::InvocationStrategy::PerWorkspace, }, - run_build_script_command: self.cargo_buildScripts_overrideCommand().clone(), - extra_args: self.cargo_extraArgs().clone(), - extra_env: self.cargo_extraEnv().clone(), - target_dir: self.target_dir_from_config(), + run_build_script_command: self.cargo_buildScripts_overrideCommand(source_root).clone(), + extra_args: self.cargo_extraArgs(source_root).clone(), + extra_env: self.cargo_extraEnv(source_root).clone(), + target_dir: self.target_dir_from_config(source_root), } } + pub(crate) fn completion_snippets_default() -> FxHashMap { + serde_json::from_str( + r#"{ + "Arc::new": { + "postfix": "arc", + "body": "Arc::new(${receiver})", + "requires": "std::sync::Arc", + "description": "Put the expression into an `Arc`", + "scope": "expr" + }, + "Rc::new": { + "postfix": "rc", + "body": "Rc::new(${receiver})", + "requires": "std::rc::Rc", + "description": "Put the expression into an `Rc`", + "scope": "expr" + }, + "Box::pin": { + "postfix": "pinbox", + "body": "Box::pin(${receiver})", + "requires": "std::boxed::Box", + "description": "Put the expression into a pinned `Box`", + "scope": "expr" + }, + "Ok": { + "postfix": "ok", + "body": "Ok(${receiver})", + "description": "Wrap the expression in a `Result::Ok`", + "scope": "expr" + }, + "Err": { + "postfix": "err", + "body": "Err(${receiver})", + "description": "Wrap the expression in a `Result::Err`", + "scope": "expr" + }, + "Some": { + "postfix": "some", + "body": "Some(${receiver})", + "description": "Wrap the expression in an `Option::Some`", + "scope": "expr" + } + }"#, + ) + .unwrap() + } + pub fn rustfmt(&self, source_root_id: Option) -> RustfmtConfig { match &self.rustfmt_overrideCommand(source_root_id) { Some(args) if !args.is_empty() => { @@ -1869,37 +1923,37 @@ impl Config { } } - pub fn flycheck_workspace(&self) -> bool { - *self.check_workspace() + pub fn flycheck_workspace(&self, source_root: Option) -> bool { + *self.check_workspace(source_root) } - pub(crate) fn cargo_test_options(&self) -> CargoOptions { + pub(crate) fn cargo_test_options(&self, source_root: Option) -> CargoOptions { CargoOptions { - target_triples: self.cargo_target().clone().into_iter().collect(), + target_triples: self.cargo_target(source_root).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(source_root), + all_features: matches!(self.cargo_features(source_root), CargoFeaturesDef::All), + features: match self.cargo_features(source_root).clone() { CargoFeaturesDef::All => vec![], CargoFeaturesDef::Selected(it) => it, }, - extra_args: self.extra_args().clone(), - extra_test_bin_args: self.runnables_extraTestBinaryArgs().clone(), - extra_env: self.extra_env().clone(), - target_dir: self.target_dir_from_config(), + extra_args: self.extra_args(source_root).clone(), + extra_test_bin_args: self.runnables_extraTestBinaryArgs(source_root).clone(), + extra_env: self.extra_env(source_root).clone(), + target_dir: self.target_dir_from_config(source_root), } } - pub(crate) fn flycheck(&self) -> FlycheckConfig { - match &self.check_overrideCommand() { + pub(crate) fn flycheck(&self, source_root: Option) -> FlycheckConfig { + match &self.check_overrideCommand(source_root) { Some(args) if !args.is_empty() => { let mut args = args.clone(); let command = args.remove(0); FlycheckConfig::CustomCommand { command, args, - extra_env: self.check_extra_env(), - invocation_strategy: match self.check_invocationStrategy() { + extra_env: self.check_extra_env(source_root), + invocation_strategy: match self.check_invocationStrategy(source_root) { InvocationStrategy::Once => crate::flycheck::InvocationStrategy::Once, InvocationStrategy::PerWorkspace => { crate::flycheck::InvocationStrategy::PerWorkspace @@ -1908,44 +1962,50 @@ impl Config { } } Some(_) | None => FlycheckConfig::CargoCommand { - command: self.check_command().clone(), + command: self.check_command(source_root).clone(), options: CargoOptions { target_triples: self - .check_targets() + .check_targets(source_root) .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(source_root).clone().into_iter().collect() + }), + all_targets: self + .check_allTargets(source_root) + .unwrap_or(*self.cargo_allTargets(source_root)), no_default_features: self - .check_noDefaultFeatures() - .unwrap_or(*self.cargo_noDefaultFeatures()), + .check_noDefaultFeatures(source_root) + .unwrap_or(*self.cargo_noDefaultFeatures(source_root)), all_features: matches!( - self.check_features().as_ref().unwrap_or(self.cargo_features()), + self.check_features(source_root) + .as_ref() + .unwrap_or(self.cargo_features(source_root)), CargoFeaturesDef::All ), features: match self - .check_features() + .check_features(source_root) .clone() - .unwrap_or_else(|| self.cargo_features().clone()) + .unwrap_or_else(|| self.cargo_features(source_root).clone()) { CargoFeaturesDef::All => vec![], CargoFeaturesDef::Selected(it) => it, }, - extra_args: self.check_extra_args(), - extra_test_bin_args: self.runnables_extraTestBinaryArgs().clone(), - extra_env: self.check_extra_env(), - target_dir: self.target_dir_from_config(), + extra_args: self.check_extra_args(source_root), + extra_test_bin_args: self.runnables_extraTestBinaryArgs(source_root).clone(), + extra_env: self.check_extra_env(source_root), + target_dir: self.target_dir_from_config(source_root), }, ansi_color_output: self.color_diagnostic_output(), }, } } - fn target_dir_from_config(&self) -> Option { - self.cargo_targetDir().as_ref().and_then(|target_dir| match target_dir { + fn target_dir_from_config(&self, source_root: Option) -> Option { + self.cargo_targetDir(source_root).as_ref().and_then(|target_dir| match target_dir { TargetDirectory::UseSubdirectory(true) => { Some(Utf8PathBuf::from("target/rust-analyzer")) } @@ -1955,19 +2015,19 @@ impl Config { }) } - pub fn check_on_save(&self) -> bool { - *self.checkOnSave() + pub fn check_on_save(&self, source_root: Option) -> bool { + *self.checkOnSave(source_root) } - pub fn script_rebuild_on_save(&self) -> bool { - *self.cargo_buildScripts_rebuildOnSave() + pub fn script_rebuild_on_save(&self, source_root: Option) -> bool { + *self.cargo_buildScripts_rebuildOnSave(source_root) } - pub fn runnables(&self) -> RunnablesConfig { + pub fn runnables(&self, source_root: Option) -> 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(source_root).clone(), + cargo_extra_args: self.runnables_extraArgs(source_root).clone(), + extra_test_binary_args: self.runnables_extraTestBinaryArgs(source_root).clone(), } } @@ -2006,19 +2066,19 @@ impl Config { } } - pub fn workspace_symbol(&self) -> WorkspaceSymbolConfig { + pub fn workspace_symbol(&self, source_root: Option) -> WorkspaceSymbolConfig { WorkspaceSymbolConfig { - search_scope: match self.workspace_symbol_search_scope() { + search_scope: match self.workspace_symbol_search_scope(source_root) { WorkspaceSymbolSearchScopeDef::Workspace => WorkspaceSymbolSearchScope::Workspace, WorkspaceSymbolSearchScopeDef::WorkspaceAndDependencies => { WorkspaceSymbolSearchScope::WorkspaceAndDependencies } }, - search_kind: match self.workspace_symbol_search_kind() { + search_kind: match self.workspace_symbol_search_kind(source_root) { WorkspaceSymbolSearchKindDef::OnlyTypes => WorkspaceSymbolSearchKind::OnlyTypes, WorkspaceSymbolSearchKindDef::AllSymbols => WorkspaceSymbolSearchKind::AllSymbols, }, - search_limit: *self.workspace_symbol_search_limit(), + search_limit: *self.workspace_symbol_search_limit(source_root), } } @@ -2259,18 +2319,6 @@ where 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)] #[serde(rename_all = "snake_case")] enum ExprFillDefaultDef { @@ -3522,9 +3570,9 @@ 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()) + matches!(config.flycheck(None), FlycheckConfig::CargoCommand { options, .. } if options.target_dir.is_none()) ); } @@ -3540,9 +3588,9 @@ 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"))) + matches!(config.flycheck(None), FlycheckConfig::CargoCommand { options, .. } if options.target_dir == Some(Utf8PathBuf::from("target/rust-analyzer"))) ); } @@ -3559,83 +3607,11 @@ mod tests { (config, _, _) = config.apply_change(change); assert_eq!( - config.cargo_targetDir(), + config.cargo_targetDir(None), &Some(TargetDirectory::Directory(Utf8PathBuf::from("other_folder"))) ); assert!( - matches!(config.flycheck(), FlycheckConfig::CargoCommand { options, .. } if options.target_dir == Some(Utf8PathBuf::from("other_folder"))) + matches!(config.flycheck(None), FlycheckConfig::CargoCommand { options, .. } if options.target_dir == Some(Utf8PathBuf::from("other_folder"))) ); } - - #[test] - fn toml_unknown_key() { - let config = - Config::new(AbsPathBuf::assert(project_root()), Default::default(), vec![], None); - - let mut change = ConfigChange::default(); - - change.change_user_config(Some( - toml::toml! { - [cargo.cfgs] - these = "these" - should = "should" - be = "be" - valid = "valid" - - [invalid.config] - err = "error" - - [cargo] - target = "ok" - - // FIXME: This should be an error - [cargo.sysroot] - non-table = "expected" - } - .to_string() - .into(), - )); - - let (config, 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); - let mut change = ConfigChange::default(); - - change.change_user_config(Some( - toml::toml! { - [cargo.cfgs] - these = "these" - should = "should" - be = "be" - valid = "valid" - } - .to_string() - .into(), - )); - let (_, e, _) = config.apply_change(change); - expect_test::expect![[r#" - ConfigErrors( - [], - ) - "#]] - .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 9d0082c370c7..89487aa673bf 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 @@ -540,7 +540,7 @@ impl GlobalState { } pub(crate) fn respond(&mut self, response: lsp_server::Response) { - if let Some((method, start)) = self.req_queue.incoming.complete(response.id.clone()) { + if let Some((method, start)) = self.req_queue.incoming.complete(&response.id) { if let Some(err) = &response.error { if err.message.starts_with("server panicked") { self.poke_rust_analyzer_developer(format!("{}, check the log", err.message)) @@ -631,6 +631,10 @@ impl GlobalStateSnapshot { file_id_to_url(&self.vfs_read(), id) } + pub(crate) fn vfs_path_to_file_id(&self, vfs_path: &VfsPath) -> anyhow::Result { + vfs_path_to_file_id(&self.vfs_read(), vfs_path) + } + pub(crate) fn file_line_index(&self, file_id: FileId) -> Cancellable { let endings = self.vfs.read().1[&file_id]; let index = self.analysis.file_line_index(file_id)?; @@ -725,3 +729,9 @@ pub(crate) fn url_to_file_id(vfs: &vfs::Vfs, url: &Url) -> anyhow::Result anyhow::Result { + let res = + vfs.file_id(vfs_path).ok_or_else(|| anyhow::format_err!("file not found: {vfs_path}"))?; + Ok(res) +} diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/dispatch.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/dispatch.rs index f03de8ce0f03..ed7bf27843b5 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/dispatch.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/dispatch.rs @@ -325,14 +325,14 @@ impl NotificationDispatcher<'_> { pub(crate) fn on_sync_mut( &mut self, f: fn(&mut GlobalState, N::Params) -> anyhow::Result<()>, - ) -> anyhow::Result<&mut Self> + ) -> &mut Self where N: lsp_types::notification::Notification, N::Params: DeserializeOwned + Send + Debug, { let not = match self.not.take() { Some(it) => it, - None => return Ok(self), + None => return self, }; let _guard = tracing::info_span!("notification", method = ?not.method).entered(); @@ -344,7 +344,7 @@ impl NotificationDispatcher<'_> { } Err(ExtractError::MethodMismatch(not)) => { self.not = Some(not); - return Ok(self); + return self; } }; @@ -355,8 +355,10 @@ impl NotificationDispatcher<'_> { version(), N::METHOD )); - f(self.global_state, params)?; - Ok(self) + if let Err(e) = f(self.global_state, params) { + tracing::error!(handler = %N::METHOD, error = %e, "notification handler failed"); + } + self } pub(crate) fn finish(&mut self) { 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 38b88ff2d040..49b1ba32a795 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 @@ -145,14 +145,18 @@ pub(crate) fn handle_did_save_text_document( state: &mut GlobalState, params: DidSaveTextDocumentParams, ) -> anyhow::Result<()> { - if state.config.script_rebuild_on_save() && state.build_deps_changed { - state.build_deps_changed = false; - state - .fetch_build_data_queue - .request_op("build_deps_changed - save notification".to_owned(), ()); - } - if let Ok(vfs_path) = from_proto::vfs_path(¶ms.text_document.uri) { + let snap = state.snapshot(); + let file_id = snap.vfs_path_to_file_id(&vfs_path)?; + let sr = snap.analysis.source_root_id(file_id)?; + + if state.config.script_rebuild_on_save(Some(sr)) && state.build_deps_changed { + state.build_deps_changed = false; + state + .fetch_build_data_queue + .request_op("build_deps_changed - save notification".to_owned(), ()); + } + // Re-fetch workspaces if a workspace related file has changed if let Some(path) = vfs_path.as_path() { let additional_files = &state @@ -182,15 +186,16 @@ pub(crate) fn handle_did_save_text_document( } } - if !state.config.check_on_save() || run_flycheck(state, vfs_path) { + if !state.config.check_on_save(Some(sr)) || run_flycheck(state, vfs_path) { return Ok(()); } - } else if state.config.check_on_save() { + } else if state.config.check_on_save(None) { // No specific flycheck was triggered, so let's trigger all of them. for flycheck in state.flycheck.iter() { flycheck.restart_workspace(None); } } + Ok(()) } @@ -288,6 +293,7 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool { let file_id = state.vfs.read().0.file_id(&vfs_path); if let Some(file_id) = file_id { let world = state.snapshot(); + let source_root_id = world.analysis.source_root_id(file_id).ok(); let mut updated = false; let task = move || -> std::result::Result<(), ide::Cancelled> { // Is the target binary? If so we let flycheck run only for the workspace that contains the crate. @@ -351,7 +357,7 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool { .targets .iter() .any(|&it| crate_root_paths.contains(&cargo[it].root.as_path())); - has_target_with_root.then(|| cargo[pkg].name.clone()) + has_target_with_root.then(|| cargo.package_flag(&cargo[pkg])) }), project_model::ProjectWorkspaceKind::Json(project) => { if !project.crates().any(|(_, krate)| { @@ -373,9 +379,9 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool { for (id, package) in workspace_ids.clone() { if id == flycheck.id() { updated = true; - match package - .filter(|_| !world.config.flycheck_workspace() || target.is_some()) - { + match package.filter(|_| { + !world.config.flycheck_workspace(source_root_id) || target.is_some() + }) { Some(package) => flycheck .restart_for_package(package, target.clone().map(TupleExt::head)), None => flycheck.restart_workspace(saved_file.clone()), 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 1ad5ff0c8cdc..bcbd970a0d21 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 @@ -40,7 +40,10 @@ use crate::{ hack_recover_crate_name, line_index::LineEndings, lsp::{ - ext::InternalTestingFetchConfigParams, + ext::{ + InternalTestingFetchConfigOption, InternalTestingFetchConfigParams, + InternalTestingFetchConfigResponse, + }, from_proto, to_proto, utils::{all_edits_are_disjoint, invalid_params_error}, LspError, @@ -256,7 +259,7 @@ pub(crate) fn handle_run_test( let handle = CargoTestHandle::new( test_path, - state.config.cargo_test_options(), + state.config.cargo_test_options(None), cargo.workspace_root(), test_target, state.test_run_sender.clone(), @@ -565,7 +568,7 @@ pub(crate) fn handle_workspace_symbol( ) -> anyhow::Result> { let _p = tracing::info_span!("handle_workspace_symbol").entered(); - let config = snap.config.workspace_symbol(); + let config = snap.config.workspace_symbol(None); let (all_symbols, libs) = decide_search_scope_and_kind(¶ms, &config); let query = { @@ -852,6 +855,7 @@ pub(crate) fn handle_runnables( ) -> anyhow::Result> { let _p = tracing::info_span!("handle_runnables").entered(); let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; + let source_root = snap.analysis.source_root_id(file_id).ok(); let line_index = snap.file_line_index(file_id)?; let offset = params.position.and_then(|it| from_proto::offset(&line_index, it).ok()); let target_spec = TargetSpec::for_file(&snap, file_id)?; @@ -894,7 +898,7 @@ pub(crate) fn handle_runnables( } // Add `cargo check` and `cargo test` for all targets of the whole package - let config = snap.config.runnables(); + let config = snap.config.runnables(source_root); match target_spec { Some(TargetSpec::Cargo(spec)) => { let is_crate_no_std = snap.analysis.is_crate_no_std(spec.crate_id)?; @@ -1602,14 +1606,14 @@ pub(crate) fn handle_inlay_hints_resolve( anyhow::ensure!(snap.file_exists(file_id), "Invalid LSP resolve data"); let line_index = snap.file_line_index(file_id)?; - let hint_position = from_proto::offset(&line_index, original_hint.position)?; + let range = from_proto::text_range(&line_index, resolve_data.resolve_range)?; let mut forced_resolve_inlay_hints_config = snap.config.inlay_hints(); forced_resolve_inlay_hints_config.fields_to_resolve = InlayFieldsToResolve::empty(); let resolve_hints = snap.analysis.inlay_hints_resolve( &forced_resolve_inlay_hints_config, file_id, - hint_position, + range, hash, |hint| { std::hash::BuildHasher::hash_one( @@ -2119,7 +2123,7 @@ fn run_rustfmt( RustfmtConfig::Rustfmt { extra_args, enable_range_formatting } => { // FIXME: Set RUSTUP_TOOLCHAIN let mut cmd = process::Command::new(toolchain::Tool::Rustfmt.path()); - cmd.envs(snap.config.extra_env()); + cmd.envs(snap.config.extra_env(source_root_id)); cmd.args(extra_args); if let Some(edition) = edition { @@ -2177,7 +2181,7 @@ fn run_rustfmt( _ => process::Command::new(cmd), }; - cmd.envs(snap.config.extra_env()); + cmd.envs(snap.config.extra_env(source_root_id)); cmd.args(args); cmd } @@ -2291,7 +2295,7 @@ pub(crate) fn fetch_dependency_list( pub(crate) fn internal_testing_fetch_config( state: GlobalStateSnapshot, params: InternalTestingFetchConfigParams, -) -> anyhow::Result { +) -> anyhow::Result> { let source_root = params .text_document .map(|it| { @@ -2301,15 +2305,18 @@ pub(crate) fn internal_testing_fetch_config( .map_err(anyhow::Error::from) }) .transpose()?; - serde_json::to_value(match &*params.config { - "local" => state.config.assist(source_root).assist_emit_must_use, - "workspace" => matches!( - state.config.rustfmt(source_root), - RustfmtConfig::Rustfmt { enable_range_formatting: true, .. } - ), - _ => return Err(anyhow::anyhow!("Unknown test config key: {}", params.config)), - }) - .map_err(Into::into) + Ok(Some(match params.config { + InternalTestingFetchConfigOption::AssistEmitMustUse => { + InternalTestingFetchConfigResponse::AssistEmitMustUse( + state.config.assist(source_root).assist_emit_must_use, + ) + } + InternalTestingFetchConfigOption::CheckWorkspace => { + InternalTestingFetchConfigResponse::CheckWorkspace( + state.config.flycheck_workspace(source_root), + ) + } + })) } /// Searches for the directory of a Rust crate given this crate's root file path. diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs index 28f4b809d6c8..118469df730c 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs @@ -167,6 +167,7 @@ fn integrated_completion_benchmark() { prefer_absolute: false, snippets: Vec::new(), limit: None, + add_semicolon_to_unit: true, }; let position = FilePosition { file_id, offset: TextSize::try_from(completion_offset).unwrap() }; @@ -213,6 +214,7 @@ fn integrated_completion_benchmark() { prefer_absolute: false, snippets: Vec::new(), limit: None, + add_semicolon_to_unit: true, }; let position = FilePosition { file_id, offset: TextSize::try_from(completion_offset).unwrap() }; @@ -257,6 +259,7 @@ fn integrated_completion_benchmark() { prefer_absolute: false, snippets: Vec::new(), limit: None, + add_semicolon_to_unit: true, }; let position = FilePosition { file_id, offset: TextSize::try_from(completion_offset).unwrap() }; 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 8d1a686dc4d4..618481bbc66b 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 @@ -16,9 +16,22 @@ use serde::{Deserialize, Serialize}; pub enum InternalTestingFetchConfig {} +#[derive(Deserialize, Serialize, Debug)] +pub enum InternalTestingFetchConfigOption { + AssistEmitMustUse, + CheckWorkspace, +} + +#[derive(Deserialize, Serialize, Debug, PartialEq, Eq)] +pub enum InternalTestingFetchConfigResponse { + AssistEmitMustUse(bool), + CheckWorkspace(bool), +} + impl Request for InternalTestingFetchConfig { type Params = InternalTestingFetchConfigParams; - type Result = serde_json::Value; + // Option is solely to circumvent Default bound. + type Result = Option; const METHOD: &'static str = "rust-analyzer-internal/internalTestingFetchConfig"; } @@ -26,7 +39,7 @@ impl Request for InternalTestingFetchConfig { #[serde(rename_all = "camelCase")] pub struct InternalTestingFetchConfigParams { pub text_document: Option, - pub config: String, + pub config: InternalTestingFetchConfigOption, } pub enum AnalyzerStatus {} @@ -819,6 +832,7 @@ pub struct InlayHintResolveData { pub file_id: u32, // This is a string instead of a u64 as javascript can't represent u64 fully pub hash: String, + pub resolve_range: lsp_types::Range, pub version: Option, } 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 eb6bc2a9ce9b..4902c9f88c14 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 @@ -80,6 +80,7 @@ pub(crate) fn symbol_kind(symbol_kind: SymbolKind) -> lsp_types::SymbolKind { | SymbolKind::ValueParam | SymbolKind::Label => lsp_types::SymbolKind::VARIABLE, SymbolKind::Union => lsp_types::SymbolKind::STRUCT, + SymbolKind::InlineAsmRegOrRegClass => lsp_types::SymbolKind::VARIABLE, } } @@ -159,6 +160,7 @@ pub(crate) fn completion_item_kind( SymbolKind::Variant => lsp_types::CompletionItemKind::ENUM_MEMBER, SymbolKind::BuiltinAttr => lsp_types::CompletionItemKind::FUNCTION, SymbolKind::ToolModule => lsp_types::CompletionItemKind::MODULE, + SymbolKind::InlineAsmRegOrRegClass => lsp_types::CompletionItemKind::KEYWORD, }, } } @@ -228,8 +230,12 @@ pub(crate) fn completion_items( line_index: &LineIndex, version: Option, tdpp: lsp_types::TextDocumentPositionParams, - items: Vec, + mut items: Vec, ) -> Vec { + if config.completion_hide_deprecated() { + items.retain(|item| !item.deprecated); + } + let max_relevance = items.iter().map(|it| it.relevance.score()).max().unwrap_or_default(); let mut res = Vec::with_capacity(items.len()); for item in items { @@ -452,10 +458,13 @@ pub(crate) fn inlay_hint( file_id: FileId, mut inlay_hint: InlayHint, ) -> Cancellable { - let resolve_hash = inlay_hint.needs_resolve().then(|| { - std::hash::BuildHasher::hash_one( - &std::hash::BuildHasherDefault::::default(), - &inlay_hint, + let resolve_range_and_hash = inlay_hint.needs_resolve().map(|range| { + ( + range, + std::hash::BuildHasher::hash_one( + &std::hash::BuildHasherDefault::::default(), + &inlay_hint, + ), ) }); @@ -465,7 +474,7 @@ pub(crate) fn inlay_hint( .visual_studio_code_version() // https://github.com/microsoft/vscode/issues/193124 .map_or(true, |version| VersionReq::parse(">=1.86.0").unwrap().matches(version)) - && resolve_hash.is_some() + && resolve_range_and_hash.is_some() && fields_to_resolve.resolve_text_edits { something_to_resolve |= inlay_hint.text_edit.is_some(); @@ -477,16 +486,17 @@ pub(crate) fn inlay_hint( snap, fields_to_resolve, &mut something_to_resolve, - resolve_hash.is_some(), + resolve_range_and_hash.is_some(), inlay_hint.label, )?; - let data = match resolve_hash { - Some(hash) if something_to_resolve => Some( + let data = match resolve_range_and_hash { + Some((resolve_range, hash)) if something_to_resolve => Some( to_value(lsp_ext::InlayHintResolveData { file_id: file_id.index(), hash: hash.to_string(), version: snap.file_version(file_id), + resolve_range: range(line_index, resolve_range), }) .unwrap(), ), @@ -694,6 +704,7 @@ fn semantic_token_type_and_modifiers( SymbolKind::ProcMacro => types::PROC_MACRO, SymbolKind::BuiltinAttr => types::BUILTIN_ATTRIBUTE, SymbolKind::ToolModule => types::TOOL_MODULE, + SymbolKind::InlineAsmRegOrRegClass => types::KEYWORD, }, HlTag::AttributeBracket => types::ATTRIBUTE_BRACKET, HlTag::BoolLiteral => types::BOOLEAN, @@ -1365,8 +1376,9 @@ pub(crate) fn runnable( snap: &GlobalStateSnapshot, runnable: Runnable, ) -> Cancellable> { - let config = snap.config.runnables(); let target_spec = TargetSpec::for_file(snap, runnable.nav.file_id)?; + let source_root = snap.analysis.source_root_id(runnable.nav.file_id).ok(); + let config = snap.config.runnables(source_root); match target_spec { Some(TargetSpec::Cargo(spec)) => { 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 616d6b49351c..835592302576 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 @@ -4,6 +4,7 @@ use std::{ fmt, ops::Div as _, + panic::AssertUnwindSafe, time::{Duration, Instant}, }; @@ -195,7 +196,7 @@ impl GlobalState { ) { return Ok(()); } - self.handle_event(event)?; + self.handle_event(event); } Err(anyhow::anyhow!("A receiver has been dropped, something panicked!")) @@ -277,7 +278,7 @@ impl GlobalState { .map(Some) } - fn handle_event(&mut self, event: Event) -> anyhow::Result<()> { + fn handle_event(&mut self, event: Event) { let loop_start = Instant::now(); let _p = tracing::info_span!("GlobalState::handle_event", event = %event).entered(); @@ -294,7 +295,7 @@ impl GlobalState { match event { Event::Lsp(msg) => match msg { lsp_server::Message::Request(req) => self.on_new_request(loop_start, req), - lsp_server::Message::Notification(not) => self.on_notification(not)?, + lsp_server::Message::Notification(not) => self.on_notification(not), lsp_server::Message::Response(resp) => self.complete_request(resp), }, Event::QueuedTask(task) => { @@ -404,7 +405,7 @@ impl GlobalState { if self.is_quiescent() { let became_quiescent = !was_quiescent; if became_quiescent { - if self.config.check_on_save() { + if self.config.check_on_save(None) { // Project has loaded properly, kick off initial flycheck self.flycheck.iter().for_each(|flycheck| flycheck.restart_workspace(None)); } @@ -434,7 +435,7 @@ impl GlobalState { let project_or_mem_docs_changed = became_quiescent || state_changed || memdocs_added_or_removed; - if project_or_mem_docs_changed && self.config.publish_diagnostics() { + if project_or_mem_docs_changed && self.config.publish_diagnostics(None) { self.update_diagnostics(); } if project_or_mem_docs_changed && self.config.test_explorer() { @@ -455,7 +456,7 @@ impl GlobalState { } } - if self.config.cargo_autoreload_config() + if self.config.cargo_autoreload_config(None) || self.config.discover_workspace_config().is_some() { if let Some((cause, FetchWorkspaceRequest { path, force_crate_graph_reload })) = @@ -486,7 +487,6 @@ impl GlobalState { "overly long loop turn took {loop_duration:?} (event handling took {event_handling_duration:?}): {event_dbg_msg}" )); } - Ok(()) } fn prime_caches(&mut self, cause: String) { @@ -552,23 +552,33 @@ impl GlobalState { let fetch_semantic = self.vfs_done && self.fetch_workspaces_queue.last_op_result().is_some(); move |sender| { - let diags = fetch_native_diagnostics( - &snapshot, - subscriptions.clone(), - slice.clone(), - NativeDiagnosticsFetchKind::Syntax, - ); + // We aren't observing the semantics token cache here + let snapshot = AssertUnwindSafe(&snapshot); + let Ok(diags) = std::panic::catch_unwind(|| { + fetch_native_diagnostics( + &snapshot, + subscriptions.clone(), + slice.clone(), + NativeDiagnosticsFetchKind::Syntax, + ) + }) else { + return; + }; sender .send(Task::Diagnostics(DiagnosticsTaskKind::Syntax(generation, diags))) .unwrap(); if fetch_semantic { - let diags = fetch_native_diagnostics( - &snapshot, - subscriptions, - slice, - NativeDiagnosticsFetchKind::Semantic, - ); + let Ok(diags) = std::panic::catch_unwind(|| { + fetch_native_diagnostics( + &snapshot, + subscriptions.clone(), + slice.clone(), + NativeDiagnosticsFetchKind::Semantic, + ) + }) else { + return; + }; sender .send(Task::Diagnostics(DiagnosticsTaskKind::Semantic( generation, diags, @@ -875,7 +885,7 @@ impl GlobalState { self.discover_workspace_queue.op_completed(()); let mut config = Config::clone(&*self.config); - config.add_linked_projects(project, buildfile); + config.add_discovered_project_from_command(project, buildfile); self.update_configuration(config); } DiscoverProjectMessage::Progress { message } => { @@ -925,7 +935,7 @@ impl GlobalState { FlycheckMessage::AddDiagnostic { id, workspace_root, diagnostic } => { let snap = self.snapshot(); let diagnostics = crate::diagnostics::to_proto::map_rust_diagnostic_to_lsp( - &self.config.diagnostics_map(), + &self.config.diagnostics_map(None), &diagnostic, &workspace_root, &snap, @@ -973,9 +983,9 @@ impl GlobalState { // When we're running multiple flychecks, we have to include a disambiguator in // the title, or the editor complains. Note that this is a user-facing string. let title = if self.flycheck.len() == 1 { - format!("{}", self.config.flycheck()) + format!("{}", self.config.flycheck(None)) } else { - format!("{} (#{})", self.config.flycheck(), id + 1) + format!("{} (#{})", self.config.flycheck(None), id + 1) }; self.report_progress( &title, @@ -1105,37 +1115,32 @@ impl GlobalState { } /// Handles an incoming notification. - fn on_notification(&mut self, not: Notification) -> anyhow::Result<()> { + fn on_notification(&mut self, not: Notification) { let _p = span!(Level::INFO, "GlobalState::on_notification", not.method = ?not.method).entered(); use crate::handlers::notification as handlers; use lsp_types::notification as notifs; NotificationDispatcher { not: Some(not), global_state: self } - .on_sync_mut::(handlers::handle_cancel)? + .on_sync_mut::(handlers::handle_cancel) .on_sync_mut::( handlers::handle_work_done_progress_cancel, - )? - .on_sync_mut::(handlers::handle_did_open_text_document)? - .on_sync_mut::( - handlers::handle_did_change_text_document, - )? - .on_sync_mut::(handlers::handle_did_close_text_document)? - .on_sync_mut::(handlers::handle_did_save_text_document)? + ) + .on_sync_mut::(handlers::handle_did_open_text_document) + .on_sync_mut::(handlers::handle_did_change_text_document) + .on_sync_mut::(handlers::handle_did_close_text_document) + .on_sync_mut::(handlers::handle_did_save_text_document) .on_sync_mut::( handlers::handle_did_change_configuration, - )? + ) .on_sync_mut::( handlers::handle_did_change_workspace_folders, - )? - .on_sync_mut::( - handlers::handle_did_change_watched_files, - )? - .on_sync_mut::(handlers::handle_cancel_flycheck)? - .on_sync_mut::(handlers::handle_clear_flycheck)? - .on_sync_mut::(handlers::handle_run_flycheck)? - .on_sync_mut::(handlers::handle_abort_run_test)? + ) + .on_sync_mut::(handlers::handle_did_change_watched_files) + .on_sync_mut::(handlers::handle_cancel_flycheck) + .on_sync_mut::(handlers::handle_clear_flycheck) + .on_sync_mut::(handlers::handle_run_flycheck) + .on_sync_mut::(handlers::handle_abort_run_test) .finish(); - Ok(()) } } 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 eab973387240..5c4c858e1509 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 @@ -37,9 +37,13 @@ impl Default for OpQueue { } impl OpQueue { + /// Request an operation to start. pub(crate) fn request_op(&mut self, reason: Cause, args: Args) { self.op_requested = Some((reason, args)); } + + /// If there was an operation requested, mark this queue as + /// started and return the request arguments. pub(crate) fn should_start_op(&mut self) -> Option<(Cause, Args)> { if self.op_in_progress { return None; @@ -47,18 +51,25 @@ impl OpQueue { self.op_in_progress = self.op_requested.is_some(); self.op_requested.take() } + + /// Mark an operation as completed. pub(crate) fn op_completed(&mut self, result: Output) { assert!(self.op_in_progress); self.op_in_progress = false; self.last_op_result = result; } + /// Get the result of the last operation. pub(crate) fn last_op_result(&self) -> &Output { &self.last_op_result } + + // Is there an operation that has started, but hasn't yet finished? pub(crate) fn op_in_progress(&self) -> bool { self.op_in_progress } + + // Has an operation been requested? pub(crate) fn op_requested(&self) -> bool { self.op_requested.is_some() } 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 68366136eda6..f6765715c5a1 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,9 @@ use std::{iter, mem}; use hir::{db::DefDatabase, ChangeWithProcMacros, ProcMacros, ProcMacrosBuilder}; +use ide::CrateId; use ide_db::{ - base_db::{salsa::Durability, CrateGraph, ProcMacroPaths, Version}, + base_db::{salsa::Durability, CrateGraph, CrateWorkspaceData, ProcMacroPaths}, FxHashMap, }; use itertools::Itertools; @@ -100,7 +101,7 @@ impl GlobalState { { 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() { + } else if self.config.flycheck(None) != old_config.flycheck(None) { self.reload_flycheck(); } @@ -122,7 +123,7 @@ impl GlobalState { }; let mut message = String::new(); - if !self.config.cargo_autoreload() + if !self.config.cargo_autoreload_config(None) && self.is_quiescent() && self.fetch_workspaces_queue.op_requested() && self.config.discover_workspace_config().is_none() @@ -264,7 +265,7 @@ impl GlobalState { .map(ManifestPath::try_from) .filter_map(Result::ok) .collect(); - let cargo_config = self.config.cargo(); + let cargo_config = self.config.cargo(None); 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 @@ -357,7 +358,7 @@ impl GlobalState { pub(crate) fn fetch_build_data(&mut self, cause: Cause) { info!(%cause, "will fetch build data"); let workspaces = Arc::clone(&self.workspaces); - let config = self.config.cargo(); + let config = self.config.cargo(None); let root_path = self.config.root_path().clone(); self.task_pool.handle.spawn_with_sender(ThreadIntent::Worker, move |sender| { @@ -382,7 +383,7 @@ impl GlobalState { pub(crate) fn fetch_proc_macros(&mut self, cause: Cause, paths: Vec) { info!(%cause, "will load proc macros"); - let ignored_proc_macros = self.config.ignored_proc_macros().clone(); + let ignored_proc_macros = self.config.ignored_proc_macros(None).clone(); let proc_macro_clients = self.proc_macro_clients.clone(); self.task_pool.handle.spawn_with_sender(ThreadIntent::Worker, move |sender| { @@ -507,7 +508,7 @@ impl GlobalState { // FIXME: can we abort the build scripts here if they are already running? self.workspaces = Arc::new(workspaces); - if self.config.run_build_scripts() { + if self.config.run_build_scripts(None) { self.build_deps_changed = false; self.fetch_build_data_queue.request_op("workspace updated".to_owned(), ()); } @@ -627,7 +628,7 @@ impl GlobalState { .. } => cargo_config_extra_env .iter() - .chain(self.config.extra_env()) + .chain(self.config.extra_env(None)) .map(|(a, b)| (a.clone(), b.clone())) .chain( ws.sysroot @@ -692,7 +693,7 @@ impl GlobalState { }) .collect(); - let (crate_graph, proc_macro_paths, layouts, toolchains) = { + let (crate_graph, proc_macro_paths, ws_data) = { // Create crate graph from all the workspaces let vfs = &mut self.vfs.write().0; @@ -702,7 +703,7 @@ impl GlobalState { vfs.file_id(&vfs_path) }; - ws_to_crate_graph(&self.workspaces, self.config.extra_env(), load) + ws_to_crate_graph(&self.workspaces, self.config.extra_env(None), load) }; let mut change = ChangeWithProcMacros::new(); if self.config.expand_proc_macros() { @@ -721,9 +722,7 @@ impl GlobalState { .collect(), ); } - change.set_crate_graph(crate_graph); - change.set_target_data_layouts(layouts); - change.set_toolchains(toolchains); + change.set_crate_graph(crate_graph, ws_data); self.analysis_host.apply_change(change); self.report_progress( "Building CrateGraph", @@ -791,7 +790,7 @@ impl GlobalState { fn reload_flycheck(&mut self) { let _p = tracing::info_span!("GlobalState::reload_flycheck").entered(); - let config = self.config.flycheck(); + let config = self.config.flycheck(None); let sender = self.flycheck_sender.clone(); let invocation_strategy = match config { FlycheckConfig::CargoCommand { .. } => { @@ -863,51 +862,27 @@ pub fn ws_to_crate_graph( workspaces: &[ProjectWorkspace], extra_env: &FxHashMap, mut load: impl FnMut(&AbsPath) -> Option, -) -> (CrateGraph, Vec, Vec, Arc>>, Vec>) { +) -> (CrateGraph, Vec, FxHashMap>) { let mut crate_graph = CrateGraph::default(); let mut proc_macro_paths = Vec::default(); - let mut layouts = Vec::default(); - let mut toolchains = Vec::default(); - let e = Err(Arc::from("missing layout")); + let mut ws_data = FxHashMap::default(); for ws in workspaces { let (other, mut crate_proc_macros) = ws.to_crate_graph(&mut load, extra_env); - let num_layouts = layouts.len(); - let num_toolchains = toolchains.len(); let ProjectWorkspace { toolchain, target_layout, .. } = ws; - let mapping = crate_graph.extend( - other, - &mut crate_proc_macros, - |(cg_id, cg_data), (_o_id, o_data)| { - // if the newly created crate graph's layout is equal to the crate of the merged graph, then - // we can merge the crates. - let id = cg_id.into_raw().into_u32() as usize; - layouts[id] == *target_layout && toolchains[id] == *toolchain && cg_data == o_data - }, - ); + let mapping = crate_graph.extend(other, &mut crate_proc_macros); // Populate the side tables for the newly merged crates - mapping.values().for_each(|val| { - let idx = val.into_raw().into_u32() as usize; - // we only need to consider crates that were not merged and remapped, as the - // ones that were remapped already have the correct layout and toolchain - if idx >= num_layouts { - if layouts.len() <= idx { - layouts.resize(idx + 1, e.clone()); - } - layouts[idx].clone_from(target_layout); - } - if idx >= num_toolchains { - if toolchains.len() <= idx { - toolchains.resize(idx + 1, None); - } - toolchains[idx].clone_from(toolchain); - } - }); + ws_data.extend(mapping.values().copied().zip(iter::repeat(Arc::new(CrateWorkspaceData { + toolchain: toolchain.clone(), + data_layout: target_layout.clone(), + proc_macro_cwd: Some(ws.workspace_root().to_owned()), + })))); proc_macro_paths.push(crate_proc_macros); } + crate_graph.shrink_to_fit(); proc_macro_paths.shrink_to_fit(); - (crate_graph, proc_macro_paths, layouts, toolchains) + (crate_graph, proc_macro_paths, ws_data) } pub(crate) fn should_refresh_for_change( 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 954e13cbf272..b4aa73d2780d 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 @@ -113,7 +113,7 @@ impl CargoTargetSpec { kind: &RunnableKind, cfg: &Option, ) -> (Vec, Vec) { - let config = snap.config.runnables(); + let config = snap.config.runnables(None); let extra_test_binary_args = config.extra_test_binary_args; let mut cargo_args = Vec::new(); @@ -168,7 +168,7 @@ impl CargoTargetSpec { (Default::default(), Default::default()) }; - let cargo_config = snap.config.cargo(); + let cargo_config = snap.config.cargo(None); match &cargo_config.features { CargoFeatures::All => { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/crate_graph.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/crate_graph.rs deleted file mode 100644 index 04b6713b8d18..000000000000 --- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/crate_graph.rs +++ /dev/null @@ -1,126 +0,0 @@ -use std::path::PathBuf; - -use project_model::{ - CargoWorkspace, ManifestPath, Metadata, ProjectWorkspace, ProjectWorkspaceKind, Sysroot, - WorkspaceBuildScripts, -}; -use rust_analyzer::ws_to_crate_graph; -use rustc_hash::FxHashMap; -use serde::de::DeserializeOwned; -use vfs::{AbsPathBuf, FileId}; - -fn load_cargo_with_fake_sysroot(file: &str) -> ProjectWorkspace { - let meta: Metadata = get_test_json_file(file); - let manifest_path = - ManifestPath::try_from(AbsPathBuf::try_from(meta.workspace_root.clone()).unwrap()).unwrap(); - let cargo_workspace = CargoWorkspace::new(meta, manifest_path); - ProjectWorkspace { - kind: ProjectWorkspaceKind::Cargo { - cargo: cargo_workspace, - build_scripts: WorkspaceBuildScripts::default(), - rustc: Err(None), - cargo_config_extra_env: Default::default(), - error: None, - }, - sysroot: get_fake_sysroot(), - rustc_cfg: Vec::new(), - cfg_overrides: Default::default(), - toolchain: None, - target_layout: Err("target_data_layout not loaded".into()), - } -} - -fn get_test_json_file(file: &str) -> T { - let base = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - let file = base.join("tests/test_data").join(file); - let data = std::fs::read_to_string(file).unwrap(); - let mut json = data.parse::().unwrap(); - fixup_paths(&mut json); - return serde_json::from_value(json).unwrap(); - - fn fixup_paths(val: &mut serde_json::Value) { - match val { - serde_json::Value::String(s) => replace_root(s, true), - serde_json::Value::Array(vals) => vals.iter_mut().for_each(fixup_paths), - serde_json::Value::Object(kvals) => kvals.values_mut().for_each(fixup_paths), - serde_json::Value::Null | serde_json::Value::Bool(_) | serde_json::Value::Number(_) => { - } - } - } -} - -fn replace_root(s: &mut String, direction: bool) { - if direction { - let root = if cfg!(windows) { r#"C:\\ROOT\"# } else { "/ROOT/" }; - *s = s.replace("$ROOT$", root) - } else { - let root = if cfg!(windows) { r#"C:\\\\ROOT\\"# } else { "/ROOT/" }; - *s = s.replace(root, "$ROOT$") - } -} - -fn get_fake_sysroot_path() -> PathBuf { - let base = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - base.join("../project-model/test_data/fake-sysroot") -} - -fn get_fake_sysroot() -> Sysroot { - let sysroot_path = get_fake_sysroot_path(); - // there's no `libexec/` directory with a `proc-macro-srv` binary in that - // fake sysroot, so we give them both the same path: - let sysroot_dir = AbsPathBuf::assert_utf8(sysroot_path); - let sysroot_src_dir = sysroot_dir.clone(); - Sysroot::load(Some(sysroot_dir), Some(sysroot_src_dir)) -} - -#[test] -fn test_deduplicate_origin_dev() { - let path_map = &mut FxHashMap::default(); - let ws = load_cargo_with_fake_sysroot("deduplication_crate_graph_A.json"); - let ws2 = load_cargo_with_fake_sysroot("deduplication_crate_graph_B.json"); - - let (crate_graph, ..) = ws_to_crate_graph(&[ws, ws2], &Default::default(), |path| { - let len = path_map.len(); - Some(*path_map.entry(path.to_path_buf()).or_insert(FileId::from_raw(len as u32))) - }); - - let mut crates_named_p2 = vec![]; - for id in crate_graph.iter() { - let krate = &crate_graph[id]; - if let Some(name) = krate.display_name.as_ref() { - if name.to_string() == "p2" { - crates_named_p2.push(krate); - } - } - } - - assert_eq!(crates_named_p2.len(), 1); - let p2 = crates_named_p2[0]; - assert!(p2.origin.is_local()); -} - -#[test] -fn test_deduplicate_origin_dev_rev() { - let path_map = &mut FxHashMap::default(); - let ws = load_cargo_with_fake_sysroot("deduplication_crate_graph_B.json"); - let ws2 = load_cargo_with_fake_sysroot("deduplication_crate_graph_A.json"); - - let (crate_graph, ..) = ws_to_crate_graph(&[ws, ws2], &Default::default(), |path| { - let len = path_map.len(); - Some(*path_map.entry(path.to_path_buf()).or_insert(FileId::from_raw(len as u32))) - }); - - let mut crates_named_p2 = vec![]; - for id in crate_graph.iter() { - let krate = &crate_graph[id]; - if let Some(name) = krate.display_name.as_ref() { - if name.to_string() == "p2" { - crates_named_p2.push(krate); - } - } - } - - assert_eq!(crates_named_p2.len(), 1); - let p2 = crates_named_p2[0]; - assert!(p2.origin.is_local()); -} 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 295d1d4e8e90..a857e0c2967c 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 @@ -9,18 +9,13 @@ use lsp_types::{ use paths::Utf8PathBuf; use rust_analyzer::config::Config; -use rust_analyzer::lsp::ext::{InternalTestingFetchConfig, InternalTestingFetchConfigParams}; +use rust_analyzer::lsp::ext::{ + InternalTestingFetchConfig, InternalTestingFetchConfigOption, InternalTestingFetchConfigParams, + InternalTestingFetchConfigResponse, +}; use serde_json::json; use test_utils::skip_slow_tests; -enum QueryType { - Local, - /// A query whose config key is a part of the global configs, so that - /// testing for changes to this config means testing if global changes - /// take affect. - Workspace, -} - struct RatomlTest { urls: Vec, server: Server, @@ -158,20 +153,24 @@ impl RatomlTest { }); } - fn query(&self, query: QueryType, source_file_idx: usize) -> bool { - let config = match query { - QueryType::Local => "local".to_owned(), - QueryType::Workspace => "workspace".to_owned(), - }; + fn query( + &self, + query: InternalTestingFetchConfigOption, + source_file_idx: usize, + expected: InternalTestingFetchConfigResponse, + ) { let res = self.server.send_request::( InternalTestingFetchConfigParams { text_document: Some(TextDocumentIdentifier { uri: self.urls[source_file_idx].clone(), }), - config, + config: query, }, ); - res.as_bool().unwrap() + assert_eq!( + serde_json::from_value::(res).unwrap(), + expected + ) } } @@ -206,7 +205,11 @@ enum Value { })), ); - assert!(server.query(QueryType::Local, 1)); + server.query( + InternalTestingFetchConfigOption::AssistEmitMustUse, + 1, + InternalTestingFetchConfigResponse::AssistEmitMustUse(true), + ); } /// Checks if client config can be modified. @@ -311,7 +314,11 @@ enum Value { None, ); - assert!(server.query(QueryType::Local, 2)); + server.query( + InternalTestingFetchConfigOption::AssistEmitMustUse, + 2, + InternalTestingFetchConfigResponse::AssistEmitMustUse(true), + ); } #[test] @@ -341,12 +348,20 @@ enum Value { None, ); - assert!(!server.query(QueryType::Local, 1)); + server.query( + InternalTestingFetchConfigOption::AssistEmitMustUse, + 1, + InternalTestingFetchConfigResponse::AssistEmitMustUse(false), + ); server.create( "//- /$$CONFIG_DIR$$/rust-analyzer/rust-analyzer.toml", RatomlTest::EMIT_MUST_USE.to_owned(), ); - assert!(server.query(QueryType::Local, 1)); + server.query( + InternalTestingFetchConfigOption::AssistEmitMustUse, + 1, + InternalTestingFetchConfigResponse::AssistEmitMustUse(true), + ); } #[test] @@ -378,9 +393,17 @@ assist.emitMustUse = true"#, None, ); - assert!(server.query(QueryType::Local, 1)); + server.query( + InternalTestingFetchConfigOption::AssistEmitMustUse, + 1, + InternalTestingFetchConfigResponse::AssistEmitMustUse(true), + ); server.edit(2, String::new()); - assert!(!server.query(QueryType::Local, 1)); + server.query( + InternalTestingFetchConfigOption::AssistEmitMustUse, + 1, + InternalTestingFetchConfigResponse::AssistEmitMustUse(false), + ); } #[test] @@ -412,9 +435,17 @@ assist.emitMustUse = true"#, None, ); - assert!(server.query(QueryType::Local, 1)); + server.query( + InternalTestingFetchConfigOption::AssistEmitMustUse, + 1, + InternalTestingFetchConfigResponse::AssistEmitMustUse(true), + ); server.delete(2); - assert!(!server.query(QueryType::Local, 1)); + server.query( + InternalTestingFetchConfigOption::AssistEmitMustUse, + 1, + InternalTestingFetchConfigResponse::AssistEmitMustUse(false), + ); } #[test] @@ -461,7 +492,11 @@ pub fn add(left: usize, right: usize) -> usize { None, ); - assert!(server.query(QueryType::Local, 3)); + server.query( + InternalTestingFetchConfigOption::AssistEmitMustUse, + 3, + InternalTestingFetchConfigResponse::AssistEmitMustUse(true), + ); } #[test] @@ -508,9 +543,17 @@ pub fn add(left: usize, right: usize) -> usize { None, ); - assert!(!server.query(QueryType::Local, 3)); + server.query( + InternalTestingFetchConfigOption::AssistEmitMustUse, + 3, + InternalTestingFetchConfigResponse::AssistEmitMustUse(false), + ); server.edit(1, "assist.emitMustUse = true".to_owned()); - assert!(server.query(QueryType::Local, 3)); + server.query( + InternalTestingFetchConfigOption::AssistEmitMustUse, + 3, + InternalTestingFetchConfigResponse::AssistEmitMustUse(true), + ); } #[test] @@ -557,9 +600,17 @@ pub fn add(left: usize, right: usize) -> usize { None, ); - assert!(server.query(QueryType::Local, 3)); + server.query( + InternalTestingFetchConfigOption::AssistEmitMustUse, + 3, + InternalTestingFetchConfigResponse::AssistEmitMustUse(true), + ); server.delete(1); - assert!(!server.query(QueryType::Local, 3)); + server.query( + InternalTestingFetchConfigOption::AssistEmitMustUse, + 3, + InternalTestingFetchConfigResponse::AssistEmitMustUse(false), + ); } #[test] @@ -606,9 +657,17 @@ pub fn add(left: usize, right: usize) -> usize { None, ); - assert!(server.query(QueryType::Local, 3)); + server.query( + InternalTestingFetchConfigOption::AssistEmitMustUse, + 3, + InternalTestingFetchConfigResponse::AssistEmitMustUse(true), + ); server.create("//- /p1/p2/rust-analyzer.toml", RatomlTest::EMIT_MUST_NOT_USE.to_owned()); - assert!(!server.query(QueryType::Local, 3)); + server.query( + InternalTestingFetchConfigOption::AssistEmitMustUse, + 3, + InternalTestingFetchConfigResponse::AssistEmitMustUse(false), + ); } #[test] @@ -656,9 +715,17 @@ pub fn add(left: usize, right: usize) -> usize { None, ); - assert!(server.query(QueryType::Local, 3)); + server.query( + InternalTestingFetchConfigOption::AssistEmitMustUse, + 3, + InternalTestingFetchConfigResponse::AssistEmitMustUse(true), + ); server.delete(1); - assert!(!server.query(QueryType::Local, 3)); + server.query( + InternalTestingFetchConfigOption::AssistEmitMustUse, + 3, + InternalTestingFetchConfigResponse::AssistEmitMustUse(false), + ); } #[test] @@ -705,8 +772,16 @@ enum Value { None, ); - assert!(server.query(QueryType::Local, 3)); - assert!(server.query(QueryType::Local, 4)); + server.query( + InternalTestingFetchConfigOption::AssistEmitMustUse, + 3, + InternalTestingFetchConfigResponse::AssistEmitMustUse(true), + ); + server.query( + InternalTestingFetchConfigOption::AssistEmitMustUse, + 4, + InternalTestingFetchConfigResponse::AssistEmitMustUse(true), + ); } #[test] @@ -744,7 +819,11 @@ fn ratoml_multiple_ratoml_in_single_source_root() { None, ); - assert!(server.query(QueryType::Local, 3)); + server.query( + InternalTestingFetchConfigOption::AssistEmitMustUse, + 3, + InternalTestingFetchConfigResponse::AssistEmitMustUse(true), + ); } /// If a root is non-local, so we cannot find what its parent is @@ -765,7 +844,7 @@ fn ratoml_multiple_ratoml_in_single_source_root() { // [dependencies] // p2 = { path = "../p2" } -// #, +// "#, // r#" // //- /p1/src/lib.rs // enum Value { @@ -836,7 +915,7 @@ edition = "2021" "#, r#" //- /p1/rust-analyzer.toml -rustfmt.rangeFormatting.enable = true +check.workspace = false "#, r#" //- /p1/src/lib.rs @@ -848,7 +927,11 @@ fn main() { None, ); - assert!(server.query(QueryType::Workspace, 2)); + server.query( + InternalTestingFetchConfigOption::CheckWorkspace, + 2, + InternalTestingFetchConfigResponse::CheckWorkspace(false), + ) } #[test] @@ -868,7 +951,7 @@ edition = "2021" "#, r#" //- /p1/rust-analyzer.toml -rustfmt.rangeFormatting.enable = true +check.workspace = false "#, r#" //- /p1/src/lib.rs @@ -880,9 +963,17 @@ fn main() { None, ); - assert!(server.query(QueryType::Workspace, 2)); - server.edit(1, "rustfmt.rangeFormatting.enable = false".to_owned()); - assert!(!server.query(QueryType::Workspace, 2)); + server.query( + InternalTestingFetchConfigOption::CheckWorkspace, + 2, + InternalTestingFetchConfigResponse::CheckWorkspace(false), + ); + server.edit(1, "check.workspace = true".to_owned()); + server.query( + InternalTestingFetchConfigOption::CheckWorkspace, + 2, + InternalTestingFetchConfigResponse::CheckWorkspace(true), + ); } #[test] @@ -902,7 +993,7 @@ edition = "2021" "#, r#" //- /p1/rust-analyzer.toml -rustfmt.rangeFormatting.enable = true +check.workspace = false "#, r#" //- /p1/src/lib.rs @@ -914,7 +1005,15 @@ fn main() { None, ); - assert!(server.query(QueryType::Workspace, 2)); + server.query( + InternalTestingFetchConfigOption::CheckWorkspace, + 2, + InternalTestingFetchConfigResponse::CheckWorkspace(false), + ); server.delete(1); - assert!(!server.query(QueryType::Workspace, 2)); + server.query( + InternalTestingFetchConfigOption::CheckWorkspace, + 2, + InternalTestingFetchConfigResponse::CheckWorkspace(true), + ); } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/test_data/deduplication_crate_graph_A.json b/src/tools/rust-analyzer/crates/rust-analyzer/tests/test_data/deduplication_crate_graph_A.json deleted file mode 100644 index b0fb5845cef7..000000000000 --- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/test_data/deduplication_crate_graph_A.json +++ /dev/null @@ -1,140 +0,0 @@ -{ - "packages": [ - { - "name": "p1", - "version": "0.1.0", - "id": "p1 0.1.0 (path+file:///example_project/p1)", - "license": null, - "license_file": null, - "description": null, - "source": null, - "dependencies": [ - { - "name": "p2", - "source": null, - "req": "*", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null, - "path": "$ROOT$example_project/p2" - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "p1", - "src_path": "$ROOT$example_project/p1/src/lib.rs", - "edition": "2021", - "doc": true, - "doctest": true, - "test": true - } - ], - "features": {}, - "manifest_path": "$ROOT$example_project/p1/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [], - "categories": [], - "keywords": [], - "readme": null, - "repository": null, - "homepage": null, - "documentation": null, - "edition": "2021", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "p2", - "version": "0.1.0", - "id": "p2 0.1.0 (path+file:///example_project/p2)", - "license": null, - "license_file": null, - "description": null, - "source": null, - "dependencies": [], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "p2", - "src_path": "$ROOT$example_project/p2/src/lib.rs", - "edition": "2021", - "doc": true, - "doctest": true, - "test": true - } - ], - "features": {}, - "manifest_path": "$ROOT$example_project/p2/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [], - "categories": [], - "keywords": [], - "readme": null, - "repository": null, - "homepage": null, - "documentation": null, - "edition": "2021", - "links": null, - "default_run": null, - "rust_version": null - } - ], - "workspace_members": [ - "p1 0.1.0 (path+file:///example_project/p1)" - ], - "workspace_default_members": [ - "p1 0.1.0 (path+file:///example_project/p1)" - ], - "resolve": { - "nodes": [ - { - "id": "p1 0.1.0 (path+file:///example_project/p1)", - "dependencies": [ - "p2 0.1.0 (path+file:///example_project/p2)" - ], - "deps": [ - { - "name": "p2", - "pkg": "p2 0.1.0 (path+file:///example_project/p2)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - } - ], - "features": [] - }, - { - "id": "p2 0.1.0 (path+file:///example_project/p2)", - "dependencies": [], - "deps": [], - "features": [] - } - ], - "root": "p1 0.1.0 (path+file:///example_project/p1)" - }, - "target_directory": "$ROOT$example_project/p1/target", - "version": 1, - "workspace_root": "$ROOT$example_project/p1", - "metadata": null -} \ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/test_data/deduplication_crate_graph_B.json b/src/tools/rust-analyzer/crates/rust-analyzer/tests/test_data/deduplication_crate_graph_B.json deleted file mode 100644 index b5d1e16e62e0..000000000000 --- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/test_data/deduplication_crate_graph_B.json +++ /dev/null @@ -1,66 +0,0 @@ -{ - "packages": [ - { - "name": "p2", - "version": "0.1.0", - "id": "p2 0.1.0 (path+file:///example_project/p2)", - "license": null, - "license_file": null, - "description": null, - "source": null, - "dependencies": [], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "p2", - "src_path": "$ROOT$example_project/p2/src/lib.rs", - "edition": "2021", - "doc": true, - "doctest": true, - "test": true - } - ], - "features": {}, - "manifest_path": "$ROOT$example_project/p2/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [], - "categories": [], - "keywords": [], - "readme": null, - "repository": null, - "homepage": null, - "documentation": null, - "edition": "2021", - "links": null, - "default_run": null, - "rust_version": null - } - ], - "workspace_members": [ - "p2 0.1.0 (path+file:///example_project/p2)" - ], - "workspace_default_members": [ - "p2 0.1.0 (path+file:///example_project/p2)" - ], - "resolve": { - "nodes": [ - { - "id": "p2 0.1.0 (path+file:///example_project/p2)", - "dependencies": [], - "deps": [], - "features": [] - } - ], - "root": "p2 0.1.0 (path+file:///example_project/p2)" - }, - "target_directory": "$ROOT$example_project/p2/target", - "version": 1, - "workspace_root": "$ROOT$example_project/p2", - "metadata": null -} \ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/span/src/hygiene.rs b/src/tools/rust-analyzer/crates/span/src/hygiene.rs index 874480c59fbf..cb9c092f5fcd 100644 --- a/src/tools/rust-analyzer/crates/span/src/hygiene.rs +++ b/src/tools/rust-analyzer/crates/span/src/hygiene.rs @@ -81,7 +81,7 @@ pub struct SyntaxContextData { /// Invariant: Only [`SyntaxContextId::ROOT`] has a [`None`] outer expansion. // FIXME: The None case needs to encode the context crate id. We can encode that as the MSB of // MacroCallId is reserved anyways so we can do bit tagging here just fine. - // The bigger issue is that that will cause interning to now create completely separate chains + // The bigger issue is that this will cause interning to now create completely separate chains // per crate. Though that is likely not a problem as `MacroCallId`s are already crate calling dependent. pub outer_expn: Option, pub outer_transparency: Transparency, diff --git a/src/tools/rust-analyzer/crates/span/src/map.rs b/src/tools/rust-analyzer/crates/span/src/map.rs index c539754979d6..f80de05ec652 100644 --- a/src/tools/rust-analyzer/crates/span/src/map.rs +++ b/src/tools/rust-analyzer/crates/span/src/map.rs @@ -13,6 +13,7 @@ use crate::{ /// Maps absolute text ranges for the corresponding file to the relevant span data. #[derive(Debug, PartialEq, Eq, Clone, Hash)] pub struct SpanMap { + /// The offset stored here is the *end* of the node. spans: Vec<(TextSize, SpanData)>, /// Index of the matched macro arm on successful expansion for declarative macros. // FIXME: Does it make sense to have this here? @@ -104,6 +105,52 @@ where pub fn iter(&self) -> impl Iterator)> + '_ { self.spans.iter().copied() } + + /// Merges this span map with another span map, where `other` is inserted at (and replaces) `other_range`. + /// + /// The length of the replacement node needs to be `other_size`. + pub fn merge(&mut self, other_range: TextRange, other_size: TextSize, other: &SpanMap) { + // I find the following diagram helpful to illustrate the bounds and why we use `<` or `<=`: + // -------------------------------------------------------------------- + // 1 3 5 6 7 10 11 <-- offsets we store + // 0-1 1-3 3-5 5-6 6-7 7-10 10-11 <-- ranges these offsets refer to + // 3 .. 7 <-- other_range + // 3-5 5-6 6-7 <-- ranges we replace (len = 7-3 = 4) + // ^^^^^^^^^^^ ^^^^^^^^^^ + // remove shift + // 2 3 5 9 <-- offsets we insert + // 0-2 2-3 3-5 5-9 <-- ranges we insert (other_size = 9-0 = 9) + // ------------------------------------ + // 1 3 + // 0-1 1-3 <-- these remain intact + // 5 6 8 12 + // 3-5 5-6 6-8 8-12 <-- we shift these by other_range.start() and insert them + // 15 16 + // 12-15 15-16 <-- we shift these by other_size-other_range.len() = 9-4 = 5 + // ------------------------------------ + // 1 3 5 6 8 12 15 16 <-- final offsets we store + // 0-1 1-3 3-5 5-6 6-8 8-12 12-15 15-16 <-- final ranges + + self.spans.retain_mut(|(offset, _)| { + if other_range.start() < *offset && *offset <= other_range.end() { + false + } else { + if *offset > other_range.end() { + *offset += other_size; + *offset -= other_range.len(); + } + true + } + }); + + self.spans + .extend(other.spans.iter().map(|&(offset, span)| (offset + other_range.start(), span))); + + self.spans.sort_unstable_by_key(|&(offset, _)| offset); + + // Matched arm info is no longer correct once we have multiple macros. + self.matched_arm = None; + } } #[derive(PartialEq, Eq, Hash, Debug)] diff --git a/src/tools/rust-analyzer/crates/syntax-bridge/src/lib.rs b/src/tools/rust-analyzer/crates/syntax-bridge/src/lib.rs index 56e43e82ed27..0ccd08867602 100644 --- a/src/tools/rust-analyzer/crates/syntax-bridge/src/lib.rs +++ b/src/tools/rust-analyzer/crates/syntax-bridge/src/lib.rs @@ -17,6 +17,7 @@ use tt::{ token_to_literal, }; +pub mod prettify_macro_expansion; mod to_parser_input; pub use to_parser_input::to_parser_input; // FIXME: we probably should re-think `token_tree_to_syntax_node` interfaces diff --git a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/insert_whitespace_into_node.rs b/src/tools/rust-analyzer/crates/syntax-bridge/src/prettify_macro_expansion.rs similarity index 85% rename from src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/insert_whitespace_into_node.rs rename to src/tools/rust-analyzer/crates/syntax-bridge/src/prettify_macro_expansion.rs index dd4a665e8eb4..fc7caaa98865 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/insert_whitespace_into_node.rs +++ b/src/tools/rust-analyzer/crates/syntax-bridge/src/prettify_macro_expansion.rs @@ -7,13 +7,20 @@ use syntax::{ SyntaxNode, SyntaxToken, WalkEvent, T, }; -// FIXME: It would also be cool to share logic here and in the mbe tests, -// which are pretty unreadable at the moment. /// Renders a [`SyntaxNode`] with whitespace inserted between tokens that require them. -pub fn insert_ws_into(syn: SyntaxNode) -> SyntaxNode { +/// +/// This is an internal API that is only exported because `mbe` needs it for tests and cannot depend +/// on `hir-expand`. For any purpose other than tests, you are supposed to use the `prettify_macro_expansion` +/// from `hir-expand` that handles `$crate` for you. +#[deprecated = "use `hir_expand::prettify_macro_expansion()` instead"] +pub fn prettify_macro_expansion( + syn: SyntaxNode, + dollar_crate_replacement: &mut dyn FnMut(&SyntaxToken) -> SyntaxToken, +) -> SyntaxNode { let mut indent = 0; let mut last: Option = None; let mut mods = Vec::new(); + let mut dollar_crate_replacements = Vec::new(); let syn = syn.clone_subtree().clone_for_update(); let before = Position::before; @@ -51,6 +58,9 @@ pub fn insert_ws_into(syn: SyntaxNode) -> SyntaxNode { } _ => continue, }; + if token.kind() == SyntaxKind::IDENT && token.text() == "$crate" { + dollar_crate_replacements.push((token.clone(), dollar_crate_replacement(&token))); + } let tok = &token; let is_next = |f: fn(SyntaxKind) -> bool, default| -> bool { @@ -122,6 +132,9 @@ pub fn insert_ws_into(syn: SyntaxNode) -> SyntaxNode { for (pos, insert) in mods { ted::insert(pos, insert); } + for (old, new) in dollar_crate_replacements { + ted::replace(old, new); + } if let Some(it) = syn.last_token().filter(|it| it.kind() == SyntaxKind::WHITESPACE) { ted::remove(it); diff --git a/src/tools/rust-analyzer/crates/syntax/rust.ungram b/src/tools/rust-analyzer/crates/syntax/rust.ungram index 43375ce6ae05..52ad439e4dec 100644 --- a/src/tools/rust-analyzer/crates/syntax/rust.ungram +++ b/src/tools/rust-analyzer/crates/syntax/rust.ungram @@ -391,8 +391,33 @@ Expr = OffsetOfExpr = Attr* 'builtin' '#' 'offset_of' '(' Type ',' fields:(NameRef ('.' NameRef)* ) ')' +// asm := "asm!(" format_string *("," format_string) *("," operand) [","] ")" +// global_asm := "global_asm!(" format_string *("," format_string) *("," operand) [","] ")" +// format_string := STRING_LITERAL / RAW_STRING_LITERAL AsmExpr = - Attr* 'builtin' '#' 'asm' '(' Expr ')' + Attr* 'builtin' '#' 'asm' '(' template:(Expr (',' Expr)*) (AsmPiece (',' AsmPiece)*)? ','? ')' + +// operand_expr := expr / "_" / expr "=>" expr / expr "=>" "_" +AsmOperandExpr = in_expr:Expr ('=>' out_expr:Expr)? +// dir_spec := "in" / "out" / "lateout" / "inout" / "inlateout" +AsmDirSpec = 'in' | 'out' | 'lateout' | 'inout' | 'inlateout' +// reg_spec := / "\"" "\"" +AsmRegSpec = '@string' | NameRef +// reg_operand := [ident "="] dir_spec "(" reg_spec ")" operand_expr +AsmRegOperand = AsmDirSpec '(' AsmRegSpec ')' AsmOperandExpr +// clobber_abi := "clobber_abi(" *("," ) [","] ")" +AsmClobberAbi = 'clobber_abi' '(' ('@string' (',' '@string')* ','?) ')' +// option := "pure" / "nomem" / "readonly" / "preserves_flags" / "noreturn" / "nostack" / "att_syntax" / "raw" +AsmOption = 'pure' | 'nomem' | 'readonly' | 'preserves_flags' | 'noreturn' | 'nostack' | 'att_syntax' | 'raw' | 'may_unwind' +// options := "options(" option *("," option) [","] ")" +AsmOptions = 'options' '(' AsmOption *(',' AsmOption) ','? ')' +AsmLabel = 'label' BlockExpr +AsmSym = 'sym' Path +AsmConst = 'const' Expr +// operand := reg_operand / clobber_abi / options +AsmOperand = AsmRegOperand | AsmLabel | AsmSym | AsmConst +AsmOperandNamed = (Name '=')? AsmOperand +AsmPiece = AsmOperandNamed | AsmClobberAbi | AsmOptions FormatArgsExpr = Attr* 'builtin' '#' 'format_args' '(' 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 c9b39e9922c9..c81a19f3bda4 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 @@ -64,6 +64,53 @@ impl ArrayType { pub fn semicolon_token(&self) -> Option { support::token(&self.syntax, T![;]) } } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct AsmClobberAbi { + pub(crate) syntax: SyntaxNode, +} +impl AsmClobberAbi { + #[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 clobber_abi_token(&self) -> Option { + support::token(&self.syntax, T![clobber_abi]) + } + #[inline] + pub fn string_token(&self) -> Option { support::token(&self.syntax, T![string]) } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct AsmConst { + pub(crate) syntax: SyntaxNode, +} +impl AsmConst { + #[inline] + pub fn expr(&self) -> Option { support::child(&self.syntax) } + #[inline] + pub fn const_token(&self) -> Option { support::token(&self.syntax, T![const]) } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct AsmDirSpec { + pub(crate) syntax: SyntaxNode, +} +impl AsmDirSpec { + #[inline] + pub fn in_token(&self) -> Option { support::token(&self.syntax, T![in]) } + #[inline] + pub fn inlateout_token(&self) -> Option { + support::token(&self.syntax, T![inlateout]) + } + #[inline] + pub fn inout_token(&self) -> Option { support::token(&self.syntax, T![inout]) } + #[inline] + pub fn lateout_token(&self) -> Option { support::token(&self.syntax, T![lateout]) } + #[inline] + pub fn out_token(&self) -> Option { support::token(&self.syntax, T![out]) } +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct AsmExpr { pub(crate) syntax: SyntaxNode, @@ -71,7 +118,9 @@ pub struct AsmExpr { impl ast::HasAttrs for AsmExpr {} impl AsmExpr { #[inline] - pub fn expr(&self) -> Option { support::child(&self.syntax) } + pub fn asm_pieces(&self) -> AstChildren { support::children(&self.syntax) } + #[inline] + pub fn template(&self) -> AstChildren { support::children(&self.syntax) } #[inline] pub fn pound_token(&self) -> Option { support::token(&self.syntax, T![#]) } #[inline] @@ -79,11 +128,142 @@ impl AsmExpr { #[inline] pub fn r_paren_token(&self) -> Option { support::token(&self.syntax, T![')']) } #[inline] + pub fn comma_token(&self) -> Option { support::token(&self.syntax, T![,]) } + #[inline] pub fn asm_token(&self) -> Option { support::token(&self.syntax, T![asm]) } #[inline] pub fn builtin_token(&self) -> Option { support::token(&self.syntax, T![builtin]) } } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct AsmLabel { + pub(crate) syntax: SyntaxNode, +} +impl AsmLabel { + #[inline] + pub fn block_expr(&self) -> Option { support::child(&self.syntax) } + #[inline] + pub fn label_token(&self) -> Option { support::token(&self.syntax, T![label]) } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct AsmOperandExpr { + pub(crate) syntax: SyntaxNode, +} +impl AsmOperandExpr { + #[inline] + pub fn in_expr(&self) -> Option { support::child(&self.syntax) } + #[inline] + pub fn out_expr(&self) -> Option { support::child(&self.syntax) } + #[inline] + pub fn fat_arrow_token(&self) -> Option { support::token(&self.syntax, T![=>]) } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct AsmOperandNamed { + pub(crate) syntax: SyntaxNode, +} +impl ast::HasName for AsmOperandNamed {} +impl AsmOperandNamed { + #[inline] + pub fn asm_operand(&self) -> Option { support::child(&self.syntax) } + #[inline] + pub fn eq_token(&self) -> Option { support::token(&self.syntax, T![=]) } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct AsmOption { + pub(crate) syntax: SyntaxNode, +} +impl AsmOption { + #[inline] + pub fn att_syntax_token(&self) -> Option { + support::token(&self.syntax, T![att_syntax]) + } + #[inline] + pub fn may_unwind_token(&self) -> Option { + support::token(&self.syntax, T![may_unwind]) + } + #[inline] + pub fn nomem_token(&self) -> Option { support::token(&self.syntax, T![nomem]) } + #[inline] + pub fn noreturn_token(&self) -> Option { + support::token(&self.syntax, T![noreturn]) + } + #[inline] + pub fn nostack_token(&self) -> Option { support::token(&self.syntax, T![nostack]) } + #[inline] + pub fn preserves_flags_token(&self) -> Option { + support::token(&self.syntax, T![preserves_flags]) + } + #[inline] + pub fn pure_token(&self) -> Option { support::token(&self.syntax, T![pure]) } + #[inline] + pub fn raw_token(&self) -> Option { support::token(&self.syntax, T![raw]) } + #[inline] + pub fn readonly_token(&self) -> Option { + support::token(&self.syntax, T![readonly]) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct AsmOptions { + pub(crate) syntax: SyntaxNode, +} +impl AsmOptions { + #[inline] + pub fn asm_option(&self) -> Option { support::child(&self.syntax) } + #[inline] + pub fn asm_options(&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 comma_token(&self) -> Option { support::token(&self.syntax, T![,]) } + #[inline] + pub fn options_token(&self) -> Option { support::token(&self.syntax, T![options]) } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct AsmRegOperand { + pub(crate) syntax: SyntaxNode, +} +impl AsmRegOperand { + #[inline] + pub fn asm_dir_spec(&self) -> Option { support::child(&self.syntax) } + #[inline] + pub fn asm_operand_expr(&self) -> Option { support::child(&self.syntax) } + #[inline] + pub fn asm_reg_spec(&self) -> Option { support::child(&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![')']) } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct AsmRegSpec { + pub(crate) syntax: SyntaxNode, +} +impl AsmRegSpec { + #[inline] + pub fn name_ref(&self) -> Option { support::child(&self.syntax) } + #[inline] + pub fn string_token(&self) -> Option { support::token(&self.syntax, T![string]) } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct AsmSym { + pub(crate) syntax: SyntaxNode, +} +impl AsmSym { + #[inline] + pub fn path(&self) -> Option { support::child(&self.syntax) } + #[inline] + pub fn sym_token(&self) -> Option { support::token(&self.syntax, T![sym]) } +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct AssocItemList { pub(crate) syntax: SyntaxNode, @@ -2051,6 +2231,21 @@ impl ast::HasGenericParams for Adt {} impl ast::HasName for Adt {} impl ast::HasVisibility for Adt {} +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum AsmOperand { + AsmConst(AsmConst), + AsmLabel(AsmLabel), + AsmRegOperand(AsmRegOperand), + AsmSym(AsmSym), +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum AsmPiece { + AsmClobberAbi(AsmClobberAbi), + AsmOperandNamed(AsmOperandNamed), + AsmOptions(AsmOptions), +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum AssocItem { Const(Const), @@ -2316,6 +2511,48 @@ impl AstNode for ArrayType { #[inline] fn syntax(&self) -> &SyntaxNode { &self.syntax } } +impl AstNode for AsmClobberAbi { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_CLOBBER_ABI } + #[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 AsmConst { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_CONST } + #[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 AsmDirSpec { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_DIR_SPEC } + #[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 AsmExpr { #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_EXPR } @@ -2330,6 +2567,118 @@ impl AstNode for AsmExpr { #[inline] fn syntax(&self) -> &SyntaxNode { &self.syntax } } +impl AstNode for AsmLabel { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_LABEL } + #[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 AsmOperandExpr { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_OPERAND_EXPR } + #[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 AsmOperandNamed { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_OPERAND_NAMED } + #[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 AsmOption { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_OPTION } + #[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 AsmOptions { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_OPTIONS } + #[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 AsmRegOperand { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_REG_OPERAND } + #[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 AsmRegSpec { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_REG_SPEC } + #[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 AsmSym { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_SYM } + #[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 AssocItemList { #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == ASSOC_ITEM_LIST } @@ -4268,6 +4617,84 @@ impl AstNode for Adt { } } } +impl From for AsmOperand { + #[inline] + fn from(node: AsmConst) -> AsmOperand { AsmOperand::AsmConst(node) } +} +impl From for AsmOperand { + #[inline] + fn from(node: AsmLabel) -> AsmOperand { AsmOperand::AsmLabel(node) } +} +impl From for AsmOperand { + #[inline] + fn from(node: AsmRegOperand) -> AsmOperand { AsmOperand::AsmRegOperand(node) } +} +impl From for AsmOperand { + #[inline] + fn from(node: AsmSym) -> AsmOperand { AsmOperand::AsmSym(node) } +} +impl AstNode for AsmOperand { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + matches!(kind, ASM_CONST | ASM_LABEL | ASM_REG_OPERAND | ASM_SYM) + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + let res = match syntax.kind() { + ASM_CONST => AsmOperand::AsmConst(AsmConst { syntax }), + ASM_LABEL => AsmOperand::AsmLabel(AsmLabel { syntax }), + ASM_REG_OPERAND => AsmOperand::AsmRegOperand(AsmRegOperand { syntax }), + ASM_SYM => AsmOperand::AsmSym(AsmSym { syntax }), + _ => return None, + }; + Some(res) + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + match self { + AsmOperand::AsmConst(it) => &it.syntax, + AsmOperand::AsmLabel(it) => &it.syntax, + AsmOperand::AsmRegOperand(it) => &it.syntax, + AsmOperand::AsmSym(it) => &it.syntax, + } + } +} +impl From for AsmPiece { + #[inline] + fn from(node: AsmClobberAbi) -> AsmPiece { AsmPiece::AsmClobberAbi(node) } +} +impl From for AsmPiece { + #[inline] + fn from(node: AsmOperandNamed) -> AsmPiece { AsmPiece::AsmOperandNamed(node) } +} +impl From for AsmPiece { + #[inline] + fn from(node: AsmOptions) -> AsmPiece { AsmPiece::AsmOptions(node) } +} +impl AstNode for AsmPiece { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + matches!(kind, ASM_CLOBBER_ABI | ASM_OPERAND_NAMED | ASM_OPTIONS) + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + let res = match syntax.kind() { + ASM_CLOBBER_ABI => AsmPiece::AsmClobberAbi(AsmClobberAbi { syntax }), + ASM_OPERAND_NAMED => AsmPiece::AsmOperandNamed(AsmOperandNamed { syntax }), + ASM_OPTIONS => AsmPiece::AsmOptions(AsmOptions { syntax }), + _ => return None, + }; + Some(res) + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + match self { + AsmPiece::AsmClobberAbi(it) => &it.syntax, + AsmPiece::AsmOperandNamed(it) => &it.syntax, + AsmPiece::AsmOptions(it) => &it.syntax, + } + } +} impl From for AssocItem { #[inline] fn from(node: Const) -> AssocItem { AssocItem::Const(node) } @@ -5803,7 +6230,8 @@ impl AstNode for AnyHasName { fn can_cast(kind: SyntaxKind) -> bool { matches!( kind, - CONST + ASM_OPERAND_NAMED + | CONST | CONST_PARAM | ENUM | FN @@ -5832,6 +6260,10 @@ impl AstNode for AnyHasName { #[inline] fn syntax(&self) -> &SyntaxNode { &self.syntax } } +impl From for AnyHasName { + #[inline] + fn from(node: AsmOperandNamed) -> AnyHasName { AnyHasName { syntax: node.syntax } } +} impl From for AnyHasName { #[inline] fn from(node: Const) -> AnyHasName { AnyHasName { syntax: node.syntax } } @@ -6072,6 +6504,16 @@ impl std::fmt::Display for Adt { std::fmt::Display::fmt(self.syntax(), f) } } +impl std::fmt::Display for AsmOperand { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} +impl std::fmt::Display for AsmPiece { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} impl std::fmt::Display for AssocItem { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) @@ -6142,11 +6584,66 @@ impl std::fmt::Display for ArrayType { std::fmt::Display::fmt(self.syntax(), f) } } +impl std::fmt::Display for AsmClobberAbi { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} +impl std::fmt::Display for AsmConst { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} +impl std::fmt::Display for AsmDirSpec { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} impl std::fmt::Display for AsmExpr { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } +impl std::fmt::Display for AsmLabel { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} +impl std::fmt::Display for AsmOperandExpr { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} +impl std::fmt::Display for AsmOperandNamed { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} +impl std::fmt::Display for AsmOption { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} +impl std::fmt::Display for AsmOptions { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} +impl std::fmt::Display for AsmRegOperand { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} +impl std::fmt::Display for AsmRegSpec { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} +impl std::fmt::Display for AsmSym { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} impl std::fmt::Display for AssocItemList { 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 abf1a1f38207..fcdc97ce3270 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs @@ -1162,7 +1162,7 @@ pub mod tokens { pub(super) static SOURCE_FILE: LazyLock> = LazyLock::new(|| { SourceFile::parse( - "const C: <()>::Item = ( true && true , true || true , 1 != 1, 2 == 2, 3 < 3, 4 <= 4, 5 > 5, 6 >= 6, !true, *p, &p , &mut p, { let _ @ [] })\n;\n\nimpl A for B where: {}", Edition::CURRENT, + "use crate::foo; const C: <()>::Item = ( true && true , true || true , 1 != 1, 2 == 2, 3 < 3, 4 <= 4, 5 > 5, 6 >= 6, !true, *p, &p , &mut p, async { let _ @ [] })\n;\n\nimpl A for B where: {}", Edition::CURRENT, ) }); @@ -1188,6 +1188,17 @@ pub mod tokens { .unwrap() } + pub fn crate_kw() -> SyntaxToken { + SOURCE_FILE + .tree() + .syntax() + .clone_for_update() + .descendants_with_tokens() + .filter_map(|it| it.into_token()) + .find(|it| it.kind() == CRATE_KW) + .unwrap() + } + pub fn whitespace(text: &str) -> SyntaxToken { assert!(text.trim().is_empty()); let sf = SourceFile::parse(text, Edition::CURRENT).ok().unwrap(); diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/traits.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/traits.rs index 152b0cb98c2a..5d6aa4331b02 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/traits.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/traits.rs @@ -75,6 +75,33 @@ pub trait HasAttrs: AstNode { fn has_atom_attr(&self, atom: &str) -> bool { self.attrs().filter_map(|x| x.as_simple_atom()).any(|x| x == atom) } + + /// Returns all attributes of this node, including inner attributes that may not be directly under this node + /// but under a child. + fn attrs_including_inner(self) -> impl Iterator + where + Self: Sized, + { + let inner_attrs_node = if let Some(it) = + support::child::(self.syntax()).and_then(|it| it.stmt_list()) + { + Some(it.syntax) + } else if let Some(it) = support::child::(self.syntax()) { + Some(it.syntax) + } else if let Some(it) = support::child::(self.syntax()) { + Some(it.syntax) + } else if let Some(it) = support::child::(self.syntax()) { + Some(it.syntax) + } else if let Some(it) = support::child::(self.syntax()) { + Some(it.syntax) + } else if let Some(it) = support::child::(self.syntax()) { + Some(it.syntax) + } else { + None + }; + + self.attrs().chain(inner_attrs_node.into_iter().flat_map(|it| support::children(&it))) + } } pub trait HasDocComments: HasAttrs { diff --git a/src/tools/rust-analyzer/crates/syntax/src/hacks.rs b/src/tools/rust-analyzer/crates/syntax/src/hacks.rs index 9e63448ce963..2184359f1d0c 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/hacks.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/hacks.rs @@ -8,10 +8,15 @@ use crate::{ast, AstNode}; pub fn parse_expr_from_str(s: &str, edition: Edition) -> Option { let s = s.trim(); - let file = ast::SourceFile::parse(&format!("const _: () = {s};"), edition); - let expr = file.syntax_node().descendants().find_map(ast::Expr::cast)?; - if expr.syntax().text() != s { - return None; - } - Some(expr) + + let file = ast::SourceFile::parse( + // Need a newline because the text may contain line comments. + &format!("const _: () = ({s}\n);"), + edition, + ); + let expr = file.syntax_node().descendants().find_map(ast::ParenExpr::cast)?; + // Can't check the text because the original text may contain whitespace and comments. + // Wrap in parentheses to better allow for verification. Of course, the real fix is + // to get rid of this hack. + expr.expr() } diff --git a/src/tools/rust-analyzer/crates/syntax/src/lib.rs b/src/tools/rust-analyzer/crates/syntax/src/lib.rs index b68374848b90..c1554c4b2942 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/lib.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/lib.rs @@ -40,6 +40,7 @@ pub mod ast; #[doc(hidden)] pub mod fuzz; pub mod hacks; +pub mod syntax_editor; pub mod ted; pub mod utils; @@ -65,7 +66,7 @@ pub use rowan::{ TokenAtOffset, WalkEvent, }; pub use rustc_lexer::unescape; -pub use smol_str::{format_smolstr, SmolStr, ToSmolStr}; +pub use smol_str::{format_smolstr, SmolStr, SmolStrBuilder, ToSmolStr}; /// `Parse` is the result of the parsing: a syntax tree and a collection of /// errors. diff --git a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs new file mode 100644 index 000000000000..eb114f5e5f1f --- /dev/null +++ b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs @@ -0,0 +1,611 @@ +//! Syntax Tree editor +//! +//! Inspired by Roslyn's [`SyntaxEditor`], but is temporarily built upon mutable syntax tree editing. +//! +//! [`SyntaxEditor`]: https://github.com/dotnet/roslyn/blob/43b0b05cc4f492fd5de00f6f6717409091df8daa/src/Workspaces/Core/Portable/Editing/SyntaxEditor.cs + +use std::{ + num::NonZeroU32, + ops::RangeInclusive, + sync::atomic::{AtomicU32, Ordering}, +}; + +use rowan::TextRange; +use rustc_hash::FxHashMap; + +use crate::{SyntaxElement, SyntaxNode, SyntaxToken}; + +mod edit_algo; +mod mapping; + +pub use mapping::{SyntaxMapping, SyntaxMappingBuilder}; + +#[derive(Debug)] +pub struct SyntaxEditor { + root: SyntaxNode, + changes: Vec, + mappings: SyntaxMapping, + annotations: Vec<(SyntaxElement, SyntaxAnnotation)>, +} + +impl SyntaxEditor { + /// Creates a syntax editor to start editing from `root` + pub fn new(root: SyntaxNode) -> Self { + Self { root, changes: vec![], mappings: SyntaxMapping::new(), annotations: vec![] } + } + + pub fn add_annotation(&mut self, element: impl Element, annotation: SyntaxAnnotation) { + self.annotations.push((element.syntax_element(), annotation)) + } + + pub fn merge(&mut self, mut other: SyntaxEditor) { + debug_assert!( + self.root == other.root || other.root.ancestors().any(|node| node == self.root), + "{:?} is not in the same tree as {:?}", + other.root, + self.root + ); + + self.changes.append(&mut other.changes); + self.mappings.merge(other.mappings); + self.annotations.append(&mut other.annotations); + } + + pub fn insert(&mut self, position: Position, element: impl Element) { + debug_assert!(is_ancestor_or_self(&position.parent(), &self.root)); + self.changes.push(Change::Insert(position, element.syntax_element())) + } + + pub fn insert_all(&mut self, position: Position, elements: Vec) { + debug_assert!(is_ancestor_or_self(&position.parent(), &self.root)); + self.changes.push(Change::InsertAll(position, elements)) + } + + pub fn delete(&mut self, element: impl Element) { + let element = element.syntax_element(); + debug_assert!(is_ancestor_or_self_of_element(&element, &self.root)); + debug_assert!( + !matches!(&element, SyntaxElement::Node(node) if node == &self.root), + "should not delete root node" + ); + self.changes.push(Change::Replace(element.syntax_element(), None)); + } + + pub fn replace(&mut self, old: impl Element, new: impl Element) { + let old = old.syntax_element(); + debug_assert!(is_ancestor_or_self_of_element(&old, &self.root)); + self.changes.push(Change::Replace(old.syntax_element(), Some(new.syntax_element()))); + } + + pub fn replace_with_many(&mut self, old: impl Element, new: Vec) { + let old = old.syntax_element(); + debug_assert!(is_ancestor_or_self_of_element(&old, &self.root)); + debug_assert!( + !(matches!(&old, SyntaxElement::Node(node) if node == &self.root) && new.len() > 1), + "cannot replace root node with many elements" + ); + self.changes.push(Change::ReplaceWithMany(old.syntax_element(), new)); + } + + pub fn replace_all(&mut self, range: RangeInclusive, new: Vec) { + if range.start() == range.end() { + self.replace_with_many(range.start(), new); + return; + } + + debug_assert!(is_ancestor_or_self_of_element(range.start(), &self.root)); + self.changes.push(Change::ReplaceAll(range, new)) + } + + pub fn finish(self) -> SyntaxEdit { + edit_algo::apply_edits(self) + } +} + +/// Represents a completed [`SyntaxEditor`] operation. +pub struct SyntaxEdit { + old_root: SyntaxNode, + new_root: SyntaxNode, + changed_elements: Vec, + annotations: FxHashMap>, +} + +impl SyntaxEdit { + /// Root of the initial unmodified syntax tree. + pub fn old_root(&self) -> &SyntaxNode { + &self.old_root + } + + /// Root of the modified syntax tree. + pub fn new_root(&self) -> &SyntaxNode { + &self.new_root + } + + /// Which syntax elements in the modified syntax tree were inserted or + /// modified as part of the edit. + /// + /// Note that for syntax nodes, only the upper-most parent of a set of + /// changes is included, not any child elements that may have been modified. + pub fn changed_elements(&self) -> &[SyntaxElement] { + self.changed_elements.as_slice() + } + + /// Finds which syntax elements have been annotated with the given + /// annotation. + /// + /// Note that an annotation might not appear in the modified syntax tree if + /// the syntax elements that were annotated did not make it into the final + /// syntax tree. + pub fn find_annotation(&self, annotation: SyntaxAnnotation) -> &[SyntaxElement] { + self.annotations.get(&annotation).as_ref().map_or(&[], |it| it.as_slice()) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[repr(transparent)] +pub struct SyntaxAnnotation(NonZeroU32); + +impl SyntaxAnnotation { + /// Creates a unique syntax annotation to attach data to. + pub fn new() -> Self { + static COUNTER: AtomicU32 = AtomicU32::new(1); + + // Only consistency within a thread matters, as SyntaxElements are !Send + let id = COUNTER.fetch_add(1, Ordering::Relaxed); + + Self(NonZeroU32::new(id).expect("syntax annotation id overflow")) + } +} + +impl Default for SyntaxAnnotation { + fn default() -> Self { + Self::new() + } +} + +/// Position describing where to insert elements +#[derive(Debug)] +pub struct Position { + repr: PositionRepr, +} + +impl Position { + pub(crate) fn parent(&self) -> SyntaxNode { + self.place().0 + } + + pub(crate) fn place(&self) -> (SyntaxNode, usize) { + match &self.repr { + PositionRepr::FirstChild(parent) => (parent.clone(), 0), + PositionRepr::After(child) => (child.parent().unwrap(), child.index() + 1), + } + } +} + +#[derive(Debug)] +enum PositionRepr { + FirstChild(SyntaxNode), + After(SyntaxElement), +} + +impl Position { + pub fn after(elem: impl Element) -> Position { + let repr = PositionRepr::After(elem.syntax_element()); + Position { repr } + } + + pub fn before(elem: impl Element) -> Position { + let elem = elem.syntax_element(); + let repr = match elem.prev_sibling_or_token() { + Some(it) => PositionRepr::After(it), + None => PositionRepr::FirstChild(elem.parent().unwrap()), + }; + Position { repr } + } + + pub fn first_child_of(node: &(impl Into + Clone)) -> Position { + let repr = PositionRepr::FirstChild(node.clone().into()); + Position { repr } + } + + pub fn last_child_of(node: &(impl Into + Clone)) -> Position { + let node = node.clone().into(); + let repr = match node.last_child_or_token() { + Some(it) => PositionRepr::After(it), + None => PositionRepr::FirstChild(node), + }; + Position { repr } + } +} + +#[derive(Debug)] +enum Change { + /// Inserts a single element at the specified position. + Insert(Position, SyntaxElement), + /// Inserts many elements in-order at the specified position. + InsertAll(Position, Vec), + /// Represents both a replace single element and a delete element operation. + Replace(SyntaxElement, Option), + /// Replaces a single element with many elements. + ReplaceWithMany(SyntaxElement, Vec), + /// Replaces a range of elements with another list of elements. + /// Range will always have start != end. + ReplaceAll(RangeInclusive, Vec), +} + +impl Change { + fn target_range(&self) -> TextRange { + match self { + Change::Insert(target, _) | Change::InsertAll(target, _) => match &target.repr { + PositionRepr::FirstChild(parent) => TextRange::at( + parent.first_child_or_token().unwrap().text_range().start(), + 0.into(), + ), + PositionRepr::After(child) => TextRange::at(child.text_range().end(), 0.into()), + }, + Change::Replace(target, _) | Change::ReplaceWithMany(target, _) => target.text_range(), + Change::ReplaceAll(range, _) => { + range.start().text_range().cover(range.end().text_range()) + } + } + } + + fn target_parent(&self) -> SyntaxNode { + match self { + Change::Insert(target, _) | Change::InsertAll(target, _) => target.parent(), + Change::Replace(target, _) | Change::ReplaceWithMany(target, _) => match target { + SyntaxElement::Node(target) => target.parent().unwrap_or_else(|| target.clone()), + SyntaxElement::Token(target) => target.parent().unwrap(), + }, + Change::ReplaceAll(target, _) => target.start().parent().unwrap(), + } + } + + fn change_kind(&self) -> ChangeKind { + match self { + Change::Insert(_, _) | Change::InsertAll(_, _) => ChangeKind::Insert, + Change::Replace(_, _) | Change::ReplaceWithMany(_, _) => ChangeKind::Replace, + Change::ReplaceAll(_, _) => ChangeKind::ReplaceRange, + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +enum ChangeKind { + Insert, + ReplaceRange, + Replace, +} + +/// Utility trait to allow calling syntax editor functions with references or owned +/// nodes. Do not use outside of this module. +pub trait Element { + fn syntax_element(self) -> SyntaxElement; +} + +impl Element for &'_ E { + fn syntax_element(self) -> SyntaxElement { + self.clone().syntax_element() + } +} + +impl Element for SyntaxElement { + fn syntax_element(self) -> SyntaxElement { + self + } +} + +impl Element for SyntaxNode { + fn syntax_element(self) -> SyntaxElement { + self.into() + } +} + +impl Element for SyntaxToken { + fn syntax_element(self) -> SyntaxElement { + self.into() + } +} + +fn is_ancestor_or_self(node: &SyntaxNode, ancestor: &SyntaxNode) -> bool { + node == ancestor || node.ancestors().any(|it| &it == ancestor) +} + +fn is_ancestor_or_self_of_element(node: &SyntaxElement, ancestor: &SyntaxNode) -> bool { + matches!(node, SyntaxElement::Node(node) if node == ancestor) + || node.ancestors().any(|it| &it == ancestor) +} + +#[cfg(test)] +mod tests { + use expect_test::expect; + use itertools::Itertools; + + use crate::{ + ast::{self, make, HasName}, + AstNode, + }; + + use super::*; + + fn make_ident_pat( + editor: Option<&mut SyntaxEditor>, + ref_: bool, + mut_: bool, + name: ast::Name, + ) -> ast::IdentPat { + let ast = make::ident_pat(ref_, mut_, name.clone()).clone_for_update(); + + if let Some(editor) = editor { + let mut mapping = SyntaxMappingBuilder::new(ast.syntax().clone()); + mapping.map_node(name.syntax().clone(), ast.name().unwrap().syntax().clone()); + mapping.finish(editor); + } + + ast + } + + fn make_let_stmt( + editor: Option<&mut SyntaxEditor>, + pattern: ast::Pat, + ty: Option, + initializer: Option, + ) -> ast::LetStmt { + let ast = + make::let_stmt(pattern.clone(), ty.clone(), initializer.clone()).clone_for_update(); + + if let Some(editor) = editor { + let mut mapping = SyntaxMappingBuilder::new(ast.syntax().clone()); + mapping.map_node(pattern.syntax().clone(), ast.pat().unwrap().syntax().clone()); + if let Some(input) = ty { + mapping.map_node(input.syntax().clone(), ast.ty().unwrap().syntax().clone()); + } + if let Some(input) = initializer { + mapping + .map_node(input.syntax().clone(), ast.initializer().unwrap().syntax().clone()); + } + mapping.finish(editor); + } + + ast + } + + fn make_block_expr( + editor: Option<&mut SyntaxEditor>, + stmts: impl IntoIterator, + tail_expr: Option, + ) -> ast::BlockExpr { + let stmts = stmts.into_iter().collect_vec(); + let input = stmts.iter().map(|it| it.syntax().clone()).collect_vec(); + + let ast = make::block_expr(stmts, tail_expr.clone()).clone_for_update(); + + if let Some((editor, stmt_list)) = editor.zip(ast.stmt_list()) { + let mut mapping = SyntaxMappingBuilder::new(stmt_list.syntax().clone()); + + mapping.map_children( + input.into_iter(), + stmt_list.statements().map(|it| it.syntax().clone()), + ); + + if let Some((input, output)) = tail_expr.zip(stmt_list.tail_expr()) { + mapping.map_node(input.syntax().clone(), output.syntax().clone()); + } + + mapping.finish(editor); + } + + ast + } + + #[test] + fn basic_usage() { + let root = make::match_arm( + [make::wildcard_pat().into()], + None, + make::expr_tuple([ + make::expr_bin_op( + make::expr_literal("2").into(), + ast::BinaryOp::ArithOp(ast::ArithOp::Add), + make::expr_literal("2").into(), + ), + make::expr_literal("true").into(), + ]), + ); + + let to_wrap = root.syntax().descendants().find_map(ast::TupleExpr::cast).unwrap(); + let to_replace = root.syntax().descendants().find_map(ast::BinExpr::cast).unwrap(); + + let mut editor = SyntaxEditor::new(root.syntax().clone()); + + let name = make::name("var_name"); + let name_ref = make::name_ref("var_name").clone_for_update(); + + let placeholder_snippet = SyntaxAnnotation::new(); + editor.add_annotation(name.syntax(), placeholder_snippet); + editor.add_annotation(name_ref.syntax(), placeholder_snippet); + + let make_ident_pat = make_ident_pat(Some(&mut editor), false, false, name); + let make_let_stmt = make_let_stmt( + Some(&mut editor), + make_ident_pat.into(), + None, + Some(to_replace.clone().into()), + ); + let new_block = make_block_expr( + Some(&mut editor), + [make_let_stmt.into()], + Some(to_wrap.clone().into()), + ); + + editor.replace(to_replace.syntax(), name_ref.syntax()); + editor.replace(to_wrap.syntax(), new_block.syntax()); + + let edit = editor.finish(); + + let expect = expect![[r#" + _ => { + let var_name = 2 + 2; + (var_name, true) + }"#]]; + expect.assert_eq(&edit.new_root.to_string()); + + assert_eq!(edit.find_annotation(placeholder_snippet).len(), 2); + assert!(edit + .annotations + .iter() + .flat_map(|(_, elements)| elements) + .all(|element| element.ancestors().any(|it| &it == edit.new_root()))) + } + + #[test] + fn test_insert_independent() { + let root = make::block_expr( + [make::let_stmt( + make::ext::simple_ident_pat(make::name("second")).into(), + None, + Some(make::expr_literal("2").into()), + ) + .into()], + None, + ); + + let second_let = root.syntax().descendants().find_map(ast::LetStmt::cast).unwrap(); + + let mut editor = SyntaxEditor::new(root.syntax().clone()); + + editor.insert( + Position::first_child_of(root.stmt_list().unwrap().syntax()), + make_let_stmt( + None, + make::ext::simple_ident_pat(make::name("first")).into(), + None, + Some(make::expr_literal("1").into()), + ) + .syntax(), + ); + + editor.insert( + Position::after(second_let.syntax()), + make_let_stmt( + None, + make::ext::simple_ident_pat(make::name("third")).into(), + None, + Some(make::expr_literal("3").into()), + ) + .syntax(), + ); + + let edit = editor.finish(); + + let expect = expect![[r#" + let first = 1;{ + let second = 2;let third = 3; + }"#]]; + expect.assert_eq(&edit.new_root.to_string()); + } + + #[test] + fn test_insert_dependent() { + let root = make::block_expr( + [], + Some( + make::block_expr( + [make::let_stmt( + make::ext::simple_ident_pat(make::name("second")).into(), + None, + Some(make::expr_literal("2").into()), + ) + .into()], + None, + ) + .into(), + ), + ); + + let inner_block = + root.syntax().descendants().flat_map(ast::BlockExpr::cast).nth(1).unwrap(); + let second_let = root.syntax().descendants().find_map(ast::LetStmt::cast).unwrap(); + + let mut editor = SyntaxEditor::new(root.syntax().clone()); + + let new_block_expr = + make_block_expr(Some(&mut editor), [], Some(ast::Expr::BlockExpr(inner_block.clone()))); + + let first_let = make_let_stmt( + Some(&mut editor), + make::ext::simple_ident_pat(make::name("first")).into(), + None, + Some(make::expr_literal("1").into()), + ); + + let third_let = make_let_stmt( + Some(&mut editor), + make::ext::simple_ident_pat(make::name("third")).into(), + None, + Some(make::expr_literal("3").into()), + ); + + editor.insert( + Position::first_child_of(inner_block.stmt_list().unwrap().syntax()), + first_let.syntax(), + ); + editor.insert(Position::after(second_let.syntax()), third_let.syntax()); + editor.replace(inner_block.syntax(), new_block_expr.syntax()); + + let edit = editor.finish(); + + let expect = expect![[r#" + { + { + let first = 1;{ + let second = 2;let third = 3; + } + } + }"#]]; + expect.assert_eq(&edit.new_root.to_string()); + } + + #[test] + fn test_replace_root_with_dependent() { + let root = make::block_expr( + [make::let_stmt( + make::ext::simple_ident_pat(make::name("second")).into(), + None, + Some(make::expr_literal("2").into()), + ) + .into()], + None, + ); + + let inner_block = root.clone(); + + let mut editor = SyntaxEditor::new(root.syntax().clone()); + + let new_block_expr = + make_block_expr(Some(&mut editor), [], Some(ast::Expr::BlockExpr(inner_block.clone()))); + + let first_let = make_let_stmt( + Some(&mut editor), + make::ext::simple_ident_pat(make::name("first")).into(), + None, + Some(make::expr_literal("1").into()), + ); + + editor.insert( + Position::first_child_of(inner_block.stmt_list().unwrap().syntax()), + first_let.syntax(), + ); + editor.replace(inner_block.syntax(), new_block_expr.syntax()); + + let edit = editor.finish(); + + let expect = expect![[r#" + { + let first = 1;{ + let second = 2; + } + }"#]]; + expect.assert_eq(&edit.new_root.to_string()); + } +} diff --git a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edit_algo.rs b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edit_algo.rs new file mode 100644 index 000000000000..b769c941105b --- /dev/null +++ b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edit_algo.rs @@ -0,0 +1,367 @@ +//! Implementation of applying changes to a syntax tree. + +use std::{cmp::Ordering, collections::VecDeque, ops::RangeInclusive}; + +use rowan::TextRange; +use rustc_hash::FxHashMap; + +use crate::{ + syntax_editor::{mapping::MissingMapping, Change, ChangeKind, PositionRepr}, + SyntaxElement, SyntaxNode, SyntaxNodePtr, +}; + +use super::{SyntaxEdit, SyntaxEditor}; + +pub(super) fn apply_edits(editor: SyntaxEditor) -> SyntaxEdit { + // Algorithm overview: + // + // - Sort changes by (range, type) + // - Ensures that parent edits are before child edits + // - Ensures that inserts will be guaranteed to be inserted at the right range + // - Validate changes + // - Checking for invalid changes is easy since the changes will be sorted by range + // - Fixup change targets + // - standalone change? map to original syntax tree + // - dependent change? + // - try to map to parent change (either independent or another dependent) + // - note: need to keep track of a parent change stack, since a change can be a parent of multiple changes + // - Apply changes + // - find changes to apply to real tree by applying nested changes first + // - changed nodes become part of the changed node set (useful for the formatter to only change those parts) + // - Propagate annotations + + let SyntaxEditor { root, mut changes, mappings, annotations } = editor; + + let mut node_depths = FxHashMap::::default(); + let mut get_node_depth = |node: SyntaxNode| { + *node_depths.entry(node).or_insert_with_key(|node| node.ancestors().count()) + }; + + // Sort changes by range, then depth, then change kind, so that we can: + // - ensure that parent edits are ordered before child edits + // - ensure that inserts will be guaranteed to be inserted at the right range + // - easily check for disjoint replace ranges + changes.sort_by(|a, b| { + a.target_range() + .start() + .cmp(&b.target_range().start()) + .then_with(|| { + let a_target = a.target_parent(); + let b_target = b.target_parent(); + + if a_target == b_target { + return Ordering::Equal; + } + + get_node_depth(a_target).cmp(&get_node_depth(b_target)) + }) + .then(a.change_kind().cmp(&b.change_kind())) + }); + + let disjoint_replaces_ranges = changes + .iter() + .zip(changes.iter().skip(1)) + .filter(|(l, r)| { + // We only care about checking for disjoint replace ranges + matches!( + (l.change_kind(), r.change_kind()), + ( + ChangeKind::Replace | ChangeKind::ReplaceRange, + ChangeKind::Replace | ChangeKind::ReplaceRange + ) + ) + }) + .all(|(l, r)| { + get_node_depth(l.target_parent()) != get_node_depth(r.target_parent()) + || l.target_range().intersect(r.target_range()).is_none() + }); + + if stdx::never!( + !disjoint_replaces_ranges, + "some replace change ranges intersect: {:?}", + changes + ) { + return SyntaxEdit { + old_root: root.clone(), + new_root: root, + annotations: Default::default(), + changed_elements: vec![], + }; + } + + #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] + struct DependentChange { + parent: u32, + child: u32, + } + + // Build change tree + let mut changed_ancestors: VecDeque = VecDeque::new(); + let mut dependent_changes = vec![]; + let mut independent_changes = vec![]; + + for (change_index, change) in changes.iter().enumerate() { + // Check if this change is dependent on another change (i.e. it's contained within another range) + if let Some(index) = changed_ancestors + .iter() + .rev() + .position(|ancestor| ancestor.affected_range().contains_range(change.target_range())) + { + // Pop off any ancestors that aren't applicable + changed_ancestors.drain((index + 1)..); + + // FIXME: Resolve changes that depend on a range of elements + let ancestor = &changed_ancestors[index]; + + dependent_changes.push(DependentChange { + parent: ancestor.change_index as u32, + child: change_index as u32, + }); + } else { + // This change is independent of any other change + + // Drain the changed ancestors since we're no longer in a set of dependent changes + changed_ancestors.drain(..); + + independent_changes.push(change_index as u32); + } + + // Add to changed ancestors, if applicable + match change { + Change::Insert(_, _) | Change::InsertAll(_, _) => {} + Change::Replace(target, _) | Change::ReplaceWithMany(target, _) => { + changed_ancestors.push_back(ChangedAncestor::single(target, change_index)) + } + Change::ReplaceAll(range, _) => { + changed_ancestors.push_back(ChangedAncestor::multiple(range, change_index)) + } + } + } + + // Map change targets to the correct syntax nodes + let tree_mutator = TreeMutator::new(&root); + let mut changed_elements = vec![]; + + for index in independent_changes { + match &mut changes[index as usize] { + Change::Insert(target, _) | Change::InsertAll(target, _) => { + match &mut target.repr { + PositionRepr::FirstChild(parent) => { + *parent = tree_mutator.make_syntax_mut(parent); + } + PositionRepr::After(child) => { + *child = tree_mutator.make_element_mut(child); + } + }; + } + Change::Replace(target, _) | Change::ReplaceWithMany(target, _) => { + *target = tree_mutator.make_element_mut(target); + } + Change::ReplaceAll(range, _) => { + let start = tree_mutator.make_element_mut(range.start()); + let end = tree_mutator.make_element_mut(range.end()); + + *range = start..=end; + } + } + + // Collect changed elements + match &changes[index as usize] { + Change::Insert(_, element) => changed_elements.push(element.clone()), + Change::InsertAll(_, elements) => changed_elements.extend(elements.iter().cloned()), + Change::Replace(_, Some(element)) => changed_elements.push(element.clone()), + Change::Replace(_, None) => {} + Change::ReplaceWithMany(_, elements) => { + changed_elements.extend(elements.iter().cloned()) + } + Change::ReplaceAll(_, elements) => changed_elements.extend(elements.iter().cloned()), + } + } + + for DependentChange { parent, child } in dependent_changes.into_iter() { + let (input_ancestor, output_ancestor) = match &changes[parent as usize] { + // No change will depend on an insert since changes can only depend on nodes in the root tree + Change::Insert(_, _) | Change::InsertAll(_, _) => unreachable!(), + Change::Replace(target, Some(new_target)) => { + (to_owning_node(target), to_owning_node(new_target)) + } + // Silently drop outdated change + Change::Replace(_, None) => continue, + Change::ReplaceAll(_, _) | Change::ReplaceWithMany(_, _) => { + unimplemented!("cannot resolve changes that depend on replacing many elements") + } + }; + + let upmap_target_node = |target: &SyntaxNode| { + match mappings.upmap_child(target, &input_ancestor, &output_ancestor) { + Ok(it) => it, + Err(MissingMapping(current)) => unreachable!("no mappings exist between {current:?} (ancestor of {input_ancestor:?}) and {output_ancestor:?}"), + } + }; + + let upmap_target = |target: &SyntaxElement| { + match mappings.upmap_child_element(target, &input_ancestor, &output_ancestor) { + Ok(it) => it, + Err(MissingMapping(current)) => unreachable!("no mappings exist between {current:?} (ancestor of {input_ancestor:?}) and {output_ancestor:?}"), + } + }; + + match &mut changes[child as usize] { + Change::Insert(target, _) | Change::InsertAll(target, _) => match &mut target.repr { + PositionRepr::FirstChild(parent) => { + *parent = upmap_target_node(parent); + } + PositionRepr::After(child) => { + *child = upmap_target(child); + } + }, + Change::Replace(target, _) | Change::ReplaceWithMany(target, _) => { + *target = upmap_target(target); + } + Change::ReplaceAll(range, _) => { + *range = upmap_target(range.start())..=upmap_target(range.end()); + } + } + } + + // Apply changes + let mut root = tree_mutator.mutable_clone; + + for change in changes { + match change { + Change::Insert(position, element) => { + let (parent, index) = position.place(); + parent.splice_children(index..index, vec![element]); + } + Change::InsertAll(position, elements) => { + let (parent, index) = position.place(); + parent.splice_children(index..index, elements); + } + Change::Replace(target, None) => { + target.detach(); + } + Change::Replace(SyntaxElement::Node(target), Some(new_target)) if target == root => { + root = new_target.into_node().expect("root node replacement should be a node"); + } + Change::Replace(target, Some(new_target)) => { + let parent = target.parent().unwrap(); + parent.splice_children(target.index()..target.index() + 1, vec![new_target]); + } + Change::ReplaceWithMany(target, elements) => { + let parent = target.parent().unwrap(); + parent.splice_children(target.index()..target.index() + 1, elements); + } + Change::ReplaceAll(range, elements) => { + let start = range.start().index(); + let end = range.end().index(); + let parent = range.start().parent().unwrap(); + parent.splice_children(start..end + 1, elements); + } + } + } + + // Propagate annotations + let annotations = annotations.into_iter().filter_map(|(element, annotation)| { + match mappings.upmap_element(&element, &root) { + // Needed to follow the new tree to find the resulting element + Some(Ok(mapped)) => Some((mapped, annotation)), + // Element did not need to be mapped + None => Some((element, annotation)), + // Element did not make it to the final tree + Some(Err(_)) => None, + } + }); + + let mut annotation_groups = FxHashMap::default(); + + for (element, annotation) in annotations { + annotation_groups.entry(annotation).or_insert(vec![]).push(element); + } + + SyntaxEdit { + old_root: tree_mutator.immutable, + new_root: root, + changed_elements, + annotations: annotation_groups, + } +} + +fn to_owning_node(element: &SyntaxElement) -> SyntaxNode { + match element { + SyntaxElement::Node(node) => node.clone(), + SyntaxElement::Token(token) => token.parent().unwrap().clone(), + } +} + +struct ChangedAncestor { + kind: ChangedAncestorKind, + change_index: usize, +} + +enum ChangedAncestorKind { + Single { node: SyntaxNode }, + Range { _changed_elements: RangeInclusive, _in_parent: SyntaxNode }, +} + +impl ChangedAncestor { + fn single(element: &SyntaxElement, change_index: usize) -> Self { + let kind = match element { + SyntaxElement::Node(node) => ChangedAncestorKind::Single { node: node.clone() }, + SyntaxElement::Token(token) => { + ChangedAncestorKind::Single { node: token.parent().unwrap() } + } + }; + + Self { kind, change_index } + } + + fn multiple(range: &RangeInclusive, change_index: usize) -> Self { + Self { + kind: ChangedAncestorKind::Range { + _changed_elements: range.clone(), + _in_parent: range.start().parent().unwrap(), + }, + change_index, + } + } + + fn affected_range(&self) -> TextRange { + match &self.kind { + ChangedAncestorKind::Single { node } => node.text_range(), + ChangedAncestorKind::Range { _changed_elements: changed_nodes, _in_parent: _ } => { + TextRange::new( + changed_nodes.start().text_range().start(), + changed_nodes.end().text_range().end(), + ) + } + } + } +} + +struct TreeMutator { + immutable: SyntaxNode, + mutable_clone: SyntaxNode, +} + +impl TreeMutator { + fn new(immutable: &SyntaxNode) -> TreeMutator { + let immutable = immutable.clone(); + let mutable_clone = immutable.clone_for_update(); + TreeMutator { immutable, mutable_clone } + } + + fn make_element_mut(&self, element: &SyntaxElement) -> SyntaxElement { + match element { + SyntaxElement::Node(node) => SyntaxElement::Node(self.make_syntax_mut(node)), + SyntaxElement::Token(token) => { + let parent = self.make_syntax_mut(&token.parent().unwrap()); + parent.children_with_tokens().nth(token.index()).unwrap() + } + } + } + + fn make_syntax_mut(&self, node: &SyntaxNode) -> SyntaxNode { + let ptr = SyntaxNodePtr::new(node); + ptr.to_node(&self.mutable_clone) + } +} diff --git a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/mapping.rs b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/mapping.rs new file mode 100644 index 000000000000..9bb5e6d93382 --- /dev/null +++ b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/mapping.rs @@ -0,0 +1,272 @@ +//! Maps syntax elements through disjoint syntax nodes. +//! +//! [`SyntaxMappingBuilder`] should be used to create mappings to add to a [`SyntaxEditor`] + +use itertools::Itertools; +use rustc_hash::FxHashMap; + +use crate::{SyntaxElement, SyntaxNode}; + +use super::SyntaxEditor; + +#[derive(Debug, Default)] +pub struct SyntaxMapping { + // important information to keep track of: + // node -> node + // token -> token (implicit in mappings) + // input parent -> output parent (for deep lookups) + + // mappings -> parents + entry_parents: Vec, + node_mappings: FxHashMap, +} + +impl SyntaxMapping { + pub fn new() -> Self { + Self::default() + } + + /// Like [`SyntaxMapping::upmap_child`] but for syntax elements. + pub fn upmap_child_element( + &self, + child: &SyntaxElement, + input_ancestor: &SyntaxNode, + output_ancestor: &SyntaxNode, + ) -> Result { + match child { + SyntaxElement::Node(node) => { + self.upmap_child(node, input_ancestor, output_ancestor).map(SyntaxElement::Node) + } + SyntaxElement::Token(token) => { + let upmap_parent = + self.upmap_child(&token.parent().unwrap(), input_ancestor, output_ancestor)?; + + let element = upmap_parent.children_with_tokens().nth(token.index()).unwrap(); + debug_assert!( + element.as_token().is_some_and(|it| it.kind() == token.kind()), + "token upmapping mapped to the wrong node ({token:?} -> {element:?})" + ); + + Ok(element) + } + } + } + + /// Maps a child node of the input ancestor to the corresponding node in + /// the output ancestor. + pub fn upmap_child( + &self, + child: &SyntaxNode, + input_ancestor: &SyntaxNode, + output_ancestor: &SyntaxNode, + ) -> Result { + debug_assert!( + child == input_ancestor + || child.ancestors().any(|ancestor| &ancestor == input_ancestor) + ); + + // Build a list mapping up to the first mappable ancestor + let to_first_upmap = if child != input_ancestor { + std::iter::successors(Some((child.index(), child.clone())), |(_, current)| { + let parent = current.parent().unwrap(); + + if &parent == input_ancestor { + return None; + } + + Some((parent.index(), parent)) + }) + .map(|(i, _)| i) + .collect::>() + } else { + vec![] + }; + + // Progressively up-map the input ancestor until we get to the output ancestor + let to_output_ancestor = if input_ancestor != output_ancestor { + self.upmap_to_ancestor(input_ancestor, output_ancestor)? + } else { + vec![] + }; + + let to_map_down = + to_output_ancestor.into_iter().rev().chain(to_first_upmap.into_iter().rev()); + + let mut target = output_ancestor.clone(); + + for index in to_map_down { + target = target + .children_with_tokens() + .nth(index) + .and_then(|it| it.into_node()) + .expect("equivalent ancestor node should be present in target tree"); + } + + debug_assert_eq!(child.kind(), target.kind()); + + Ok(target) + } + + fn upmap_to_ancestor( + &self, + input_ancestor: &SyntaxNode, + output_ancestor: &SyntaxNode, + ) -> Result, MissingMapping> { + let mut current = + self.upmap_node_single(input_ancestor).unwrap_or_else(|| input_ancestor.clone()); + let mut upmap_chain = vec![current.index()]; + + loop { + let Some(parent) = current.parent() else { break }; + + if &parent == output_ancestor { + return Ok(upmap_chain); + } + + current = match self.upmap_node_single(&parent) { + Some(next) => next, + None => parent, + }; + upmap_chain.push(current.index()); + } + + Err(MissingMapping(current)) + } + + pub fn upmap_element( + &self, + input: &SyntaxElement, + output_root: &SyntaxNode, + ) -> Option> { + match input { + SyntaxElement::Node(node) => { + Some(self.upmap_node(node, output_root)?.map(SyntaxElement::Node)) + } + SyntaxElement::Token(token) => { + let upmap_parent = match self.upmap_node(&token.parent().unwrap(), output_root)? { + Ok(it) => it, + Err(err) => return Some(Err(err)), + }; + + let element = upmap_parent.children_with_tokens().nth(token.index()).unwrap(); + debug_assert!( + element.as_token().is_some_and(|it| it.kind() == token.kind()), + "token upmapping mapped to the wrong node ({token:?} -> {element:?})" + ); + + Some(Ok(element)) + } + } + } + + pub fn upmap_node( + &self, + input: &SyntaxNode, + output_root: &SyntaxNode, + ) -> Option> { + // Try to follow the mapping tree, if it exists + let input_mapping = self.upmap_node_single(input); + let input_ancestor = + input.ancestors().find_map(|ancestor| self.upmap_node_single(&ancestor)); + + match (input_mapping, input_ancestor) { + (Some(input_mapping), _) => { + // A mapping exists at the input, follow along the tree + Some(self.upmap_child(&input_mapping, &input_mapping, output_root)) + } + (None, Some(input_ancestor)) => { + // A mapping exists at an ancestor, follow along the tree + Some(self.upmap_child(input, &input_ancestor, output_root)) + } + (None, None) => { + // No mapping exists at all, is the same position in the final tree + None + } + } + } + + pub fn merge(&mut self, mut other: SyntaxMapping) { + // Remap other's entry parents to be after the current list of entry parents + let remap_base: u32 = self.entry_parents.len().try_into().unwrap(); + + self.entry_parents.append(&mut other.entry_parents); + self.node_mappings.extend(other.node_mappings.into_iter().map(|(node, entry)| { + (node, MappingEntry { parent: entry.parent + remap_base, ..entry }) + })); + } + + /// Follows the input one step along the syntax mapping tree + fn upmap_node_single(&self, input: &SyntaxNode) -> Option { + let MappingEntry { parent, child_slot } = self.node_mappings.get(input)?; + + let output = self.entry_parents[*parent as usize] + .children_with_tokens() + .nth(*child_slot as usize) + .and_then(SyntaxElement::into_node) + .unwrap(); + + debug_assert_eq!(input.kind(), output.kind()); + Some(output) + } + + fn add_mapping(&mut self, syntax_mapping: SyntaxMappingBuilder) { + let SyntaxMappingBuilder { parent_node, node_mappings } = syntax_mapping; + + let parent_entry: u32 = self.entry_parents.len().try_into().unwrap(); + self.entry_parents.push(parent_node); + + let node_entries = node_mappings + .into_iter() + .map(|(node, slot)| (node, MappingEntry { parent: parent_entry, child_slot: slot })); + + self.node_mappings.extend(node_entries); + } +} + +#[derive(Debug)] +pub struct SyntaxMappingBuilder { + parent_node: SyntaxNode, + node_mappings: Vec<(SyntaxNode, u32)>, +} + +impl SyntaxMappingBuilder { + pub fn new(parent_node: SyntaxNode) -> Self { + Self { parent_node, node_mappings: vec![] } + } + + pub fn map_node(&mut self, input: SyntaxNode, output: SyntaxNode) { + debug_assert_eq!(output.parent().as_ref(), Some(&self.parent_node)); + self.node_mappings.push((input, output.index() as u32)); + } + + pub fn map_children( + &mut self, + input: impl Iterator, + output: impl Iterator, + ) { + for pairs in input.zip_longest(output) { + let (input, output) = match pairs { + itertools::EitherOrBoth::Both(l, r) => (l, r), + itertools::EitherOrBoth::Left(_) => { + unreachable!("mapping more input nodes than there are output nodes") + } + itertools::EitherOrBoth::Right(_) => break, + }; + + self.map_node(input, output); + } + } + + pub fn finish(self, editor: &mut SyntaxEditor) { + editor.mappings.add_mapping(self); + } +} + +#[derive(Debug)] +pub struct MissingMapping(pub SyntaxNode); + +#[derive(Debug, Clone, Copy)] +struct MappingEntry { + parent: u32, + child_slot: u32, +} diff --git a/src/tools/rust-analyzer/crates/syntax/src/ted.rs b/src/tools/rust-analyzer/crates/syntax/src/ted.rs index 29788d05e845..8592df159755 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ted.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ted.rs @@ -147,6 +147,11 @@ pub fn append_child_raw(node: &(impl Into + Clone), child: impl Elem insert_raw(position, child); } +pub fn prepend_child(node: &(impl Into + Clone), child: impl Element) { + let position = Position::first_child_of(node); + insert(position, child); +} + fn ws_before(position: &Position, new: &SyntaxElement) -> Option { let prev = match &position.repr { PositionRepr::FirstChild(_) => return None, 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 03e85a898ab0..593e31c2fbb8 100644 --- a/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs +++ b/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs @@ -2,8 +2,8 @@ use std::{iter, mem, str::FromStr, sync}; use base_db::{ - CrateDisplayName, CrateGraph, CrateId, CrateName, CrateOrigin, Dependency, Env, FileChange, - FileSet, LangCrateOrigin, SourceRoot, SourceRootDatabase, Version, VfsPath, + CrateDisplayName, CrateGraph, CrateId, CrateName, CrateOrigin, CrateWorkspaceData, Dependency, + Env, FileChange, FileSet, LangCrateOrigin, SourceRoot, SourceRootDatabase, Version, VfsPath, }; use cfg::CfgOptions; use hir_expand::{ @@ -13,7 +13,7 @@ use hir_expand::{ proc_macro::{ ProcMacro, ProcMacroExpander, ProcMacroExpansionError, ProcMacroKind, ProcMacrosBuilder, }, - FileRange, + quote, FileRange, }; use intern::Symbol; use rustc_hash::FxHashMap; @@ -95,8 +95,10 @@ pub trait WithFixture: Default + ExpandDatabase + SourceRootDatabase + 'static { fn test_crate(&self) -> CrateId { let crate_graph = self.crate_graph(); let mut it = crate_graph.iter(); - let res = it.next().unwrap(); - assert!(it.next().is_none()); + let mut res = it.next().unwrap(); + while crate_graph[res].origin.is_lang() { + res = it.next().unwrap(); + } res } } @@ -352,23 +354,27 @@ impl ChangeFixture { }; roots.push(root); - let mut change = ChangeWithProcMacros { - source_change, - 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(), - ), - }; + let mut change = + ChangeWithProcMacros { source_change, proc_macros: Some(proc_macros.build()) }; change.source_change.set_roots(roots); + change.source_change.set_ws_data( + crate_graph + .iter() + .zip(iter::repeat(From::from(CrateWorkspaceData { + proc_macro_cwd: None, + data_layout: target_data_layout, + toolchain, + }))) + .collect(), + ); change.source_change.set_crate_graph(crate_graph); ChangeFixture { file_position, files, change } } } -fn default_test_proc_macros() -> [(String, ProcMacro); 5] { +fn default_test_proc_macros() -> [(String, ProcMacro); 6] { [ ( r#" @@ -445,6 +451,21 @@ pub fn shorten(input: TokenStream) -> TokenStream { disabled: false, }, ), + ( + r#" +#[proc_macro_attribute] +pub fn issue_18089(_attr: TokenStream, _item: TokenStream) -> TokenStream { + loop {} +} +"# + .into(), + ProcMacro { + name: Symbol::intern("issue_18089"), + kind: ProcMacroKind::Attr, + expander: sync::Arc::new(Issue18089ProcMacroExpander), + disabled: false, + }, + ), ] } @@ -565,11 +586,41 @@ impl ProcMacroExpander for IdentityProcMacroExpander { _: Span, _: Span, _: Span, + _: Option, ) -> Result, ProcMacroExpansionError> { Ok(subtree.clone()) } } +// Expands to a macro_rules! macro, for issue #18089. +#[derive(Debug)] +struct Issue18089ProcMacroExpander; +impl ProcMacroExpander for Issue18089ProcMacroExpander { + fn expand( + &self, + subtree: &Subtree, + _: Option<&Subtree>, + _: &Env, + _: Span, + call_site: Span, + _: Span, + _: Option, + ) -> Result, ProcMacroExpansionError> { + let macro_name = &subtree.token_trees[1]; + Ok(quote! { call_site => + #[macro_export] + macro_rules! my_macro___ { + ($($token:tt)*) => {{ + }}; + } + + pub use my_macro___ as #macro_name; + + #subtree + }) + } +} + // Pastes the attribute input as its output #[derive(Debug)] struct AttributeInputReplaceProcMacroExpander; @@ -582,6 +633,7 @@ impl ProcMacroExpander for AttributeInputReplaceProcMacroExpander { _: Span, _: Span, _: Span, + _: Option, ) -> Result, ProcMacroExpansionError> { attrs .cloned() @@ -600,6 +652,7 @@ impl ProcMacroExpander for MirrorProcMacroExpander { _: Span, _: Span, _: Span, + _: Option, ) -> Result, ProcMacroExpansionError> { fn traverse(input: &Subtree) -> Subtree { let mut token_trees = vec![]; @@ -630,6 +683,7 @@ impl ProcMacroExpander for ShortenProcMacroExpander { _: Span, _: Span, _: Span, + _: Option, ) -> Result, ProcMacroExpansionError> { return Ok(traverse(input)); 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 3be4469beef0..67629fcf7cc5 100644 --- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs +++ b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs @@ -33,6 +33,7 @@ //! from: sized //! future: pin //! coroutine: pin +//! dispatch_from_dyn: unsize, pin //! hash: //! include: //! index: sized @@ -64,6 +65,7 @@ //! todo: panic //! unimplemented: panic //! column: +//! addr_of: #![rustc_coherence_is_core] @@ -169,7 +171,7 @@ pub mod default { macro_rules! impl_default { ($v:literal; $($t:ty)*) => { $( - impl const Default for $t { + impl Default for $t { fn default() -> Self { $v } @@ -420,6 +422,17 @@ pub mod ptr { } // endregion:coerce_unsized // endregion:non_null + + // region:addr_of + #[rustc_macro_transparency = "semitransparent"] + pub macro addr_of($place:expr) { + &raw const $place + } + #[rustc_macro_transparency = "semitransparent"] + pub macro addr_of_mut($place:expr) { + &raw mut $place + } + // endregion:addr_of } pub mod ops { @@ -673,7 +686,7 @@ pub mod ops { // endregion:fn // region:try mod try_ { - use super::super::convert::Infallible; + use crate::convert::Infallible; pub enum ControlFlow { #[lang = "Continue"] @@ -743,7 +756,7 @@ pub mod ops { // endregion:option // region:result // region:from - use super::super::convert::From; + use crate::convert::From; impl Try for Result { type Output = T; @@ -764,7 +777,7 @@ pub mod ops { impl> FromResidual> for Result { fn from_residual(residual: Result) -> Self { match residual { - Err(e) => Err(From::from(e)), + Err(e) => Err(F::from(e)), Ok(_) => loop {}, } } @@ -822,6 +835,24 @@ pub mod ops { } pub use self::coroutine::{Coroutine, CoroutineState}; // endregion:coroutine + + // region:dispatch_from_dyn + mod dispatch_from_dyn { + use crate::marker::Unsize; + + #[lang = "dispatch_from_dyn"] + pub trait DispatchFromDyn {} + + impl<'a, T: ?Sized + Unsize, U: ?Sized> DispatchFromDyn<&'a U> for &'a T {} + + impl<'a, T: ?Sized + Unsize, U: ?Sized> DispatchFromDyn<&'a mut U> for &'a mut T {} + + impl, U: ?Sized> DispatchFromDyn<*const U> for *const T {} + + impl, U: ?Sized> DispatchFromDyn<*mut U> for *mut T {} + } + pub use self::dispatch_from_dyn::DispatchFromDyn; + // endregion:dispatch_from_dyn } // region:eq @@ -1183,6 +1214,12 @@ pub mod pin { } } // endregion:deref + // region:dispatch_from_dyn + impl crate::ops::DispatchFromDyn> for Pin where + Ptr: crate::ops::DispatchFromDyn + { + } + // endregion:dispatch_from_dyn } // endregion:pin @@ -1309,7 +1346,10 @@ pub mod iter { self } // region:iterators - fn take(self, n: usize) -> crate::iter::Take { + fn take(self, n: usize) -> crate::iter::Take + where + Self: Sized, + { loop {} } fn filter_map(self, _f: F) -> crate::iter::FilterMap @@ -1435,6 +1475,19 @@ mod panicking { } // endregion:panic +// region:asm +mod arch { + #[rustc_builtin_macro] + pub macro asm("assembly template", $(operands,)* $(options($(option),*))?) { + /* compiler built-in */ + } + #[rustc_builtin_macro] + pub macro global_asm("assembly template", $(operands,)* $(options($(option),*))?) { + /* compiler built-in */ + } +} +// endregion:asm + #[macro_use] mod macros { // region:panic @@ -1447,16 +1500,6 @@ mod macros { } // endregion:panic - // region:asm - #[macro_export] - #[rustc_builtin_macro] - macro_rules! asm { - ($($arg:tt)*) => { - /* compiler built-in */ - }; - } - // endregion:asm - // region:assert #[macro_export] #[rustc_builtin_macro] diff --git a/src/tools/rust-analyzer/crates/tt/src/iter.rs b/src/tools/rust-analyzer/crates/tt/src/iter.rs index e96bed0319e1..587b903aa97a 100644 --- a/src/tools/rust-analyzer/crates/tt/src/iter.rs +++ b/src/tools/rust-analyzer/crates/tt/src/iter.rs @@ -57,6 +57,13 @@ impl<'a, S: Copy> TtIter<'a, S> { } } + pub fn expect_comma(&mut self) -> Result<(), ()> { + match self.expect_leaf()? { + Leaf::Punct(Punct { char: ',', .. }) => Ok(()), + _ => Err(()), + } + } + pub fn expect_ident(&mut self) -> Result<&'a Ident, ()> { match self.expect_leaf()? { Leaf::Ident(it) if it.sym != sym::underscore => Ok(it), diff --git a/src/tools/rust-analyzer/crates/vfs/src/lib.rs b/src/tools/rust-analyzer/crates/vfs/src/lib.rs index bc40e03c5a24..a26444e9ea23 100644 --- a/src/tools/rust-analyzer/crates/vfs/src/lib.rs +++ b/src/tools/rust-analyzer/crates/vfs/src/lib.rs @@ -67,7 +67,7 @@ pub struct FileId(u32); // pub struct FileId(NonMaxU32); impl FileId { - pub const MAX: u32 = 0x7fff_ffff; + const MAX: u32 = 0x7fff_ffff; #[inline] pub const fn from_raw(raw: u32) -> FileId { diff --git a/src/tools/rust-analyzer/docs/dev/lsp-extensions.md b/src/tools/rust-analyzer/docs/dev/lsp-extensions.md index 4786bd54d59b..b7bac4d29fad 100644 --- a/src/tools/rust-analyzer/docs/dev/lsp-extensions.md +++ b/src/tools/rust-analyzer/docs/dev/lsp-extensions.md @@ -1,5 +1,5 @@ $DIR/non-local-defs-impl.rs:18:1 + --> $DIR/non-local-defs-impl.rs:20:5 | -LL | impl Trait for &Local {} - | ^^^^^-----^^^^^------ - | | | - | | `&'_ Local` is not local - | | help: remove `&` to make the `impl` local - | `Trait` is not local +LL | fn foo() { + | -------- move the `impl` block outside of this function `foo` and up 3 bodies +LL | impl Trait for &Local {} + | ^^^^^-----^^^^^^----- + | | | + | | `Local` is not local + | `Trait` is not local | - = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` - = help: make this doc-test a standalone test with its own `fn main() { ... }` = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue note: the lint level is defined here --> $DIR/non-local-defs-impl.rs:11:9 diff --git a/tests/rustdoc-ui/doctest/non_local_defs.rs b/tests/rustdoc-ui/doctest/non_local_defs.rs index 83327eb1e3f4..a2f66c392231 100644 --- a/tests/rustdoc-ui/doctest/non_local_defs.rs +++ b/tests/rustdoc-ui/doctest/non_local_defs.rs @@ -4,8 +4,6 @@ //@ normalize-stderr-test: "tests/rustdoc-ui/doctest" -> "$$DIR" //@ normalize-stdout-test: "finished in \d+\.\d+s" -> "finished in $$TIME" -#![doc(test(attr(warn(non_local_definitions))))] - //! ``` //! #[macro_export] //! macro_rules! a_macro { () => {} } diff --git a/tests/rustdoc-ui/doctest/non_local_defs.stderr b/tests/rustdoc-ui/doctest/non_local_defs.stderr index 13cd2558793f..2b47e6b5bc4d 100644 --- a/tests/rustdoc-ui/doctest/non_local_defs.stderr +++ b/tests/rustdoc-ui/doctest/non_local_defs.stderr @@ -1,5 +1,5 @@ warning: non-local `macro_rules!` definition, `#[macro_export]` macro should be written at top level module - --> $DIR/non_local_defs.rs:11:1 + --> $DIR/non_local_defs.rs:9:1 | LL | macro_rules! a_macro { () => {} } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -7,11 +7,7 @@ LL | macro_rules! a_macro { () => {} } = help: remove the `#[macro_export]` or make this doc-test a standalone test with its own `fn main() { ... }` = note: a `macro_rules!` definition is non-local if it is nested inside an item and has a `#[macro_export]` attribute = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue -note: the lint level is defined here - --> $DIR/non_local_defs.rs:8:9 - | -LL | #![warn(non_local_definitions)] - | ^^^^^^^^^^^^^^^^^^^^^ + = note: `#[warn(non_local_definitions)]` on by default warning: 1 warning emitted diff --git a/tests/rustdoc-ui/doctest/non_local_defs.stdout b/tests/rustdoc-ui/doctest/non_local_defs.stdout index 61b4074886e4..bee195fcdd77 100644 --- a/tests/rustdoc-ui/doctest/non_local_defs.stdout +++ b/tests/rustdoc-ui/doctest/non_local_defs.stdout @@ -1,6 +1,6 @@ running 1 test -test $DIR/non_local_defs.rs - (line 9) ... ok +test $DIR/non_local_defs.rs - (line 7) ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME diff --git a/tests/rustdoc/const-display.rs b/tests/rustdoc/const-display.rs index ac55a6302f78..a71825d883d6 100644 --- a/tests/rustdoc/const-display.rs +++ b/tests/rustdoc/const-display.rs @@ -1,8 +1,6 @@ #![crate_name = "foo"] -#![unstable(feature = "humans", - reason = "who ever let humans program computers, we're apparently really bad at it", - issue = "none")] +#![stable(feature = "rust1", since = "1.0.0")] #![feature(foo, foo2)] #![feature(staged_api)] @@ -48,10 +46,18 @@ pub const unsafe fn foo2_gated() -> u32 { 42 } #[rustc_const_stable(feature = "rust1", since = "1.0.0")] pub const unsafe fn bar2_gated() -> u32 { 42 } -//@ has 'foo/fn.bar_not_gated.html' '//pre' 'pub const unsafe fn bar_not_gated() -> u32' -//@ !hasraw - '//span[@class="since"]' -pub const unsafe fn bar_not_gated() -> u32 { 42 } +#[unstable( + feature = "humans", + reason = "who ever let humans program computers, we're apparently really bad at it", + issue = "none", +)] +pub mod unstable { + //@ has 'foo/unstable/fn.bar_not_gated.html' '//pre' 'pub const unsafe fn bar_not_gated() -> u32' + //@ !hasraw - '//span[@class="since"]' + pub const unsafe fn bar_not_gated() -> u32 { 42 } +} +#[stable(feature = "rust1", since = "1.0.0")] pub struct Foo; impl Foo { diff --git a/tests/rustdoc/empty-mod-private.rs b/tests/rustdoc/empty-mod-private.rs index 4e408e3d4240..5a8638cd5f5c 100644 --- a/tests/rustdoc/empty-mod-private.rs +++ b/tests/rustdoc/empty-mod-private.rs @@ -2,15 +2,18 @@ //@ has 'empty_mod_private/index.html' '//a[@href="foo/index.html"]' 'foo' //@ hasraw 'empty_mod_private/sidebar-items.js' 'foo' -//@ matches 'empty_mod_private/foo/index.html' '//h1' 'Module empty_mod_private::foo' +//@ matches 'empty_mod_private/foo/index.html' '//h1' 'Module foo' +//@ matches - '//*[@class="rustdoc-breadcrumbs"]' 'empty_mod_private' mod foo {} //@ has 'empty_mod_private/index.html' '//a[@href="bar/index.html"]' 'bar' //@ hasraw 'empty_mod_private/sidebar-items.js' 'bar' -//@ matches 'empty_mod_private/bar/index.html' '//h1' 'Module empty_mod_private::bar' +//@ matches 'empty_mod_private/bar/index.html' '//h1' 'Module bar' +//@ matches - '//*[@class="rustdoc-breadcrumbs"]' 'empty_mod_private' mod bar { //@ has 'empty_mod_private/bar/index.html' '//a[@href="baz/index.html"]' 'baz' //@ hasraw 'empty_mod_private/bar/sidebar-items.js' 'baz' - //@ matches 'empty_mod_private/bar/baz/index.html' '//h1' 'Module empty_mod_private::bar::baz' + //@ matches 'empty_mod_private/bar/baz/index.html' '//h1' 'Module baz' + //@ matches - '//*[@class="rustdoc-breadcrumbs"]' 'empty_mod_private::bar' mod baz {} } diff --git a/tests/rustdoc/empty-mod-public.rs b/tests/rustdoc/empty-mod-public.rs index b5a63525524c..f06ac69f3ed8 100644 --- a/tests/rustdoc/empty-mod-public.rs +++ b/tests/rustdoc/empty-mod-public.rs @@ -1,14 +1,17 @@ //@ has 'empty_mod_public/index.html' '//a[@href="foo/index.html"]' 'foo' //@ hasraw 'empty_mod_public/sidebar-items.js' 'foo' -//@ matches 'empty_mod_public/foo/index.html' '//h1' 'Module empty_mod_public::foo' +//@ matches 'empty_mod_public/foo/index.html' '//h1' 'Module foo' +//@ matches - '//*[@class="rustdoc-breadcrumbs"]' 'empty_mod_public' pub mod foo {} //@ has 'empty_mod_public/index.html' '//a[@href="bar/index.html"]' 'bar' //@ hasraw 'empty_mod_public/sidebar-items.js' 'bar' -//@ matches 'empty_mod_public/bar/index.html' '//h1' 'Module empty_mod_public::bar' +//@ matches 'empty_mod_public/bar/index.html' '//h1' 'Module bar' +//@ matches - '//*[@class="rustdoc-breadcrumbs"]' 'empty_mod_public' pub mod bar { //@ has 'empty_mod_public/bar/index.html' '//a[@href="baz/index.html"]' 'baz' //@ hasraw 'empty_mod_public/bar/sidebar-items.js' 'baz' - //@ matches 'empty_mod_public/bar/baz/index.html' '//h1' 'Module empty_mod_public::bar::baz' + //@ matches 'empty_mod_public/bar/baz/index.html' '//h1' 'Module baz' + //@ matches - '//*[@class="rustdoc-breadcrumbs"]' 'empty_mod_public::bar' pub mod baz {} } diff --git a/tests/rustdoc/html-no-source.rs b/tests/rustdoc/html-no-source.rs index 100ab0031f78..248afbd00eff 100644 --- a/tests/rustdoc/html-no-source.rs +++ b/tests/rustdoc/html-no-source.rs @@ -11,14 +11,14 @@ //@ files 'src/foo' '[]' //@ has foo/fn.foo.html -//@ has - '//div[@class="main-heading"]/*[@class="out-of-band"]' '1.0.0 · ' -//@ !has - '//div[@class="main-heading"]/*[@class="out-of-band"]' '1.0.0 · source · ' +//@ has - '//div[@class="main-heading"]/*[@class="sub-heading"]' '1.0.0' +//@ !has - '//div[@class="main-heading"]/*[@class="sub-heading"]' '1.0.0 · source' #[stable(feature = "bar", since = "1.0")] pub fn foo() {} //@ has foo/struct.Bar.html -//@ has - '//div[@class="main-heading"]/*[@class="out-of-band"]' '1.0.0 · ' -//@ !has - '//div[@class="main-heading"]/*[@class="out-of-band"]' '1.0.0 · source · ' +//@ has - '//div[@class="main-heading"]/*[@class="sub-heading"]' '1.0.0' +//@ !has - '//div[@class="main-heading"]/*[@class="sub-heading"]' '1.0.0 · source' #[stable(feature = "bar", since = "1.0")] pub struct Bar; diff --git a/tests/rustdoc/inline_cross/renamed-via-module.rs b/tests/rustdoc/inline_cross/renamed-via-module.rs index bdaf2cf1f620..8bcdd9f7a39d 100644 --- a/tests/rustdoc/inline_cross/renamed-via-module.rs +++ b/tests/rustdoc/inline_cross/renamed-via-module.rs @@ -10,15 +10,19 @@ extern crate foo; //@ has - '//a/[@href="struct.DeprecatedStepBy.html"]' "DeprecatedStepBy" //@ has - '//a/[@href="struct.StepBy.html"]' "StepBy" //@ has foo/iter/struct.DeprecatedStepBy.html -//@ has - '//h1' "Struct foo::iter::DeprecatedStepBy" +//@ has - '//h1' "Struct DeprecatedStepBy" +//@ matches - '//*[@class="rustdoc-breadcrumbs"]' 'foo::iter' //@ has foo/iter/struct.StepBy.html -//@ has - '//h1' "Struct foo::iter::StepBy" +//@ has - '//h1' "Struct StepBy" +//@ matches - '//*[@class="rustdoc-breadcrumbs"]' 'foo::iter' //@ has bar/iter/index.html //@ has - '//a/[@href="struct.DeprecatedStepBy.html"]' "DeprecatedStepBy" //@ has - '//a/[@href="struct.StepBy.html"]' "StepBy" //@ has bar/iter/struct.DeprecatedStepBy.html -//@ has - '//h1' "Struct bar::iter::DeprecatedStepBy" +//@ has - '//h1' "Struct DeprecatedStepBy" +//@ matches - '//*[@class="rustdoc-breadcrumbs"]' 'bar::iter' //@ has bar/iter/struct.StepBy.html -//@ has - '//h1' "Struct bar::iter::StepBy" +//@ has - '//h1' "Struct StepBy" +//@ matches - '//*[@class="rustdoc-breadcrumbs"]' 'bar::iter' pub use foo::iter; diff --git a/tests/rustdoc/keyword.rs b/tests/rustdoc/keyword.rs index 0157c35288e8..519e1944bc78 100644 --- a/tests/rustdoc/keyword.rs +++ b/tests/rustdoc/keyword.rs @@ -6,7 +6,6 @@ //@ has foo/index.html '//a[@href="keyword.match.html"]' 'match' //@ has foo/index.html '//div[@class="sidebar-elems"]//li/a' 'Keywords' //@ has foo/index.html '//div[@class="sidebar-elems"]//li/a/@href' '#keywords' -//@ has foo/keyword.match.html '//a[@class="keyword"]' 'match' //@ has foo/keyword.match.html '//h1' 'Keyword match' //@ has foo/keyword.match.html '//section[@id="main-content"]//div[@class="docblock"]//p' 'this is a test!' //@ has foo/index.html '//a/@href' '../foo/index.html' diff --git a/tests/rustdoc/primitive-reference.rs b/tests/rustdoc/primitive-reference.rs index c12d65ee0c57..bd6b2a32f754 100644 --- a/tests/rustdoc/primitive-reference.rs +++ b/tests/rustdoc/primitive-reference.rs @@ -8,7 +8,6 @@ //@ has - '//div[@class="sidebar-elems"]//li/a' 'Primitive Types' //@ has - '//div[@class="sidebar-elems"]//li/a/@href' '#primitives' //@ has foo/primitive.reference.html -//@ has - '//a[@class="primitive"]' 'reference' //@ has - '//h1' 'Primitive Type reference' //@ has - '//section[@id="main-content"]//div[@class="docblock"]//p' 'this is a test!' diff --git a/tests/rustdoc/primitive-slice-auto-trait.rs b/tests/rustdoc/primitive-slice-auto-trait.rs index a877b73cf9f8..e78d1d946142 100644 --- a/tests/rustdoc/primitive-slice-auto-trait.rs +++ b/tests/rustdoc/primitive-slice-auto-trait.rs @@ -3,8 +3,7 @@ #![crate_name = "foo"] #![feature(rustc_attrs)] -//@ has foo/primitive.slice.html '//a[@class="primitive"]' 'slice' -//@ has - '//h1' 'Primitive Type slice' +//@ has foo/primitive.slice.html '//h1' 'Primitive Type slice' //@ has - '//section[@id="main-content"]//div[@class="docblock"]//p' 'this is a test!' //@ has - '//h2[@id="synthetic-implementations"]' 'Auto Trait Implementations' //@ has - '//div[@id="synthetic-implementations-list"]//h3' 'impl Send for [T]where T: Send' diff --git a/tests/rustdoc/primitive-tuple-auto-trait.rs b/tests/rustdoc/primitive-tuple-auto-trait.rs index 060c4ecfbdc1..045478e6b4f8 100644 --- a/tests/rustdoc/primitive-tuple-auto-trait.rs +++ b/tests/rustdoc/primitive-tuple-auto-trait.rs @@ -3,8 +3,7 @@ #![crate_name = "foo"] #![feature(rustc_attrs)] -//@ has foo/primitive.tuple.html '//a[@class="primitive"]' 'tuple' -//@ has - '//h1' 'Primitive Type tuple' +//@ has foo/primitive.tuple.html '//h1' 'Primitive Type tuple' //@ has - '//section[@id="main-content"]//div[@class="docblock"]//p' 'this is a test!' //@ has - '//h2[@id="synthetic-implementations"]' 'Auto Trait Implementations' //@ has - '//div[@id="synthetic-implementations-list"]//h3' 'Send' diff --git a/tests/rustdoc/primitive-unit-auto-trait.rs b/tests/rustdoc/primitive-unit-auto-trait.rs index 7751a2bf1d04..6cae094c21c8 100644 --- a/tests/rustdoc/primitive-unit-auto-trait.rs +++ b/tests/rustdoc/primitive-unit-auto-trait.rs @@ -3,8 +3,7 @@ #![crate_name = "foo"] #![feature(rustc_attrs)] -//@ has foo/primitive.unit.html '//a[@class="primitive"]' 'unit' -//@ has - '//h1' 'Primitive Type unit' +//@ has foo/primitive.unit.html '//h1' 'Primitive Type unit' //@ has - '//section[@id="main-content"]//div[@class="docblock"]//p' 'this is a test!' //@ has - '//h2[@id="synthetic-implementations"]' 'Auto Trait Implementations' //@ has - '//div[@id="synthetic-implementations-list"]//h3' 'impl Send for ()' diff --git a/tests/rustdoc/source-version-separator.rs b/tests/rustdoc/source-version-separator.rs index a998c538eed7..9709bbe3c718 100644 --- a/tests/rustdoc/source-version-separator.rs +++ b/tests/rustdoc/source-version-separator.rs @@ -3,7 +3,7 @@ #![feature(staged_api)] //@ has foo/trait.Bar.html -//@ has - '//div[@class="main-heading"]/*[@class="out-of-band"]' '1.0.0 · source · ' +//@ has - '//div[@class="main-heading"]/*[@class="sub-heading"]' '1.0.0 · source' #[stable(feature = "bar", since = "1.0")] pub trait Bar { //@ has - '//*[@id="tymethod.foo"]/*[@class="rightside"]' '3.0.0 · source' @@ -14,7 +14,7 @@ pub trait Bar { //@ has - '//div[@id="implementors-list"]//*[@class="rightside"]' '4.0.0 · source' //@ has foo/struct.Foo.html -//@ has - '//div[@class="main-heading"]/*[@class="out-of-band"]' '1.0.0 · source · ' +//@ has - '//div[@class="main-heading"]/*[@class="sub-heading"]' '1.0.0 · source' #[stable(feature = "baz", since = "1.0")] pub struct Foo; diff --git a/tests/rustdoc/stability.rs b/tests/rustdoc/stability.rs index 270da822c009..de855b43ba53 100644 --- a/tests/rustdoc/stability.rs +++ b/tests/rustdoc/stability.rs @@ -1,6 +1,6 @@ #![feature(staged_api)] -#![unstable(feature = "test", issue = "none")] +#![stable(feature = "rust1", since = "1.0.0")] //@ has stability/index.html //@ has - '//ul[@class="item-table"]/li[1]//a' AaStable @@ -10,6 +10,7 @@ #[stable(feature = "rust2", since = "2.2.2")] pub struct AaStable; +#[unstable(feature = "test", issue = "none")] pub struct Unstable { //@ has stability/struct.Unstable.html \ // '//span[@class="item-info"]//div[@class="stab unstable"]' \ @@ -21,3 +22,31 @@ pub struct Unstable { #[stable(feature = "rust2", since = "2.2.2")] pub struct ZzStable; + +#[unstable(feature = "unstable", issue = "none")] +pub mod unstable { + //@ !hasraw stability/unstable/struct.Foo.html '//span[@class="since"]' + //@ has - '//div[@class="stab unstable"]' 'experimental' + #[stable(feature = "rust1", since = "1.0.0")] + pub struct Foo; +} + +#[stable(feature = "rust2", since = "2.2.2")] +pub mod stable_later { + //@ has stability/stable_later/struct.Bar.html '//span[@class="since"]' '2.2.2' + #[stable(feature = "rust1", since = "1.0.0")] + pub struct Bar; +} + +#[stable(feature = "rust1", since = "1.0.0")] +pub mod stable_earlier { + //@ has stability/stable_earlier/struct.Foo.html '//span[@class="since"]' '1.0.0' + #[doc(inline)] + #[stable(feature = "rust1", since = "1.0.0")] + pub use crate::unstable::Foo; + + //@ has stability/stable_earlier/struct.Bar.html '//span[@class="since"]' '1.0.0' + #[doc(inline)] + #[stable(feature = "rust1", since = "1.0.0")] + pub use crate::stable_later::Bar; +} diff --git a/tests/rustdoc/titles.rs b/tests/rustdoc/titles.rs index bdf950b0a622..922068adc592 100644 --- a/tests/rustdoc/titles.rs +++ b/tests/rustdoc/titles.rs @@ -5,53 +5,65 @@ //@ matches 'foo/index.html' '//div[@class="sidebar-crate"]/h2/a' 'foo' //@ count 'foo/index.html' '//h2[@class="location"]' 0 -//@ matches 'foo/foo_mod/index.html' '//h1' 'Module foo::foo_mod' -//@ matches 'foo/foo_mod/index.html' '//h2[@class="location"]' 'Module foo_mod' +//@ matches 'foo/foo_mod/index.html' '//h1' 'Module foo_mod' +//@ matches - '//*[@class="rustdoc-breadcrumbs"]' 'foo' +//@ matches - '//h2[@class="location"]' 'Module foo_mod' pub mod foo_mod { pub struct __Thing {} } extern "C" { - //@ matches 'foo/fn.foo_ffn.html' '//h1' 'Function foo::foo_ffn' + //@ matches 'foo/fn.foo_ffn.html' '//h1' 'Function foo_ffn' + //@ matches - '//*[@class="rustdoc-breadcrumbs"]' 'foo' pub fn foo_ffn(); } -//@ matches 'foo/fn.foo_fn.html' '//h1' 'Function foo::foo_fn' +//@ matches 'foo/fn.foo_fn.html' '//h1' 'Function foo_fn' +//@ matches - '//*[@class="rustdoc-breadcrumbs"]' 'foo' pub fn foo_fn() {} -//@ matches 'foo/trait.FooTrait.html' '//h1' 'Trait foo::FooTrait' -//@ matches 'foo/trait.FooTrait.html' '//h2[@class="location"]' 'FooTrait' +//@ matches 'foo/trait.FooTrait.html' '//h1' 'Trait FooTrait' +//@ matches - '//*[@class="rustdoc-breadcrumbs"]' 'foo' +//@ matches - '//h2[@class="location"]' 'FooTrait' pub trait FooTrait {} -//@ matches 'foo/struct.FooStruct.html' '//h1' 'Struct foo::FooStruct' -//@ matches 'foo/struct.FooStruct.html' '//h2[@class="location"]' 'FooStruct' +//@ matches 'foo/struct.FooStruct.html' '//h1' 'Struct FooStruct' +//@ matches - '//*[@class="rustdoc-breadcrumbs"]' 'foo' +//@ matches - '//h2[@class="location"]' 'FooStruct' pub struct FooStruct; -//@ matches 'foo/enum.FooEnum.html' '//h1' 'Enum foo::FooEnum' -//@ matches 'foo/enum.FooEnum.html' '//h2[@class="location"]' 'FooEnum' +//@ matches 'foo/enum.FooEnum.html' '//h1' 'Enum FooEnum' +//@ matches - '//*[@class="rustdoc-breadcrumbs"]' 'foo' +//@ matches - '//h2[@class="location"]' 'FooEnum' pub enum FooEnum {} -//@ matches 'foo/type.FooType.html' '//h1' 'Type Alias foo::FooType' -//@ matches 'foo/type.FooType.html' '//h2[@class="location"]' 'FooType' +//@ matches 'foo/type.FooType.html' '//h1' 'Type Alias FooType' +//@ matches - '//*[@class="rustdoc-breadcrumbs"]' 'foo' +//@ matches - '//h2[@class="location"]' 'FooType' pub type FooType = FooStruct; -//@ matches 'foo/macro.foo_macro.html' '//h1' 'Macro foo::foo_macro' +//@ matches 'foo/macro.foo_macro.html' '//h1' 'Macro foo_macro' +//@ matches - '//*[@class="rustdoc-breadcrumbs"]' 'foo' #[macro_export] macro_rules! foo_macro { () => {}; } //@ matches 'foo/primitive.bool.html' '//h1' 'Primitive Type bool' +//@ count - '//*[@class="rustdoc-breadcrumbs"]' 0 #[rustc_doc_primitive = "bool"] mod bool {} -//@ matches 'foo/static.FOO_STATIC.html' '//h1' 'Static foo::FOO_STATIC' +//@ matches 'foo/static.FOO_STATIC.html' '//h1' 'Static FOO_STATIC' +//@ matches - '//*[@class="rustdoc-breadcrumbs"]' 'foo' pub static FOO_STATIC: FooStruct = FooStruct; extern "C" { - //@ matches 'foo/static.FOO_FSTATIC.html' '//h1' 'Static foo::FOO_FSTATIC' + //@ matches 'foo/static.FOO_FSTATIC.html' '//h1' 'Static FOO_FSTATIC' + //@ matches - '//*[@class="rustdoc-breadcrumbs"]' 'foo' pub static FOO_FSTATIC: FooStruct; } -//@ matches 'foo/constant.FOO_CONSTANT.html' '//h1' 'Constant foo::FOO_CONSTANT' +//@ matches 'foo/constant.FOO_CONSTANT.html' '//h1' 'Constant FOO_CONSTANT' +//@ matches - '//*[@class="rustdoc-breadcrumbs"]' 'foo' pub const FOO_CONSTANT: FooStruct = FooStruct; diff --git a/tests/rustdoc/version-separator-without-source.rs b/tests/rustdoc/version-separator-without-source.rs index e439681484c3..7cd1780f1d30 100644 --- a/tests/rustdoc/version-separator-without-source.rs +++ b/tests/rustdoc/version-separator-without-source.rs @@ -4,14 +4,14 @@ #![crate_name = "foo"] //@ has foo/fn.foo.html -//@ has - '//div[@class="main-heading"]/*[@class="out-of-band"]' '1.0.0 · ' -//@ !has - '//div[@class="main-heading"]/*[@class="out-of-band"]' '1.0.0 · source · ' +//@ has - '//div[@class="main-heading"]/*[@class="sub-heading"]' '1.0.0' +//@ !has - '//div[@class="main-heading"]/*[@class="sub-heading"]' '1.0.0 · source' #[stable(feature = "bar", since = "1.0")] pub fn foo() {} //@ has foo/struct.Bar.html -//@ has - '//div[@class="main-heading"]/*[@class="out-of-band"]' '1.0.0 · ' -//@ !has - '//div[@class="main-heading"]/*[@class="out-of-band"]' '1.0.0 · source · ' +//@ has - '//div[@class="main-heading"]/*[@class="sub-heading"]' '1.0.0' +//@ !has - '//div[@class="main-heading"]/*[@class="sub-heading"]' '1.0.0 · source' #[stable(feature = "bar", since = "1.0")] pub struct Bar; diff --git a/tests/ui/asm/aarch64/type-check-4.rs b/tests/ui/asm/aarch64/type-check-4.rs index 1169c3dcfa84..c24567bd5b00 100644 --- a/tests/ui/asm/aarch64/type-check-4.rs +++ b/tests/ui/asm/aarch64/type-check-4.rs @@ -1,31 +1,27 @@ //@ only-aarch64 -//@ compile-flags: -C target-feature=+neon +//@ build-fail -#![feature(repr_simd)] - -use std::arch::aarch64::float64x2_t; -use std::arch::{asm, global_asm}; - -#[repr(simd)] -#[derive(Copy, Clone)] -struct Simd256bit([f64; 4]); +use std::arch::global_asm; fn main() {} // Constants must be... constant -static S: i32 = 1; +static mut S: i32 = 1; const fn const_foo(x: i32) -> i32 { x } const fn const_bar(x: T) -> T { x } -global_asm!("{}", const S); -//~^ ERROR referencing statics +global_asm!("{}", const unsafe { S }); +//~^ ERROR: evaluation of constant value failed +//~| mutable global memory global_asm!("{}", const const_foo(0)); -global_asm!("{}", const const_foo(S)); -//~^ ERROR referencing statics +global_asm!("{}", const const_foo(unsafe { S })); +//~^ ERROR: evaluation of constant value failed +//~| mutable global memory global_asm!("{}", const const_bar(0)); -global_asm!("{}", const const_bar(S)); -//~^ ERROR referencing statics +global_asm!("{}", const const_bar(unsafe { S })); +//~^ ERROR: evaluation of constant value failed +//~| mutable global memory diff --git a/tests/ui/asm/aarch64/type-check-4.stderr b/tests/ui/asm/aarch64/type-check-4.stderr index 89eb8467cdef..8c22dfcdb5ed 100644 --- a/tests/ui/asm/aarch64/type-check-4.stderr +++ b/tests/ui/asm/aarch64/type-check-4.stderr @@ -1,39 +1,21 @@ -error[E0658]: referencing statics in constants is unstable - --> $DIR/type-check-4.rs:24:25 +error[E0080]: evaluation of constant value failed + --> $DIR/type-check-4.rs:17:34 | -LL | global_asm!("{}", const S); - | ^ - | - = note: see issue #119618 for more information - = help: add `#![feature(const_refs_to_static)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - = note: `static` and `const` variables can refer to other `const` variables. A `const` variable, however, cannot refer to a `static` variable. - = help: to fix this, the value can be extracted to a `const` and then used. +LL | global_asm!("{}", const unsafe { S }); + | ^ constant accesses mutable global memory -error[E0658]: referencing statics in constants is unstable - --> $DIR/type-check-4.rs:27:35 - | -LL | global_asm!("{}", const const_foo(S)); - | ^ +error[E0080]: evaluation of constant value failed + --> $DIR/type-check-4.rs:21:44 | - = note: see issue #119618 for more information - = help: add `#![feature(const_refs_to_static)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - = note: `static` and `const` variables can refer to other `const` variables. A `const` variable, however, cannot refer to a `static` variable. - = help: to fix this, the value can be extracted to a `const` and then used. +LL | global_asm!("{}", const const_foo(unsafe { S })); + | ^ constant accesses mutable global memory -error[E0658]: referencing statics in constants is unstable - --> $DIR/type-check-4.rs:30:35 - | -LL | global_asm!("{}", const const_bar(S)); - | ^ +error[E0080]: evaluation of constant value failed + --> $DIR/type-check-4.rs:25:44 | - = note: see issue #119618 for more information - = help: add `#![feature(const_refs_to_static)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - = note: `static` and `const` variables can refer to other `const` variables. A `const` variable, however, cannot refer to a `static` variable. - = help: to fix this, the value can be extracted to a `const` and then used. +LL | global_asm!("{}", const const_bar(unsafe { S })); + | ^ constant accesses mutable global memory error: aborting due to 3 previous errors -For more information about this error, try `rustc --explain E0658`. +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/asm/const-refs-to-static.rs b/tests/ui/asm/const-refs-to-static.rs index 9fc010b57630..ce2c5b3246ec 100644 --- a/tests/ui/asm/const-refs-to-static.rs +++ b/tests/ui/asm/const-refs-to-static.rs @@ -2,8 +2,6 @@ //@ ignore-nvptx64 //@ ignore-spirv -#![feature(const_refs_to_static)] - use std::arch::{asm, global_asm}; use std::ptr::addr_of; diff --git a/tests/ui/asm/const-refs-to-static.stderr b/tests/ui/asm/const-refs-to-static.stderr index 8fd69da0d1e9..10e1ca5bd606 100644 --- a/tests/ui/asm/const-refs-to-static.stderr +++ b/tests/ui/asm/const-refs-to-static.stderr @@ -1,5 +1,5 @@ error: invalid type for `const` operand - --> $DIR/const-refs-to-static.rs:12:19 + --> $DIR/const-refs-to-static.rs:10:19 | LL | global_asm!("{}", const addr_of!(FOO)); | ^^^^^^------------- @@ -9,7 +9,7 @@ LL | global_asm!("{}", const addr_of!(FOO)); = help: `const` operands must be of an integer type error: invalid type for `const` operand - --> $DIR/const-refs-to-static.rs:17:25 + --> $DIR/const-refs-to-static.rs:15:25 | LL | unsafe { asm!("{}", const addr_of!(FOO)) }; | ^^^^^^------------- diff --git a/tests/ui/asm/x86_64/type-check-4.rs b/tests/ui/asm/x86_64/type-check-4.rs index 9503cd6d8ab2..ebc6edc07e4c 100644 --- a/tests/ui/asm/x86_64/type-check-4.rs +++ b/tests/ui/asm/x86_64/type-check-4.rs @@ -1,26 +1,27 @@ //@ only-x86_64 -//@ compile-flags: -C target-feature=+avx512f +//@ build-fail -use std::arch::{asm, global_asm}; - -use std::arch::x86_64::{_mm256_setzero_ps, _mm_setzero_ps}; +use std::arch::global_asm; fn main() {} // Constants must be... constant -static S: i32 = 1; +static mut S: i32 = 1; const fn const_foo(x: i32) -> i32 { x } const fn const_bar(x: T) -> T { x } -global_asm!("{}", const S); -//~^ ERROR referencing statics +global_asm!("{}", const unsafe { S }); +//~^ ERROR evaluation of constant value failed +//~| mutable global memory global_asm!("{}", const const_foo(0)); -global_asm!("{}", const const_foo(S)); -//~^ ERROR referencing statics +global_asm!("{}", const const_foo(unsafe { S })); +//~^ ERROR evaluation of constant value failed +//~| mutable global memory global_asm!("{}", const const_bar(0)); -global_asm!("{}", const const_bar(S)); -//~^ ERROR referencing statics +global_asm!("{}", const const_bar(unsafe { S })); +//~^ ERROR evaluation of constant value failed +//~| mutable global memory diff --git a/tests/ui/asm/x86_64/type-check-4.stderr b/tests/ui/asm/x86_64/type-check-4.stderr index f1bbc9e7d33d..8c22dfcdb5ed 100644 --- a/tests/ui/asm/x86_64/type-check-4.stderr +++ b/tests/ui/asm/x86_64/type-check-4.stderr @@ -1,39 +1,21 @@ -error[E0658]: referencing statics in constants is unstable - --> $DIR/type-check-4.rs:19:25 +error[E0080]: evaluation of constant value failed + --> $DIR/type-check-4.rs:17:34 | -LL | global_asm!("{}", const S); - | ^ - | - = note: see issue #119618 for more information - = help: add `#![feature(const_refs_to_static)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - = note: `static` and `const` variables can refer to other `const` variables. A `const` variable, however, cannot refer to a `static` variable. - = help: to fix this, the value can be extracted to a `const` and then used. +LL | global_asm!("{}", const unsafe { S }); + | ^ constant accesses mutable global memory -error[E0658]: referencing statics in constants is unstable - --> $DIR/type-check-4.rs:22:35 - | -LL | global_asm!("{}", const const_foo(S)); - | ^ +error[E0080]: evaluation of constant value failed + --> $DIR/type-check-4.rs:21:44 | - = note: see issue #119618 for more information - = help: add `#![feature(const_refs_to_static)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - = note: `static` and `const` variables can refer to other `const` variables. A `const` variable, however, cannot refer to a `static` variable. - = help: to fix this, the value can be extracted to a `const` and then used. +LL | global_asm!("{}", const const_foo(unsafe { S })); + | ^ constant accesses mutable global memory -error[E0658]: referencing statics in constants is unstable - --> $DIR/type-check-4.rs:25:35 - | -LL | global_asm!("{}", const const_bar(S)); - | ^ +error[E0080]: evaluation of constant value failed + --> $DIR/type-check-4.rs:25:44 | - = note: see issue #119618 for more information - = help: add `#![feature(const_refs_to_static)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - = note: `static` and `const` variables can refer to other `const` variables. A `const` variable, however, cannot refer to a `static` variable. - = help: to fix this, the value can be extracted to a `const` and then used. +LL | global_asm!("{}", const const_bar(unsafe { S })); + | ^ constant accesses mutable global memory error: aborting due to 3 previous errors -For more information about this error, try `rustc --explain E0658`. +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/associated-type-bounds/nested-associated-type-bound-incompleteness.rs b/tests/ui/associated-type-bounds/nested-associated-type-bound-incompleteness.rs new file mode 100644 index 000000000000..eb616631d1dc --- /dev/null +++ b/tests/ui/associated-type-bounds/nested-associated-type-bound-incompleteness.rs @@ -0,0 +1,28 @@ +// Demonstrates a mostly-theoretical inference guidance now that we turn the where +// clause on `Trait` into an item bound, given that we prefer item bounds somewhat +// greedily in trait selection. + +trait Bound {} +impl Bound for U {} + +trait Trait +where + <::Assoc as Other>::Assoc: Bound, +{ + type Assoc: Other; +} + +trait Other { + type Assoc; +} + +fn impls_trait, U>() -> Vec { vec![] } + +fn foo() { + let mut vec_u = impls_trait::<<::Assoc as Other>::Assoc, _>(); + vec_u.sort(); + drop::>(vec_u); + //~^ ERROR mismatched types +} + +fn main() {} diff --git a/tests/ui/associated-type-bounds/nested-associated-type-bound-incompleteness.stderr b/tests/ui/associated-type-bounds/nested-associated-type-bound-incompleteness.stderr new file mode 100644 index 000000000000..c77400f38228 --- /dev/null +++ b/tests/ui/associated-type-bounds/nested-associated-type-bound-incompleteness.stderr @@ -0,0 +1,16 @@ +error[E0308]: mismatched types + --> $DIR/nested-associated-type-bound-incompleteness.rs:24:21 + | +LL | drop::>(vec_u); + | --------------- ^^^^^ expected `Vec`, found `Vec` + | | + | arguments to this function are incorrect + | + = note: expected struct `Vec` + found struct `Vec` +note: function defined here + --> $SRC_DIR/core/src/mem/mod.rs:LL:COL + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/associated-type-bounds/nested-gat-projection.rs b/tests/ui/associated-type-bounds/nested-gat-projection.rs new file mode 100644 index 000000000000..ad37da9ed194 --- /dev/null +++ b/tests/ui/associated-type-bounds/nested-gat-projection.rs @@ -0,0 +1,31 @@ +//@ check-pass + +trait Trait +where + for<'a> Self::Gat<'a>: OtherTrait, + for<'a, 'b, 'c> as OtherTrait>::OtherGat<'b>: HigherRanked<'c>, +{ + type Gat<'a>; +} + +trait OtherTrait { + type OtherGat<'b>; +} + +trait HigherRanked<'c> {} + +fn lower_ranked OtherTrait: HigherRanked<'c>>>() {} + +fn higher_ranked() +where + for<'a> T::Gat<'a>: OtherTrait, + for<'a, 'b, 'c> as OtherTrait>::OtherGat<'b>: HigherRanked<'c>, +{ +} + +fn test() { + lower_ranked::>(); + higher_ranked::(); +} + +fn main() {} diff --git a/tests/ui/associated-types/imply-relevant-nested-item-bounds-2.rs b/tests/ui/associated-types/imply-relevant-nested-item-bounds-2.rs new file mode 100644 index 000000000000..864c31893504 --- /dev/null +++ b/tests/ui/associated-types/imply-relevant-nested-item-bounds-2.rs @@ -0,0 +1,28 @@ +//@ check-pass +//@ revisions: current next +//@[next] compile-flags: -Znext-solver + +trait Trait +where + Self::Assoc: Clone, +{ + type Assoc; +} + +fn foo(x: &T::Assoc) -> T::Assoc { + x.clone() +} + +trait Trait2 +where + Self::Assoc: Iterator, + ::Item: Clone, +{ + type Assoc; +} + +fn foo2(x: &::Item) -> ::Item { + x.clone() +} + +fn main() {} diff --git a/tests/ui/associated-types/imply-relevant-nested-item-bounds-for-gat.rs b/tests/ui/associated-types/imply-relevant-nested-item-bounds-for-gat.rs new file mode 100644 index 000000000000..4e3b0b3b148a --- /dev/null +++ b/tests/ui/associated-types/imply-relevant-nested-item-bounds-for-gat.rs @@ -0,0 +1,19 @@ +//@ check-pass + +// Test that `for<'a> Self::Gat<'a>: Debug` is implied in the definition of `Foo`, +// just as it would be if it weren't a GAT but just a regular associated type. + +use std::fmt::Debug; + +trait Foo +where + for<'a> Self::Gat<'a>: Debug, +{ + type Gat<'a>; +} + +fn test(x: T::Gat<'static>) { + println!("{:?}", x); +} + +fn main() {} diff --git a/tests/ui/associated-types/imply-relevant-nested-item-bounds.rs b/tests/ui/associated-types/imply-relevant-nested-item-bounds.rs new file mode 100644 index 000000000000..5a477a5b3494 --- /dev/null +++ b/tests/ui/associated-types/imply-relevant-nested-item-bounds.rs @@ -0,0 +1,23 @@ +//@ check-pass +//@ revisions: current next +//@[next] compile-flags: -Znext-solver + +trait Foo +where + Self::Iterator: Iterator, + ::Item: Bar, +{ + type Iterator; + + fn iter() -> Self::Iterator; +} + +trait Bar { + fn bar(&self); +} + +fn x() { + T::iter().next().unwrap().bar(); +} + +fn main() {} diff --git a/tests/ui/borrowck/two-phase-surprise-no-conflict.stderr b/tests/ui/borrowck/two-phase-surprise-no-conflict.stderr index b3bf2f924fc3..89b15a7a659d 100644 --- a/tests/ui/borrowck/two-phase-surprise-no-conflict.stderr +++ b/tests/ui/borrowck/two-phase-surprise-no-conflict.stderr @@ -75,7 +75,7 @@ LL | reg.register_univ(Box::new(CapturePass::new(®.sess_mut))); | ^^^^^^^^^^^^^^^^^^-----------------------------------------^ | | | | | | | immutable borrow occurs here - | | cast requires that `reg.sess_mut` is borrowed for `'a` + | | coercion requires that `reg.sess_mut` is borrowed for `'a` | mutable borrow occurs here | = note: due to object lifetime defaults, `Box LateLintPass<'b>>` actually means `Box<(dyn for<'b> LateLintPass<'b> + 'static)>` @@ -119,7 +119,7 @@ LL | reg.register_univ(Box::new(CapturePass::new_mut(&mut reg.sess_mut))); | ^^^^^^^^^^^^^^^^^^-------------------------------------------------^ | | | | | | | first mutable borrow occurs here - | | cast requires that `reg.sess_mut` is borrowed for `'a` + | | coercion requires that `reg.sess_mut` is borrowed for `'a` | second mutable borrow occurs here | = note: due to object lifetime defaults, `Box LateLintPass<'b>>` actually means `Box<(dyn for<'b> LateLintPass<'b> + 'static)>` diff --git a/tests/ui/cast/casts-differing-anon.stderr b/tests/ui/cast/casts-differing-anon.stderr index 8ddd97137c30..fc4882d2d277 100644 --- a/tests/ui/cast/casts-differing-anon.stderr +++ b/tests/ui/cast/casts-differing-anon.stderr @@ -4,7 +4,7 @@ error[E0606]: casting `*mut impl Debug + ?Sized` as `*mut impl Debug + ?Sized` i LL | b_raw = f_raw as *mut _; | ^^^^^^^^^^^^^^^ | - = note: vtable kinds may not match + = note: the pointers may have different metadata error: aborting due to 1 previous error diff --git a/tests/ui/cast/ptr-to-trait-obj-different-args.rs b/tests/ui/cast/ptr-to-trait-obj-different-args.rs index c6038cfe8640..bb103f789f55 100644 --- a/tests/ui/cast/ptr-to-trait-obj-different-args.rs +++ b/tests/ui/cast/ptr-to-trait-obj-different-args.rs @@ -18,14 +18,14 @@ fn main() { let b: *const dyn B = a as _; //~ error: casting `*const dyn A` as `*const dyn B` is invalid let x: *const dyn Trait = &(); - let y: *const dyn Trait = x as _; //~ error: mismatched types + let y: *const dyn Trait = x as _; //~ error: casting `*const dyn Trait` as `*const dyn Trait` is invalid _ = (b, y); } fn generic(x: *const dyn Trait, t: *const dyn Trait) { - let _: *const dyn Trait = x as _; //~ error: mismatched types - let _: *const dyn Trait = t as _; //~ error: mismatched types + let _: *const dyn Trait = x as _; //~ error: casting `*const (dyn Trait + 'static)` as `*const dyn Trait` is invalid + let _: *const dyn Trait = t as _; //~ error: casting `*const (dyn Trait + 'static)` as `*const dyn Trait` is invalid } trait Assocked { @@ -33,5 +33,5 @@ trait Assocked { } fn change_assoc(x: *mut dyn Assocked) -> *mut dyn Assocked { - x as _ //~ error: mismatched types + x as _ //~ error: casting `*mut (dyn Assocked + 'static)` as `*mut (dyn Assocked + 'static)` is invalid } diff --git a/tests/ui/cast/ptr-to-trait-obj-different-args.stderr b/tests/ui/cast/ptr-to-trait-obj-different-args.stderr index 8e60ca42f0a5..e571a43959ff 100644 --- a/tests/ui/cast/ptr-to-trait-obj-different-args.stderr +++ b/tests/ui/cast/ptr-to-trait-obj-different-args.stderr @@ -4,53 +4,40 @@ error[E0606]: casting `*const dyn A` as `*const dyn B` is invalid LL | let b: *const dyn B = a as _; | ^^^^^^ | - = note: vtable kinds may not match + = note: the trait objects may have different vtables -error[E0308]: mismatched types +error[E0606]: casting `*const dyn Trait` as `*const dyn Trait` is invalid --> $DIR/ptr-to-trait-obj-different-args.rs:21:34 | LL | let y: *const dyn Trait = x as _; - | ^^^^^^ expected `X`, found `Y` + | ^^^^^^ | - = note: expected trait object `dyn Trait` - found trait object `dyn Trait` - = help: `dyn Trait` implements `Trait` so you could box the found value and coerce it to the trait object `Box`, you will have to change the expected type as well + = note: the trait objects may have different vtables -error[E0308]: mismatched types +error[E0606]: casting `*const (dyn Trait + 'static)` as `*const dyn Trait` is invalid --> $DIR/ptr-to-trait-obj-different-args.rs:27:34 | -LL | fn generic(x: *const dyn Trait, t: *const dyn Trait) { - | - found this type parameter LL | let _: *const dyn Trait = x as _; - | ^^^^^^ expected `X`, found type parameter `T` + | ^^^^^^ | - = note: expected trait object `dyn Trait` - found trait object `dyn Trait` - = help: `dyn Trait` implements `Trait` so you could box the found value and coerce it to the trait object `Box`, you will have to change the expected type as well + = note: the trait objects may have different vtables -error[E0308]: mismatched types +error[E0606]: casting `*const (dyn Trait + 'static)` as `*const dyn Trait` is invalid --> $DIR/ptr-to-trait-obj-different-args.rs:28:34 | -LL | fn generic(x: *const dyn Trait, t: *const dyn Trait) { - | - expected this type parameter -LL | let _: *const dyn Trait = x as _; LL | let _: *const dyn Trait = t as _; - | ^^^^^^ expected type parameter `T`, found `X` + | ^^^^^^ | - = note: expected trait object `dyn Trait` - found trait object `dyn Trait` - = help: `dyn Trait` implements `Trait` so you could box the found value and coerce it to the trait object `Box`, you will have to change the expected type as well + = note: the trait objects may have different vtables -error[E0308]: mismatched types +error[E0606]: casting `*mut (dyn Assocked + 'static)` as `*mut (dyn Assocked + 'static)` is invalid --> $DIR/ptr-to-trait-obj-different-args.rs:36:5 | LL | x as _ - | ^^^^^^ expected `u8`, found `u32` + | ^^^^^^ | - = note: expected trait object `dyn Assocked` - found trait object `dyn Assocked` + = note: the trait objects may have different vtables error: aborting due to 5 previous errors -Some errors have detailed explanations: E0308, E0606. -For more information about an error, try `rustc --explain E0308`. +For more information about this error, try `rustc --explain E0606`. diff --git a/tests/ui/cast/ptr-to-trait-obj-different-regions-id-trait.current.stderr b/tests/ui/cast/ptr-to-trait-obj-different-regions-id-trait.current.stderr index 5a5b4bfcacf4..4e43d3b93fa3 100644 --- a/tests/ui/cast/ptr-to-trait-obj-different-regions-id-trait.current.stderr +++ b/tests/ui/cast/ptr-to-trait-obj-different-regions-id-trait.current.stderr @@ -1,11 +1,11 @@ error: lifetime may not live long enough - --> $DIR/ptr-to-trait-obj-different-regions-id-trait.rs:24:17 + --> $DIR/ptr-to-trait-obj-different-regions-id-trait.rs:24:27 | LL | fn m<'a>() { | -- lifetime `'a` defined here LL | let unsend: *const dyn Cat<'a> = &(); LL | let _send = unsend as *const S>; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static` | = note: requirement occurs because of the type `S>`, which makes the generic argument `dyn Cat<'_>` invariant = note: the struct `S` is invariant over the parameter `T` diff --git a/tests/ui/cast/ptr-to-trait-obj-different-regions-id-trait.next.stderr b/tests/ui/cast/ptr-to-trait-obj-different-regions-id-trait.next.stderr index 5a5b4bfcacf4..4e43d3b93fa3 100644 --- a/tests/ui/cast/ptr-to-trait-obj-different-regions-id-trait.next.stderr +++ b/tests/ui/cast/ptr-to-trait-obj-different-regions-id-trait.next.stderr @@ -1,11 +1,11 @@ error: lifetime may not live long enough - --> $DIR/ptr-to-trait-obj-different-regions-id-trait.rs:24:17 + --> $DIR/ptr-to-trait-obj-different-regions-id-trait.rs:24:27 | LL | fn m<'a>() { | -- lifetime `'a` defined here LL | let unsend: *const dyn Cat<'a> = &(); LL | let _send = unsend as *const S>; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static` | = note: requirement occurs because of the type `S>`, which makes the generic argument `dyn Cat<'_>` invariant = note: the struct `S` is invariant over the parameter `T` diff --git a/tests/ui/cast/ptr-to-trait-obj-different-regions-misc.rs b/tests/ui/cast/ptr-to-trait-obj-different-regions-misc.rs index 01c347bfae5a..d7c6c50d8beb 100644 --- a/tests/ui/cast/ptr-to-trait-obj-different-regions-misc.rs +++ b/tests/ui/cast/ptr-to-trait-obj-different-regions-misc.rs @@ -33,5 +33,10 @@ fn change_assoc_1<'a, 'b>( //~| error: lifetime may not live long enough } +// This tests the default borrow check error, without the special casing for return values. +fn require_static(_: *const dyn Trait<'static>) {} +fn extend_to_static<'a>(ptr: *const dyn Trait<'a>) { + require_static(ptr as _) //~ error: lifetime may not live long enough +} fn main() {} diff --git a/tests/ui/cast/ptr-to-trait-obj-different-regions-misc.stderr b/tests/ui/cast/ptr-to-trait-obj-different-regions-misc.stderr index 7044e4dec1fe..6069f4f3b556 100644 --- a/tests/ui/cast/ptr-to-trait-obj-different-regions-misc.stderr +++ b/tests/ui/cast/ptr-to-trait-obj-different-regions-misc.stderr @@ -132,5 +132,13 @@ help: `'b` and `'a` must be the same: replace one with the other | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: aborting due to 8 previous errors +error: lifetime may not live long enough + --> $DIR/ptr-to-trait-obj-different-regions-misc.rs:39:20 + | +LL | fn extend_to_static<'a>(ptr: *const dyn Trait<'a>) { + | -- lifetime `'a` defined here +LL | require_static(ptr as _) + | ^^^^^^^^ cast requires that `'a` must outlive `'static` + +error: aborting due to 9 previous errors diff --git a/tests/ui/cast/ptr-to-trait-obj-wrap-upcast.stderr b/tests/ui/cast/ptr-to-trait-obj-wrap-upcast.stderr index 38c8ba96bc5b..5687aba625ff 100644 --- a/tests/ui/cast/ptr-to-trait-obj-wrap-upcast.stderr +++ b/tests/ui/cast/ptr-to-trait-obj-wrap-upcast.stderr @@ -4,7 +4,7 @@ error[E0606]: casting `*const (dyn Sub + 'static)` as `*const Wrapper LL | ptr as _ | ^^^^^^^^ | - = note: vtable kinds may not match + = note: the trait objects may have different vtables error: aborting due to 1 previous error diff --git a/tests/ui/closures/binder/closure-return-type-mismatch.rs b/tests/ui/closures/binder/closure-return-type-mismatch.rs new file mode 100644 index 000000000000..398a4c43ee2e --- /dev/null +++ b/tests/ui/closures/binder/closure-return-type-mismatch.rs @@ -0,0 +1,15 @@ +// We used to bind the closure return type `&'a ()` with the late-bound vars of +// the owner (here `main` & `env` resp.) instead of the ones of the enclosing +// function-like / closure inside diagnostic code which was incorrect. + +#![feature(closure_lifetime_binder)] + +// issue: rust-lang/rust#130391 +fn main() { + let _ = for<'a> |x: &'a u8| -> &'a () { x }; //~ ERROR mismatched types +} + +// issue: rust-lang/rust#130663 +fn env<'r>() { + let _ = for<'a> |x: &'a u8| -> &'a () { x }; //~ ERROR mismatched types +} diff --git a/tests/ui/closures/binder/closure-return-type-mismatch.stderr b/tests/ui/closures/binder/closure-return-type-mismatch.stderr new file mode 100644 index 000000000000..67045654f995 --- /dev/null +++ b/tests/ui/closures/binder/closure-return-type-mismatch.stderr @@ -0,0 +1,25 @@ +error[E0308]: mismatched types + --> $DIR/closure-return-type-mismatch.rs:9:45 + | +LL | let _ = for<'a> |x: &'a u8| -> &'a () { x }; + | ------ ^ expected `&()`, found `&u8` + | | + | expected `&'a ()` because of return type + | + = note: expected reference `&'a ()` + found reference `&'a u8` + +error[E0308]: mismatched types + --> $DIR/closure-return-type-mismatch.rs:14:45 + | +LL | let _ = for<'a> |x: &'a u8| -> &'a () { x }; + | ------ ^ expected `&()`, found `&u8` + | | + | expected `&'a ()` because of return type + | + = note: expected reference `&'a ()` + found reference `&'a u8` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/closures/closure-return-type-mismatch.rs b/tests/ui/closures/closure-return-type-mismatch.rs index 1631bb303e54..e5cda1659de4 100644 --- a/tests/ui/closures/closure-return-type-mismatch.rs +++ b/tests/ui/closures/closure-return-type-mismatch.rs @@ -15,3 +15,7 @@ fn main() { b }; } + +// issue: rust-lang/rust#130858 rust-lang/rust#125655 +static FOO: fn() -> bool = || -> bool { 1 }; +//~^ ERROR mismatched types diff --git a/tests/ui/closures/closure-return-type-mismatch.stderr b/tests/ui/closures/closure-return-type-mismatch.stderr index 3a2f098d1efb..052bbbb5ed57 100644 --- a/tests/ui/closures/closure-return-type-mismatch.stderr +++ b/tests/ui/closures/closure-return-type-mismatch.stderr @@ -1,3 +1,11 @@ +error[E0308]: mismatched types + --> $DIR/closure-return-type-mismatch.rs:20:41 + | +LL | static FOO: fn() -> bool = || -> bool { 1 }; + | ---- ^ expected `bool`, found integer + | | + | expected `bool` because of return type + error[E0308]: mismatched types --> $DIR/closure-return-type-mismatch.rs:7:9 | @@ -19,6 +27,6 @@ LL | if false { LL | return "hello" | ^^^^^^^ expected `bool`, found `&str` -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/codegen/virtual-function-elimination.rs b/tests/ui/codegen/virtual-function-elimination.rs new file mode 100644 index 000000000000..3cbeb1293e50 --- /dev/null +++ b/tests/ui/codegen/virtual-function-elimination.rs @@ -0,0 +1,17 @@ +//@ build-pass +//@ compile-flags: -Zvirtual-function-elimination=true -Clto=true +//@ only-x86_64 +//@ no-prefer-dynamic + +// issue #123955 +pub fn test0() { + _ = Box::new(()) as Box; +} + +// issue #124092 +const X: for<'b> fn(&'b ()) = |&()| (); +pub fn test1() { + let _dyn_debug = Box::new(X) as Box as Box; +} + +fn main() {} diff --git a/tests/ui/const-generics/generic_const_exprs/unify-op-with-fn-call.stderr b/tests/ui/const-generics/generic_const_exprs/unify-op-with-fn-call.stderr index db93bcca60fb..bae8249845c0 100644 --- a/tests/ui/const-generics/generic_const_exprs/unify-op-with-fn-call.stderr +++ b/tests/ui/const-generics/generic_const_exprs/unify-op-with-fn-call.stderr @@ -1,3 +1,11 @@ +error: `-Znext-solver=globally` and `generic_const_exprs` are incompatible, using them at the same time is not allowed + --> $DIR/unify-op-with-fn-call.rs:3:12 + | +LL | #![feature(generic_const_exprs, adt_const_params, const_trait_impl, effects)] + | ^^^^^^^^^^^^^^^^^^^ + | + = help: remove one of these features + error: const `impl` for trait `Add` which is not marked with `#[const_trait]` --> $DIR/unify-op-with-fn-call.rs:10:12 | @@ -67,7 +75,7 @@ error[E0284]: type annotations needed: cannot normalize `foo2::{constant#0}` LL | bar2::<{ std::ops::Add::add(N, N) }>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot normalize `foo2::{constant#0}` -error: aborting due to 8 previous errors +error: aborting due to 9 previous errors Some errors have detailed explanations: E0284, E0741. For more information about an error, try `rustc --explain E0284`. diff --git a/tests/ui/const-generics/issues/issue-88119.stderr b/tests/ui/const-generics/issues/issue-88119.stderr index b5eec3046fd9..98bb81968100 100644 --- a/tests/ui/const-generics/issues/issue-88119.stderr +++ b/tests/ui/const-generics/issues/issue-88119.stderr @@ -1,3 +1,11 @@ +error: `-Znext-solver=globally` and `generic_const_exprs` are incompatible, using them at the same time is not allowed + --> $DIR/issue-88119.rs:4:39 + | +LL | #![feature(const_trait_impl, effects, generic_const_exprs)] + | ^^^^^^^^^^^^^^^^^^^ + | + = help: remove one of these features + error[E0284]: type annotations needed: cannot normalize `<&T as ConstName>::{constant#0}` --> $DIR/issue-88119.rs:19:49 | @@ -28,6 +36,6 @@ LL | where LL | [(); name_len::()]:, | --------------------- unsatisfied trait bound introduced here -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0284`. diff --git a/tests/ui/const-generics/wrong-normalization.rs b/tests/ui/const-generics/wrong-normalization.rs index 8b2323e3d479..f1ce317b3f78 100644 --- a/tests/ui/const-generics/wrong-normalization.rs +++ b/tests/ui/const-generics/wrong-normalization.rs @@ -15,6 +15,5 @@ pub struct I8; impl as Identity>::Identity { //~^ ERROR no nominal type found for inherent implementation -//~| ERROR no associated item named `MIN` found for type `i8` pub fn foo(&self) {} } diff --git a/tests/ui/const-generics/wrong-normalization.stderr b/tests/ui/const-generics/wrong-normalization.stderr index 379a5593dd64..2f8dfc895b27 100644 --- a/tests/ui/const-generics/wrong-normalization.stderr +++ b/tests/ui/const-generics/wrong-normalization.stderr @@ -6,18 +6,6 @@ LL | impl as Identity>::Identity { | = note: either implement a trait on it or create a newtype to wrap it instead -error[E0599]: no associated item named `MIN` found for type `i8` in the current scope - --> $DIR/wrong-normalization.rs:16:15 - | -LL | impl as Identity>::Identity { - | ^^^ associated item not found in `i8` - | -help: you are looking for the module in `std`, not the primitive type - | -LL | impl as Identity>::Identity { - | +++++ - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error -Some errors have detailed explanations: E0118, E0599. -For more information about an error, try `rustc --explain E0118`. +For more information about this error, try `rustc --explain E0118`. diff --git a/tests/ui/consts/const-eval/raw-pointer-ub.rs b/tests/ui/consts/const-eval/raw-pointer-ub.rs index 3320e6278128..5724293f145f 100644 --- a/tests/ui/consts/const-eval/raw-pointer-ub.rs +++ b/tests/ui/consts/const-eval/raw-pointer-ub.rs @@ -1,6 +1,3 @@ -#![feature(const_intrinsic_copy)] - - const MISALIGNED_LOAD: () = unsafe { let mem = [0u32; 8]; let ptr = mem.as_ptr().byte_add(1); diff --git a/tests/ui/consts/const-eval/raw-pointer-ub.stderr b/tests/ui/consts/const-eval/raw-pointer-ub.stderr index aeb46725c064..3426a768cb60 100644 --- a/tests/ui/consts/const-eval/raw-pointer-ub.stderr +++ b/tests/ui/consts/const-eval/raw-pointer-ub.stderr @@ -1,11 +1,11 @@ error[E0080]: evaluation of constant value failed - --> $DIR/raw-pointer-ub.rs:7:16 + --> $DIR/raw-pointer-ub.rs:4:16 | LL | let _val = *ptr; | ^^^^ accessing memory based on pointer with alignment 1, but alignment 4 is required error[E0080]: evaluation of constant value failed - --> $DIR/raw-pointer-ub.rs:14:5 + --> $DIR/raw-pointer-ub.rs:11:5 | LL | *ptr = 0; | ^^^^^^^^ accessing memory based on pointer with alignment 1, but alignment 4 is required @@ -20,19 +20,19 @@ note: inside `copy_nonoverlapping::` note: inside `std::ptr::const_ptr::::copy_to_nonoverlapping` --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL note: inside `MISALIGNED_COPY` - --> $DIR/raw-pointer-ub.rs:22:5 + --> $DIR/raw-pointer-ub.rs:19:5 | LL | y.copy_to_nonoverlapping(&mut z, 1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0080]: evaluation of constant value failed - --> $DIR/raw-pointer-ub.rs:34:16 + --> $DIR/raw-pointer-ub.rs:31:16 | LL | let _val = (*ptr).0; | ^^^^^^^^ accessing memory based on pointer with alignment 4, but alignment 16 is required error[E0080]: evaluation of constant value failed - --> $DIR/raw-pointer-ub.rs:41:16 + --> $DIR/raw-pointer-ub.rs:38:16 | LL | let _val = *ptr; | ^^^^ 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 diff --git a/tests/ui/consts/const-fn-not-safe-for-const.rs b/tests/ui/consts/const-fn-not-safe-for-const.rs index 6d8404880ca3..8a0cd86819ed 100644 --- a/tests/ui/consts/const-fn-not-safe-for-const.rs +++ b/tests/ui/consts/const-fn-not-safe-for-const.rs @@ -18,12 +18,10 @@ static Y: u32 = 0; const fn get_Y() -> u32 { Y - //~^ ERROR referencing statics in constant functions } const fn get_Y_addr() -> &'static u32 { &Y - //~^ ERROR referencing statics in constant functions } const fn get() -> u32 { diff --git a/tests/ui/consts/const-fn-not-safe-for-const.stderr b/tests/ui/consts/const-fn-not-safe-for-const.stderr index 7d7e94da86f7..674e05a0ba99 100644 --- a/tests/ui/consts/const-fn-not-safe-for-const.stderr +++ b/tests/ui/consts/const-fn-not-safe-for-const.stderr @@ -6,31 +6,6 @@ LL | random() | = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants -error[E0658]: referencing statics in constant functions is unstable - --> $DIR/const-fn-not-safe-for-const.rs:20:5 - | -LL | Y - | ^ - | - = note: see issue #119618 for more information - = help: add `#![feature(const_refs_to_static)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - = note: `static` and `const` variables can refer to other `const` variables. A `const` variable, however, cannot refer to a `static` variable. - = help: to fix this, the value can be extracted to a `const` and then used. - -error[E0658]: referencing statics in constant functions is unstable - --> $DIR/const-fn-not-safe-for-const.rs:25:6 - | -LL | &Y - | ^ - | - = note: see issue #119618 for more information - = help: add `#![feature(const_refs_to_static)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - = note: `static` and `const` variables can refer to other `const` variables. A `const` variable, however, cannot refer to a `static` variable. - = help: to fix this, the value can be extracted to a `const` and then used. - -error: aborting due to 3 previous errors +error: aborting due to 1 previous error -Some errors have detailed explanations: E0015, E0658. -For more information about an error, try `rustc --explain E0015`. +For more information about this error, try `rustc --explain E0015`. diff --git a/tests/ui/consts/const-mut-refs/mut_ref_in_final_dynamic_check.rs b/tests/ui/consts/const-mut-refs/mut_ref_in_final_dynamic_check.rs index 7bf178484cc7..2539fcccb848 100644 --- a/tests/ui/consts/const-mut-refs/mut_ref_in_final_dynamic_check.rs +++ b/tests/ui/consts/const-mut-refs/mut_ref_in_final_dynamic_check.rs @@ -1,7 +1,6 @@ //@ normalize-stderr-test: "(the raw bytes of the constant) \(size: [0-9]*, align: [0-9]*\)" -> "$1 (size: $$SIZE, align: $$ALIGN)" //@ normalize-stderr-test: "( 0x[0-9a-f][0-9a-f] │)? ([0-9a-f][0-9a-f] |__ |╾─*ALLOC[0-9]+(\+[a-z0-9]+)?()?─*╼ )+ *│.*" -> " HEX_DUMP" //@ normalize-stderr-test: "HEX_DUMP\s*\n\s*HEX_DUMP" -> "HEX_DUMP" -#![feature(const_refs_to_static)] use std::sync::Mutex; diff --git a/tests/ui/consts/const-mut-refs/mut_ref_in_final_dynamic_check.stderr b/tests/ui/consts/const-mut-refs/mut_ref_in_final_dynamic_check.stderr index 4ea6fa62475e..aebac56f8c5c 100644 --- a/tests/ui/consts/const-mut-refs/mut_ref_in_final_dynamic_check.stderr +++ b/tests/ui/consts/const-mut-refs/mut_ref_in_final_dynamic_check.stderr @@ -1,5 +1,5 @@ error[E0080]: it is undefined behavior to use this value - --> $DIR/mut_ref_in_final_dynamic_check.rs:19:1 + --> $DIR/mut_ref_in_final_dynamic_check.rs:18:1 | LL | const MUT: Option<&mut i32> = helper(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at ..0: encountered reference to mutable memory in `const` @@ -10,7 +10,7 @@ LL | const MUT: Option<&mut i32> = helper(); } error[E0080]: it is undefined behavior to use this value - --> $DIR/mut_ref_in_final_dynamic_check.rs:26:1 + --> $DIR/mut_ref_in_final_dynamic_check.rs:25:1 | LL | const INT2PTR: Option<&mut i32> = helper_int2ptr(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at ..0: encountered a dangling reference (0x2a[noalloc] has no provenance) @@ -21,7 +21,7 @@ LL | const INT2PTR: Option<&mut i32> = helper_int2ptr(); } error[E0080]: it is undefined behavior to use this value - --> $DIR/mut_ref_in_final_dynamic_check.rs:28:1 + --> $DIR/mut_ref_in_final_dynamic_check.rs:27:1 | LL | static INT2PTR_STATIC: Option<&mut i32> = helper_int2ptr(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at ..0: encountered a dangling reference (0x2a[noalloc] has no provenance) @@ -32,7 +32,7 @@ LL | static INT2PTR_STATIC: Option<&mut i32> = helper_int2ptr(); } error[E0080]: it is undefined behavior to use this value - --> $DIR/mut_ref_in_final_dynamic_check.rs:35:1 + --> $DIR/mut_ref_in_final_dynamic_check.rs:34:1 | LL | const DANGLING: Option<&mut i32> = helper_dangling(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at ..0: encountered a dangling reference (use-after-free) @@ -43,7 +43,7 @@ LL | const DANGLING: Option<&mut i32> = helper_dangling(); } error[E0080]: it is undefined behavior to use this value - --> $DIR/mut_ref_in_final_dynamic_check.rs:36:1 + --> $DIR/mut_ref_in_final_dynamic_check.rs:35:1 | LL | static DANGLING_STATIC: Option<&mut i32> = helper_dangling(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at ..0: encountered a dangling reference (use-after-free) diff --git a/tests/ui/consts/const-ref-to-static-linux-vtable.rs b/tests/ui/consts/const-ref-to-static-linux-vtable.rs index b9d29d0c1f43..87af63d41ab6 100644 --- a/tests/ui/consts/const-ref-to-static-linux-vtable.rs +++ b/tests/ui/consts/const-ref-to-static-linux-vtable.rs @@ -1,6 +1,6 @@ -//@check-pass +//@ check-pass //! This is the reduced version of the "Linux kernel vtable" use-case. -#![feature(const_refs_to_static)] + use std::ptr::addr_of_mut; #[repr(C)] diff --git a/tests/ui/consts/const_refs_to_static-ice-121413.rs b/tests/ui/consts/const_refs_to_static-ice-121413.rs index 8fc3912efd08..5b2d27ae8534 100644 --- a/tests/ui/consts/const_refs_to_static-ice-121413.rs +++ b/tests/ui/consts/const_refs_to_static-ice-121413.rs @@ -3,7 +3,6 @@ // issue: rust-lang/rust#121413 //@ compile-flags: -Zextra-const-ub-checks // ignore-tidy-linelength -#![feature(const_refs_to_static)] const REF_INTERIOR_MUT: &usize = { //~^ HELP consider importing this struct static FOO: Sync = AtomicUsize::new(0); diff --git a/tests/ui/consts/const_refs_to_static-ice-121413.stderr b/tests/ui/consts/const_refs_to_static-ice-121413.stderr index fbe32a70293a..c977c698a92e 100644 --- a/tests/ui/consts/const_refs_to_static-ice-121413.stderr +++ b/tests/ui/consts/const_refs_to_static-ice-121413.stderr @@ -1,5 +1,5 @@ error[E0433]: failed to resolve: use of undeclared type `AtomicUsize` - --> $DIR/const_refs_to_static-ice-121413.rs:9:24 + --> $DIR/const_refs_to_static-ice-121413.rs:8:24 | LL | static FOO: Sync = AtomicUsize::new(0); | ^^^^^^^^^^^ use of undeclared type `AtomicUsize` @@ -10,7 +10,7 @@ LL + use std::sync::atomic::AtomicUsize; | warning: trait objects without an explicit `dyn` are deprecated - --> $DIR/const_refs_to_static-ice-121413.rs:9:17 + --> $DIR/const_refs_to_static-ice-121413.rs:8:17 | LL | static FOO: Sync = AtomicUsize::new(0); | ^^^^ @@ -24,7 +24,7 @@ LL | static FOO: dyn Sync = AtomicUsize::new(0); | +++ error[E0277]: the size for values of type `(dyn Sync + 'static)` cannot be known at compilation time - --> $DIR/const_refs_to_static-ice-121413.rs:9:17 + --> $DIR/const_refs_to_static-ice-121413.rs:8:17 | LL | static FOO: Sync = AtomicUsize::new(0); | ^^^^ doesn't have a size known at compile-time @@ -32,7 +32,7 @@ LL | static FOO: Sync = AtomicUsize::new(0); = help: the trait `Sized` is not implemented for `(dyn Sync + 'static)` error[E0277]: the size for values of type `(dyn Sync + 'static)` cannot be known at compilation time - --> $DIR/const_refs_to_static-ice-121413.rs:9:24 + --> $DIR/const_refs_to_static-ice-121413.rs:8:24 | LL | static FOO: Sync = AtomicUsize::new(0); | ^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time diff --git a/tests/ui/consts/const_refs_to_static.rs b/tests/ui/consts/const_refs_to_static.rs index f41725b786eb..3c59697e8eda 100644 --- a/tests/ui/consts/const_refs_to_static.rs +++ b/tests/ui/consts/const_refs_to_static.rs @@ -1,5 +1,4 @@ //@ run-pass -#![feature(const_refs_to_static)] static S: i32 = 0; static mut S_MUT: i32 = 0; diff --git a/tests/ui/consts/const_refs_to_static_fail.rs b/tests/ui/consts/const_refs_to_static_fail.rs index a69902c34392..44e848ab6377 100644 --- a/tests/ui/consts/const_refs_to_static_fail.rs +++ b/tests/ui/consts/const_refs_to_static_fail.rs @@ -1,6 +1,8 @@ //@ normalize-stderr-test: "(the raw bytes of the constant) \(size: [0-9]*, align: [0-9]*\)" -> "$1 (size: $$SIZE, align: $$ALIGN)" //@ normalize-stderr-test: "([0-9a-f][0-9a-f] |╾─*ALLOC[0-9]+(\+[a-z0-9]+)?()?─*╼ )+ *│.*" -> "HEX_DUMP" -#![feature(const_refs_to_static, sync_unsafe_cell)] + +#![feature(sync_unsafe_cell)] + use std::cell::SyncUnsafeCell; static S: SyncUnsafeCell = SyncUnsafeCell::new(0); diff --git a/tests/ui/consts/const_refs_to_static_fail.stderr b/tests/ui/consts/const_refs_to_static_fail.stderr index cdabd86b183e..297561dbcf13 100644 --- a/tests/ui/consts/const_refs_to_static_fail.stderr +++ b/tests/ui/consts/const_refs_to_static_fail.stderr @@ -1,5 +1,5 @@ error[E0080]: it is undefined behavior to use this value - --> $DIR/const_refs_to_static_fail.rs:9:1 + --> $DIR/const_refs_to_static_fail.rs:11:1 | LL | const C1: &SyncUnsafeCell = &S; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered reference to mutable memory in `const` @@ -10,13 +10,13 @@ LL | const C1: &SyncUnsafeCell = &S; } note: erroneous constant encountered - --> $DIR/const_refs_to_static_fail.rs:12:14 + --> $DIR/const_refs_to_static_fail.rs:14:14 | LL | assert!(*C1.get() == 0); | ^^ error[E0080]: evaluation of constant value failed - --> $DIR/const_refs_to_static_fail.rs:16:13 + --> $DIR/const_refs_to_static_fail.rs:18:13 | LL | assert!(*C2 == 0); | ^^^ constant accesses mutable global memory diff --git a/tests/ui/consts/const_refs_to_static_fail_invalid.rs b/tests/ui/consts/const_refs_to_static_fail_invalid.rs index c58606d2ebb5..a160862a0fa6 100644 --- a/tests/ui/consts/const_refs_to_static_fail_invalid.rs +++ b/tests/ui/consts/const_refs_to_static_fail_invalid.rs @@ -1,6 +1,5 @@ //@ normalize-stderr-test: "(the raw bytes of the constant) \(size: [0-9]*, align: [0-9]*\)" -> "$1 (size: $$SIZE, align: $$ALIGN)" //@ normalize-stderr-test: "([0-9a-f][0-9a-f] |╾─*ALLOC[0-9]+(\+[a-z0-9]+)?()?─*╼ )+ *│.*" -> "HEX_DUMP" -#![feature(const_refs_to_static)] #![allow(static_mut_refs)] fn invalid() { diff --git a/tests/ui/consts/const_refs_to_static_fail_invalid.stderr b/tests/ui/consts/const_refs_to_static_fail_invalid.stderr index d5bb4847746e..0153f501174b 100644 --- a/tests/ui/consts/const_refs_to_static_fail_invalid.stderr +++ b/tests/ui/consts/const_refs_to_static_fail_invalid.stderr @@ -1,5 +1,5 @@ error[E0080]: it is undefined behavior to use this value - --> $DIR/const_refs_to_static_fail_invalid.rs:9:5 + --> $DIR/const_refs_to_static_fail_invalid.rs:8:5 | LL | const C: &bool = unsafe { std::mem::transmute(&S) }; | ^^^^^^^^^^^^^^ constructing invalid value at .: encountered 0x0a, but expected a boolean @@ -10,7 +10,7 @@ LL | const C: &bool = unsafe { std::mem::transmute(&S) }; } error[E0080]: it is undefined behavior to use this value - --> $DIR/const_refs_to_static_fail_invalid.rs:25:5 + --> $DIR/const_refs_to_static_fail_invalid.rs:24:5 | LL | const C: &i8 = unsafe { &S }; | ^^^^^^^^^^^^ constructing invalid value: encountered reference to `extern` static in `const` @@ -21,7 +21,7 @@ LL | const C: &i8 = unsafe { &S }; } error[E0080]: it is undefined behavior to use this value - --> $DIR/const_refs_to_static_fail_invalid.rs:39:5 + --> $DIR/const_refs_to_static_fail_invalid.rs:38:5 | LL | const C: &i32 = unsafe { &S_MUT }; | ^^^^^^^^^^^^^ constructing invalid value: encountered reference to mutable memory in `const` @@ -32,19 +32,19 @@ LL | const C: &i32 = unsafe { &S_MUT }; } error: could not evaluate constant pattern - --> $DIR/const_refs_to_static_fail_invalid.rs:15:9 + --> $DIR/const_refs_to_static_fail_invalid.rs:14:9 | LL | C => {} | ^ error: could not evaluate constant pattern - --> $DIR/const_refs_to_static_fail_invalid.rs:31:9 + --> $DIR/const_refs_to_static_fail_invalid.rs:30:9 | LL | C => {} | ^ error: could not evaluate constant pattern - --> $DIR/const_refs_to_static_fail_invalid.rs:46:9 + --> $DIR/const_refs_to_static_fail_invalid.rs:45:9 | LL | C => {} | ^ diff --git a/tests/ui/consts/issue-17718-const-bad-values.rs b/tests/ui/consts/issue-17718-const-bad-values.rs index 52f8c9bf149a..fca6cb085379 100644 --- a/tests/ui/consts/issue-17718-const-bad-values.rs +++ b/tests/ui/consts/issue-17718-const-bad-values.rs @@ -1,10 +1,15 @@ +//@ normalize-stderr-32bit: "\(size: \d+, align: \d+\)" -> "(size: $$PTR, align: $$PTR)" +//@ normalize-stderr-64bit: "\(size: \d+, align: \d+\)" -> "(size: $$PTR, align: $$PTR)" +//@ normalize-stderr-test: "([0-9a-f][0-9a-f] |╾─*A(LLOC)?[0-9]+(\+[a-z0-9]+)?()?─*╼ )+ *│.*" -> "HEX_DUMP" + #![allow(static_mut_refs)] const C1: &'static mut [usize] = &mut []; //~^ ERROR: mutable references are not allowed -static mut S: usize = 3; -const C2: &'static mut usize = unsafe { &mut S }; -//~^ ERROR: referencing statics in constants +static mut S: i32 = 3; +const C2: &'static mut i32 = unsafe { &mut S }; +//~^ ERROR: it is undefined behavior to use this value +//~| reference to mutable memory fn main() {} diff --git a/tests/ui/consts/issue-17718-const-bad-values.stderr b/tests/ui/consts/issue-17718-const-bad-values.stderr index 57fcb1c7e9a5..102491e90bac 100644 --- a/tests/ui/consts/issue-17718-const-bad-values.stderr +++ b/tests/ui/consts/issue-17718-const-bad-values.stderr @@ -1,22 +1,21 @@ error[E0764]: mutable references are not allowed in the final value of constants - --> $DIR/issue-17718-const-bad-values.rs:3:34 + --> $DIR/issue-17718-const-bad-values.rs:7:34 | LL | const C1: &'static mut [usize] = &mut []; | ^^^^^^^ -error[E0658]: referencing statics in constants is unstable - --> $DIR/issue-17718-const-bad-values.rs:7:46 +error[E0080]: it is undefined behavior to use this value + --> $DIR/issue-17718-const-bad-values.rs:11:1 | -LL | const C2: &'static mut usize = unsafe { &mut S }; - | ^ +LL | const C2: &'static mut i32 = unsafe { &mut S }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered reference to mutable memory in `const` | - = note: see issue #119618 for more information - = help: add `#![feature(const_refs_to_static)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - = note: `static` and `const` variables can refer to other `const` variables. A `const` variable, however, cannot refer to a `static` variable. - = help: to fix this, the value can be extracted to a `const` and then used. + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: $PTR, align: $PTR) { + HEX_DUMP + } error: aborting due to 2 previous errors -Some errors have detailed explanations: E0658, E0764. -For more information about an error, try `rustc --explain E0658`. +Some errors have detailed explanations: E0080, E0764. +For more information about an error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/issue-17718-references.rs b/tests/ui/consts/issue-17718-references.rs index 6a8955f46343..120ec28c4049 100644 --- a/tests/ui/consts/issue-17718-references.rs +++ b/tests/ui/consts/issue-17718-references.rs @@ -1,23 +1,26 @@ +//@ check-pass #![allow(warnings)] -struct Struct { a: usize } +struct Struct { + a: usize, +} const C: usize = 1; static S: usize = 1; const T1: &'static usize = &C; -const T2: &'static usize = &S; //~ ERROR: referencing statics in constants +const T2: &'static usize = &S; static T3: &'static usize = &C; static T4: &'static usize = &S; const T5: usize = C; -const T6: usize = S; //~ ERROR: referencing statics in constants +const T6: usize = S; static T7: usize = C; static T8: usize = S; const T9: Struct = Struct { a: C }; const T10: Struct = Struct { a: S }; -//~^ ERROR: referencing statics in constants + static T11: Struct = Struct { a: C }; static T12: Struct = Struct { a: S }; diff --git a/tests/ui/consts/issue-17718-references.stderr b/tests/ui/consts/issue-17718-references.stderr deleted file mode 100644 index 8b5722037812..000000000000 --- a/tests/ui/consts/issue-17718-references.stderr +++ /dev/null @@ -1,39 +0,0 @@ -error[E0658]: referencing statics in constants is unstable - --> $DIR/issue-17718-references.rs:9:29 - | -LL | const T2: &'static usize = &S; - | ^ - | - = note: see issue #119618 for more information - = help: add `#![feature(const_refs_to_static)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - = note: `static` and `const` variables can refer to other `const` variables. A `const` variable, however, cannot refer to a `static` variable. - = help: to fix this, the value can be extracted to a `const` and then used. - -error[E0658]: referencing statics in constants is unstable - --> $DIR/issue-17718-references.rs:14:19 - | -LL | const T6: usize = S; - | ^ - | - = note: see issue #119618 for more information - = help: add `#![feature(const_refs_to_static)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - = note: `static` and `const` variables can refer to other `const` variables. A `const` variable, however, cannot refer to a `static` variable. - = help: to fix this, the value can be extracted to a `const` and then used. - -error[E0658]: referencing statics in constants is unstable - --> $DIR/issue-17718-references.rs:19:33 - | -LL | const T10: Struct = Struct { a: S }; - | ^ - | - = note: see issue #119618 for more information - = help: add `#![feature(const_refs_to_static)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - = note: `static` and `const` variables can refer to other `const` variables. A `const` variable, however, cannot refer to a `static` variable. - = help: to fix this, the value can be extracted to a `const` and then used. - -error: aborting due to 3 previous errors - -For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/consts/issue-52060.rs b/tests/ui/consts/issue-52060.rs deleted file mode 100644 index 0f16ede04001..000000000000 --- a/tests/ui/consts/issue-52060.rs +++ /dev/null @@ -1,7 +0,0 @@ -// Regression test for https://github.com/rust-lang/rust/issues/52060 -// The compiler shouldn't ICE in this case -static A: &'static [u32] = &[1]; -static B: [u32; 1] = [0; A.len()]; -//~^ ERROR referencing statics in constants - -fn main() {} diff --git a/tests/ui/consts/issue-52060.stderr b/tests/ui/consts/issue-52060.stderr deleted file mode 100644 index 644a5314622d..000000000000 --- a/tests/ui/consts/issue-52060.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error[E0658]: referencing statics in constants is unstable - --> $DIR/issue-52060.rs:4:26 - | -LL | static B: [u32; 1] = [0; A.len()]; - | ^ - | - = note: see issue #119618 for more information - = help: add `#![feature(const_refs_to_static)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - = note: `static` and `const` variables can refer to other `const` variables. A `const` variable, however, cannot refer to a `static` variable. - = help: to fix this, the value can be extracted to a `const` and then used. - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/consts/min_const_fn/min_const_fn.rs b/tests/ui/consts/min_const_fn/min_const_fn.rs index ed5aa40b66c3..e6d9d184e041 100644 --- a/tests/ui/consts/min_const_fn/min_const_fn.rs +++ b/tests/ui/consts/min_const_fn/min_const_fn.rs @@ -74,8 +74,8 @@ const fn foo11_2(t: T) -> T { t } // not ok static BAR: u32 = 42; -const fn foo25() -> u32 { BAR } //~ ERROR referencing statics in constant functions -const fn foo26() -> &'static u32 { &BAR } //~ ERROR referencing statics in constant functions +const fn foo25() -> u32 { BAR } +const fn foo26() -> &'static u32 { &BAR } const fn foo30(x: *const u32) -> usize { x as usize } //~^ ERROR pointers cannot be cast to integers const fn foo30_with_unsafe(x: *const u32) -> usize { unsafe { x as usize } } diff --git a/tests/ui/consts/min_const_fn/min_const_fn.stderr b/tests/ui/consts/min_const_fn/min_const_fn.stderr index c02f8c76d441..0e939e5121aa 100644 --- a/tests/ui/consts/min_const_fn/min_const_fn.stderr +++ b/tests/ui/consts/min_const_fn/min_const_fn.stderr @@ -22,30 +22,6 @@ LL | const fn into_inner_s(self) -> T { self.0 } | | | the destructor for this type cannot be evaluated in constant functions -error[E0658]: referencing statics in constant functions is unstable - --> $DIR/min_const_fn.rs:77:27 - | -LL | const fn foo25() -> u32 { BAR } - | ^^^ - | - = note: see issue #119618 for more information - = help: add `#![feature(const_refs_to_static)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - = note: `static` and `const` variables can refer to other `const` variables. A `const` variable, however, cannot refer to a `static` variable. - = help: to fix this, the value can be extracted to a `const` and then used. - -error[E0658]: referencing statics in constant functions is unstable - --> $DIR/min_const_fn.rs:78:37 - | -LL | const fn foo26() -> &'static u32 { &BAR } - | ^^^ - | - = note: see issue #119618 for more information - = help: add `#![feature(const_refs_to_static)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - = note: `static` and `const` variables can refer to other `const` variables. A `const` variable, however, cannot refer to a `static` variable. - = help: to fix this, the value can be extracted to a `const` and then used. - error: pointers cannot be cast to integers during const eval --> $DIR/min_const_fn.rs:79:42 | @@ -98,7 +74,6 @@ LL | const fn no_apit(_x: impl std::fmt::Debug) {} | | | the destructor for this type cannot be evaluated in constant functions -error: aborting due to 11 previous errors +error: aborting due to 9 previous errors -Some errors have detailed explanations: E0493, E0658. -For more information about an error, try `rustc --explain E0493`. +For more information about this error, try `rustc --explain E0493`. diff --git a/tests/ui/consts/miri_unleashed/const_refers_to_static.stderr b/tests/ui/consts/miri_unleashed/const_refers_to_static.stderr index df910546d11a..f8e0606fbd77 100644 --- a/tests/ui/consts/miri_unleashed/const_refers_to_static.stderr +++ b/tests/ui/consts/miri_unleashed/const_refers_to_static.stderr @@ -29,36 +29,11 @@ LL | const REF_INTERIOR_MUT: &usize = { warning: skipping const checks | -help: skipping check for `const_refs_to_static` feature - --> $DIR/const_refers_to_static.rs:10:5 - | -LL | FOO.fetch_add(1, Ordering::Relaxed) - | ^^^ help: skipping check that does not even have a feature gate --> $DIR/const_refers_to_static.rs:10:5 | LL | FOO.fetch_add(1, Ordering::Relaxed) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -help: skipping check for `const_refs_to_static` feature - --> $DIR/const_refers_to_static.rs:15:17 - | -LL | unsafe { *(&FOO as *const _ as *const usize) } - | ^^^ -help: skipping check for `const_refs_to_static` feature - --> $DIR/const_refers_to_static.rs:19:32 - | -LL | const READ_MUT: u32 = unsafe { MUTABLE }; - | ^^^^^^^ -help: skipping check for `const_refs_to_static` feature - --> $DIR/const_refers_to_static.rs:25:18 - | -LL | unsafe { &*(&FOO as *const _ as *const usize) } - | ^^^ -help: skipping check for `const_refs_to_static` feature - --> $DIR/const_refers_to_static.rs:30:25 - | -LL | const REF_IMMUT: &u8 = &MY_STATIC; - | ^^^^^^^^^ error: aborting due to 4 previous errors; 1 warning emitted diff --git a/tests/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.stderr b/tests/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.stderr index 7a7b7bc57da7..147d3f238f77 100644 --- a/tests/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.stderr +++ b/tests/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.stderr @@ -61,29 +61,6 @@ error: could not evaluate constant pattern LL | U8_MUT3 => true, | ^^^^^^^ -warning: skipping const checks - | -help: skipping check for `const_refs_to_static` feature - --> $DIR/const_refers_to_static_cross_crate.rs:14:15 - | -LL | unsafe { &static_cross_crate::ZERO } - | ^^^^^^^^^^^^^^^^^^^^^^^^ -help: skipping check for `const_refs_to_static` feature - --> $DIR/const_refers_to_static_cross_crate.rs:19:15 - | -LL | unsafe { &static_cross_crate::ZERO[0] } - | ^^^^^^^^^^^^^^^^^^^^^^^^ -help: skipping check for `const_refs_to_static` feature - --> $DIR/const_refers_to_static_cross_crate.rs:25:17 - | -LL | unsafe { &(*static_cross_crate::ZERO_REF)[0] } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -help: skipping check for `const_refs_to_static` feature - --> $DIR/const_refers_to_static_cross_crate.rs:29:15 - | -LL | match static_cross_crate::OPT_ZERO { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 8 previous errors; 1 warning emitted +error: aborting due to 8 previous errors For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/miri_unleashed/mutable_references.rs b/tests/ui/consts/miri_unleashed/mutable_references.rs index 6ac61e670016..a60058cc5c01 100644 --- a/tests/ui/consts/miri_unleashed/mutable_references.rs +++ b/tests/ui/consts/miri_unleashed/mutable_references.rs @@ -27,9 +27,12 @@ const BLUNT: &mut i32 = &mut 42; //~^ ERROR: it is undefined behavior to use this value //~| pointing to read-only memory -const SUBTLE: &mut i32 = unsafe { static mut STATIC: i32 = 0; &mut STATIC }; -//~^ ERROR: it is undefined behavior to use this value -//~| static +const SUBTLE: &mut i32 = unsafe { + //~^ ERROR: it is undefined behavior to use this value + //~| constructing invalid value: encountered reference to mutable memory in `const` + static mut STATIC: i32 = 0; + &mut STATIC +}; // # Interior mutability @@ -105,7 +108,6 @@ const RAW_MUT_CAST: SyncPtr = SyncPtr { x: &mut 42 as *mut _ as *const _ }; const RAW_MUT_COERCE: SyncPtr = SyncPtr { x: &mut 0 }; //~^ ERROR mutable pointer in final value - fn main() { unsafe { *MEH.x.get() = 99; diff --git a/tests/ui/consts/miri_unleashed/mutable_references.stderr b/tests/ui/consts/miri_unleashed/mutable_references.stderr index 874dd0389d45..ce5cedac8bcd 100644 --- a/tests/ui/consts/miri_unleashed/mutable_references.stderr +++ b/tests/ui/consts/miri_unleashed/mutable_references.stderr @@ -46,7 +46,7 @@ LL | const BLUNT: &mut i32 = &mut 42; error[E0080]: it is undefined behavior to use this value --> $DIR/mutable_references.rs:30:1 | -LL | const SUBTLE: &mut i32 = unsafe { static mut STATIC: i32 = 0; &mut STATIC }; +LL | const SUBTLE: &mut i32 = unsafe { | ^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered reference to mutable memory in `const` | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. @@ -55,7 +55,7 @@ LL | const SUBTLE: &mut i32 = unsafe { static mut STATIC: i32 = 0; &mut STATIC } } error[E0080]: it is undefined behavior to use this value - --> $DIR/mutable_references.rs:40:1 + --> $DIR/mutable_references.rs:43:1 | LL | static MEH: Meh = Meh { x: &UnsafeCell::new(42) }; | ^^^^^^^^^^^^^^^ constructing invalid value at .x.: encountered `UnsafeCell` in read-only memory @@ -66,7 +66,7 @@ LL | static MEH: Meh = Meh { x: &UnsafeCell::new(42) }; } error[E0080]: it is undefined behavior to use this value - --> $DIR/mutable_references.rs:46:1 + --> $DIR/mutable_references.rs:49:1 | LL | const MUH: Meh = Meh { | ^^^^^^^^^^^^^^ constructing invalid value at .x.: encountered `UnsafeCell` in read-only memory @@ -77,7 +77,7 @@ LL | const MUH: Meh = Meh { } error[E0080]: it is undefined behavior to use this value - --> $DIR/mutable_references.rs:58:1 + --> $DIR/mutable_references.rs:61:1 | LL | const SNEAKY: &dyn Sync = &Synced { x: UnsafeCell::new(42) }; | ^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at ...x: encountered `UnsafeCell` in read-only memory @@ -88,7 +88,7 @@ LL | const SNEAKY: &dyn Sync = &Synced { x: UnsafeCell::new(42) }; } error[E0080]: it is undefined behavior to use this value - --> $DIR/mutable_references.rs:65:1 + --> $DIR/mutable_references.rs:68:1 | LL | static mut MUT_TO_READONLY: &mut i32 = unsafe { &mut *(&READONLY as *const _ as *mut _) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered mutable reference or box pointing to read-only memory @@ -99,7 +99,7 @@ LL | static mut MUT_TO_READONLY: &mut i32 = unsafe { &mut *(&READONLY as *const } error[E0080]: it is undefined behavior to use this value - --> $DIR/mutable_references.rs:72:1 + --> $DIR/mutable_references.rs:75:1 | LL | const POINTS_TO_MUTABLE: &i32 = unsafe { &MUTABLE }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered reference to mutable memory in `const` @@ -110,37 +110,37 @@ LL | const POINTS_TO_MUTABLE: &i32 = unsafe { &MUTABLE }; } error[E0080]: evaluation of constant value failed - --> $DIR/mutable_references.rs:75:43 + --> $DIR/mutable_references.rs:78:43 | LL | const POINTS_TO_MUTABLE2: &i32 = unsafe { &*MUTABLE_REF }; | ^^^^^^^^^^^^^ constant accesses mutable global memory error: encountered mutable pointer in final value of constant - --> $DIR/mutable_references.rs:79:1 + --> $DIR/mutable_references.rs:82:1 | LL | const POINTS_TO_MUTABLE_INNER: *const i32 = &mut 42 as *mut _ as *const _; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: encountered mutable pointer in final value of constant - --> $DIR/mutable_references.rs:82:1 + --> $DIR/mutable_references.rs:85:1 | LL | const POINTS_TO_MUTABLE_INNER2: *const i32 = &mut 42 as *const _; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: encountered mutable pointer in final value of constant - --> $DIR/mutable_references.rs:102:1 + --> $DIR/mutable_references.rs:105:1 | LL | const RAW_MUT_CAST: SyncPtr = SyncPtr { x: &mut 42 as *mut _ as *const _ }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: encountered mutable pointer in final value of constant - --> $DIR/mutable_references.rs:105:1 + --> $DIR/mutable_references.rs:108:1 | LL | const RAW_MUT_COERCE: SyncPtr = SyncPtr { x: &mut 0 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0594]: cannot assign to `*OH_YES`, as `OH_YES` is an immutable static item - --> $DIR/mutable_references.rs:113:5 + --> $DIR/mutable_references.rs:115:5 | LL | *OH_YES = 99; | ^^^^^^^^^^^^ cannot assign @@ -172,63 +172,48 @@ help: skipping check that does not even have a feature gate | LL | const BLUNT: &mut i32 = &mut 42; | ^^^^^^^ -help: skipping check for `const_refs_to_static` feature - --> $DIR/mutable_references.rs:30:68 - | -LL | const SUBTLE: &mut i32 = unsafe { static mut STATIC: i32 = 0; &mut STATIC }; - | ^^^^^^ help: skipping check that does not even have a feature gate - --> $DIR/mutable_references.rs:40:28 + --> $DIR/mutable_references.rs:43:28 | LL | static MEH: Meh = Meh { x: &UnsafeCell::new(42) }; | ^^^^^^^^^^^^^^^^^^^^ help: skipping check that does not even have a feature gate - --> $DIR/mutable_references.rs:49:8 + --> $DIR/mutable_references.rs:52:8 | LL | x: &UnsafeCell::new(42), | ^^^^^^^^^^^^^^^^^^^^ help: skipping check that does not even have a feature gate - --> $DIR/mutable_references.rs:58:27 + --> $DIR/mutable_references.rs:61:27 | LL | const SNEAKY: &dyn Sync = &Synced { x: UnsafeCell::new(42) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -help: skipping check for `const_refs_to_static` feature - --> $DIR/mutable_references.rs:72:43 - | -LL | const POINTS_TO_MUTABLE: &i32 = unsafe { &MUTABLE }; - | ^^^^^^^ -help: skipping check for `const_refs_to_static` feature - --> $DIR/mutable_references.rs:75:45 - | -LL | const POINTS_TO_MUTABLE2: &i32 = unsafe { &*MUTABLE_REF }; - | ^^^^^^^^^^^ help: skipping check that does not even have a feature gate - --> $DIR/mutable_references.rs:79:45 + --> $DIR/mutable_references.rs:82:45 | LL | const POINTS_TO_MUTABLE_INNER: *const i32 = &mut 42 as *mut _ as *const _; | ^^^^^^^ help: skipping check that does not even have a feature gate - --> $DIR/mutable_references.rs:82:46 + --> $DIR/mutable_references.rs:85:46 | LL | const POINTS_TO_MUTABLE_INNER2: *const i32 = &mut 42 as *const _; | ^^^^^^^ help: skipping check that does not even have a feature gate - --> $DIR/mutable_references.rs:87:47 + --> $DIR/mutable_references.rs:90:47 | LL | const INTERIOR_MUTABLE_BEHIND_RAW: *mut i32 = &UnsafeCell::new(42) as *const _ as *mut _; | ^^^^^^^^^^^^^^^^^^^^ help: skipping check that does not even have a feature gate - --> $DIR/mutable_references.rs:99:51 + --> $DIR/mutable_references.rs:102:51 | LL | const RAW_SYNC: SyncPtr = SyncPtr { x: &AtomicI32::new(42) }; | ^^^^^^^^^^^^^^^^^^^ help: skipping check that does not even have a feature gate - --> $DIR/mutable_references.rs:102:49 + --> $DIR/mutable_references.rs:105:49 | LL | const RAW_MUT_CAST: SyncPtr = SyncPtr { x: &mut 42 as *mut _ as *const _ }; | ^^^^^^^ help: skipping check that does not even have a feature gate - --> $DIR/mutable_references.rs:105:51 + --> $DIR/mutable_references.rs:108:51 | LL | const RAW_MUT_COERCE: SyncPtr = SyncPtr { x: &mut 0 }; | ^^^^^^ diff --git a/tests/ui/consts/no-ice-from-static-in-const-issue-52060.rs b/tests/ui/consts/no-ice-from-static-in-const-issue-52060.rs new file mode 100644 index 000000000000..e0f9e462d32e --- /dev/null +++ b/tests/ui/consts/no-ice-from-static-in-const-issue-52060.rs @@ -0,0 +1,9 @@ +// Regression test for https://github.com/rust-lang/rust/issues/52060 +// The compiler shouldn't ICE in this case + +static mut A: &'static [u32] = &[1]; +static B: [u32; 1] = [0; unsafe { A.len() }]; +//~^ ERROR: evaluation of constant value failed +//~| mutable global memory + +fn main() {} diff --git a/tests/ui/consts/no-ice-from-static-in-const-issue-52060.stderr b/tests/ui/consts/no-ice-from-static-in-const-issue-52060.stderr new file mode 100644 index 000000000000..ca4d3224ec79 --- /dev/null +++ b/tests/ui/consts/no-ice-from-static-in-const-issue-52060.stderr @@ -0,0 +1,9 @@ +error[E0080]: evaluation of constant value failed + --> $DIR/no-ice-from-static-in-const-issue-52060.rs:5:35 + | +LL | static B: [u32; 1] = [0; unsafe { A.len() }]; + | ^ constant accesses mutable global memory + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/coroutine/arg-count-mismatch-on-unit-input.rs b/tests/ui/coroutine/arg-count-mismatch-on-unit-input.rs new file mode 100644 index 000000000000..448c7100df65 --- /dev/null +++ b/tests/ui/coroutine/arg-count-mismatch-on-unit-input.rs @@ -0,0 +1,11 @@ +#![feature(coroutines, coroutine_trait, stmt_expr_attributes)] + +use std::ops::Coroutine; + +fn foo() -> impl Coroutine { + //~^ ERROR type mismatch in coroutine arguments + #[coroutine] + |_: ()| {} +} + +fn main() { } diff --git a/tests/ui/coroutine/arg-count-mismatch-on-unit-input.stderr b/tests/ui/coroutine/arg-count-mismatch-on-unit-input.stderr new file mode 100644 index 000000000000..c7d6507fd794 --- /dev/null +++ b/tests/ui/coroutine/arg-count-mismatch-on-unit-input.stderr @@ -0,0 +1,15 @@ +error[E0631]: type mismatch in coroutine arguments + --> $DIR/arg-count-mismatch-on-unit-input.rs:5:13 + | +LL | fn foo() -> impl Coroutine { + | ^^^^^^^^^^^^^^^^^^ expected due to this +... +LL | |_: ()| {} + | ------- found signature defined here + | + = note: expected coroutine signature `fn(u8) -> _` + found coroutine signature `fn(()) -> _` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0631`. diff --git a/tests/ui/dropck/dropck_trait_cycle_checked.stderr b/tests/ui/dropck/dropck_trait_cycle_checked.stderr index 63fd07a91633..f32736f1a674 100644 --- a/tests/ui/dropck/dropck_trait_cycle_checked.stderr +++ b/tests/ui/dropck/dropck_trait_cycle_checked.stderr @@ -2,7 +2,7 @@ error[E0597]: `o2` does not live long enough --> $DIR/dropck_trait_cycle_checked.rs:111:13 | LL | let (o1, o2, o3): (Box, Box, Box) = (O::new(), O::new(), O::new()); - | -- binding `o2` declared here -------- cast requires that `o2` is borrowed for `'static` + | -- binding `o2` declared here -------- coercion requires that `o2` is borrowed for `'static` LL | o1.set0(&o2); | ^^^ borrowed value does not live long enough ... @@ -15,7 +15,7 @@ error[E0597]: `o3` does not live long enough --> $DIR/dropck_trait_cycle_checked.rs:112:13 | LL | let (o1, o2, o3): (Box, Box, Box) = (O::new(), O::new(), O::new()); - | -- binding `o3` declared here -------- cast requires that `o3` is borrowed for `'static` + | -- binding `o3` declared here -------- coercion requires that `o3` is borrowed for `'static` LL | o1.set0(&o2); LL | o1.set1(&o3); | ^^^ borrowed value does not live long enough @@ -29,7 +29,7 @@ error[E0597]: `o2` does not live long enough --> $DIR/dropck_trait_cycle_checked.rs:113:13 | LL | let (o1, o2, o3): (Box, Box, Box) = (O::new(), O::new(), O::new()); - | -- binding `o2` declared here -------- cast requires that `o2` is borrowed for `'static` + | -- binding `o2` declared here -------- coercion requires that `o2` is borrowed for `'static` ... LL | o2.set0(&o2); | ^^^ borrowed value does not live long enough @@ -43,7 +43,7 @@ error[E0597]: `o3` does not live long enough --> $DIR/dropck_trait_cycle_checked.rs:114:13 | LL | let (o1, o2, o3): (Box, Box, Box) = (O::new(), O::new(), O::new()); - | -- binding `o3` declared here -------- cast requires that `o3` is borrowed for `'static` + | -- binding `o3` declared here -------- coercion requires that `o3` is borrowed for `'static` ... LL | o2.set1(&o3); | ^^^ borrowed value does not live long enough @@ -57,7 +57,7 @@ error[E0597]: `o1` does not live long enough --> $DIR/dropck_trait_cycle_checked.rs:115:13 | LL | let (o1, o2, o3): (Box, Box, Box) = (O::new(), O::new(), O::new()); - | -- binding `o1` declared here -------- cast requires that `o1` is borrowed for `'static` + | -- binding `o1` declared here -------- coercion requires that `o1` is borrowed for `'static` ... LL | o3.set0(&o1); | ^^^ borrowed value does not live long enough @@ -71,7 +71,7 @@ error[E0597]: `o2` does not live long enough --> $DIR/dropck_trait_cycle_checked.rs:116:13 | LL | let (o1, o2, o3): (Box, Box, Box) = (O::new(), O::new(), O::new()); - | -- binding `o2` declared here -------- cast requires that `o2` is borrowed for `'static` + | -- binding `o2` declared here -------- coercion requires that `o2` is borrowed for `'static` ... LL | o3.set1(&o2); | ^^^ borrowed value does not live long enough diff --git a/tests/ui/dyn-star/dyn-to-rigid.rs b/tests/ui/dyn-star/dyn-to-rigid.rs index e80ee15902ee..dc33e288f24e 100644 --- a/tests/ui/dyn-star/dyn-to-rigid.rs +++ b/tests/ui/dyn-star/dyn-to-rigid.rs @@ -5,7 +5,7 @@ trait Tr {} fn f(x: dyn* Tr) -> usize { x as usize - //~^ ERROR casting `(dyn* Tr + 'static)` as `usize` is invalid + //~^ ERROR non-primitive cast: `(dyn* Tr + 'static)` as `usize` } fn main() {} diff --git a/tests/ui/dyn-star/dyn-to-rigid.stderr b/tests/ui/dyn-star/dyn-to-rigid.stderr index b198c5245dea..49b8f268aa4e 100644 --- a/tests/ui/dyn-star/dyn-to-rigid.stderr +++ b/tests/ui/dyn-star/dyn-to-rigid.stderr @@ -1,9 +1,9 @@ -error[E0606]: casting `(dyn* Tr + 'static)` as `usize` is invalid +error[E0605]: non-primitive cast: `(dyn* Tr + 'static)` as `usize` --> $DIR/dyn-to-rigid.rs:7:5 | LL | x as usize - | ^^^^^^^^^^ + | ^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0606`. +For more information about this error, try `rustc --explain E0605`. diff --git a/tests/ui/dyn-star/enum-cast.rs b/tests/ui/dyn-star/enum-cast.rs new file mode 100644 index 000000000000..6e895e9527ab --- /dev/null +++ b/tests/ui/dyn-star/enum-cast.rs @@ -0,0 +1,18 @@ +//@ check-pass + +// This used to ICE, because the compiler confused a pointer-like to dyn* coercion +// with a c-like enum to integer cast. + +#![feature(dyn_star)] +#![expect(incomplete_features)] + +enum E { + Num(usize), +} + +trait Trait {} +impl Trait for E {} + +fn main() { + let _ = E::Num(42) as dyn* Trait; +} diff --git a/tests/ui/feature-gates/feature-gate-const-refs-to-static.rs b/tests/ui/feature-gates/feature-gate-const-refs-to-static.rs deleted file mode 100644 index 008b754dc6cf..000000000000 --- a/tests/ui/feature-gates/feature-gate-const-refs-to-static.rs +++ /dev/null @@ -1,11 +0,0 @@ -static S: i32 = 0; -static mut S_MUT: i32 = 0; - -const C1: &i32 = &S; //~ERROR: referencing statics in constants is unstable -const C1_READ: () = { - assert!(*C1 == 0); -}; -const C2: *const i32 = unsafe { std::ptr::addr_of!(S_MUT) }; //~ERROR: referencing statics in constants is unstable - -fn main() { -} diff --git a/tests/ui/feature-gates/feature-gate-const-refs-to-static.stderr b/tests/ui/feature-gates/feature-gate-const-refs-to-static.stderr deleted file mode 100644 index 5af484712501..000000000000 --- a/tests/ui/feature-gates/feature-gate-const-refs-to-static.stderr +++ /dev/null @@ -1,27 +0,0 @@ -error[E0658]: referencing statics in constants is unstable - --> $DIR/feature-gate-const-refs-to-static.rs:4:19 - | -LL | const C1: &i32 = &S; - | ^ - | - = note: see issue #119618 for more information - = help: add `#![feature(const_refs_to_static)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - = note: `static` and `const` variables can refer to other `const` variables. A `const` variable, however, cannot refer to a `static` variable. - = help: to fix this, the value can be extracted to a `const` and then used. - -error[E0658]: referencing statics in constants is unstable - --> $DIR/feature-gate-const-refs-to-static.rs:8:52 - | -LL | const C2: *const i32 = unsafe { std::ptr::addr_of!(S_MUT) }; - | ^^^^^ - | - = note: see issue #119618 for more information - = help: add `#![feature(const_refs_to_static)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - = note: `static` and `const` variables can refer to other `const` variables. A `const` variable, however, cannot refer to a `static` variable. - = help: to fix this, the value can be extracted to a `const` and then used. - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/feature-gates/feature-gate-unqualified-local-imports.rs b/tests/ui/feature-gates/feature-gate-unqualified-local-imports.rs new file mode 100644 index 000000000000..29929e40f89b --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-unqualified-local-imports.rs @@ -0,0 +1,6 @@ +//@ check-pass + +#![allow(unqualified_local_imports)] +//~^ WARNING unknown lint: `unqualified_local_imports` + +fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-unqualified-local-imports.stderr b/tests/ui/feature-gates/feature-gate-unqualified-local-imports.stderr new file mode 100644 index 000000000000..22cd3bf4c6fa --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-unqualified-local-imports.stderr @@ -0,0 +1,13 @@ +warning: unknown lint: `unqualified_local_imports` + --> $DIR/feature-gate-unqualified-local-imports.rs:3:1 + | +LL | #![allow(unqualified_local_imports)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: the `unqualified_local_imports` lint is unstable + = help: add `#![feature(unqualified_local_imports)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = note: `#[warn(unknown_lints)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/impl-trait/where-allowed.rs b/tests/ui/impl-trait/where-allowed.rs index 72ce617693e4..3f435f0f443b 100644 --- a/tests/ui/impl-trait/where-allowed.rs +++ b/tests/ui/impl-trait/where-allowed.rs @@ -42,7 +42,7 @@ fn in_dyn_Fn_return_in_parameters(_: &dyn Fn() -> impl Debug) { panic!() } fn in_dyn_Fn_parameter_in_return() -> &'static dyn Fn(impl Debug) { panic!() } //~^ ERROR `impl Trait` is not allowed in the parameters of `Fn` trait bounds -// Allowed +// Allowed (but it's still ambiguous; nothing constrains the RPIT in this body). fn in_dyn_Fn_return_in_return() -> &'static dyn Fn() -> impl Debug { panic!() } //~^ ERROR: type annotations needed @@ -79,7 +79,6 @@ fn in_impl_Trait_in_parameters(_: impl Iterator) { panic!( // Allowed fn in_impl_Trait_in_return() -> impl IntoIterator { vec![vec![0; 10], vec![12; 7], vec![8; 3]] - //~^ ERROR: no function or associated item named `into_vec` found for slice `[_]` } // Disallowed diff --git a/tests/ui/impl-trait/where-allowed.stderr b/tests/ui/impl-trait/where-allowed.stderr index 1fb69db98c16..2770a6cc40e5 100644 --- a/tests/ui/impl-trait/where-allowed.stderr +++ b/tests/ui/impl-trait/where-allowed.stderr @@ -17,7 +17,7 @@ LL | fn in_impl_Fn_parameter_in_return() -> &'static impl Fn(impl Debug) { panic | outer `impl Trait` error[E0658]: `impl Trait` in associated types is unstable - --> $DIR/where-allowed.rs:122:16 + --> $DIR/where-allowed.rs:121:16 | LL | type Out = impl Debug; | ^^^^^^^^^^ @@ -27,7 +27,7 @@ LL | type Out = impl Debug; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: `impl Trait` in type aliases is unstable - --> $DIR/where-allowed.rs:159:23 + --> $DIR/where-allowed.rs:158:23 | LL | type InTypeAlias = impl Debug; | ^^^^^^^^^^ @@ -37,7 +37,7 @@ LL | type InTypeAlias = impl Debug; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: `impl Trait` in type aliases is unstable - --> $DIR/where-allowed.rs:162:39 + --> $DIR/where-allowed.rs:161:39 | LL | type InReturnInTypeAlias = fn() -> impl Debug; | ^^^^^^^^^^ @@ -143,7 +143,7 @@ LL | fn in_Fn_return_in_generics impl Debug> (_: F) { panic!() } = note: `impl Trait` is only allowed in arguments and return types of functions and methods error[E0562]: `impl Trait` is not allowed in field types - --> $DIR/where-allowed.rs:86:32 + --> $DIR/where-allowed.rs:85:32 | LL | struct InBraceStructField { x: impl Debug } | ^^^^^^^^^^ @@ -151,7 +151,7 @@ LL | struct InBraceStructField { x: impl Debug } = note: `impl Trait` is only allowed in arguments and return types of functions and methods error[E0562]: `impl Trait` is not allowed in field types - --> $DIR/where-allowed.rs:90:41 + --> $DIR/where-allowed.rs:89:41 | LL | struct InAdtInBraceStructField { x: Vec } | ^^^^^^^^^^ @@ -159,7 +159,7 @@ LL | struct InAdtInBraceStructField { x: Vec } = note: `impl Trait` is only allowed in arguments and return types of functions and methods error[E0562]: `impl Trait` is not allowed in field types - --> $DIR/where-allowed.rs:94:27 + --> $DIR/where-allowed.rs:93:27 | LL | struct InTupleStructField(impl Debug); | ^^^^^^^^^^ @@ -167,7 +167,7 @@ LL | struct InTupleStructField(impl Debug); = note: `impl Trait` is only allowed in arguments and return types of functions and methods error[E0562]: `impl Trait` is not allowed in field types - --> $DIR/where-allowed.rs:99:25 + --> $DIR/where-allowed.rs:98:25 | LL | InBraceVariant { x: impl Debug }, | ^^^^^^^^^^ @@ -175,7 +175,7 @@ LL | InBraceVariant { x: impl Debug }, = note: `impl Trait` is only allowed in arguments and return types of functions and methods error[E0562]: `impl Trait` is not allowed in field types - --> $DIR/where-allowed.rs:101:20 + --> $DIR/where-allowed.rs:100:20 | LL | InTupleVariant(impl Debug), | ^^^^^^^^^^ @@ -183,7 +183,7 @@ LL | InTupleVariant(impl Debug), = note: `impl Trait` is only allowed in arguments and return types of functions and methods error[E0562]: `impl Trait` is not allowed in `extern fn` parameters - --> $DIR/where-allowed.rs:143:33 + --> $DIR/where-allowed.rs:142:33 | LL | fn in_foreign_parameters(_: impl Debug); | ^^^^^^^^^^ @@ -191,7 +191,7 @@ LL | fn in_foreign_parameters(_: impl Debug); = note: `impl Trait` is only allowed in arguments and return types of functions and methods error[E0562]: `impl Trait` is not allowed in `extern fn` return types - --> $DIR/where-allowed.rs:146:31 + --> $DIR/where-allowed.rs:145:31 | LL | fn in_foreign_return() -> impl Debug; | ^^^^^^^^^^ @@ -199,7 +199,7 @@ LL | fn in_foreign_return() -> impl Debug; = note: `impl Trait` is only allowed in arguments and return types of functions and methods error[E0562]: `impl Trait` is not allowed in `fn` pointer return types - --> $DIR/where-allowed.rs:162:39 + --> $DIR/where-allowed.rs:161:39 | LL | type InReturnInTypeAlias = fn() -> impl Debug; | ^^^^^^^^^^ @@ -207,7 +207,7 @@ LL | type InReturnInTypeAlias = fn() -> impl Debug; = note: `impl Trait` is only allowed in arguments and return types of functions and methods error[E0562]: `impl Trait` is not allowed in traits - --> $DIR/where-allowed.rs:167:16 + --> $DIR/where-allowed.rs:166:16 | LL | impl PartialEq for () { | ^^^^^^^^^^ @@ -215,7 +215,7 @@ LL | impl PartialEq for () { = note: `impl Trait` is only allowed in arguments and return types of functions and methods error[E0562]: `impl Trait` is not allowed in impl headers - --> $DIR/where-allowed.rs:172:24 + --> $DIR/where-allowed.rs:171:24 | LL | impl PartialEq<()> for impl Debug { | ^^^^^^^^^^ @@ -223,7 +223,7 @@ LL | impl PartialEq<()> for impl Debug { = note: `impl Trait` is only allowed in arguments and return types of functions and methods error[E0562]: `impl Trait` is not allowed in impl headers - --> $DIR/where-allowed.rs:177:6 + --> $DIR/where-allowed.rs:176:6 | LL | impl impl Debug { | ^^^^^^^^^^ @@ -231,7 +231,7 @@ LL | impl impl Debug { = note: `impl Trait` is only allowed in arguments and return types of functions and methods error[E0562]: `impl Trait` is not allowed in impl headers - --> $DIR/where-allowed.rs:183:24 + --> $DIR/where-allowed.rs:182:24 | LL | impl InInherentImplAdt { | ^^^^^^^^^^ @@ -239,7 +239,7 @@ LL | impl InInherentImplAdt { = note: `impl Trait` is only allowed in arguments and return types of functions and methods error[E0562]: `impl Trait` is not allowed in bounds - --> $DIR/where-allowed.rs:189:11 + --> $DIR/where-allowed.rs:188:11 | LL | where impl Debug: Debug | ^^^^^^^^^^ @@ -247,7 +247,7 @@ LL | where impl Debug: Debug = note: `impl Trait` is only allowed in arguments and return types of functions and methods error[E0562]: `impl Trait` is not allowed in bounds - --> $DIR/where-allowed.rs:196:15 + --> $DIR/where-allowed.rs:195:15 | LL | where Vec: Debug | ^^^^^^^^^^ @@ -255,7 +255,7 @@ LL | where Vec: Debug = note: `impl Trait` is only allowed in arguments and return types of functions and methods error[E0562]: `impl Trait` is not allowed in bounds - --> $DIR/where-allowed.rs:203:24 + --> $DIR/where-allowed.rs:202:24 | LL | where T: PartialEq | ^^^^^^^^^^ @@ -263,7 +263,7 @@ LL | where T: PartialEq = note: `impl Trait` is only allowed in arguments and return types of functions and methods error[E0562]: `impl Trait` is not allowed in the parameters of `Fn` trait bounds - --> $DIR/where-allowed.rs:210:17 + --> $DIR/where-allowed.rs:209:17 | LL | where T: Fn(impl Debug) | ^^^^^^^^^^ @@ -271,7 +271,7 @@ LL | where T: Fn(impl Debug) = note: `impl Trait` is only allowed in arguments and return types of functions and methods error[E0562]: `impl Trait` is not allowed in the return type of `Fn` trait bounds - --> $DIR/where-allowed.rs:217:22 + --> $DIR/where-allowed.rs:216:22 | LL | where T: Fn() -> impl Debug | ^^^^^^^^^^ @@ -279,7 +279,7 @@ LL | where T: Fn() -> impl Debug = note: `impl Trait` is only allowed in arguments and return types of functions and methods error[E0562]: `impl Trait` is not allowed in generic parameter defaults - --> $DIR/where-allowed.rs:223:40 + --> $DIR/where-allowed.rs:222:40 | LL | struct InStructGenericParamDefault(T); | ^^^^^^^^^^ @@ -287,7 +287,7 @@ LL | struct InStructGenericParamDefault(T); = note: `impl Trait` is only allowed in arguments and return types of functions and methods error[E0562]: `impl Trait` is not allowed in generic parameter defaults - --> $DIR/where-allowed.rs:227:36 + --> $DIR/where-allowed.rs:226:36 | LL | enum InEnumGenericParamDefault { Variant(T) } | ^^^^^^^^^^ @@ -295,7 +295,7 @@ LL | enum InEnumGenericParamDefault { Variant(T) } = note: `impl Trait` is only allowed in arguments and return types of functions and methods error[E0562]: `impl Trait` is not allowed in generic parameter defaults - --> $DIR/where-allowed.rs:231:38 + --> $DIR/where-allowed.rs:230:38 | LL | trait InTraitGenericParamDefault {} | ^^^^^^^^^^ @@ -303,7 +303,7 @@ LL | trait InTraitGenericParamDefault {} = note: `impl Trait` is only allowed in arguments and return types of functions and methods error[E0562]: `impl Trait` is not allowed in generic parameter defaults - --> $DIR/where-allowed.rs:235:41 + --> $DIR/where-allowed.rs:234:41 | LL | type InTypeAliasGenericParamDefault = T; | ^^^^^^^^^^ @@ -311,7 +311,7 @@ LL | type InTypeAliasGenericParamDefault = T; = note: `impl Trait` is only allowed in arguments and return types of functions and methods error[E0562]: `impl Trait` is not allowed in generic parameter defaults - --> $DIR/where-allowed.rs:239:11 + --> $DIR/where-allowed.rs:238:11 | LL | impl T {} | ^^^^^^^^^^ @@ -319,7 +319,7 @@ LL | impl T {} = note: `impl Trait` is only allowed in arguments and return types of functions and methods error[E0562]: `impl Trait` is not allowed in generic parameter defaults - --> $DIR/where-allowed.rs:246:40 + --> $DIR/where-allowed.rs:245:40 | LL | fn in_method_generic_param_default(_: T) {} | ^^^^^^^^^^ @@ -327,7 +327,7 @@ LL | fn in_method_generic_param_default(_: T) {} = note: `impl Trait` is only allowed in arguments and return types of functions and methods error[E0562]: `impl Trait` is not allowed in the type of variable bindings - --> $DIR/where-allowed.rs:252:29 + --> $DIR/where-allowed.rs:251:29 | LL | let _in_local_variable: impl Fn() = || {}; | ^^^^^^^^^ @@ -335,7 +335,7 @@ LL | let _in_local_variable: impl Fn() = || {}; = note: `impl Trait` is only allowed in arguments and return types of functions and methods error[E0562]: `impl Trait` is not allowed in closure return types - --> $DIR/where-allowed.rs:254:46 + --> $DIR/where-allowed.rs:253:46 | LL | let _in_return_in_local_variable = || -> impl Fn() { || {} }; | ^^^^^^^^^ @@ -363,7 +363,7 @@ LL | fn in_impl_Fn_return_in_return() -> &'static impl Fn() -> impl Debug { pani where Args: Tuple, F: Fn, A: Allocator, F: ?Sized; error: defaults for type parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions - --> $DIR/where-allowed.rs:239:7 + --> $DIR/where-allowed.rs:238:7 | LL | impl T {} | ^^^^^^^^^^^^^^ @@ -373,25 +373,15 @@ LL | impl T {} = note: `#[deny(invalid_type_param_default)]` on by default error[E0118]: no nominal type found for inherent implementation - --> $DIR/where-allowed.rs:239:1 + --> $DIR/where-allowed.rs:238:1 | LL | impl T {} | ^^^^^^^^^^^^^^^^^^^^^^^ impl requires a nominal type | = note: either implement a trait on it or create a newtype to wrap it instead -error[E0599]: no function or associated item named `into_vec` found for slice `[_]` in the current scope - --> $DIR/where-allowed.rs:81:5 - | -LL | vec![vec![0; 10], vec![12; 7], vec![8; 3]] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function or associated item not found in `[_]` - | -help: there is an associated function `to_vec` with a similar name - --> $SRC_DIR/alloc/src/slice.rs:LL:COL - = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) - error[E0053]: method `in_trait_impl_return` has an incompatible type for trait - --> $DIR/where-allowed.rs:129:34 + --> $DIR/where-allowed.rs:128:34 | LL | type Out = impl Debug; | ---------- the expected opaque type @@ -400,7 +390,7 @@ LL | fn in_trait_impl_return() -> impl Debug { () } | ^^^^^^^^^^ expected opaque type, found a different opaque type | note: type in trait - --> $DIR/where-allowed.rs:119:34 + --> $DIR/where-allowed.rs:118:34 | LL | fn in_trait_impl_return() -> Self::Out; | ^^^^^^^^^ @@ -413,7 +403,7 @@ LL | fn in_trait_impl_return() -> <() as DummyTrait>::Out { () } | ~~~~~~~~~~~~~~~~~~~~~~~ error: unconstrained opaque type - --> $DIR/where-allowed.rs:122:16 + --> $DIR/where-allowed.rs:121:16 | LL | type Out = impl Debug; | ^^^^^^^^^^ @@ -421,7 +411,7 @@ LL | type Out = impl Debug; = note: `Out` must be used in combination with a concrete type within the same impl error: defaults for type parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions - --> $DIR/where-allowed.rs:246:36 + --> $DIR/where-allowed.rs:245:36 | LL | fn in_method_generic_param_default(_: T) {} | ^^^^^^^^^^^^^^ @@ -429,13 +419,13 @@ 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 -error: aborting due to 50 previous errors +error: aborting due to 49 previous errors -Some errors have detailed explanations: E0053, E0118, E0283, E0562, E0599, E0658, E0666. +Some errors have detailed explanations: E0053, E0118, E0283, E0562, 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 + --> $DIR/where-allowed.rs:238:7 | LL | impl T {} | ^^^^^^^^^^^^^^ @@ -446,7 +436,7 @@ LL | impl T {} Future breakage diagnostic: error: defaults for type parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions - --> $DIR/where-allowed.rs:246:36 + --> $DIR/where-allowed.rs:245:36 | LL | fn in_method_generic_param_default(_: T) {} | ^^^^^^^^^^^^^^ diff --git a/tests/ui/kindck/kindck-impl-type-params.stderr b/tests/ui/kindck/kindck-impl-type-params.stderr index aad020e4ec97..5892596dc6ad 100644 --- a/tests/ui/kindck/kindck-impl-type-params.stderr +++ b/tests/ui/kindck/kindck-impl-type-params.stderr @@ -112,13 +112,13 @@ LL | struct Foo; // does not impl Copy | error: lifetime may not live long enough - --> $DIR/kindck-impl-type-params.rs:30:13 + --> $DIR/kindck-impl-type-params.rs:30:19 | LL | fn foo<'a>() { | -- lifetime `'a` defined here LL | let t: S<&'a isize> = S(marker::PhantomData); LL | let a = &t as &dyn Gettable<&'a isize>; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static` + | ^^^^^^^^^^^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static` error: aborting due to 7 previous errors diff --git a/tests/ui/lifetimes/issue-90600-expected-return-static-indirect.stderr b/tests/ui/lifetimes/issue-90600-expected-return-static-indirect.stderr index 598f14241913..e4cd54ac3374 100644 --- a/tests/ui/lifetimes/issue-90600-expected-return-static-indirect.stderr +++ b/tests/ui/lifetimes/issue-90600-expected-return-static-indirect.stderr @@ -7,7 +7,7 @@ LL | let refcell = RefCell::new(&mut foo); | ^^^^^^^^ borrowed value does not live long enough LL | LL | let read = &refcell as &RefCell; - | -------- cast requires that `foo` is borrowed for `'static` + | ------------------------------ cast requires that `foo` is borrowed for `'static` ... LL | } | - `foo` dropped here while still borrowed @@ -19,7 +19,7 @@ LL | fn inner(mut foo: &[u8]) { | - let's call the lifetime of this reference `'1` ... LL | let read = &refcell as &RefCell; - | ^^^^^^^^ cast requires that `'1` must outlive `'static` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cast requires that `'1` must outlive `'static` error: aborting due to 2 previous errors diff --git a/tests/ui/lint/improper-types-stack-overflow-130310.rs b/tests/ui/lint/improper-types-stack-overflow-130310.rs deleted file mode 100644 index 60eb87398175..000000000000 --- a/tests/ui/lint/improper-types-stack-overflow-130310.rs +++ /dev/null @@ -1,20 +0,0 @@ -// Regression test for #130310 -// Tests that we do not fall into infinite -// recursion while checking FFI safety of -// recursive types like `A` below - -//@ build-pass -use std::marker::PhantomData; - -#[repr(C)] -struct A { - a: *const A>, // Recursive because of this field - p: PhantomData, -} - -extern "C" { - fn f(a: *const A<()>); - //~^ WARN `extern` block uses type `*const A<()>`, which is not FFI-safe -} - -fn main() {} diff --git a/tests/ui/lint/improper-types-stack-overflow-130310.stderr b/tests/ui/lint/improper-types-stack-overflow-130310.stderr deleted file mode 100644 index 6981bb257554..000000000000 --- a/tests/ui/lint/improper-types-stack-overflow-130310.stderr +++ /dev/null @@ -1,11 +0,0 @@ -warning: `extern` block uses type `*const A<()>`, which is not FFI-safe - --> $DIR/improper-types-stack-overflow-130310.rs:16:13 - | -LL | fn f(a: *const A<()>); - | ^^^^^^^^^^^^ not FFI-safe - | - = note: type is infinitely recursive - = note: `#[warn(improper_ctypes)]` on by default - -warning: 1 warning emitted - diff --git a/tests/ui/lint/let_underscore/issue-119696-err-on-fn.rs b/tests/ui/lint/let_underscore/issue-119696-err-on-fn.rs index b885352dfd94..0973e2f1637d 100644 --- a/tests/ui/lint/let_underscore/issue-119696-err-on-fn.rs +++ b/tests/ui/lint/let_underscore/issue-119696-err-on-fn.rs @@ -2,7 +2,7 @@ #![deny(let_underscore_drop)] fn main() { - let _ = foo(); //~ ERROR non-binding let on a type that implements `Drop` + let _ = foo(); //~ ERROR non-binding let on a type that has a destructor } async fn from_config(_: Config) {} diff --git a/tests/ui/lint/let_underscore/issue-119696-err-on-fn.stderr b/tests/ui/lint/let_underscore/issue-119696-err-on-fn.stderr index 86e521580b87..70f9979556a3 100644 --- a/tests/ui/lint/let_underscore/issue-119696-err-on-fn.stderr +++ b/tests/ui/lint/let_underscore/issue-119696-err-on-fn.stderr @@ -1,4 +1,4 @@ -error: non-binding let on a type that implements `Drop` +error: non-binding let on a type that has a destructor --> $DIR/issue-119696-err-on-fn.rs:5:5 | LL | let _ = foo(); diff --git a/tests/ui/lint/let_underscore/issue-119697-extra-let.rs b/tests/ui/lint/let_underscore/issue-119697-extra-let.rs index 1dc80a123f61..84abb933911f 100644 --- a/tests/ui/lint/let_underscore/issue-119697-extra-let.rs +++ b/tests/ui/lint/let_underscore/issue-119697-extra-let.rs @@ -12,9 +12,9 @@ pub fn ice_cold(beverage: Tait) { // Must destructure at least one field of `Foo` let Foo { field } = beverage; // boom - _ = field; //~ ERROR non-binding let on a type that implements `Drop` + _ = field; //~ ERROR non-binding let on a type that has a destructor - let _ = field; //~ ERROR non-binding let on a type that implements `Drop` + let _ = field; //~ ERROR non-binding let on a type that has a destructor } diff --git a/tests/ui/lint/let_underscore/issue-119697-extra-let.stderr b/tests/ui/lint/let_underscore/issue-119697-extra-let.stderr index 16df2c720eaf..e4b1872bba55 100644 --- a/tests/ui/lint/let_underscore/issue-119697-extra-let.stderr +++ b/tests/ui/lint/let_underscore/issue-119697-extra-let.stderr @@ -1,4 +1,4 @@ -error: non-binding let on a type that implements `Drop` +error: non-binding let on a type that has a destructor --> $DIR/issue-119697-extra-let.rs:15:5 | LL | _ = field; @@ -18,7 +18,7 @@ help: consider immediately dropping the value LL | drop(field); | ~~~~~ + -error: non-binding let on a type that implements `Drop` +error: non-binding let on a type that has a destructor --> $DIR/issue-119697-extra-let.rs:17:5 | LL | let _ = field; diff --git a/tests/ui/lint/let_underscore/let_underscore_drop.rs b/tests/ui/lint/let_underscore/let_underscore_drop.rs index 58988ec05d79..f5a5e4299a1a 100644 --- a/tests/ui/lint/let_underscore/let_underscore_drop.rs +++ b/tests/ui/lint/let_underscore/let_underscore_drop.rs @@ -10,7 +10,7 @@ impl Drop for NontrivialDrop { } fn main() { - let _ = NontrivialDrop; //~WARNING non-binding let on a type that implements `Drop` + let _ = NontrivialDrop; //~WARNING non-binding let on a type that has a destructor let (_, _) = (NontrivialDrop, NontrivialDrop); // This should be ignored. } diff --git a/tests/ui/lint/let_underscore/let_underscore_drop.stderr b/tests/ui/lint/let_underscore/let_underscore_drop.stderr index 7b7de202e462..09f2587063bb 100644 --- a/tests/ui/lint/let_underscore/let_underscore_drop.stderr +++ b/tests/ui/lint/let_underscore/let_underscore_drop.stderr @@ -1,4 +1,4 @@ -warning: non-binding let on a type that implements `Drop` +warning: non-binding let on a type that has a destructor --> $DIR/let_underscore_drop.rs:13:5 | LL | let _ = NontrivialDrop; diff --git a/tests/ui/lint/lint-ctypes-non-recursion-limit.rs b/tests/ui/lint/lint-ctypes-non-recursion-limit.rs new file mode 100644 index 000000000000..61e95dc5a464 --- /dev/null +++ b/tests/ui/lint/lint-ctypes-non-recursion-limit.rs @@ -0,0 +1,32 @@ +//@ check-pass + +#![recursion_limit = "5"] +#![allow(unused)] +#![deny(improper_ctypes)] + +#[repr(C)] +struct F1(*const ()); +#[repr(C)] +struct F2(*const ()); +#[repr(C)] +struct F3(*const ()); +#[repr(C)] +struct F4(*const ()); +#[repr(C)] +struct F5(*const ()); +#[repr(C)] +struct F6(*const ()); + +#[repr(C)] +struct B { + f1: F1, + f2: F2, + f3: F3, + f4: F4, + f5: F5, + f6: F6, +} + +extern "C" fn foo(_: B) {} + +fn main() {} diff --git a/tests/ui/lint/non-local-defs/cargo-update.rs b/tests/ui/lint/non-local-defs/cargo-update.rs index 3c62a655a9f6..8b8c15795d37 100644 --- a/tests/ui/lint/non-local-defs/cargo-update.rs +++ b/tests/ui/lint/non-local-defs/cargo-update.rs @@ -10,8 +10,6 @@ // of the `cargo update` suggestion we assert it here. //@ error-pattern: `cargo update -p non_local_macro` -#![warn(non_local_definitions)] - extern crate non_local_macro; struct LocalStruct; diff --git a/tests/ui/lint/non-local-defs/cargo-update.stderr b/tests/ui/lint/non-local-defs/cargo-update.stderr index 4dd41519455c..77ee28b48cc7 100644 --- a/tests/ui/lint/non-local-defs/cargo-update.stderr +++ b/tests/ui/lint/non-local-defs/cargo-update.stderr @@ -1,5 +1,5 @@ warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item - --> $DIR/cargo-update.rs:19:1 + --> $DIR/cargo-update.rs:17:1 | LL | non_local_macro::non_local_impl!(LocalStruct); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -10,15 +10,10 @@ LL | non_local_macro::non_local_impl!(LocalStruct); | = note: the macro `non_local_macro::non_local_impl` defines the non-local `impl`, and may need to be changed = note: the macro `non_local_macro::non_local_impl` may come from an old version of the `non_local_macro` crate, try updating your dependency with `cargo update -p non_local_macro` - = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` = note: items in an anonymous const item (`const _: () = { ... }`) are treated as in the same scope as the anonymous const's declaration for the purpose of this lint = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue -note: the lint level is defined here - --> $DIR/cargo-update.rs:13:9 - | -LL | #![warn(non_local_definitions)] - | ^^^^^^^^^^^^^^^^^^^^^ + = note: `#[warn(non_local_definitions)]` on by default = note: this warning originates in the macro `non_local_macro::non_local_impl` (in Nightly builds, run with -Z macro-backtrace for more info) warning: 1 warning emitted diff --git a/tests/ui/lint/non-local-defs/consts.rs b/tests/ui/lint/non-local-defs/consts.rs index e7ee611529b9..d8a497e43e50 100644 --- a/tests/ui/lint/non-local-defs/consts.rs +++ b/tests/ui/lint/non-local-defs/consts.rs @@ -2,8 +2,6 @@ //@ edition:2021 //@ rustc-env:CARGO_CRATE_NAME=non_local_def -#![warn(non_local_definitions)] - struct Test; trait Uto {} diff --git a/tests/ui/lint/non-local-defs/consts.stderr b/tests/ui/lint/non-local-defs/consts.stderr index ed7bd56fe4a5..7f76056c0210 100644 --- a/tests/ui/lint/non-local-defs/consts.stderr +++ b/tests/ui/lint/non-local-defs/consts.stderr @@ -1,5 +1,5 @@ warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item - --> $DIR/consts.rs:15:5 + --> $DIR/consts.rs:13:5 | LL | const Z: () = { | ----------- @@ -8,23 +8,18 @@ LL | const Z: () = { | move the `impl` block outside of this constant `Z` ... LL | impl Uto for &Test {} - | ^^^^^---^^^^^----- - | | | - | | `&'_ Test` is not local + | ^^^^^---^^^^^^---- + | | | + | | `Test` is not local | `Uto` is not local | - = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` = note: items in an anonymous const item (`const _: () = { ... }`) are treated as in the same scope as the anonymous const's declaration for the purpose of this lint = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue -note: the lint level is defined here - --> $DIR/consts.rs:5:9 - | -LL | #![warn(non_local_definitions)] - | ^^^^^^^^^^^^^^^^^^^^^ + = note: `#[warn(non_local_definitions)]` on by default warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item - --> $DIR/consts.rs:26:5 + --> $DIR/consts.rs:24:5 | LL | static A: u32 = { | ------------- move the `impl` block outside of this static `A` @@ -34,13 +29,12 @@ LL | impl Uto2 for Test {} | | `Test` is not local | `Uto2` is not local | - = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` = note: items in an anonymous const item (`const _: () = { ... }`) are treated as in the same scope as the anonymous const's declaration for the purpose of this lint = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item - --> $DIR/consts.rs:34:5 + --> $DIR/consts.rs:32:5 | LL | const B: u32 = { | ------------ move the `impl` block outside of this constant `B` @@ -50,13 +44,12 @@ LL | impl Uto3 for Test {} | | `Test` is not local | `Uto3` is not local | - = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` = note: items in an anonymous const item (`const _: () = { ... }`) are treated as in the same scope as the anonymous const's declaration for the purpose of this lint = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item - --> $DIR/consts.rs:45:5 + --> $DIR/consts.rs:43:5 | LL | fn main() { | --------- move the `impl` block outside of this function `main` @@ -65,11 +58,11 @@ LL | impl Test { | | | `Test` is not local | - = note: methods and associated constants are still usable outside the current expression, only `impl Local` and `impl dyn Local` can ever be private, and only if the type is nested in the same item as the `impl` + = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item - --> $DIR/consts.rs:52:9 + --> $DIR/consts.rs:50:9 | LL | const { | ___________- @@ -84,11 +77,11 @@ LL | | 1 LL | | }; | |_____- move the `impl` block outside of this inline constant `` and up 2 bodies | - = note: methods and associated constants are still usable outside the current expression, only `impl Local` and `impl dyn Local` can ever be private, and only if the type is nested in the same item as the `impl` + = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item - --> $DIR/consts.rs:61:9 + --> $DIR/consts.rs:59:9 | LL | const _: u32 = { | ------------ move the `impl` block outside of this constant `_` and up 2 bodies @@ -97,12 +90,12 @@ LL | impl Test { | | | `Test` is not local | - = note: methods and associated constants are still usable outside the current expression, only `impl Local` and `impl dyn Local` can ever be private, and only if the type is nested in the same item as the `impl` + = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` = note: items in an anonymous const item (`const _: () = { ... }`) are treated as in the same scope as the anonymous const's declaration for the purpose of this lint = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item - --> $DIR/consts.rs:74:9 + --> $DIR/consts.rs:72:9 | LL | let _a = || { | -- move the `impl` block outside of this closure `` and up 2 bodies @@ -112,12 +105,11 @@ LL | impl Uto9 for Test {} | | `Test` is not local | `Uto9` is not local | - = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item - --> $DIR/consts.rs:81:9 + --> $DIR/consts.rs:79:9 | LL | type A = [u32; { | ____________________- @@ -131,7 +123,6 @@ LL | | LL | | }]; | |_____- move the `impl` block outside of this constant expression `` and up 2 bodies | - = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue diff --git a/tests/ui/lint/non-local-defs/exhaustive-trait.rs b/tests/ui/lint/non-local-defs/exhaustive-trait.rs index 79f8cc4620b9..40d2314460f8 100644 --- a/tests/ui/lint/non-local-defs/exhaustive-trait.rs +++ b/tests/ui/lint/non-local-defs/exhaustive-trait.rs @@ -1,8 +1,6 @@ //@ check-pass //@ edition:2021 -#![warn(non_local_definitions)] - struct Dog; fn main() { diff --git a/tests/ui/lint/non-local-defs/exhaustive-trait.stderr b/tests/ui/lint/non-local-defs/exhaustive-trait.stderr index 24c9a6b4f01e..c58ec12aaaca 100644 --- a/tests/ui/lint/non-local-defs/exhaustive-trait.stderr +++ b/tests/ui/lint/non-local-defs/exhaustive-trait.stderr @@ -1,5 +1,5 @@ warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item - --> $DIR/exhaustive-trait.rs:9:5 + --> $DIR/exhaustive-trait.rs:7:5 | LL | fn main() { | --------- move the `impl` block outside of this function `main` @@ -9,92 +9,84 @@ LL | impl PartialEq<()> for Dog { | | `Dog` is not local | `PartialEq` is not local | - = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue -note: the lint level is defined here - --> $DIR/exhaustive-trait.rs:4:9 - | -LL | #![warn(non_local_definitions)] - | ^^^^^^^^^^^^^^^^^^^^^ + = note: `#[warn(non_local_definitions)]` on by default warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item - --> $DIR/exhaustive-trait.rs:16:5 + --> $DIR/exhaustive-trait.rs:14:5 | LL | fn main() { | --------- move the `impl` block outside of this function `main` ... LL | impl PartialEq<()> for &Dog { - | ^^^^^---------^^^^^^^^^---- - | | | - | | `&'_ Dog` is not local + | ^^^^^---------^^^^^^^^^^--- + | | | + | | `Dog` is not local | `PartialEq` is not local | - = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item - --> $DIR/exhaustive-trait.rs:23:5 + --> $DIR/exhaustive-trait.rs:21:5 | LL | fn main() { | --------- move the `impl` block outside of this function `main` ... LL | impl PartialEq for () { - | ^^^^^---------^^^^^^^^^^-- - | | | - | | `()` is not local + | ^^^^^---------^---^^^^^^^^ + | | | + | | `Dog` is not local | `PartialEq` is not local | - = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item - --> $DIR/exhaustive-trait.rs:30:5 + --> $DIR/exhaustive-trait.rs:28:5 | LL | fn main() { | --------- move the `impl` block outside of this function `main` ... LL | impl PartialEq<&Dog> for () { - | ^^^^^---------^^^^^^^^^^^-- - | | | - | | `()` is not local + | ^^^^^---------^^---^^^^^^^^ + | | | + | | `Dog` is not local | `PartialEq` is not local | - = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item - --> $DIR/exhaustive-trait.rs:37:5 + --> $DIR/exhaustive-trait.rs:35:5 | LL | fn main() { | --------- move the `impl` block outside of this function `main` ... LL | impl PartialEq for &Dog { - | ^^^^^---------^^^^^^^^^^---- - | | | - | | `&'_ Dog` is not local + | ^^^^^---------^---^^^^^^^--- + | | | | + | | | `Dog` is not local + | | `Dog` is not local | `PartialEq` is not local | - = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item - --> $DIR/exhaustive-trait.rs:44:5 + --> $DIR/exhaustive-trait.rs:42:5 | LL | fn main() { | --------- move the `impl` block outside of this function `main` ... LL | impl PartialEq<&Dog> for &Dog { - | ^^^^^---------^^^^^^^^^^^---- - | | | - | | `&'_ Dog` is not local + | ^^^^^---------^^---^^^^^^^--- + | | | | + | | | `Dog` is not local + | | `Dog` is not local | `PartialEq` is not local | - = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue diff --git a/tests/ui/lint/non-local-defs/exhaustive.rs b/tests/ui/lint/non-local-defs/exhaustive.rs index f59a85c7ed94..5036e427060f 100644 --- a/tests/ui/lint/non-local-defs/exhaustive.rs +++ b/tests/ui/lint/non-local-defs/exhaustive.rs @@ -1,8 +1,6 @@ //@ check-pass //@ edition:2021 -#![warn(non_local_definitions)] - use std::fmt::Display; trait Trait {} @@ -57,18 +55,13 @@ fn main() { struct InsideMain; + impl Trait for &InsideMain {} impl Trait for *mut InsideMain {} - //~^ WARN non-local `impl` definition impl Trait for *mut [InsideMain] {} - //~^ WARN non-local `impl` definition impl Trait for [InsideMain; 8] {} - //~^ WARN non-local `impl` definition impl Trait for (InsideMain,) {} - //~^ WARN non-local `impl` definition impl Trait for fn(InsideMain) -> () {} - //~^ WARN non-local `impl` definition impl Trait for fn() -> InsideMain {} - //~^ WARN non-local `impl` definition fn inside_inside() { impl Display for InsideMain { diff --git a/tests/ui/lint/non-local-defs/exhaustive.stderr b/tests/ui/lint/non-local-defs/exhaustive.stderr index 6d8c2ec0bc7c..dd41b2206d84 100644 --- a/tests/ui/lint/non-local-defs/exhaustive.stderr +++ b/tests/ui/lint/non-local-defs/exhaustive.stderr @@ -1,5 +1,5 @@ warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item - --> $DIR/exhaustive.rs:12:5 + --> $DIR/exhaustive.rs:10:5 | LL | fn main() { | --------- move the `impl` block outside of this function `main` @@ -8,16 +8,12 @@ LL | impl Test { | | | `Test` is not local | - = note: methods and associated constants are still usable outside the current expression, only `impl Local` and `impl dyn Local` can ever be private, and only if the type is nested in the same item as the `impl` + = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue -note: the lint level is defined here - --> $DIR/exhaustive.rs:4:9 - | -LL | #![warn(non_local_definitions)] - | ^^^^^^^^^^^^^^^^^^^^^ + = note: `#[warn(non_local_definitions)]` on by default warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item - --> $DIR/exhaustive.rs:17:5 + --> $DIR/exhaustive.rs:15:5 | LL | fn main() { | --------- move the `impl` block outside of this function `main` @@ -28,12 +24,11 @@ LL | impl Display for Test { | | `Test` is not local | `Display` is not local | - = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item - --> $DIR/exhaustive.rs:24:5 + --> $DIR/exhaustive.rs:22:5 | LL | fn main() { | --------- move the `impl` block outside of this function `main` @@ -43,11 +38,11 @@ LL | impl dyn Trait {} | | | `Trait` is not local | - = note: methods and associated constants are still usable outside the current expression, only `impl Local` and `impl dyn Local` can ever be private, and only if the type is nested in the same item as the `impl` + = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item - --> $DIR/exhaustive.rs:27:5 + --> $DIR/exhaustive.rs:25:5 | LL | fn main() { | --------- move the `impl` block outside of this function `main` @@ -58,124 +53,116 @@ LL | impl Trait for Vec { } | | `Vec` is not local | `Trait` is not local | - = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item - --> $DIR/exhaustive.rs:30:5 + --> $DIR/exhaustive.rs:28:5 | LL | fn main() { | --------- move the `impl` block outside of this function `main` ... LL | impl Trait for &dyn Trait {} - | ^^^^^-----^^^^^---------- - | | | - | | `&'_ dyn Trait` is not local + | ^^^^^-----^^^^^^^^^^----- + | | | + | | `Trait` is not local | `Trait` is not local | - = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item - --> $DIR/exhaustive.rs:33:5 + --> $DIR/exhaustive.rs:31:5 | LL | fn main() { | --------- move the `impl` block outside of this function `main` ... LL | impl Trait for *mut Test {} - | ^^^^^-----^^^^^--------- - | | | - | | `*mut Test` is not local + | ^^^^^-----^^^^^^^^^^---- + | | | + | | `Test` is not local | `Trait` is not local | - = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item - --> $DIR/exhaustive.rs:36:5 + --> $DIR/exhaustive.rs:34:5 | LL | fn main() { | --------- move the `impl` block outside of this function `main` ... LL | impl Trait for *mut [Test] {} - | ^^^^^-----^^^^^----------- - | | | - | | `*mut [Test]` is not local + | ^^^^^-----^^^^^^^^^^^----^ + | | | + | | `Test` is not local | `Trait` is not local | - = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item - --> $DIR/exhaustive.rs:39:5 + --> $DIR/exhaustive.rs:37:5 | LL | fn main() { | --------- move the `impl` block outside of this function `main` ... LL | impl Trait for [Test; 8] {} - | ^^^^^-----^^^^^--------- - | | | - | | `[Test; 8]` is not local + | ^^^^^-----^^^^^^----^^^^ + | | | + | | `Test` is not local | `Trait` is not local | - = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item - --> $DIR/exhaustive.rs:42:5 + --> $DIR/exhaustive.rs:40:5 | LL | fn main() { | --------- move the `impl` block outside of this function `main` ... LL | impl Trait for (Test,) {} - | ^^^^^-----^^^^^------- - | | | - | | `(Test,)` is not local + | ^^^^^-----^^^^^^----^^ + | | | + | | `Test` is not local | `Trait` is not local | - = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item - --> $DIR/exhaustive.rs:45:5 + --> $DIR/exhaustive.rs:43:5 | LL | fn main() { | --------- move the `impl` block outside of this function `main` ... LL | impl Trait for fn(Test) -> () {} - | ^^^^^-----^^^^^-------------- - | | | - | | `fn(: Test) -> ()` is not local + | ^^^^^-----^^^^^^^^----^^^^^^^ + | | | + | | `Test` is not local | `Trait` is not local | - = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item - --> $DIR/exhaustive.rs:48:5 + --> $DIR/exhaustive.rs:46:5 | LL | fn main() { | --------- move the `impl` block outside of this function `main` ... LL | impl Trait for fn() -> Test {} - | ^^^^^-----^^^^^------------ - | | | - | | `fn() -> Test` is not local + | ^^^^^-----^^^^^^^^^^^^^---- + | | | + | | `Test` is not local | `Trait` is not local | - = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item - --> $DIR/exhaustive.rs:52:9 + --> $DIR/exhaustive.rs:50:9 | LL | let _a = || { | -- move the `impl` block outside of this closure `` and up 2 bodies @@ -185,139 +172,11 @@ LL | impl Trait for Test {} | | `Test` is not local | `Trait` is not local | - = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type - = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` - = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue - -warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item - --> $DIR/exhaustive.rs:60:5 - | -LL | impl Trait for *mut InsideMain {} - | ^^^^^-----^^^^^--------------- - | | | - | | `*mut InsideMain` is not local - | | help: remove `*mut ` to make the `impl` local - | `Trait` is not local - | - = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type - = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` -help: move the `impl` block outside of this function `main` - --> $DIR/exhaustive.rs:11:1 - | -LL | fn main() { - | ^^^^^^^^^ -... -LL | struct InsideMain; - | ----------------- may need to be moved as well - = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue - -warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item - --> $DIR/exhaustive.rs:62:5 - | -LL | impl Trait for *mut [InsideMain] {} - | ^^^^^-----^^^^^----------------- - | | | - | | `*mut [InsideMain]` is not local - | `Trait` is not local - | - = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type - = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` -help: move the `impl` block outside of this function `main` - --> $DIR/exhaustive.rs:11:1 - | -LL | fn main() { - | ^^^^^^^^^ -... -LL | struct InsideMain; - | ----------------- may need to be moved as well - = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue - -warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item - --> $DIR/exhaustive.rs:64:5 - | -LL | impl Trait for [InsideMain; 8] {} - | ^^^^^-----^^^^^--------------- - | | | - | | `[InsideMain; 8]` is not local - | `Trait` is not local - | - = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type - = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` -help: move the `impl` block outside of this function `main` - --> $DIR/exhaustive.rs:11:1 - | -LL | fn main() { - | ^^^^^^^^^ -... -LL | struct InsideMain; - | ----------------- may need to be moved as well - = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue - -warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item - --> $DIR/exhaustive.rs:66:5 - | -LL | impl Trait for (InsideMain,) {} - | ^^^^^-----^^^^^------------- - | | | - | | `(InsideMain,)` is not local - | `Trait` is not local - | - = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` -help: move the `impl` block outside of this function `main` - --> $DIR/exhaustive.rs:11:1 - | -LL | fn main() { - | ^^^^^^^^^ -... -LL | struct InsideMain; - | ----------------- may need to be moved as well = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item - --> $DIR/exhaustive.rs:68:5 - | -LL | impl Trait for fn(InsideMain) -> () {} - | ^^^^^-----^^^^^-------------------- - | | | - | | `fn(: InsideMain) -> ()` is not local - | `Trait` is not local - | - = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type - = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` -help: move the `impl` block outside of this function `main` - --> $DIR/exhaustive.rs:11:1 - | -LL | fn main() { - | ^^^^^^^^^ -... -LL | struct InsideMain; - | ----------------- may need to be moved as well - = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue - -warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item - --> $DIR/exhaustive.rs:70:5 - | -LL | impl Trait for fn() -> InsideMain {} - | ^^^^^-----^^^^^------------------ - | | | - | | `fn() -> InsideMain` is not local - | `Trait` is not local - | - = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type - = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` -help: move the `impl` block outside of this function `main` - --> $DIR/exhaustive.rs:11:1 - | -LL | fn main() { - | ^^^^^^^^^ -... -LL | struct InsideMain; - | ----------------- may need to be moved as well - = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue - -warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item - --> $DIR/exhaustive.rs:74:9 + --> $DIR/exhaustive.rs:67:9 | LL | fn inside_inside() { | ------------------ move the `impl` block outside of this function `inside_inside` and up 2 bodies @@ -327,12 +186,11 @@ LL | impl Display for InsideMain { | | `InsideMain` is not local | `Display` is not local | - = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item - --> $DIR/exhaustive.rs:81:9 + --> $DIR/exhaustive.rs:74:9 | LL | fn inside_inside() { | ------------------ move the `impl` block outside of this function `inside_inside` and up 2 bodies @@ -342,8 +200,8 @@ LL | impl InsideMain { | | | `InsideMain` is not local | - = note: methods and associated constants are still usable outside the current expression, only `impl Local` and `impl dyn Local` can ever be private, and only if the type is nested in the same item as the `impl` + = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue -warning: 20 warnings emitted +warning: 14 warnings emitted diff --git a/tests/ui/lint/non-local-defs/from-local-for-global.rs b/tests/ui/lint/non-local-defs/from-local-for-global.rs index 1d8f4845c286..6654fcc4f23d 100644 --- a/tests/ui/lint/non-local-defs/from-local-for-global.rs +++ b/tests/ui/lint/non-local-defs/from-local-for-global.rs @@ -1,8 +1,6 @@ //@ check-pass //@ edition:2021 -#![warn(non_local_definitions)] - struct Cat; struct Wrap(T); @@ -18,7 +16,6 @@ fn main() { struct Elephant; impl From>> for () { - //~^ WARN non-local `impl` definition fn from(_: Wrap>) -> Self { todo!() } @@ -32,7 +29,6 @@ impl StillNonLocal for &str {} fn only_global() { struct Foo; impl StillNonLocal for &Foo {} - //~^ WARN non-local `impl` definition } struct GlobalSameFunction; @@ -40,7 +36,6 @@ struct GlobalSameFunction; fn same_function() { struct Local1(GlobalSameFunction); impl From for GlobalSameFunction { - //~^ WARN non-local `impl` definition fn from(x: Local1) -> GlobalSameFunction { x.0 } @@ -48,7 +43,6 @@ fn same_function() { struct Local2(GlobalSameFunction); impl From for GlobalSameFunction { - //~^ WARN non-local `impl` definition fn from(x: Local2) -> GlobalSameFunction { x.0 } @@ -61,8 +55,6 @@ fn diff_function_1() { struct Local(GlobalDifferentFunction); impl From for GlobalDifferentFunction { - // FIXME(Urgau): Should warn but doesn't since we currently consider - // the other impl to be "global", but that's not the case for the type-system fn from(x: Local) -> GlobalDifferentFunction { x.0 } @@ -73,8 +65,6 @@ fn diff_function_2() { struct Local(GlobalDifferentFunction); impl From for GlobalDifferentFunction { - // FIXME(Urgau): Should warn but doesn't since we currently consider - // the other impl to be "global", but that's not the case for the type-system fn from(x: Local) -> GlobalDifferentFunction { x.0 } diff --git a/tests/ui/lint/non-local-defs/from-local-for-global.stderr b/tests/ui/lint/non-local-defs/from-local-for-global.stderr index 04eba8435fc0..6839ebd2c8a3 100644 --- a/tests/ui/lint/non-local-defs/from-local-for-global.stderr +++ b/tests/ui/lint/non-local-defs/from-local-for-global.stderr @@ -1,104 +1,17 @@ warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item - --> $DIR/from-local-for-global.rs:10:5 + --> $DIR/from-local-for-global.rs:8:5 | LL | fn main() { | --------- move the `impl` block outside of this function `main` LL | impl From for () { - | ^^^^^----^^^^^^^^^^-- - | | | - | | `()` is not local + | ^^^^^----^---^^^^^^^^ + | | | + | | `Cat` is not local | `From` is not local | - = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue -note: the lint level is defined here - --> $DIR/from-local-for-global.rs:4:9 - | -LL | #![warn(non_local_definitions)] - | ^^^^^^^^^^^^^^^^^^^^^ - -warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item - --> $DIR/from-local-for-global.rs:20:5 - | -LL | impl From>> for () { - | ^^^^^----^^^^^^^^^^^^^^^^^^^^^^^^^^^-- - | | | - | `From` is not local `()` is not local - | - = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type - = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` -help: move the `impl` block outside of this function `main` - --> $DIR/from-local-for-global.rs:9:1 - | -LL | fn main() { - | ^^^^^^^^^ -... -LL | struct Elephant; - | --------------- may need to be moved as well - = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue - -warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item - --> $DIR/from-local-for-global.rs:34:5 - | -LL | impl StillNonLocal for &Foo {} - | ^^^^^-------------^^^^^---- - | | | - | | `&'_ Foo` is not local - | | help: remove `&` to make the `impl` local - | `StillNonLocal` is not local - | - = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type - = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` -help: move the `impl` block outside of this function `only_global` - --> $DIR/from-local-for-global.rs:32:1 - | -LL | fn only_global() { - | ^^^^^^^^^^^^^^^^ -LL | struct Foo; - | ---------- may need to be moved as well - = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue - -warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item - --> $DIR/from-local-for-global.rs:42:5 - | -LL | impl From for GlobalSameFunction { - | ^^^^^----^^^^^^^^^^^^^------------------ - | | | - | | `GlobalSameFunction` is not local - | `From` is not local - | - = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type - = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` -help: move the `impl` block outside of this function `same_function` - --> $DIR/from-local-for-global.rs:40:1 - | -LL | fn same_function() { - | ^^^^^^^^^^^^^^^^^^ -LL | struct Local1(GlobalSameFunction); - | ------------- may need to be moved as well - = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue - -warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item - --> $DIR/from-local-for-global.rs:50:5 - | -LL | impl From for GlobalSameFunction { - | ^^^^^----^^^^^^^^^^^^^------------------ - | | | - | | `GlobalSameFunction` is not local - | `From` is not local - | - = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type - = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` -help: move the `impl` block outside of this function `same_function` - --> $DIR/from-local-for-global.rs:40:1 - | -LL | fn same_function() { - | ^^^^^^^^^^^^^^^^^^ -... -LL | struct Local2(GlobalSameFunction); - | ------------- may need to be moved as well - = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue + = note: `#[warn(non_local_definitions)]` on by default -warning: 5 warnings emitted +warning: 1 warning emitted diff --git a/tests/ui/lint/non-local-defs/generics.rs b/tests/ui/lint/non-local-defs/generics.rs index 13e392c510c6..381b3caacb6b 100644 --- a/tests/ui/lint/non-local-defs/generics.rs +++ b/tests/ui/lint/non-local-defs/generics.rs @@ -1,8 +1,6 @@ //@ check-pass //@ edition:2021 -#![warn(non_local_definitions)] - trait Global {} fn main() { @@ -32,7 +30,6 @@ fn fun() { #[derive(Debug)] struct OwO; impl Default for UwU { - //~^ WARN non-local `impl` definition fn default() -> Self { UwU(OwO) } @@ -43,7 +40,6 @@ fn meow() { #[derive(Debug)] struct Cat; impl AsRef for () { - //~^ WARN non-local `impl` definition fn as_ref(&self) -> &Cat { &Cat } } } @@ -54,7 +50,6 @@ fn fun2() { #[derive(Debug, Default)] struct B; impl PartialEq for G { - //~^ WARN non-local `impl` definition fn eq(&self, _: &B) -> bool { true } @@ -69,14 +64,12 @@ fn rawr() { struct Lion; impl From>> for () { - //~^ WARN non-local `impl` definition fn from(_: Wrap>) -> Self { todo!() } } impl From<()> for Wrap { - //~^ WARN non-local `impl` definition fn from(_: ()) -> Self { todo!() } diff --git a/tests/ui/lint/non-local-defs/generics.stderr b/tests/ui/lint/non-local-defs/generics.stderr index 35366ed8ecf9..aefe8921fe2c 100644 --- a/tests/ui/lint/non-local-defs/generics.stderr +++ b/tests/ui/lint/non-local-defs/generics.stderr @@ -1,165 +1,47 @@ warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item - --> $DIR/generics.rs:11:5 + --> $DIR/generics.rs:9:5 | +LL | fn main() { + | --------- move the `impl` block outside of this function `main` +... LL | impl Global for Vec { } | ^^^^^^^^^^^^^^^------^^^^^---^^^ | | | | | `Vec` is not local | `Global` is not local | - = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` -help: move the `impl` block outside of this function `main` - --> $DIR/generics.rs:8:1 - | -LL | fn main() { - | ^^^^^^^^^ -LL | trait Local {}; - | ----------- may need to be moved as well = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue -note: the lint level is defined here - --> $DIR/generics.rs:4:9 - | -LL | #![warn(non_local_definitions)] - | ^^^^^^^^^^^^^^^^^^^^^ + = note: `#[warn(non_local_definitions)]` on by default warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item - --> $DIR/generics.rs:22:5 + --> $DIR/generics.rs:20:5 | +LL | fn bad() { + | -------- move the `impl` block outside of this function `bad` +LL | struct Local; LL | impl Uto7 for Test where Local: std::any::Any {} | ^^^^^----^^^^^---- | | | | | `Test` is not local | `Uto7` is not local | - = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` -help: move the `impl` block outside of this function `bad` - --> $DIR/generics.rs:20:1 - | -LL | fn bad() { - | ^^^^^^^^ -LL | struct Local; - | ------------ may need to be moved as well = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item - --> $DIR/generics.rs:25:5 + --> $DIR/generics.rs:23:5 | LL | fn bad() { | -------- move the `impl` block outside of this function `bad` ... LL | impl Uto8 for T {} - | ^^^^^^^^----^^^^^- - | | | - | | `T` is not local + | ^^^^^^^^----^^^^^^ + | | | `Uto8` is not local | - = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type - = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` - = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue - -warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item - --> $DIR/generics.rs:34:5 - | -LL | impl Default for UwU { - | ^^^^^-------^^^^^---^^^^^ - | | | - | | `UwU` is not local - | `Default` is not local - | - = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type - = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` -help: move the `impl` block outside of this function `fun` - --> $DIR/generics.rs:31:1 - | -LL | fn fun() { - | ^^^^^^^^ -LL | #[derive(Debug)] -LL | struct OwO; - | ---------- may need to be moved as well - = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue - -warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item - --> $DIR/generics.rs:45:5 - | -LL | impl AsRef for () { - | ^^^^^-----^^^^^^^^^^-- - | | | - | | `()` is not local - | `AsRef` is not local - | - = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type - = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` -help: move the `impl` block outside of this function `meow` - --> $DIR/generics.rs:42:1 - | -LL | fn meow() { - | ^^^^^^^^^ -LL | #[derive(Debug)] -LL | struct Cat; - | ---------- may need to be moved as well - = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue - -warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item - --> $DIR/generics.rs:56:5 - | -LL | impl PartialEq for G { - | ^^^^^---------^^^^^^^^- - | | | - | | `G` is not local - | `PartialEq` is not local - | - = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` -help: move the `impl` block outside of this function `fun2` - --> $DIR/generics.rs:53:1 - | -LL | fn fun2() { - | ^^^^^^^^^ -LL | #[derive(Debug, Default)] -LL | struct B; - | -------- may need to be moved as well - = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue - -warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item - --> $DIR/generics.rs:71:5 - | -LL | impl From>> for () { - | ^^^^^----^^^^^^^^^^^^^^^^^^^^^^^-- - | | | - | `From` is not local `()` is not local - | - = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type - = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` -help: move the `impl` block outside of this function `rawr` - --> $DIR/generics.rs:68:1 - | -LL | fn rawr() { - | ^^^^^^^^^ -LL | struct Lion; - | ----------- may need to be moved as well - = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue - -warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item - --> $DIR/generics.rs:78:5 - | -LL | impl From<()> for Wrap { - | ^^^^^----^^^^^^^^^----^^^^^^ - | | | - | | `Wrap` is not local - | `From` is not local - | - = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type - = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` -help: move the `impl` block outside of this function `rawr` - --> $DIR/generics.rs:68:1 - | -LL | fn rawr() { - | ^^^^^^^^^ -LL | struct Lion; - | ----------- may need to be moved as well = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue -warning: 8 warnings emitted +warning: 3 warnings emitted diff --git a/tests/ui/lint/non-local-defs/inside-macro_rules.rs b/tests/ui/lint/non-local-defs/inside-macro_rules.rs index 744a1f7a6f1a..9f21cc89852e 100644 --- a/tests/ui/lint/non-local-defs/inside-macro_rules.rs +++ b/tests/ui/lint/non-local-defs/inside-macro_rules.rs @@ -1,8 +1,6 @@ //@ check-pass //@ edition:2021 -#![warn(non_local_definitions)] - macro_rules! m { () => { trait MacroTrait {} diff --git a/tests/ui/lint/non-local-defs/inside-macro_rules.stderr b/tests/ui/lint/non-local-defs/inside-macro_rules.stderr index 89835372c8a5..faab6aea2b31 100644 --- a/tests/ui/lint/non-local-defs/inside-macro_rules.stderr +++ b/tests/ui/lint/non-local-defs/inside-macro_rules.stderr @@ -1,5 +1,5 @@ warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item - --> $DIR/inside-macro_rules.rs:11:13 + --> $DIR/inside-macro_rules.rs:9:13 | LL | fn my_func() { | ------------ move the `impl` block outside of this function `my_func` @@ -13,14 +13,9 @@ LL | m!(); | ---- in this macro invocation | = note: the macro `m` defines the non-local `impl`, and may need to be changed - = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue -note: the lint level is defined here - --> $DIR/inside-macro_rules.rs:4:9 - | -LL | #![warn(non_local_definitions)] - | ^^^^^^^^^^^^^^^^^^^^^ + = note: `#[warn(non_local_definitions)]` on by default = note: this warning originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info) warning: 1 warning emitted diff --git a/tests/ui/lint/non-local-defs/local.rs b/tests/ui/lint/non-local-defs/local.rs index e9dbff1300f2..166ee88c0210 100644 --- a/tests/ui/lint/non-local-defs/local.rs +++ b/tests/ui/lint/non-local-defs/local.rs @@ -1,8 +1,6 @@ //@ check-pass //@ edition:2021 -#![warn(non_local_definitions)] - use std::fmt::Debug; trait GlobalTrait {} diff --git a/tests/ui/lint/non-local-defs/macro_rules.rs b/tests/ui/lint/non-local-defs/macro_rules.rs index 20672cf0a322..ed30a24903d0 100644 --- a/tests/ui/lint/non-local-defs/macro_rules.rs +++ b/tests/ui/lint/non-local-defs/macro_rules.rs @@ -3,8 +3,6 @@ //@ aux-build:non_local_macro.rs //@ rustc-env:CARGO_CRATE_NAME=non_local_def -#![warn(non_local_definitions)] - extern crate non_local_macro; const B: u32 = { diff --git a/tests/ui/lint/non-local-defs/macro_rules.stderr b/tests/ui/lint/non-local-defs/macro_rules.stderr index f9995bf82183..4e86fc7b987e 100644 --- a/tests/ui/lint/non-local-defs/macro_rules.stderr +++ b/tests/ui/lint/non-local-defs/macro_rules.stderr @@ -1,5 +1,5 @@ warning: non-local `macro_rules!` definition, `#[macro_export]` macro should be written at top level module - --> $DIR/macro_rules.rs:12:5 + --> $DIR/macro_rules.rs:10:5 | LL | macro_rules! m0 { () => { } }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -7,14 +7,10 @@ LL | macro_rules! m0 { () => { } }; = help: remove the `#[macro_export]` or move this `macro_rules!` outside the of the current constant `B` = note: a `macro_rules!` definition is non-local if it is nested inside an item and has a `#[macro_export]` attribute = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue -note: the lint level is defined here - --> $DIR/macro_rules.rs:6:9 - | -LL | #![warn(non_local_definitions)] - | ^^^^^^^^^^^^^^^^^^^^^ + = note: `#[warn(non_local_definitions)]` on by default warning: non-local `macro_rules!` definition, `#[macro_export]` macro should be written at top level module - --> $DIR/macro_rules.rs:18:1 + --> $DIR/macro_rules.rs:16:1 | LL | non_local_macro::non_local_macro_rules!(my_macro); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -26,7 +22,7 @@ LL | non_local_macro::non_local_macro_rules!(my_macro); = note: this warning originates in the macro `non_local_macro::non_local_macro_rules` (in Nightly builds, run with -Z macro-backtrace for more info) warning: non-local `macro_rules!` definition, `#[macro_export]` macro should be written at top level module - --> $DIR/macro_rules.rs:23:5 + --> $DIR/macro_rules.rs:21:5 | LL | macro_rules! m { () => { } }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -36,7 +32,7 @@ LL | macro_rules! m { () => { } }; = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue warning: non-local `macro_rules!` definition, `#[macro_export]` macro should be written at top level module - --> $DIR/macro_rules.rs:31:13 + --> $DIR/macro_rules.rs:29:13 | LL | macro_rules! m2 { () => { } }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/lint/non-local-defs/suggest-moving-inner.rs b/tests/ui/lint/non-local-defs/ref-complex.rs similarity index 76% rename from tests/ui/lint/non-local-defs/suggest-moving-inner.rs rename to tests/ui/lint/non-local-defs/ref-complex.rs index 9360ace4d805..ce4e0a3dc0a1 100644 --- a/tests/ui/lint/non-local-defs/suggest-moving-inner.rs +++ b/tests/ui/lint/non-local-defs/ref-complex.rs @@ -1,7 +1,5 @@ //@ check-pass -#![warn(non_local_definitions)] - trait Trait {} fn main() { @@ -12,7 +10,6 @@ fn main() { trait HasFoo {} impl Trait for &Vec> - //~^ WARN non-local `impl` definition where T: HasFoo {} diff --git a/tests/ui/lint/non-local-defs/suggest-moving-inner.stderr b/tests/ui/lint/non-local-defs/suggest-moving-inner.stderr deleted file mode 100644 index a214415316f8..000000000000 --- a/tests/ui/lint/non-local-defs/suggest-moving-inner.stderr +++ /dev/null @@ -1,33 +0,0 @@ -warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item - --> $DIR/suggest-moving-inner.rs:14:5 - | -LL | impl Trait for &Vec> - | ^^^^^^^^-----^^^^^^^^^^^^^^^^^---------------------------------- - | | | - | | `&'_ Vec>` is not local - | `Trait` is not local - | - = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type - = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` -help: move the `impl` block outside of this function `main` - --> $DIR/suggest-moving-inner.rs:7:1 - | -LL | fn main() { - | ^^^^^^^^^ -LL | mod below { -LL | pub struct Type(T); - | ------------------ may need to be moved as well -LL | } -LL | struct InsideMain; - | ----------------- may need to be moved as well -LL | trait HasFoo {} - | ------------ may need to be moved as well - = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue -note: the lint level is defined here - --> $DIR/suggest-moving-inner.rs:3:9 - | -LL | #![warn(non_local_definitions)] - | ^^^^^^^^^^^^^^^^^^^^^ - -warning: 1 warning emitted - diff --git a/tests/ui/lint/non-local-defs/trait-solver-overflow-123573.rs b/tests/ui/lint/non-local-defs/trait-solver-overflow-123573.rs index b726398bf9c9..6e8014f10f85 100644 --- a/tests/ui/lint/non-local-defs/trait-solver-overflow-123573.rs +++ b/tests/ui/lint/non-local-defs/trait-solver-overflow-123573.rs @@ -3,8 +3,6 @@ // https://github.com/rust-lang/rust/issues/123573#issue-2229428739 -#![warn(non_local_definitions)] - pub trait Test {} impl<'a, T: 'a> Test for &[T] where &'a T: Test {} @@ -12,5 +10,4 @@ impl<'a, T: 'a> Test for &[T] where &'a T: Test {} fn main() { struct Local {} impl Test for &Local {} - //~^ WARN non-local `impl` definition } diff --git a/tests/ui/lint/non-local-defs/trait-solver-overflow-123573.stderr b/tests/ui/lint/non-local-defs/trait-solver-overflow-123573.stderr deleted file mode 100644 index 2eb71cecacaa..000000000000 --- a/tests/ui/lint/non-local-defs/trait-solver-overflow-123573.stderr +++ /dev/null @@ -1,28 +0,0 @@ -warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item - --> $DIR/trait-solver-overflow-123573.rs:14:5 - | -LL | impl Test for &Local {} - | ^^^^^----^^^^^------ - | | | - | | `&'_ Local` is not local - | | help: remove `&` to make the `impl` local - | `Test` is not local - | - = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type - = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` -help: move the `impl` block outside of this function `main` - --> $DIR/trait-solver-overflow-123573.rs:12:1 - | -LL | fn main() { - | ^^^^^^^^^ -LL | struct Local {} - | ------------ may need to be moved as well - = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue -note: the lint level is defined here - --> $DIR/trait-solver-overflow-123573.rs:6:9 - | -LL | #![warn(non_local_definitions)] - | ^^^^^^^^^^^^^^^^^^^^^ - -warning: 1 warning emitted - diff --git a/tests/ui/lint/non-local-defs/weird-exprs.rs b/tests/ui/lint/non-local-defs/weird-exprs.rs index fbf1fd941eec..1d9cecea0c97 100644 --- a/tests/ui/lint/non-local-defs/weird-exprs.rs +++ b/tests/ui/lint/non-local-defs/weird-exprs.rs @@ -1,8 +1,6 @@ //@ check-pass //@ edition:2021 -#![warn(non_local_definitions)] - trait Uto {} struct Test; diff --git a/tests/ui/lint/non-local-defs/weird-exprs.stderr b/tests/ui/lint/non-local-defs/weird-exprs.stderr index 49aba904ebb0..f6ce063929e8 100644 --- a/tests/ui/lint/non-local-defs/weird-exprs.stderr +++ b/tests/ui/lint/non-local-defs/weird-exprs.stderr @@ -1,29 +1,24 @@ warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item - --> $DIR/weird-exprs.rs:10:5 + --> $DIR/weird-exprs.rs:8:5 | LL | type A = [u32; { | ________________- LL | | impl Uto for *mut Test {} - | | ^^^^^---^^^^^--------- - | | | | - | | | `*mut Test` is not local + | | ^^^^^---^^^^^^^^^^---- + | | | | + | | | `Test` is not local | | `Uto` is not local LL | | ... | LL | | }]; | |_- move the `impl` block outside of this constant expression `` | - = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue -note: the lint level is defined here - --> $DIR/weird-exprs.rs:4:9 - | -LL | #![warn(non_local_definitions)] - | ^^^^^^^^^^^^^^^^^^^^^ + = note: `#[warn(non_local_definitions)]` on by default warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item - --> $DIR/weird-exprs.rs:18:9 + --> $DIR/weird-exprs.rs:16:9 | LL | Discr = { | _____________- @@ -37,12 +32,11 @@ LL | | LL | | } | |_____- move the `impl` block outside of this constant expression `` | - = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item - --> $DIR/weird-exprs.rs:27:9 + --> $DIR/weird-exprs.rs:25:9 | LL | let _array = [0i32; { | _________________________- @@ -57,63 +51,61 @@ LL | | 1 LL | | }]; | |_____- move the `impl` block outside of this constant expression `` and up 2 bodies | - = note: methods and associated constants are still usable outside the current expression, only `impl Local` and `impl dyn Local` can ever be private, and only if the type is nested in the same item as the `impl` + = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item - --> $DIR/weird-exprs.rs:36:9 + --> $DIR/weird-exprs.rs:34:9 | LL | type A = [u32; { | ____________________- LL | | impl Uto for &Test {} - | | ^^^^^---^^^^^----- - | | | | - | | | `&'_ Test` is not local + | | ^^^^^---^^^^^^---- + | | | | + | | | `Test` is not local | | `Uto` is not local LL | | ... | LL | | }]; | |_____- move the `impl` block outside of this constant expression `` and up 2 bodies | - = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item - --> $DIR/weird-exprs.rs:43:9 + --> $DIR/weird-exprs.rs:41:9 | LL | fn a(_: [u32; { | ___________________- LL | | impl Uto for &(Test,) {} - | | ^^^^^---^^^^^-------- - | | | | - | | | `&'_ (Test,)` is not local + | | ^^^^^---^^^^^^^----^^ + | | | | + | | | `Test` is not local | | `Uto` is not local LL | | ... | LL | | }]) {} | |_____- move the `impl` block outside of this constant expression `` and up 2 bodies | - = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item - --> $DIR/weird-exprs.rs:50:9 + --> $DIR/weird-exprs.rs:48:9 | LL | fn b() -> [u32; { | _____________________- LL | | impl Uto for &(Test,Test) {} - | | ^^^^^---^^^^^------------ - | | | | - | | | `&'_ (Test, Test)` is not local + | | ^^^^^---^^^^^^^----^----^ + | | | | | + | | | | `Test` is not local + | | | `Test` is not local | | `Uto` is not local LL | | ... | LL | | }] { todo!() } | |_____- move the `impl` block outside of this constant expression `` and up 2 bodies | - = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue diff --git a/tests/ui/lint/unqualified_local_imports.rs b/tests/ui/lint/unqualified_local_imports.rs new file mode 100644 index 000000000000..9de71471342d --- /dev/null +++ b/tests/ui/lint/unqualified_local_imports.rs @@ -0,0 +1,38 @@ +//@compile-flags: --edition 2018 +#![feature(unqualified_local_imports)] +#![deny(unqualified_local_imports)] + +mod localmod { + pub struct S; + pub struct T; +} + +// Not a local import, so no lint. +use std::cell::Cell; + +// Implicitly local import, gets lint. +use localmod::S; //~ERROR: unqualified + +// Explicitly local import, no lint. +use self::localmod::T; + +macro_rules! mymacro { + ($cond:expr) => { + if !$cond { + continue; + } + }; +} +// Macro import: no lint, as there is no other way to write it. +pub(crate) use mymacro; + +#[allow(unused)] +enum LocalEnum { + VarA, + VarB, +} + +fn main() { + // Import in a function, no lint. + use LocalEnum::*; +} diff --git a/tests/ui/lint/unqualified_local_imports.stderr b/tests/ui/lint/unqualified_local_imports.stderr new file mode 100644 index 000000000000..81d12f55949e --- /dev/null +++ b/tests/ui/lint/unqualified_local_imports.stderr @@ -0,0 +1,14 @@ +error: `use` of a local item without leading `self::`, `super::`, or `crate::` + --> $DIR/unqualified_local_imports.rs:14:5 + | +LL | use localmod::S; + | ^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/unqualified_local_imports.rs:3:9 + | +LL | #![deny(unqualified_local_imports)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/macros/break-last-token-twice.rs b/tests/ui/macros/break-last-token-twice.rs new file mode 100644 index 000000000000..791f349ab38d --- /dev/null +++ b/tests/ui/macros/break-last-token-twice.rs @@ -0,0 +1,16 @@ +//@ check-pass + +macro_rules! m { + (static $name:ident: $t:ty = $e:expr) => { + let $name: $t = $e; + } +} + +fn main() { + m! { + // Tricky: the trailing `>>=` token here is broken twice: + // - into `>` and `>=` + // - then the `>=` is broken into `>` and `=` + static _x: Vec>= vec![] + } +} diff --git a/tests/ui/mir/early-otherwise-branch-ice.rs b/tests/ui/mir/early-otherwise-branch-ice.rs new file mode 100644 index 000000000000..c1938eb75077 --- /dev/null +++ b/tests/ui/mir/early-otherwise-branch-ice.rs @@ -0,0 +1,18 @@ +// Changes in https://github.com/rust-lang/rust/pull/129047 lead to several mir-opt ICE regressions, +// this test is added to make sure this does not regress. + +//@ compile-flags: -C opt-level=3 +//@ check-pass + +#![crate_type = "lib"] + +use std::task::Poll; + +pub fn poll(val: Poll>, u8>>) { + match val { + Poll::Ready(Ok(Some(_trailers))) => {} + Poll::Ready(Err(_err)) => {} + Poll::Ready(Ok(None)) => {} + Poll::Pending => {} + } +} diff --git a/tests/ui/mir/validate/validate-unsize-cast.rs b/tests/ui/mir/validate/validate-unsize-cast.rs new file mode 100644 index 000000000000..198af8d2e13a --- /dev/null +++ b/tests/ui/mir/validate/validate-unsize-cast.rs @@ -0,0 +1,33 @@ +//@ compile-flags: -Zmir-opt-level=0 -Zmir-enable-passes=+Inline,+GVN -Zvalidate-mir + +#![feature(unsize)] + +use std::marker::Unsize; + +pub trait CastTo: Unsize {} + +// Not well-formed! +impl CastTo for T {} +//~^ ERROR the trait bound `T: Unsize` is not satisfied + +pub trait Cast { + fn cast(&self) + where + Self: CastTo; +} +impl Cast for T { + #[inline(always)] + fn cast(&self) + where + Self: CastTo, + { + let x: &U = self; + } +} + +fn main() { + // When we inline this call, then we run GVN, then + // GVN tries to evaluate the `() -> [i32]` unsize. + // That's invalid! + ().cast::<[i32]>(); +} diff --git a/tests/ui/mir/validate/validate-unsize-cast.stderr b/tests/ui/mir/validate/validate-unsize-cast.stderr new file mode 100644 index 000000000000..cfb47b34e980 --- /dev/null +++ b/tests/ui/mir/validate/validate-unsize-cast.stderr @@ -0,0 +1,20 @@ +error[E0277]: the trait bound `T: Unsize` is not satisfied + --> $DIR/validate-unsize-cast.rs:10:42 + | +LL | impl CastTo for T {} + | ^ the trait `Unsize` is not implemented for `T` + | + = note: all implementations of `Unsize` are provided automatically by the compiler, see for more information +note: required by a bound in `CastTo` + --> $DIR/validate-unsize-cast.rs:7:30 + | +LL | pub trait CastTo: Unsize {} + | ^^^^^^^^^ required by this bound in `CastTo` +help: consider further restricting this bound + | +LL | impl, U: ?Sized> CastTo for T {} + | ++++++++++++++++++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/mismatched_types/cast-rfc0401.stderr b/tests/ui/mismatched_types/cast-rfc0401.stderr index 142a52aef13d..3d12ba9899bc 100644 --- a/tests/ui/mismatched_types/cast-rfc0401.stderr +++ b/tests/ui/mismatched_types/cast-rfc0401.stderr @@ -4,7 +4,7 @@ error[E0606]: casting `*const U` as `*const V` is invalid LL | u as *const V | ^^^^^^^^^^^^^ | - = note: vtable kinds may not match + = note: the pointers may have different metadata error[E0606]: casting `*const U` as `*const str` is invalid --> $DIR/cast-rfc0401.rs:8:5 @@ -12,7 +12,7 @@ error[E0606]: casting `*const U` as `*const str` is invalid LL | u as *const str | ^^^^^^^^^^^^^^^ | - = note: vtable kinds may not match + = note: the pointers may have different metadata error[E0609]: no field `f` on type `fn() {main}` --> $DIR/cast-rfc0401.rs:65:18 @@ -208,7 +208,7 @@ error[E0606]: casting `*const dyn Foo` as `*const [u16]` is invalid LL | let _ = cf as *const [u16]; | ^^^^^^^^^^^^^^^^^^ | - = note: vtable kinds may not match + = note: the pointers have different metadata error[E0606]: casting `*const dyn Foo` as `*const dyn Bar` is invalid --> $DIR/cast-rfc0401.rs:69:13 @@ -216,7 +216,7 @@ error[E0606]: casting `*const dyn Foo` as `*const dyn Bar` is invalid LL | let _ = cf as *const dyn Bar; | ^^^^^^^^^^^^^^^^^^^^ | - = note: vtable kinds may not match + = note: the trait objects may have different vtables error[E0277]: the size for values of type `[u8]` cannot be known at compilation time --> $DIR/cast-rfc0401.rs:53:13 diff --git a/tests/ui/nll/issue-54779-anon-static-lifetime.stderr b/tests/ui/nll/issue-54779-anon-static-lifetime.stderr index 92298c6617ff..a454ed265685 100644 --- a/tests/ui/nll/issue-54779-anon-static-lifetime.stderr +++ b/tests/ui/nll/issue-54779-anon-static-lifetime.stderr @@ -5,7 +5,7 @@ LL | cx: &dyn DebugContext, | - let's call the lifetime of this reference `'1` ... LL | bar.debug_with(cx); - | ^^ cast requires that `'1` must outlive `'static` + | ^^ coercion requires that `'1` must outlive `'static` error: aborting due to 1 previous error diff --git a/tests/ui/nll/user-annotations/cast_static_lifetime.stderr b/tests/ui/nll/user-annotations/cast_static_lifetime.stderr index 35eec233ed5c..efd14fe875db 100644 --- a/tests/ui/nll/user-annotations/cast_static_lifetime.stderr +++ b/tests/ui/nll/user-annotations/cast_static_lifetime.stderr @@ -4,10 +4,9 @@ error[E0597]: `x` does not live long enough LL | let x = 22_u32; | - binding `x` declared here LL | let y: &u32 = (&x) as &'static u32; - | ^^^^---------------- + | ^^^^ ------------ type annotation requires that `x` is borrowed for `'static` | | | borrowed value does not live long enough - | type annotation requires that `x` is borrowed for `'static` LL | } | - `x` dropped here while still borrowed diff --git a/tests/ui/nll/user-annotations/type_ascription_static_lifetime.stderr b/tests/ui/nll/user-annotations/type_ascription_static_lifetime.stderr index 3e2706309b34..2ed0fadc0655 100644 --- a/tests/ui/nll/user-annotations/type_ascription_static_lifetime.stderr +++ b/tests/ui/nll/user-annotations/type_ascription_static_lifetime.stderr @@ -4,10 +4,9 @@ error[E0597]: `x` does not live long enough LL | let x = 22_u32; | - binding `x` declared here LL | let y: &u32 = type_ascribe!(&x, &'static u32); - | --------------^^--------------- - | | | - | | borrowed value does not live long enough - | type annotation requires that `x` is borrowed for `'static` + | ^^ ------------ type annotation requires that `x` is borrowed for `'static` + | | + | borrowed value does not live long enough LL | } | - `x` dropped here while still borrowed diff --git a/tests/ui/parser/block-no-opening-brace.rs b/tests/ui/parser/block-no-opening-brace.rs index e90a34104e8f..2fde37ce6ace 100644 --- a/tests/ui/parser/block-no-opening-brace.rs +++ b/tests/ui/parser/block-no-opening-brace.rs @@ -4,28 +4,46 @@ fn main() {} -fn f1() { +fn in_loop() { loop let x = 0; //~ ERROR expected `{`, found keyword `let` drop(0); - } +} -fn f2() { +fn in_while() { while true let x = 0; //~ ERROR expected `{`, found keyword `let` - } +} -fn f3() { +fn in_for() { for x in 0..1 let x = 0; //~ ERROR expected `{`, found keyword `let` - } +} + -fn f4() { +// FIXME +fn in_try() { try //~ ERROR expected expression, found reserved keyword `try` let x = 0; - } +} -fn f5() { +// FIXME(#80931) +fn in_async() { async let x = 0; //~ ERROR expected one of `move`, `|`, or `||`, found keyword `let` +} + +// FIXME(#78168) +fn in_const() { + let x = const 2; //~ ERROR expected expression, found keyword `const` +} + +// FIXME(#78168) +fn in_const_in_match() { + let x = 2; + match x { + const 2 => {} + //~^ ERROR expected identifier, found keyword `const` + //~| ERROR expected one of `=>`, `if`, or `|`, found `2` } +} diff --git a/tests/ui/parser/block-no-opening-brace.stderr b/tests/ui/parser/block-no-opening-brace.stderr index f232f480ce9c..83360944ed56 100644 --- a/tests/ui/parser/block-no-opening-brace.stderr +++ b/tests/ui/parser/block-no-opening-brace.stderr @@ -38,18 +38,36 @@ LL | { let x = 0; } | + + error: expected expression, found reserved keyword `try` - --> $DIR/block-no-opening-brace.rs:24:5 + --> $DIR/block-no-opening-brace.rs:26:5 | LL | try | ^^^ expected expression error: expected one of `move`, `|`, or `||`, found keyword `let` - --> $DIR/block-no-opening-brace.rs:30:9 + --> $DIR/block-no-opening-brace.rs:33:9 | LL | async | - expected one of `move`, `|`, or `||` LL | let x = 0; | ^^^ unexpected token -error: aborting due to 5 previous errors +error: expected expression, found keyword `const` + --> $DIR/block-no-opening-brace.rs:38:13 + | +LL | let x = const 2; + | ^^^^^ expected expression + +error: expected identifier, found keyword `const` + --> $DIR/block-no-opening-brace.rs:45:9 + | +LL | const 2 => {} + | ^^^^^ expected identifier, found keyword + +error: expected one of `=>`, `if`, or `|`, found `2` + --> $DIR/block-no-opening-brace.rs:45:15 + | +LL | const 2 => {} + | ^ expected one of `=>`, `if`, or `|` + +error: aborting due to 8 previous errors diff --git a/tests/ui/regions/regions-close-object-into-object-4.stderr b/tests/ui/regions/regions-close-object-into-object-4.stderr index b8b414b7e125..f6a79be09477 100644 --- a/tests/ui/regions/regions-close-object-into-object-4.stderr +++ b/tests/ui/regions/regions-close-object-into-object-4.stderr @@ -30,12 +30,11 @@ error[E0310]: the parameter type `U` may not live long enough --> $DIR/regions-close-object-into-object-4.rs:9:5 | LL | Box::new(B(&*v)) as Box - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | | the parameter type `U` must be valid for the static lifetime... | ...so that the type `U` will meet its required lifetime bounds | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` help: consider adding an explicit lifetime bound | LL | fn i<'a, T, U: 'static>(v: Box+'a>) -> Box { diff --git a/tests/ui/regions/regions-close-object-into-object-5.stderr b/tests/ui/regions/regions-close-object-into-object-5.stderr index 4a2f4f847a30..881d9e03cdfa 100644 --- a/tests/ui/regions/regions-close-object-into-object-5.stderr +++ b/tests/ui/regions/regions-close-object-into-object-5.stderr @@ -30,12 +30,11 @@ error[E0310]: the parameter type `T` may not live long enough --> $DIR/regions-close-object-into-object-5.rs:17:5 | LL | Box::new(B(&*v)) as Box - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | | the parameter type `T` must be valid for the static lifetime... | ...so that the type `T` will meet its required lifetime bounds | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` help: consider adding an explicit lifetime bound | LL | fn f<'a, T: 'static, U>(v: Box + 'static>) -> Box { diff --git a/tests/ui/regions/regions-close-over-type-parameter-1.stderr b/tests/ui/regions/regions-close-over-type-parameter-1.stderr index 1cd5b7f22507..7c8c5fe5cf68 100644 --- a/tests/ui/regions/regions-close-over-type-parameter-1.stderr +++ b/tests/ui/regions/regions-close-over-type-parameter-1.stderr @@ -2,7 +2,7 @@ error[E0310]: the parameter type `A` may not live long enough --> $DIR/regions-close-over-type-parameter-1.rs:11:5 | LL | Box::new(v) as Box - | ^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | | the parameter type `A` must be valid for the static lifetime... | ...so that the type `A` will meet its required lifetime bounds @@ -18,7 +18,7 @@ error[E0309]: the parameter type `A` may not live long enough LL | fn make_object3<'a, 'b, A: SomeTrait + 'a>(v: A) -> Box { | -- the parameter type `A` must be valid for the lifetime `'b` as defined here... LL | Box::new(v) as Box - | ^^^^^^^^^^^ ...so that the type `A` will meet its required lifetime bounds + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ...so that the type `A` will meet its required lifetime bounds | help: consider adding an explicit lifetime bound | diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/const-trait-bounds.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-trait-bounds.stderr index 7db6a77c77bf..698b1b5b5781 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/const-trait-bounds.stderr +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-trait-bounds.stderr @@ -1,3 +1,11 @@ +error: `-Znext-solver=globally` and `generic_const_exprs` are incompatible, using them at the same time is not allowed + --> $DIR/const-trait-bounds.rs:4:39 + | +LL | #![feature(const_trait_impl, effects, generic_const_exprs)] + | ^^^^^^^^^^^^^^^^^^^ + | + = help: remove one of these features + error[E0284]: type annotations needed: cannot normalize `process::{constant#0}` --> $DIR/const-trait-bounds.rs:12:35 | @@ -16,6 +24,6 @@ error[E0284]: type annotations needed: cannot normalize `process::{constant#1 LL | input | ^^^^^ cannot normalize `process::{constant#1}` -error: aborting due to 3 previous errors +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0284`. diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/unsatisfied-const-trait-bound.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/unsatisfied-const-trait-bound.rs index c50c755f667f..5fffe54fc1a9 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/unsatisfied-const-trait-bound.rs +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/unsatisfied-const-trait-bound.rs @@ -1,3 +1,4 @@ +//@ known-bug: unknown // Ensure that we print unsatisfied always-const trait bounds as `const Trait` in diagnostics. //@ compile-flags: -Znext-solver @@ -19,7 +20,7 @@ impl Trait for Ty { fn main() { // FIXME(effects): improve diagnostics on this - require::(); //~ ERROR the trait bound `Trait::{synthetic#0}: const Compat` is not satisfied + require::(); } struct Container; @@ -27,9 +28,7 @@ struct Container; // FIXME(effects): Somehow emit `the trait bound `T: const Trait` is not satisfied` here instead // and suggest changing `Trait` to `const Trait`. fn accept0(_: Container<{ T::make() }>) {} -//~^ ERROR mismatched types // FIXME(effects): Instead of suggesting `+ const Trait`, suggest // changing `~const Trait` to `const Trait`. const fn accept1(_: Container<{ T::make() }>) {} -//~^ ERROR mismatched types diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/unsatisfied-const-trait-bound.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/unsatisfied-const-trait-bound.stderr index b9f6c9e88356..0806ffa4b5df 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/unsatisfied-const-trait-bound.stderr +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/unsatisfied-const-trait-bound.stderr @@ -1,5 +1,13 @@ +error: `-Znext-solver=globally` and `generic_const_exprs` are incompatible, using them at the same time is not allowed + --> $DIR/unsatisfied-const-trait-bound.rs:5:39 + | +LL | #![feature(const_trait_impl, effects, generic_const_exprs)] + | ^^^^^^^^^^^^^^^^^^^ + | + = help: remove one of these features + error[E0308]: mismatched types - --> $DIR/unsatisfied-const-trait-bound.rs:29:37 + --> $DIR/unsatisfied-const-trait-bound.rs:30:37 | LL | fn accept0(_: Container<{ T::make() }>) {} | ^^^^^^^^^ expected `false`, found `true` @@ -17,18 +25,18 @@ LL | const fn accept1(_: Container<{ T::make() }>) {} found constant `host` error[E0277]: the trait bound `Trait::{synthetic#0}: const Compat` is not satisfied - --> $DIR/unsatisfied-const-trait-bound.rs:22:15 + --> $DIR/unsatisfied-const-trait-bound.rs:23:15 | LL | require::(); | ^^ the trait `const Compat` is not implemented for `Trait::{synthetic#0}` | note: required by a bound in `require` - --> $DIR/unsatisfied-const-trait-bound.rs:7:15 + --> $DIR/unsatisfied-const-trait-bound.rs:8:15 | LL | fn require() {} | ^^^^^^^^^^^ required by this bound in `require` -error: aborting due to 3 previous errors +error: aborting due to 4 previous errors Some errors have detailed explanations: E0277, E0308. For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/static/issue-18118-2.rs b/tests/ui/static/issue-18118-2.rs deleted file mode 100644 index 6c81eec7d7e4..000000000000 --- a/tests/ui/static/issue-18118-2.rs +++ /dev/null @@ -1,6 +0,0 @@ -pub fn main() { - const z: &'static isize = { - static p: isize = 3; - &p //~ ERROR referencing statics - }; -} diff --git a/tests/ui/static/issue-18118-2.stderr b/tests/ui/static/issue-18118-2.stderr deleted file mode 100644 index f084f2b9fdfc..000000000000 --- a/tests/ui/static/issue-18118-2.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error[E0658]: referencing statics in constants is unstable - --> $DIR/issue-18118-2.rs:4:10 - | -LL | &p - | ^ - | - = note: see issue #119618 for more information - = help: add `#![feature(const_refs_to_static)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - = note: `static` and `const` variables can refer to other `const` variables. A `const` variable, however, cannot refer to a `static` variable. - = help: to fix this, the value can be extracted to a `const` and then used. - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/statics/const_generics.rs b/tests/ui/statics/const_generics.rs index 7f64f6995a45..6cc0a65f77d5 100644 --- a/tests/ui/statics/const_generics.rs +++ b/tests/ui/statics/const_generics.rs @@ -10,7 +10,6 @@ //@[noopt] compile-flags: -Copt-level=0 //@[opt] compile-flags: -O -#![feature(const_refs_to_static)] #![feature(adt_const_params, unsized_const_params)] #![allow(incomplete_features)] diff --git a/tests/ui/statics/mutable_memory_validation.rs b/tests/ui/statics/mutable_memory_validation.rs index d16b787fef84..032b903f64e5 100644 --- a/tests/ui/statics/mutable_memory_validation.rs +++ b/tests/ui/statics/mutable_memory_validation.rs @@ -4,8 +4,6 @@ //@ normalize-stderr-test: "(the raw bytes of the constant) \(size: [0-9]*, align: [0-9]*\)" -> "$1 (size: $$SIZE, align: $$ALIGN)" //@ normalize-stderr-test: "([0-9a-f][0-9a-f] |╾─*A(LLOC)?[0-9]+(\+[a-z0-9]+)?()?─*╼ )+ *│.*" -> "HEX_DUMP" -#![feature(const_refs_to_static)] - use std::cell::UnsafeCell; struct Meh { diff --git a/tests/ui/statics/mutable_memory_validation.stderr b/tests/ui/statics/mutable_memory_validation.stderr index 60d135646792..76e1827ea12a 100644 --- a/tests/ui/statics/mutable_memory_validation.stderr +++ b/tests/ui/statics/mutable_memory_validation.stderr @@ -1,5 +1,5 @@ error[E0080]: it is undefined behavior to use this value - --> $DIR/mutable_memory_validation.rs:15:1 + --> $DIR/mutable_memory_validation.rs:13:1 | LL | const MUH: Meh = Meh { x: unsafe { &mut *(&READONLY as *const _ as *mut _) } }; | ^^^^^^^^^^^^^^ constructing invalid value at .x.: encountered `UnsafeCell` in read-only memory diff --git a/tests/ui/suggestions/lifetimes/suggest-using-tick-underscore-lifetime-in-return-trait-object.stderr b/tests/ui/suggestions/lifetimes/suggest-using-tick-underscore-lifetime-in-return-trait-object.stderr index 73fa5ddb146e..fa203150444c 100644 --- a/tests/ui/suggestions/lifetimes/suggest-using-tick-underscore-lifetime-in-return-trait-object.stderr +++ b/tests/ui/suggestions/lifetimes/suggest-using-tick-underscore-lifetime-in-return-trait-object.stderr @@ -4,7 +4,7 @@ error: lifetime may not live long enough LL | fn foo(value: &T) -> Box { | - let's call the lifetime of this reference `'1` LL | Box::new(value) as Box - | ^^^^^^^^^^^^^^^ cast requires that `'1` must outlive `'static` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cast requires that `'1` must outlive `'static` | help: to declare that the trait object captures data from argument `value`, you can add an explicit `'_` lifetime bound | diff --git a/tests/ui/traits/incoherent-impl-ambiguity.rs b/tests/ui/traits/incoherent-impl-ambiguity.rs new file mode 100644 index 000000000000..b5fed95e11c4 --- /dev/null +++ b/tests/ui/traits/incoherent-impl-ambiguity.rs @@ -0,0 +1,14 @@ +// Make sure that an invalid inherent impl doesn't totally clobber all of the +// other inherent impls, which lead to mysterious method/assoc-item probing errors. + +impl () {} +//~^ ERROR cannot define inherent `impl` for primitive types + +struct W; +impl W { + const CONST: u32 = 0; +} + +fn main() { + let _ = W::CONST; +} diff --git a/tests/ui/traits/incoherent-impl-ambiguity.stderr b/tests/ui/traits/incoherent-impl-ambiguity.stderr new file mode 100644 index 000000000000..9c050a729558 --- /dev/null +++ b/tests/ui/traits/incoherent-impl-ambiguity.stderr @@ -0,0 +1,11 @@ +error[E0390]: cannot define inherent `impl` for primitive types + --> $DIR/incoherent-impl-ambiguity.rs:4:1 + | +LL | impl () {} + | ^^^^^^^ + | + = help: consider using an extension trait instead + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0390`. diff --git a/tests/ui/traits/trait-object-lifetime-default-note.rs b/tests/ui/traits/trait-object-lifetime-default-note.rs index 31e3eb4ba415..275411ff61c8 100644 --- a/tests/ui/traits/trait-object-lifetime-default-note.rs +++ b/tests/ui/traits/trait-object-lifetime-default-note.rs @@ -8,7 +8,7 @@ fn main() { //~| NOTE borrowed value does not live long enough //~| NOTE due to object lifetime defaults, `Box` actually means `Box<(dyn A + 'static)>` require_box(Box::new(r)); - //~^ NOTE cast requires that `local` is borrowed for `'static` + //~^ NOTE coercion requires that `local` is borrowed for `'static` let _ = 0; } //~ NOTE `local` dropped here while still borrowed diff --git a/tests/ui/traits/trait-object-lifetime-default-note.stderr b/tests/ui/traits/trait-object-lifetime-default-note.stderr index 4244e34873ad..8cb9bc0d8007 100644 --- a/tests/ui/traits/trait-object-lifetime-default-note.stderr +++ b/tests/ui/traits/trait-object-lifetime-default-note.stderr @@ -7,7 +7,7 @@ LL | let r = &local; | ^^^^^^ borrowed value does not live long enough ... LL | require_box(Box::new(r)); - | ----------- cast requires that `local` is borrowed for `'static` + | ----------- coercion requires that `local` is borrowed for `'static` ... LL | } | - `local` dropped here while still borrowed diff --git a/tests/ui/traits/trait-upcasting/type-checking-test-3.stderr b/tests/ui/traits/trait-upcasting/type-checking-test-3.stderr index e6cb6a753998..0a969b611e9d 100644 --- a/tests/ui/traits/trait-upcasting/type-checking-test-3.stderr +++ b/tests/ui/traits/trait-upcasting/type-checking-test-3.stderr @@ -1,18 +1,18 @@ error: lifetime may not live long enough - --> $DIR/type-checking-test-3.rs:11:13 + --> $DIR/type-checking-test-3.rs:11:18 | LL | fn test_wrong1<'a>(x: &dyn Foo<'static>, y: &'a u32) { | -- lifetime `'a` defined here LL | let _ = x as &dyn Bar<'a>; // Error - | ^^^^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static` + | ^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static` error: lifetime may not live long enough - --> $DIR/type-checking-test-3.rs:16:13 + --> $DIR/type-checking-test-3.rs:16:18 | LL | fn test_wrong2<'a>(x: &dyn Foo<'a>) { | -- lifetime `'a` defined here LL | let _ = x as &dyn Bar<'static>; // Error - | ^^^^^^^^^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static` + | ^^^^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static` error: aborting due to 2 previous errors diff --git a/tests/ui/traits/trait-upcasting/type-checking-test-4.stderr b/tests/ui/traits/trait-upcasting/type-checking-test-4.stderr index ccced5875778..090120a2327a 100644 --- a/tests/ui/traits/trait-upcasting/type-checking-test-4.stderr +++ b/tests/ui/traits/trait-upcasting/type-checking-test-4.stderr @@ -1,18 +1,18 @@ error: lifetime may not live long enough - --> $DIR/type-checking-test-4.rs:19:13 + --> $DIR/type-checking-test-4.rs:19:18 | LL | fn test_wrong1<'a>(x: &dyn Foo<'static>, y: &'a u32) { | -- lifetime `'a` defined here LL | let _ = x as &dyn Bar<'static, 'a>; // Error - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static` + | ^^^^^^^^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static` error: lifetime may not live long enough - --> $DIR/type-checking-test-4.rs:24:13 + --> $DIR/type-checking-test-4.rs:24:18 | LL | fn test_wrong2<'a>(x: &dyn Foo<'static>, y: &'a u32) { | -- lifetime `'a` defined here LL | let _ = x as &dyn Bar<'a, 'static>; // Error - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static` + | ^^^^^^^^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static` error: lifetime may not live long enough --> $DIR/type-checking-test-4.rs:30:5 diff --git a/tests/ui/traits/upcast_soundness_bug.rs b/tests/ui/traits/upcast_soundness_bug.rs index 5eaa58f7efe7..0ddae1d1417c 100644 --- a/tests/ui/traits/upcast_soundness_bug.rs +++ b/tests/ui/traits/upcast_soundness_bug.rs @@ -57,7 +57,7 @@ pub fn user2() -> &'static dyn Trait { fn main() { let p: *const dyn Trait = &(); let p = p as *const dyn Trait; // <- this is bad! - //~^ error: mismatched types + //~^ error: casting `*const dyn Trait` as `*const dyn Trait` is invalid let p = p as *const dyn Super; // <- this upcast accesses improper vtable entry // accessing from L__unnamed_2 the position for the 'Super vtable (pointer)', // thus reading 'null pointer for missing_method' diff --git a/tests/ui/traits/upcast_soundness_bug.stderr b/tests/ui/traits/upcast_soundness_bug.stderr index 5864abcdb41f..19d1a5e5926e 100644 --- a/tests/ui/traits/upcast_soundness_bug.stderr +++ b/tests/ui/traits/upcast_soundness_bug.stderr @@ -1,13 +1,11 @@ -error[E0308]: mismatched types +error[E0606]: casting `*const dyn Trait` as `*const dyn Trait` is invalid --> $DIR/upcast_soundness_bug.rs:59:13 | LL | let p = p as *const dyn Trait; // <- this is bad! - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `u8`, found `u16` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: expected trait object `dyn Trait` - found trait object `dyn Trait` - = help: `dyn Trait` implements `Trait` so you could box the found value and coerce it to the trait object `Box`, you will have to change the expected type as well + = note: the trait objects may have different vtables error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0308`. +For more information about this error, try `rustc --explain E0606`. diff --git a/triagebot.toml b/triagebot.toml index a6a0b02d7b71..5d80b9e656ea 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -923,10 +923,8 @@ contributing_url = "https://rustc-dev-guide.rust-lang.org/getting-started.html" users_on_vacation = [ "fmease", "jhpratt", - "joboet", "jyn514", "oli-obk", - "tgross35", ] [assign.adhoc_groups]
- ? -