diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000000000..ca704082a3f18 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,10 @@ + \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5364c1e9f466c..0551d44649cdd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -190,7 +190,6 @@ jobs: env: AWS_ACCESS_KEY_ID: ${{ env.CACHES_AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets[format('AWS_SECRET_ACCESS_KEY_{0}', env.CACHES_AWS_ACCESS_KEY_ID)] }} - TOOLSTATE_REPO_ACCESS_TOKEN: ${{ secrets.TOOLSTATE_REPO_ACCESS_TOKEN }} - name: create github artifacts run: src/ci/scripts/create-doc-artifacts.sh @@ -241,3 +240,5 @@ jobs: if: needs.calculate_matrix.outputs.run_type == 'auto' env: TOOLSTATE_REPO_ACCESS_TOKEN: ${{ secrets.TOOLSTATE_REPO_ACCESS_TOKEN }} + TOOLSTATE_ISSUES_API_URL: https://api.github.com/repos/rust-lang/rust/issues + TOOLSTATE_PUBLISH: 1 diff --git a/.gitmodules b/.gitmodules index 802d61eea293b..9ad207a0d5226 100644 --- a/.gitmodules +++ b/.gitmodules @@ -33,7 +33,7 @@ [submodule "src/llvm-project"] path = src/llvm-project url = https://github.com/rust-lang/llvm-project.git - branch = rustc/18.0-2024-02-13 + branch = rustc/18.1-2024-05-19 shallow = true [submodule "src/doc/embedded-book"] path = src/doc/embedded-book @@ -43,3 +43,7 @@ path = library/backtrace url = https://github.com/rust-lang/backtrace-rs.git shallow = true +[submodule "src/tools/rustc-perf"] + path = src/tools/rustc-perf + url = https://github.com/rust-lang/rustc-perf.git + shallow = true diff --git a/Cargo.lock b/Cargo.lock index a9283d03cb322..3a4f028e695f4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -74,11 +74,11 @@ checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" [[package]] name = "ammonia" -version = "3.3.0" +version = "4.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64e6d1c7838db705c9b756557ee27c384ce695a1c51a6fe528784cb1c6840170" +checksum = "1ab99eae5ee58501ab236beb6f20f6ca39be615267b014899c89b2f0bc18a459" dependencies = [ - "html5ever", + "html5ever 0.27.0", "maplit", "once_cell", "tendril", @@ -212,9 +212,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.83" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25bdb32cbbdce2b519a9cd7df3a678443100e265d5e25ca763b7572a5104f5f3" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" dependencies = [ "backtrace", ] @@ -257,7 +257,7 @@ dependencies = [ "proc-macro2", "quote", "serde", - "syn 2.0.62", + "syn 2.0.64", ] [[package]] @@ -414,9 +414,9 @@ checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" [[package]] name = "camino" -version = "1.1.6" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c59e92b5a388f549b863a7bea62612c09f24c8393560709a54558a9abdfb3b9c" +checksum = "e0ec6b951b160caa93cc0c7b209e5a3bff7aae9062213451ac99493cd844c239" dependencies = [ "serde", ] @@ -571,7 +571,7 @@ dependencies = [ "anstream", "anstyle", "clap_lex", - "strsim 0.11.1", + "strsim", "terminal_size", ] @@ -593,7 +593,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.62", + "syn 2.0.64", ] [[package]] @@ -620,7 +620,7 @@ dependencies = [ "regex", "rustc_tools_util", "serde", - "syn 2.0.62", + "syn 2.0.64", "tempfile", "termize", "tokio", @@ -647,7 +647,7 @@ dependencies = [ "clap", "indoc", "itertools 0.12.1", - "opener", + "opener 0.6.1", "shell-escape", "walkdir", ] @@ -730,7 +730,7 @@ dependencies = [ "nom", "proc-macro2", "quote", - "syn 2.0.62", + "syn 2.0.64", ] [[package]] @@ -825,16 +825,6 @@ dependencies = [ "rand_xorshift", ] -[[package]] -name = "core-foundation" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" -dependencies = [ - "core-foundation-sys", - "libc", -] - [[package]] name = "core-foundation-sys" version = "0.8.6" @@ -957,9 +947,9 @@ dependencies = [ [[package]] name = "darling" -version = "0.20.8" +version = "0.20.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54e36fcd13ed84ffdfda6f5be89b31287cbb80c439841fe69e04841435464391" +checksum = "83b2eb4d90d12bdda5ed17de686c2acb4c57914f8f921b8da7e112b5a36f3fe1" dependencies = [ "darling_core", "darling_macro", @@ -967,27 +957,27 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.20.8" +version = "0.20.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c2cf1c23a687a1feeb728783b993c4e1ad83d99f351801977dd809b48d0a70f" +checksum = "622687fe0bac72a04e5599029151f5796111b90f1baaa9b544d807a5e31cd120" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", - "strsim 0.10.0", - "syn 2.0.62", + "strsim", + "syn 2.0.64", ] [[package]] name = "darling_macro" -version = "0.20.8" +version = "0.20.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f" +checksum = "733cabb43482b1a1b53eee8583c2b9e8684d592215ea83efd305dd31bc2f0178" dependencies = [ "darling_core", "quote", - "syn 2.0.62", + "syn 2.0.64", ] [[package]] @@ -996,13 +986,24 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0afaad2b26fa326569eb264b1363e8ae3357618c43982b3f285f0774ce76b69" +[[package]] +name = "dbus" +version = "0.9.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bb21987b9fb1613058ba3843121dd18b163b254d8a6e797e144cbac14d96d1b" +dependencies = [ + "libc", + "libdbus-sys", + "winapi", +] + [[package]] name = "declare_clippy_lint" version = "0.1.80" dependencies = [ "itertools 0.12.1", "quote", - "syn 2.0.62", + "syn 2.0.64", ] [[package]] @@ -1043,7 +1044,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.62", + "syn 2.0.64", ] [[package]] @@ -1053,7 +1054,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "206868b8242f27cecce124c19fd88157fbd0dd334df2587f36417bafbc85097b" dependencies = [ "derive_builder_core", - "syn 2.0.62", + "syn 2.0.64", ] [[package]] @@ -1076,7 +1077,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.62", + "syn 2.0.64", ] [[package]] @@ -1165,7 +1166,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.62", + "syn 2.0.64", ] [[package]] @@ -1189,9 +1190,9 @@ dependencies = [ [[package]] name = "either" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" +checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" [[package]] name = "elasticlunr-rs" @@ -1229,15 +1230,6 @@ version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" -[[package]] -name = "encoding_rs" -version = "0.8.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" -dependencies = [ - "cfg-if", -] - [[package]] name = "env_filter" version = "0.1.0" @@ -1388,21 +1380,6 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - [[package]] name = "form_urlencoded" version = "1.2.1" @@ -1503,7 +1480,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.62", + "syn 2.0.64", ] [[package]] @@ -1626,25 +1603,6 @@ dependencies = [ "serde", ] -[[package]] -name = "h2" -version = "0.3.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" -dependencies = [ - "bytes", - "fnv", - "futures-core", - "futures-sink", - "futures-util", - "http", - "indexmap", - "slab", - "tokio", - "tokio-util", - "tracing", -] - [[package]] name = "handlebars" version = "5.1.2" @@ -1735,46 +1693,26 @@ checksum = "bea68cab48b8459f17cf1c944c67ddc572d272d9f2b274140f223ecb1da4a3b7" dependencies = [ "log", "mac", - "markup5ever", + "markup5ever 0.11.0", "proc-macro2", "quote", "syn 1.0.109", ] [[package]] -name = "http" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - -[[package]] -name = "http-body" -version = "0.4.6" +name = "html5ever" +version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +checksum = "c13771afe0e6e846f1e67d038d4cb29998a6779f93c809212e4e9c32efd244d4" dependencies = [ - "bytes", - "http", - "pin-project-lite", + "log", + "mac", + "markup5ever 0.12.1", + "proc-macro2", + "quote", + "syn 2.0.64", ] -[[package]] -name = "httparse" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" - -[[package]] -name = "httpdate" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" - [[package]] name = "humansize" version = "2.1.3" @@ -1790,43 +1728,6 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" -[[package]] -name = "hyper" -version = "0.14.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" -dependencies = [ - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "socket2", - "tokio", - "tower-service", - "tracing", - "want", -] - -[[package]] -name = "hyper-tls" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" -dependencies = [ - "bytes", - "hyper", - "native-tls", - "tokio", - "tokio-native-tls", -] - [[package]] name = "iana-time-zone" version = "0.1.60" @@ -1941,7 +1842,7 @@ checksum = "d2abdd3a62551e8337af119c5899e600ca0c88ec8f23a46c60ba216c803dcf1a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.62", + "syn 2.0.64", ] [[package]] @@ -2044,9 +1945,9 @@ dependencies = [ [[package]] name = "instant" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" dependencies = [ "cfg-if", ] @@ -2070,12 +1971,6 @@ dependencies = [ "unic-langid", ] -[[package]] -name = "ipnet" -version = "2.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" - [[package]] name = "is_terminal_polyfill" version = "1.70.0" @@ -2190,13 +2085,23 @@ checksum = "db13adb97ab515a3691f56e4dbab09283d0b86cb45abd991d8634a9d6f501760" [[package]] name = "libc" -version = "0.2.153" +version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" dependencies = [ "rustc-std-workspace-core", ] +[[package]] +name = "libdbus-sys" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06085512b750d640299b79be4bad3d2fa90a9c00b1fd9e1b46364f66f0485c72" +dependencies = [ + "cc", + "pkg-config", +] + [[package]] name = "libffi" version = "3.2.0" @@ -2258,7 +2163,7 @@ dependencies = [ name = "linkchecker" version = "0.1.0" dependencies = [ - "html5ever", + "html5ever 0.26.0", "regex", ] @@ -2273,9 +2178,9 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] name = "litemap" @@ -2351,6 +2256,20 @@ dependencies = [ "tendril", ] +[[package]] +name = "markup5ever" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16ce3abbeba692c8b8441d036ef91aea6df8da2c6b6e21c7e14d3c18e526be45" +dependencies = [ + "log", + "phf 0.11.2", + "phf_codegen 0.11.2", + "string_cache", + "string_cache_codegen", + "tendril", +] + [[package]] name = "matchers" version = "0.1.0" @@ -2372,9 +2291,9 @@ dependencies = [ [[package]] name = "mdbook" -version = "0.4.37" +version = "0.4.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c33564061c3c640bed5ace7d6a2a1b65f2c64257d1ac930c15e94ed0fb561d3" +checksum = "b45a38e19bd200220ef07c892b0157ad3d2365e5b5a267ca01ad12182491eea5" dependencies = [ "ammonia", "anyhow", @@ -2387,7 +2306,7 @@ dependencies = [ "log", "memchr", "once_cell", - "opener", + "opener 0.7.1", "pulldown-cmark 0.10.3", "regex", "serde", @@ -2470,9 +2389,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" +checksum = "87dfd01fe195c66b572b37921ad8803d010623c0aca821bea2302239d155cdae" dependencies = [ "adler", "compiler_builtins", @@ -2480,17 +2399,6 @@ dependencies = [ "rustc-std-workspace-core", ] -[[package]] -name = "mio" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" -dependencies = [ - "libc", - "wasi", - "windows-sys 0.48.0", -] - [[package]] name = "miow" version = "0.6.0" @@ -2528,24 +2436,6 @@ dependencies = [ name = "miropt-test-tools" version = "0.1.0" -[[package]] -name = "native-tls" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" -dependencies = [ - "lazy_static", - "libc", - "log", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework", - "security-framework-sys", - "tempfile", -] - [[package]] name = "new_debug_unreachable" version = "1.0.6" @@ -2698,29 +2588,15 @@ dependencies = [ ] [[package]] -name = "openssl" -version = "0.10.64" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" -dependencies = [ - "bitflags 2.5.0", - "cfg-if", - "foreign-types", - "libc", - "once_cell", - "openssl-macros", - "openssl-sys", -] - -[[package]] -name = "openssl-macros" -version = "0.1.1" +name = "opener" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +checksum = "f8df34be653210fbe9ffaff41d3b92721c56ce82dfee58ee684f9afb5e3a90c0" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.62", + "bstr", + "dbus", + "normpath", + "windows-sys 0.52.0", ] [[package]] @@ -2756,7 +2632,6 @@ dependencies = [ "humansize", "humantime", "log", - "reqwest", "serde", "serde_json", "sysinfo", @@ -2764,7 +2639,6 @@ dependencies = [ "tar", "tempfile", "xz2", - "zip", ] [[package]] @@ -2912,7 +2786,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.62", + "syn 2.0.64", ] [[package]] @@ -3349,46 +3223,6 @@ dependencies = [ "walkdir", ] -[[package]] -name = "reqwest" -version = "0.11.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" -dependencies = [ - "base64", - "bytes", - "encoding_rs", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "hyper", - "hyper-tls", - "ipnet", - "js-sys", - "log", - "mime", - "native-tls", - "once_cell", - "percent-encoding", - "pin-project-lite", - "rustls-pemfile", - "serde", - "serde_json", - "serde_urlencoded", - "sync_wrapper", - "system-configuration", - "tokio", - "tokio-native-tls", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "winreg", -] - [[package]] name = "rls" version = "2.0.0" @@ -4001,7 +3835,7 @@ dependencies = [ "fluent-syntax", "proc-macro2", "quote", - "syn 2.0.62", + "syn 2.0.64", "unic-langid", ] @@ -4135,7 +3969,7 @@ version = "0.0.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.62", + "syn 2.0.64", "synstructure", ] @@ -4282,7 +4116,7 @@ version = "0.0.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.62", + "syn 2.0.64", "synstructure", ] @@ -4859,7 +4693,7 @@ version = "0.0.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.62", + "syn 2.0.64", "synstructure", ] @@ -4957,7 +4791,7 @@ dependencies = [ "proc-macro2", "quote", "serde", - "syn 2.0.62", + "syn 2.0.64", ] [[package]] @@ -5003,20 +4837,11 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "rustls-pemfile" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" -dependencies = [ - "base64", -] - [[package]] name = "rustversion" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "092474d1a01ea8278f69e6a358998405fae5b8b963ddaeb2b0b04a128bf1dfb0" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" [[package]] name = "ruzstd" @@ -5076,29 +4901,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" -[[package]] -name = "security-framework" -version = "2.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c627723fd09706bacdb5cf41499e95098555af3c3c29d014dc3c458ef6be11c0" -dependencies = [ - "bitflags 2.5.0", - "core-foundation", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "317936bbbd05227752583946b9e66d7ce3b489f84e11a94a510b4437fef407d7" -dependencies = [ - "core-foundation-sys", - "libc", -] - [[package]] name = "self_cell" version = "0.10.3" @@ -5125,22 +4927,22 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.201" +version = "1.0.202" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "780f1cebed1629e4753a1a38a3c72d30b97ec044f0aef68cb26650a3c5cf363c" +checksum = "226b61a0d411b2ba5ff6d7f73a476ac4f8bb900373459cd00fab8512828ba395" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.201" +version = "1.0.202" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5e405930b9796f1c00bee880d03fc7e0bb4b9a11afc776885ffe84320da2865" +checksum = "6048858004bcff69094cd972ed40a32500f153bd3be9f716b2eed2e8217c4838" dependencies = [ "proc-macro2", "quote", - "syn 2.0.62", + "syn 2.0.64", ] [[package]] @@ -5157,22 +4959,10 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" -dependencies = [ - "serde", -] - -[[package]] -name = "serde_urlencoded" -version = "0.7.1" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0" dependencies = [ - "form_urlencoded", - "itoa", - "ryu", "serde", ] @@ -5400,12 +5190,6 @@ dependencies = [ "quote", ] -[[package]] -name = "strsim" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" - [[package]] name = "strsim" version = "0.11.1" @@ -5452,21 +5236,15 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.62" +version = "2.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f660c3bfcefb88c538776b6685a0c472e3128b51e74d48793dc2a488196e8eb" +checksum = "7ad3dee41f36859875573074334c200d1add8e4a87bb37113ebd31d926b7b11f" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] -[[package]] -name = "sync_wrapper" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" - [[package]] name = "synstructure" version = "0.13.1" @@ -5475,7 +5253,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.62", + "syn 2.0.64", ] [[package]] @@ -5501,27 +5279,6 @@ dependencies = [ "test", ] -[[package]] -name = "system-configuration" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" -dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "system-configuration-sys", -] - -[[package]] -name = "system-configuration-sys" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" -dependencies = [ - "core-foundation-sys", - "libc", -] - [[package]] name = "tabled" version = "0.15.0" @@ -5626,22 +5383,22 @@ checksum = "a38c90d48152c236a3ab59271da4f4ae63d678c5d7ad6b7714d7cb9760be5e4b" [[package]] name = "thiserror" -version = "1.0.60" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "579e9083ca58dd9dcf91a9923bb9054071b9ebbd800b342194c9feb0ee89fc18" +checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.60" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2470041c06ec3ac1ab38d0356a6119054dedaea53e12fbefc0de730a1c08524" +checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" dependencies = [ "proc-macro2", "quote", - "syn 2.0.62", + "syn 2.0.64", ] [[package]] @@ -5757,34 +5514,7 @@ checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" dependencies = [ "backtrace", "bytes", - "libc", - "mio", - "pin-project-lite", - "socket2", - "windows-sys 0.48.0", -] - -[[package]] -name = "tokio-native-tls" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" -dependencies = [ - "native-tls", - "tokio", -] - -[[package]] -name = "tokio-util" -version = "0.7.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", "pin-project-lite", - "tokio", ] [[package]] @@ -5810,9 +5540,9 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.5" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" dependencies = [ "serde", ] @@ -5836,12 +5566,6 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea68304e134ecd095ac6c3574494fc62b909f416c4fca77e440530221e549d3d" -[[package]] -name = "tower-service" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" - [[package]] name = "tracing" version = "0.1.37" @@ -5862,7 +5586,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.62", + "syn 2.0.64", ] [[package]] @@ -5927,12 +5651,6 @@ dependencies = [ "tracing-subscriber", ] -[[package]] -name = "try-lock" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" - [[package]] name = "twox-hash" version = "1.6.3" @@ -6068,7 +5786,7 @@ checksum = "1ed7f4237ba393424195053097c1516bd4590dc82b84f2f97c5c69e12704555b" dependencies = [ "proc-macro-hack", "quote", - "syn 2.0.62", + "syn 2.0.64", "unic-langid-impl", ] @@ -6259,15 +5977,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "want" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" -dependencies = [ - "try-lock", -] - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -6300,22 +6009,10 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.62", + "syn 2.0.64", "wasm-bindgen-shared", ] -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.42" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" -dependencies = [ - "cfg-if", - "js-sys", - "wasm-bindgen", - "web-sys", -] - [[package]] name = "wasm-bindgen-macro" version = "0.2.92" @@ -6334,7 +6031,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.62", + "syn 2.0.64", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -6364,16 +6061,6 @@ dependencies = [ "semver", ] -[[package]] -name = "web-sys" -version = "0.3.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - [[package]] name = "winapi" version = "0.3.9" @@ -6425,7 +6112,7 @@ dependencies = [ "rayon", "serde", "serde_json", - "syn 2.0.62", + "syn 2.0.64", "windows-metadata", ] @@ -6592,16 +6279,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "winreg" -version = "0.50.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" -dependencies = [ - "cfg-if", - "windows-sys 0.48.0", -] - [[package]] name = "writeable" version = "0.5.4" @@ -6657,7 +6334,7 @@ checksum = "9e6936f0cce458098a201c245a11bef556c6a0181129c7034d10d76d1ec3a2b8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.62", + "syn 2.0.64", "synstructure", ] @@ -6678,7 +6355,7 @@ checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.62", + "syn 2.0.64", ] [[package]] @@ -6698,7 +6375,7 @@ checksum = "e6a647510471d372f2e6c2e6b7219e44d8c574d24fdc11c610a61455782f18c3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.62", + "syn 2.0.64", "synstructure", ] @@ -6721,17 +6398,5 @@ checksum = "7b4e5997cbf58990550ef1f0e5124a05e47e1ebd33a84af25739be6031a62c20" dependencies = [ "proc-macro2", "quote", - "syn 2.0.62", -] - -[[package]] -name = "zip" -version = "0.6.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261" -dependencies = [ - "byteorder", - "crc32fast", - "crossbeam-utils", - "flate2", + "syn 2.0.64", ] diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 5d37bbd689fe0..1a166956075c7 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -2733,6 +2733,13 @@ pub enum UseTreeKind { /// `use prefix` or `use prefix as rename` Simple(Option), /// `use prefix::{...}` + /// + /// The span represents the braces of the nested group and all elements within: + /// + /// ```text + /// use foo::{bar, baz}; + /// ^^^^^^^^^^ + /// ``` Nested { items: ThinVec<(UseTree, NodeId)>, span: Span }, /// `use prefix::*` Glob, diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs index 0684163617fe6..d5c9fc960c86b 100644 --- a/compiler/rustc_ast/src/attr/mod.rs +++ b/compiler/rustc_ast/src/attr/mod.rs @@ -13,6 +13,7 @@ use crate::util::literal::escape_string_symbol; use rustc_index::bit_set::GrowableBitSet; use rustc_span::symbol::{sym, Ident, Symbol}; use rustc_span::Span; +use smallvec::{smallvec, SmallVec}; use std::iter; use std::sync::atomic::{AtomicU32, Ordering}; use thin_vec::{thin_vec, ThinVec}; @@ -87,10 +88,20 @@ impl Attribute { AttrKind::DocComment(..) => None, } } + pub fn name_or_empty(&self) -> Symbol { self.ident().unwrap_or_else(Ident::empty).name } + pub fn path(&self) -> SmallVec<[Symbol; 1]> { + match &self.kind { + AttrKind::Normal(normal) => { + normal.item.path.segments.iter().map(|s| s.ident.name).collect() + } + AttrKind::DocComment(..) => smallvec![sym::doc], + } + } + #[inline] pub fn has_name(&self, name: Symbol) -> bool { match &self.kind { diff --git a/compiler/rustc_ast/src/ptr.rs b/compiler/rustc_ast/src/ptr.rs index e22a523dbc3ef..34c539ea16b48 100644 --- a/compiler/rustc_ast/src/ptr.rs +++ b/compiler/rustc_ast/src/ptr.rs @@ -184,7 +184,7 @@ impl<'a, T> IntoIterator for &'a P<[T]> { type Item = &'a T; type IntoIter = slice::Iter<'a, T>; fn into_iter(self) -> Self::IntoIter { - self.ptr.into_iter() + self.ptr.iter() } } diff --git a/compiler/rustc_ast_passes/messages.ftl b/compiler/rustc_ast_passes/messages.ftl index a3731e94276b5..31a184fe921e7 100644 --- a/compiler/rustc_ast_passes/messages.ftl +++ b/compiler/rustc_ast_passes/messages.ftl @@ -55,9 +55,6 @@ ast_passes_const_without_body = ast_passes_constraint_on_negative_bound = associated type constraints not allowed on negative bounds -ast_passes_deprecated_where_clause_location = - where clause not allowed here - ast_passes_equality_in_where = equality constraints are not yet supported in `where` clauses .label = not supported .suggestion = if `{$ident}` is an associated type you're trying to set, use the associated type binding syntax @@ -80,8 +77,6 @@ ast_passes_extern_types_cannot = `type`s inside `extern` blocks cannot have {$de .suggestion = remove the {$remove_descr} .label = `extern` block begins here -ast_passes_extern_without_abi = extern declarations without an explicit ABI are deprecated - ast_passes_feature_on_non_nightly = `#![feature]` may not be used on the {$channel} release channel .suggestion = remove the attribute .stable_since = the feature `{$name}` has been stable since `{$since}` and no longer requires an attribute to enable diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 9d07683f8d665..c57be3cdf3532 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -27,7 +27,6 @@ use std::ops::{Deref, DerefMut}; use thin_vec::thin_vec; use crate::errors; -use crate::fluent_generated as fluent; /// Is `self` allowed semantically as the first parameter in an `FnDecl`? enum SelfSemantic { @@ -766,11 +765,10 @@ impl<'a> AstValidator<'a> { .span_to_snippet(span) .is_ok_and(|snippet| !snippet.starts_with("#[")) { - self.lint_buffer.buffer_lint_with_diagnostic( + self.lint_buffer.buffer_lint( MISSING_ABI, id, span, - fluent::ast_passes_extern_without_abi, BuiltinLintDiag::MissingAbi(span, abi::Abi::FALLBACK), ) } @@ -1428,17 +1426,15 @@ impl<'a> Visitor<'a> for AstValidator<'a> { Self::check_decl_no_pat(&sig.decl, |span, ident, mut_ident| { if mut_ident && matches!(ctxt, FnCtxt::Assoc(_)) { if let Some(ident) = ident { - let msg = match ctxt { - FnCtxt::Foreign => fluent::ast_passes_pattern_in_foreign, - _ => fluent::ast_passes_pattern_in_bodiless, - }; - let diag = BuiltinLintDiag::PatternsInFnsWithoutBody(span, ident); - self.lint_buffer.buffer_lint_with_diagnostic( + self.lint_buffer.buffer_lint( PATTERNS_IN_FNS_WITHOUT_BODY, id, span, - msg, - diag, + BuiltinLintDiag::PatternsInFnsWithoutBody { + span, + ident, + is_foreign: matches!(ctxt, FnCtxt::Foreign), + }, ) } } else { @@ -1510,12 +1506,11 @@ impl<'a> Visitor<'a> for AstValidator<'a> { Some((right, snippet)) } }; - self.lint_buffer.buffer_lint_with_diagnostic( + self.lint_buffer.buffer_lint( DEPRECATED_WHERE_CLAUSE_LOCATION, item.id, err.span, - fluent::ast_passes_deprecated_where_clause_location, - BuiltinLintDiag::DeprecatedWhereclauseLocation(sugg), + BuiltinLintDiag::DeprecatedWhereclauseLocation(err.span, sugg), ); } diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs index 25a125f83935b..a95a7bdaf6da5 100644 --- a/compiler/rustc_ast_passes/src/errors.rs +++ b/compiler/rustc_ast_passes/src/errors.rs @@ -669,6 +669,7 @@ pub struct ConstAndCVariadic { #[derive(Diagnostic)] #[diag(ast_passes_pattern_in_foreign, code = E0130)] +// FIXME: deduplicate with rustc_lint (`BuiltinLintDiag::PatternsInFnsWithoutBody`) pub struct PatternInForeign { #[primary_span] #[label] @@ -677,6 +678,7 @@ pub struct PatternInForeign { #[derive(Diagnostic)] #[diag(ast_passes_pattern_in_bodiless, code = E0642)] +// FIXME: deduplicate with rustc_lint (`BuiltinLintDiag::PatternsInFnsWithoutBody`) pub struct PatternInBodiless { #[primary_span] #[label] diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 6622caaaab412..a522f04b21db3 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -560,6 +560,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { gate_all!(postfix_match, "postfix match is experimental"); gate_all!(mut_ref, "mutable by-reference bindings are experimental"); gate_all!(precise_capturing, "precise captures on `impl Trait` are experimental"); + gate_all!(global_registration, "global registration is experimental"); if !visitor.features.never_patterns { if let Some(spans) = spans.get(&sym::never_patterns) { diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs index c08bf28773362..5113c5adc8f7a 100644 --- a/compiler/rustc_attr/src/builtin.rs +++ b/compiler/rustc_attr/src/builtin.rs @@ -528,15 +528,10 @@ pub fn cfg_matches( try_gate_cfg(cfg.name, cfg.span, sess, features); match sess.psess.check_config.expecteds.get(&cfg.name) { Some(ExpectedValues::Some(values)) if !values.contains(&cfg.value) => { - sess.psess.buffer_lint_with_diagnostic( + sess.psess.buffer_lint( UNEXPECTED_CFGS, cfg.span, lint_node_id, - if let Some(value) = cfg.value { - format!("unexpected `cfg` condition value: `{value}`") - } else { - format!("unexpected `cfg` condition value: (none)") - }, BuiltinLintDiag::UnexpectedCfgValue( (cfg.name, cfg.name_span), cfg.value.map(|v| (v, cfg.value_span.unwrap())), @@ -544,11 +539,10 @@ pub fn cfg_matches( ); } None if sess.psess.check_config.exhaustive_names => { - sess.psess.buffer_lint_with_diagnostic( + sess.psess.buffer_lint( UNEXPECTED_CFGS, cfg.span, lint_node_id, - format!("unexpected `cfg` condition name: `{}`", cfg.name), BuiltinLintDiag::UnexpectedCfgName( (cfg.name, cfg.name_span), cfg.value.map(|v| (v, cfg.value_span.unwrap())), diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 15050c87b39e6..2f1b2ce9c4c7a 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -652,7 +652,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } // FIXME: We make sure that this is a normal top-level binding, - // but we could suggest `todo!()` for all uninitialized bindings in the pattern pattern + // but we could suggest `todo!()` for all uninitialized bindings in the pattern if let hir::StmtKind::Let(hir::LetStmt { span, ty, init: None, pat, .. }) = &ex.kind && let hir::PatKind::Binding(..) = pat.kind @@ -3343,6 +3343,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } else if string.starts_with("gen") { // `gen` is 3 chars long Some(3) + } else if string.starts_with("static") { + // `static` is 6 chars long + // This is used for `!Unpin` coroutines + Some(6) } else { None }; diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs index 35017b9e6b592..79b485085852b 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs @@ -992,7 +992,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { } } - if look_at_return && hir.get_return_block(closure_id).is_some() { + if look_at_return && hir.get_fn_id_for_return_block(closure_id).is_some() { // ...otherwise we are probably in the tail expression of the function, point at the // return type. match self.infcx.tcx.hir_node_by_def_id(hir.get_parent_item(fn_call_id).def_id) { diff --git a/compiler/rustc_borrowck/src/facts.rs b/compiler/rustc_borrowck/src/facts.rs index e7faec7bbac1e..51ea59e2092a5 100644 --- a/compiler/rustc_borrowck/src/facts.rs +++ b/compiler/rustc_borrowck/src/facts.rs @@ -15,8 +15,31 @@ use std::path::Path; #[derive(Copy, Clone, Debug)] pub struct RustcFacts; +rustc_index::newtype_index! { + /// A (kinda) newtype of `RegionVid` so we can implement `Atom` on it. + #[orderable] + #[debug_format = "'?{}"] + pub struct PoloniusRegionVid {} +} + +impl polonius_engine::Atom for PoloniusRegionVid { + fn index(self) -> usize { + self.as_usize() + } +} +impl From for PoloniusRegionVid { + fn from(value: RegionVid) -> Self { + Self::from_usize(value.as_usize()) + } +} +impl From for RegionVid { + fn from(value: PoloniusRegionVid) -> Self { + Self::from_usize(value.as_usize()) + } +} + impl polonius_engine::FactTypes for RustcFacts { - type Origin = RegionVid; + type Origin = PoloniusRegionVid; type Loan = BorrowIndex; type Point = LocationIndex; type Variable = Local; @@ -119,7 +142,7 @@ trait FactRow { ) -> Result<(), Box>; } -impl FactRow for RegionVid { +impl FactRow for PoloniusRegionVid { fn write( &self, out: &mut dyn Write, diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index abe57e26af461..1d5801467da89 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -1312,8 +1312,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { ); } - Rvalue::BinaryOp(_bin_op, box (operand1, operand2)) - | Rvalue::CheckedBinaryOp(_bin_op, box (operand1, operand2)) => { + Rvalue::BinaryOp(_bin_op, box (operand1, operand2)) => { self.consume_operand(location, (operand1, span), flow_state); self.consume_operand(location, (operand2, span), flow_state); } diff --git a/compiler/rustc_borrowck/src/polonius/loan_invalidations.rs b/compiler/rustc_borrowck/src/polonius/loan_invalidations.rs index a1e59977ede5a..6979910a02d73 100644 --- a/compiler/rustc_borrowck/src/polonius/loan_invalidations.rs +++ b/compiler/rustc_borrowck/src/polonius/loan_invalidations.rs @@ -302,8 +302,7 @@ impl<'cx, 'tcx> LoanInvalidationsGenerator<'cx, 'tcx> { ); } - Rvalue::BinaryOp(_bin_op, box (operand1, operand2)) - | Rvalue::CheckedBinaryOp(_bin_op, box (operand1, operand2)) => { + Rvalue::BinaryOp(_bin_op, box (operand1, operand2)) => { self.consume_operand(location, operand1); self.consume_operand(location, operand2); } diff --git a/compiler/rustc_borrowck/src/polonius/mod.rs b/compiler/rustc_borrowck/src/polonius/mod.rs index 40126d50d57e6..9984f76e6d446 100644 --- a/compiler/rustc_borrowck/src/polonius/mod.rs +++ b/compiler/rustc_borrowck/src/polonius/mod.rs @@ -8,7 +8,7 @@ use rustc_middle::ty::TyCtxt; use rustc_mir_dataflow::move_paths::{InitKind, InitLocation, MoveData}; use crate::borrow_set::BorrowSet; -use crate::facts::AllFacts; +use crate::facts::{AllFacts, PoloniusRegionVid}; use crate::location::LocationTable; use crate::type_check::free_region_relations::UniversalRegionRelations; use crate::universal_regions::UniversalRegions; @@ -137,7 +137,9 @@ fn emit_universal_region_facts( // the `borrow_set`, their `BorrowIndex` are synthesized as the universal region index // added to the existing number of loans, as if they succeeded them in the set. // - all_facts.universal_region.extend(universal_regions.universal_regions()); + all_facts + .universal_region + .extend(universal_regions.universal_regions().map(PoloniusRegionVid::from)); let borrow_count = borrow_set.len(); debug!( "emit_universal_region_facts: polonius placeholders, num_universals={}, borrow_count={}", @@ -148,7 +150,7 @@ fn emit_universal_region_facts( for universal_region in universal_regions.universal_regions() { let universal_region_idx = universal_region.index(); let placeholder_loan_idx = borrow_count + universal_region_idx; - all_facts.placeholder.push((universal_region, placeholder_loan_idx.into())); + all_facts.placeholder.push((universal_region.into(), placeholder_loan_idx.into())); } // 2: the universal region relations `outlives` constraints are emitted as @@ -160,7 +162,7 @@ fn emit_universal_region_facts( fr1={:?}, fr2={:?}", fr1, fr2 ); - all_facts.known_placeholder_subset.push((fr1, fr2)); + all_facts.known_placeholder_subset.push((fr1.into(), fr2.into())); } } } diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index 167ca7ba04570..b57cf9066cf33 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -1506,7 +1506,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { subset_errors.sort(); subset_errors.dedup(); - for (longer_fr, shorter_fr) in subset_errors.into_iter() { + for &(longer_fr, shorter_fr) in subset_errors.into_iter() { debug!( "check_polonius_subset_errors: subset_error longer_fr={:?},\ shorter_fr={:?}", @@ -1514,14 +1514,14 @@ impl<'tcx> RegionInferenceContext<'tcx> { ); let propagated = self.try_propagate_universal_region_error( - *longer_fr, - *shorter_fr, + longer_fr.into(), + shorter_fr.into(), &mut propagated_outlives_requirements, ); if propagated == RegionRelationCheckResult::Error { errors_buffer.push(RegionErrorKind::RegionError { - longer_fr: *longer_fr, - shorter_fr: *shorter_fr, + longer_fr: longer_fr.into(), + shorter_fr: shorter_fr.into(), fr_origin: NllRegionVariableOrigin::FreeRegion, is_reported: true, }); diff --git a/compiler/rustc_borrowck/src/type_check/liveness/polonius.rs b/compiler/rustc_borrowck/src/type_check/liveness/polonius.rs index 7f5302270439c..ccfa9f12ef45a 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/polonius.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/polonius.rs @@ -117,7 +117,7 @@ pub(super) fn populate_access_facts<'a, 'tcx>( let universal_regions = &typeck.borrowck_context.universal_regions; typeck.infcx.tcx.for_each_free_region(&local_decl.ty, |region| { let region_vid = universal_regions.to_region_vid(region); - facts.use_of_var_derefs_origin.push((local, region_vid)); + facts.use_of_var_derefs_origin.push((local, region_vid.into())); }); } } @@ -136,7 +136,7 @@ pub(super) fn add_drop_of_var_derefs_origin<'tcx>( let universal_regions = &typeck.borrowck_context.universal_regions; typeck.infcx.tcx.for_each_free_region(kind, |drop_live_region| { let region_vid = universal_regions.to_region_vid(drop_live_region); - facts.drop_of_var_derefs_origin.push((local, region_vid)); + facts.drop_of_var_derefs_origin.push((local, region_vid.into())); }); } } diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 6cf9ac45aa3ef..4e46a0c62c7c7 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -260,16 +260,14 @@ fn translate_outlives_facts(typeck: &mut TypeChecker<'_, '_>) { |constraint: &OutlivesConstraint<'_>| { if let Some(from_location) = constraint.locations.from_location() { Either::Left(iter::once(( - constraint.sup, - constraint.sub, + constraint.sup.into(), + constraint.sub.into(), location_table.mid_index(from_location), ))) } else { - Either::Right( - location_table - .all_points() - .map(move |location| (constraint.sup, constraint.sub, location)), - ) + Either::Right(location_table.all_points().map(move |location| { + (constraint.sup.into(), constraint.sub.into(), location) + })) } }, )); @@ -2417,8 +2415,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { self.check_operand(op, location); } - Rvalue::BinaryOp(_, box (left, right)) - | Rvalue::CheckedBinaryOp(_, box (left, right)) => { + Rvalue::BinaryOp(_, box (left, right)) => { self.check_operand(left, location); self.check_operand(right, location); } @@ -2445,7 +2442,6 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { | Rvalue::Cast(..) | Rvalue::ShallowInitBox(..) | Rvalue::BinaryOp(..) - | Rvalue::CheckedBinaryOp(..) | Rvalue::NullaryOp(..) | Rvalue::CopyForDeref(..) | Rvalue::UnaryOp(..) @@ -2547,7 +2543,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { if let Some(borrow_index) = borrow_set.get_index_of(&location) { let region_vid = borrow_region.as_var(); all_facts.loan_issued_at.push(( - region_vid, + region_vid.into(), borrow_index, location_table.mid_index(location), )); diff --git a/compiler/rustc_builtin_macros/messages.ftl b/compiler/rustc_builtin_macros/messages.ftl index 0f15899031938..a3d6a1c736072 100644 --- a/compiler/rustc_builtin_macros/messages.ftl +++ b/compiler/rustc_builtin_macros/messages.ftl @@ -247,5 +247,3 @@ builtin_macros_unexpected_lit = expected path to a trait, found literal .label = not a trait .str_lit = try using `#[derive({$sym})]` .other = for example, write `#[derive(Debug)]` for `Debug` - -builtin_macros_unnameable_test_items = cannot test inner items diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs index 49b1b8cf99268..1a7961bf70c11 100644 --- a/compiler/rustc_builtin_macros/src/asm.rs +++ b/compiler/rustc_builtin_macros/src/asm.rs @@ -1,6 +1,7 @@ use crate::errors; use crate::util::expr_to_spanned_string; use ast::token::IdentIsRaw; +use lint::BuiltinLintDiag; use rustc_ast as ast; use rustc_ast::ptr::P; use rustc_ast::token::{self, Delimiter}; @@ -513,7 +514,7 @@ fn expand_preparsed_asm( lint::builtin::BAD_ASM_STYLE, find_span(".intel_syntax"), ecx.current_expansion.lint_node_id, - "avoid using `.intel_syntax`, Intel syntax is the default", + BuiltinLintDiag::AvoidUsingIntelSyntax, ); } if template_str.contains(".att_syntax") { @@ -521,7 +522,7 @@ fn expand_preparsed_asm( lint::builtin::BAD_ASM_STYLE, find_span(".att_syntax"), ecx.current_expansion.lint_node_id, - "avoid using `.att_syntax`, prefer using `options(att_syntax)` instead", + BuiltinLintDiag::AvoidUsingAttSyntax, ); } } diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index 46949f731aaab..217fa5ff9f1e5 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -1621,14 +1621,13 @@ impl<'a> TraitDef<'a> { }; if let Some(ty) = exception { - cx.sess.psess.buffer_lint_with_diagnostic( + cx.sess.psess.buffer_lint( BYTE_SLICE_IN_PACKED_STRUCT_WITH_DERIVE, sp, ast::CRATE_NODE_ID, - format!( - "{ty} slice in a packed struct that derives a built-in trait" - ), - rustc_lint_defs::BuiltinLintDiag::ByteSliceInPackedStructWithDerive, + rustc_lint_defs::BuiltinLintDiag::ByteSliceInPackedStructWithDerive { + ty: ty.to_string(), + }, ); } else { // Wrap the expression in `{...}`, causing a copy. diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index a5fc74f1d6669..5cb0407bd59e6 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -556,7 +556,6 @@ fn make_format_args( let arg_name = args.explicit_args()[index].kind.ident().unwrap(); ecx.buffered_early_lint.push(BufferedEarlyLint { span: arg_name.span.into(), - msg: format!("named argument `{}` is not used by name", arg_name.name).into(), node_id: rustc_ast::CRATE_NODE_ID, lint_id: LintId::of(NAMED_ARGUMENTS_USED_POSITIONALLY), diagnostic: BuiltinLintDiag::NamedArgumentUsedPositionally { diff --git a/compiler/rustc_builtin_macros/src/source_util.rs b/compiler/rustc_builtin_macros/src/source_util.rs index 47b2ee975ca89..29e991525a9be 100644 --- a/compiler/rustc_builtin_macros/src/source_util.rs +++ b/compiler/rustc_builtin_macros/src/source_util.rs @@ -11,6 +11,7 @@ use rustc_expand::base::{ resolve_path, DummyResult, ExpandResult, ExtCtxt, MacEager, MacResult, MacroExpanderResult, }; use rustc_expand::module::DirOwnership; +use rustc_lint_defs::BuiltinLintDiag; use rustc_parse::new_parser_from_file; use rustc_parse::parser::{ForceCollect, Parser}; use rustc_session::lint::builtin::INCOMPLETE_INCLUDE; @@ -147,7 +148,7 @@ pub(crate) fn expand_include<'cx>( INCOMPLETE_INCLUDE, self.p.token.span, self.node_id, - "include macro expected single expression in source", + BuiltinLintDiag::IncompleteInclude, ); } Some(expr) diff --git a/compiler/rustc_builtin_macros/src/test_harness.rs b/compiler/rustc_builtin_macros/src/test_harness.rs index 8cf431482ff73..38ac2f15fe757 100644 --- a/compiler/rustc_builtin_macros/src/test_harness.rs +++ b/compiler/rustc_builtin_macros/src/test_harness.rs @@ -9,6 +9,7 @@ use rustc_ast::{attr, ModKind}; use rustc_expand::base::{ExtCtxt, ResolverExpand}; use rustc_expand::expand::{AstFragment, ExpansionConfig}; use rustc_feature::Features; +use rustc_lint_defs::BuiltinLintDiag; use rustc_session::lint::builtin::UNNAMEABLE_TEST_ITEMS; use rustc_session::Session; use rustc_span::hygiene::{AstPass, SyntaxContext, Transparency}; @@ -163,7 +164,7 @@ impl<'a> Visitor<'a> for InnerItemLinter<'_> { UNNAMEABLE_TEST_ITEMS, attr.span, i.id, - crate::fluent_generated::builtin_macros_unnameable_test_items, + BuiltinLintDiag::UnnameableTestItems, ); } } diff --git a/compiler/rustc_builtin_macros/src/util.rs b/compiler/rustc_builtin_macros/src/util.rs index 8dc7bc14ec3e6..652e34268ea92 100644 --- a/compiler/rustc_builtin_macros/src/util.rs +++ b/compiler/rustc_builtin_macros/src/util.rs @@ -5,7 +5,7 @@ use rustc_errors::{Applicability, Diag, ErrorGuaranteed}; use rustc_expand::base::{Annotatable, ExpandResult, ExtCtxt}; use rustc_expand::expand::AstFragment; use rustc_feature::AttributeTemplate; -use rustc_lint_defs::builtin::DUPLICATE_MACRO_ATTRIBUTES; +use rustc_lint_defs::{builtin::DUPLICATE_MACRO_ATTRIBUTES, BuiltinLintDiag}; use rustc_parse::{parser, validate_attr}; use rustc_session::errors::report_lit_error; use rustc_span::{BytePos, Span, Symbol}; @@ -46,7 +46,7 @@ pub(crate) fn warn_on_duplicate_attribute(ecx: &ExtCtxt<'_>, item: &Annotatable, DUPLICATE_MACRO_ATTRIBUTES, attr.span, ecx.current_expansion.lint_node_id, - "duplicated attribute", + BuiltinLintDiag::DuplicateMacroAttribute, ); } } diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index 5846689643fdd..8d778f736d671 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -609,14 +609,11 @@ fn codegen_stmt<'tcx>( let lhs = codegen_operand(fx, &lhs_rhs.0); let rhs = codegen_operand(fx, &lhs_rhs.1); - let res = crate::num::codegen_binop(fx, bin_op, lhs, rhs); - lval.write_cvalue(fx, res); - } - Rvalue::CheckedBinaryOp(bin_op, ref lhs_rhs) => { - let lhs = codegen_operand(fx, &lhs_rhs.0); - let rhs = codegen_operand(fx, &lhs_rhs.1); - - let res = crate::num::codegen_checked_int_binop(fx, bin_op, lhs, rhs); + let res = if let Some(bin_op) = bin_op.overflowing_to_wrapping() { + crate::num::codegen_checked_int_binop(fx, bin_op, lhs, rhs) + } else { + crate::num::codegen_binop(fx, bin_op, lhs, rhs) + }; lval.write_cvalue(fx, res); } Rvalue::UnaryOp(un_op, ref operand) => { diff --git a/compiler/rustc_codegen_cranelift/src/codegen_i128.rs b/compiler/rustc_codegen_cranelift/src/codegen_i128.rs index 4a5ef352151f3..e16b77648d12f 100644 --- a/compiler/rustc_codegen_cranelift/src/codegen_i128.rs +++ b/compiler/rustc_codegen_cranelift/src/codegen_i128.rs @@ -70,6 +70,7 @@ pub(crate) fn maybe_codegen<'tcx>( } BinOp::Lt | BinOp::Le | BinOp::Eq | BinOp::Ge | BinOp::Gt | BinOp::Ne | BinOp::Cmp => None, BinOp::Shl | BinOp::ShlUnchecked | BinOp::Shr | BinOp::ShrUnchecked => None, + BinOp::AddWithOverflow | BinOp::SubWithOverflow | BinOp::MulWithOverflow => unreachable!(), } } @@ -132,6 +133,7 @@ pub(crate) fn maybe_codegen_checked<'tcx>( Some(out_place.to_cvalue(fx)) } BinOp::AddUnchecked | BinOp::SubUnchecked | BinOp::MulUnchecked => unreachable!(), + BinOp::AddWithOverflow | BinOp::SubWithOverflow | BinOp::MulWithOverflow => unreachable!(), BinOp::Offset => unreachable!("offset should only be used on pointers, not 128bit ints"), BinOp::Div | BinOp::Rem => unreachable!(), BinOp::Cmp => unreachable!(), diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs index 452b5988dab4c..b17f191ce267e 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs @@ -348,6 +348,7 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( | sym::simd_bswap | sym::simd_bitreverse | sym::simd_ctlz + | sym::simd_ctpop | sym::simd_cttz => { intrinsic_args!(fx, args => (a); intrinsic); @@ -367,6 +368,7 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( (ty::Uint(_) | ty::Int(_), sym::simd_bswap) => fx.bcx.ins().bswap(lane), (ty::Uint(_) | ty::Int(_), sym::simd_bitreverse) => fx.bcx.ins().bitrev(lane), (ty::Uint(_) | ty::Int(_), sym::simd_ctlz) => fx.bcx.ins().clz(lane), + (ty::Uint(_) | ty::Int(_), sym::simd_ctpop) => fx.bcx.ins().popcnt(lane), (ty::Uint(_) | ty::Int(_), sym::simd_cttz) => fx.bcx.ins().ctz(lane), _ => unreachable!(), diff --git a/compiler/rustc_codegen_cranelift/src/num.rs b/compiler/rustc_codegen_cranelift/src/num.rs index 4d96a26ea4fa8..fb18f45d7dcad 100644 --- a/compiler/rustc_codegen_cranelift/src/num.rs +++ b/compiler/rustc_codegen_cranelift/src/num.rs @@ -179,6 +179,9 @@ pub(crate) fn codegen_int_binop<'tcx>( } } BinOp::Offset => unreachable!("Offset is not an integer operation"), + BinOp::AddWithOverflow | BinOp::SubWithOverflow | BinOp::MulWithOverflow => { + unreachable!("Overflow binops handled by `codegen_checked_int_binop`") + } // Compare binops handles by `codegen_binop`. BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Le | BinOp::Gt | BinOp::Ge | BinOp::Cmp => { unreachable!("{:?}({:?}, {:?})", bin_op, in_lhs.layout().ty, in_rhs.layout().ty); diff --git a/compiler/rustc_codegen_llvm/src/back/archive.rs b/compiler/rustc_codegen_llvm/src/back/archive.rs index d4a3e39cef728..c304c0cbd3bd5 100644 --- a/compiler/rustc_codegen_llvm/src/back/archive.rs +++ b/compiler/rustc_codegen_llvm/src/back/archive.rs @@ -200,21 +200,20 @@ impl ArchiveBuilderBuilder for LlvmArchiveBuilderBuilder { _ => panic!("unsupported arch {}", sess.target.arch), }; let mut dlltool_cmd = std::process::Command::new(&dlltool); - dlltool_cmd.args([ - "-d", - def_file_path.to_str().unwrap(), - "-D", - lib_name, - "-l", - output_path.to_str().unwrap(), - "-m", - dlltool_target_arch, - "-f", - dlltool_target_bitness, - "--no-leading-underscore", - "--temp-prefix", - temp_prefix.to_str().unwrap(), - ]); + dlltool_cmd + .arg("-d") + .arg(def_file_path) + .arg("-D") + .arg(lib_name) + .arg("-l") + .arg(&output_path) + .arg("-m") + .arg(dlltool_target_arch) + .arg("-f") + .arg(dlltool_target_bitness) + .arg("--no-leading-underscore") + .arg("--temp-prefix") + .arg(temp_prefix); match dlltool_cmd.output() { Err(e) => { diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index c0a1208a8c7d0..80e863af893e0 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -2336,7 +2336,10 @@ fn generic_simd_intrinsic<'ll, 'tcx>( } // Unary integer intrinsics - if matches!(name, sym::simd_bswap | sym::simd_bitreverse | sym::simd_ctlz | sym::simd_cttz) { + if matches!( + name, + sym::simd_bswap | sym::simd_bitreverse | sym::simd_ctlz | sym::simd_ctpop | sym::simd_cttz + ) { let vec_ty = bx.cx.type_vector( match *in_elem.kind() { ty::Int(i) => bx.cx.type_int_from_ty(i), @@ -2354,31 +2357,38 @@ fn generic_simd_intrinsic<'ll, 'tcx>( sym::simd_bswap => "bswap", sym::simd_bitreverse => "bitreverse", sym::simd_ctlz => "ctlz", + sym::simd_ctpop => "ctpop", sym::simd_cttz => "cttz", _ => unreachable!(), }; let int_size = in_elem.int_size_and_signed(bx.tcx()).0.bits(); let llvm_intrinsic = &format!("llvm.{}.v{}i{}", intrinsic_name, in_len, int_size,); - return if name == sym::simd_bswap && int_size == 8 { + return match name { // byte swap is no-op for i8/u8 - Ok(args[0].immediate()) - } else if matches!(name, sym::simd_ctlz | sym::simd_cttz) { - let fn_ty = bx.type_func(&[vec_ty, bx.type_i1()], vec_ty); - let f = bx.declare_cfn(llvm_intrinsic, llvm::UnnamedAddr::No, fn_ty); - Ok(bx.call( - fn_ty, - None, - None, - f, - &[args[0].immediate(), bx.const_int(bx.type_i1(), 0)], - None, - None, - )) - } else { - let fn_ty = bx.type_func(&[vec_ty], vec_ty); - let f = bx.declare_cfn(llvm_intrinsic, llvm::UnnamedAddr::No, fn_ty); - Ok(bx.call(fn_ty, None, None, f, &[args[0].immediate()], None, None)) + sym::simd_bswap if int_size == 8 => Ok(args[0].immediate()), + sym::simd_ctlz | sym::simd_cttz => { + // for the (int, i1 immediate) pair, the second arg adds `(0, true) => poison` + let fn_ty = bx.type_func(&[vec_ty, bx.type_i1()], vec_ty); + let dont_poison_on_zero = bx.const_int(bx.type_i1(), 0); + let f = bx.declare_cfn(llvm_intrinsic, llvm::UnnamedAddr::No, fn_ty); + Ok(bx.call( + fn_ty, + None, + None, + f, + &[args[0].immediate(), dont_poison_on_zero], + None, + None, + )) + } + sym::simd_bswap | sym::simd_bitreverse | sym::simd_ctpop => { + // simple unary argument cases + let fn_ty = bx.type_func(&[vec_ty], vec_ty); + let f = bx.declare_cfn(llvm_intrinsic, llvm::UnnamedAddr::No, fn_ty); + Ok(bx.call(fn_ty, None, None, f, &[args[0].immediate()], None, None)) + } + _ => unreachable!(), }; } diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 6939674ce9dd7..37b8f81ad9465 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -1230,7 +1230,10 @@ fn add_sanitizer_libraries( if sanitizer.contains(SanitizerSet::DATAFLOW) { link_sanitizer_runtime(sess, flavor, linker, "dfsan"); } - if sanitizer.contains(SanitizerSet::LEAK) { + if sanitizer.contains(SanitizerSet::LEAK) + && !sanitizer.contains(SanitizerSet::ADDRESS) + && !sanitizer.contains(SanitizerSet::HWADDRESS) + { link_sanitizer_runtime(sess, flavor, linker, "lsan"); } if sanitizer.contains(SanitizerSet::MEMORY) { diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index ff176a796759a..00b28cbd0c68a 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -576,6 +576,22 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } } + mir::Rvalue::BinaryOp(op_with_overflow, box (ref lhs, ref rhs)) + if let Some(op) = op_with_overflow.overflowing_to_wrapping() => + { + let lhs = self.codegen_operand(bx, lhs); + let rhs = self.codegen_operand(bx, rhs); + let result = self.codegen_scalar_checked_binop( + bx, + op, + lhs.immediate(), + rhs.immediate(), + lhs.layout.ty, + ); + let val_ty = op.ty(bx.tcx(), lhs.layout.ty, rhs.layout.ty); + let operand_ty = Ty::new_tup(bx.tcx(), &[val_ty, bx.tcx().types.bool]); + OperandRef { val: result, layout: bx.cx().layout_of(operand_ty) } + } mir::Rvalue::BinaryOp(op, box (ref lhs, ref rhs)) => { let lhs = self.codegen_operand(bx, lhs); let rhs = self.codegen_operand(bx, rhs); @@ -604,20 +620,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { layout: bx.cx().layout_of(op.ty(bx.tcx(), lhs.layout.ty, rhs.layout.ty)), } } - mir::Rvalue::CheckedBinaryOp(op, box (ref lhs, ref rhs)) => { - let lhs = self.codegen_operand(bx, lhs); - let rhs = self.codegen_operand(bx, rhs); - let result = self.codegen_scalar_checked_binop( - bx, - op, - lhs.immediate(), - rhs.immediate(), - lhs.layout.ty, - ); - let val_ty = op.ty(bx.tcx(), lhs.layout.ty, rhs.layout.ty); - let operand_ty = Ty::new_tup(bx.tcx(), &[val_ty, bx.tcx().types.bool]); - OperandRef { val: result, layout: bx.cx().layout_of(operand_ty) } - } mir::Rvalue::UnaryOp(op, ref operand) => { let operand = self.codegen_operand(bx, operand); @@ -928,6 +930,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { bx.select(is_lt, bx.cx().const_i8(Ordering::Less as i8), ge) } } + mir::BinOp::AddWithOverflow + | mir::BinOp::SubWithOverflow + | mir::BinOp::MulWithOverflow => { + bug!("{op:?} needs to return a pair, so call codegen_scalar_checked_binop instead") + } } } @@ -1040,7 +1047,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { mir::Rvalue::Cast(..) | // (*) mir::Rvalue::ShallowInitBox(..) | // (*) mir::Rvalue::BinaryOp(..) | - mir::Rvalue::CheckedBinaryOp(..) | mir::Rvalue::UnaryOp(..) | mir::Rvalue::Discriminant(..) | mir::Rvalue::NullaryOp(..) | diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs index ee415c380de17..cb72d55a9ba18 100644 --- a/compiler/rustc_const_eval/src/interpret/step.rs +++ b/compiler/rustc_const_eval/src/interpret/step.rs @@ -167,15 +167,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let left = self.read_immediate(&self.eval_operand(left, layout)?)?; let layout = util::binop_right_homogeneous(bin_op).then_some(left.layout); let right = self.read_immediate(&self.eval_operand(right, layout)?)?; - self.binop_ignore_overflow(bin_op, &left, &right, &dest)?; - } - - CheckedBinaryOp(bin_op, box (ref left, ref right)) => { - // Due to the extra boolean in the result, we can never reuse the `dest.layout`. - let left = self.read_immediate(&self.eval_operand(left, None)?)?; - let layout = util::binop_right_homogeneous(bin_op).then_some(left.layout); - let right = self.read_immediate(&self.eval_operand(right, layout)?)?; - self.binop_with_overflow(bin_op, &left, &right, &dest)?; + if let Some(bin_op) = bin_op.overflowing_to_wrapping() { + self.binop_with_overflow(bin_op, &left, &right, &dest)?; + } else { + self.binop_ignore_overflow(bin_op, &left, &right, &dest)?; + } } UnaryOp(un_op, ref operand) => { diff --git a/compiler/rustc_const_eval/src/lib.rs b/compiler/rustc_const_eval/src/lib.rs index a525b838afa24..7b293e2b5337a 100644 --- a/compiler/rustc_const_eval/src/lib.rs +++ b/compiler/rustc_const_eval/src/lib.rs @@ -1,9 +1,3 @@ -/*! - -Rust MIR: a lowered representation of Rust. - -*/ - #![allow(internal_features)] #![allow(rustc::diagnostic_outside_of_impl)] #![feature(rustdoc_internals)] diff --git a/compiler/rustc_const_eval/src/transform/check_consts/check.rs b/compiler/rustc_const_eval/src/transform/check_consts/check.rs index 5edf5bb39dd4c..c8c54143f6185 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs @@ -580,7 +580,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { } } - Rvalue::BinaryOp(op, box (lhs, rhs)) | Rvalue::CheckedBinaryOp(op, box (lhs, rhs)) => { + Rvalue::BinaryOp(op, box (lhs, rhs)) => { let lhs_ty = lhs.ty(self.body, self.tcx); let rhs_ty = rhs.ty(self.body, self.tcx); diff --git a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs index eae0e2f27dbdb..7e8a208659b97 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs @@ -261,7 +261,7 @@ where | Rvalue::Cast(_, operand, _) | Rvalue::ShallowInitBox(operand, _) => in_operand::(cx, in_local, operand), - Rvalue::BinaryOp(_, box (lhs, rhs)) | Rvalue::CheckedBinaryOp(_, box (lhs, rhs)) => { + Rvalue::BinaryOp(_, box (lhs, rhs)) => { in_operand::(cx, in_local, lhs) || in_operand::(cx, in_local, rhs) } diff --git a/compiler/rustc_const_eval/src/transform/check_consts/resolver.rs b/compiler/rustc_const_eval/src/transform/check_consts/resolver.rs index 5ae3ffaaec2f2..011341472b433 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/resolver.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/resolver.rs @@ -200,7 +200,6 @@ where | mir::Rvalue::Repeat(..) | mir::Rvalue::Len(..) | mir::Rvalue::BinaryOp(..) - | mir::Rvalue::CheckedBinaryOp(..) | mir::Rvalue::NullaryOp(..) | mir::Rvalue::UnaryOp(..) | mir::Rvalue::Discriminant(..) diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs index fdc7f6a69cba1..3a2b2c5f3002d 100644 --- a/compiler/rustc_const_eval/src/transform/validate.rs +++ b/compiler/rustc_const_eval/src/transform/validate.rs @@ -1037,8 +1037,8 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { ) } } - AddUnchecked | SubUnchecked | MulUnchecked | Shl | ShlUnchecked | Shr - | ShrUnchecked => { + AddUnchecked | AddWithOverflow | SubUnchecked | SubWithOverflow + | MulUnchecked | MulWithOverflow | Shl | ShlUnchecked | Shr | ShrUnchecked => { for x in [a, b] { check_kinds!( x, @@ -1067,31 +1067,6 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } } } - Rvalue::CheckedBinaryOp(op, vals) => { - use BinOp::*; - let a = vals.0.ty(&self.body.local_decls, self.tcx); - let b = vals.1.ty(&self.body.local_decls, self.tcx); - match op { - Add | Sub | Mul => { - for x in [a, b] { - check_kinds!( - x, - "Cannot perform checked arithmetic on type {:?}", - ty::Uint(..) | ty::Int(..) - ) - } - if a != b { - self.fail( - location, - format!( - "Cannot perform checked arithmetic on unequal types {a:?} and {b:?}" - ), - ); - } - } - _ => self.fail(location, format!("There is no checked version of {op:?}")), - } - } Rvalue::UnaryOp(op, operand) => { let a = operand.ty(&self.body.local_decls, self.tcx); match op { diff --git a/compiler/rustc_const_eval/src/util/mod.rs b/compiler/rustc_const_eval/src/util/mod.rs index 0c3b59a0e78ec..66a1addfb525e 100644 --- a/compiler/rustc_const_eval/src/util/mod.rs +++ b/compiler/rustc_const_eval/src/util/mod.rs @@ -19,7 +19,9 @@ pub fn binop_left_homogeneous(op: mir::BinOp) -> bool { match op { Add | AddUnchecked | Sub | SubUnchecked | Mul | MulUnchecked | Div | Rem | BitXor | BitAnd | BitOr | Offset | Shl | ShlUnchecked | Shr | ShrUnchecked => true, - Eq | Ne | Lt | Le | Gt | Ge | Cmp => false, + AddWithOverflow | SubWithOverflow | MulWithOverflow | Eq | Ne | Lt | Le | Gt | Ge | Cmp => { + false + } } } @@ -29,8 +31,9 @@ pub fn binop_left_homogeneous(op: mir::BinOp) -> bool { pub fn binop_right_homogeneous(op: mir::BinOp) -> bool { use rustc_middle::mir::BinOp::*; match op { - Add | AddUnchecked | Sub | SubUnchecked | Mul | MulUnchecked | Div | Rem | BitXor - | BitAnd | BitOr | Eq | Ne | Lt | Le | Gt | Ge | Cmp => true, + Add | AddUnchecked | AddWithOverflow | Sub | SubUnchecked | SubWithOverflow | Mul + | MulUnchecked | MulWithOverflow | Div | Rem | BitXor | BitAnd | BitOr | Eq | Ne | Lt + | Le | Gt | Ge | Cmp => true, Offset | Shl | ShlUnchecked | Shr | ShrUnchecked => false, } } diff --git a/compiler/rustc_errors/src/diagnostic_impls.rs b/compiler/rustc_errors/src/diagnostic_impls.rs index 4bf7dccab9238..662c8a7b8be07 100644 --- a/compiler/rustc_errors/src/diagnostic_impls.rs +++ b/compiler/rustc_errors/src/diagnostic_impls.rs @@ -282,6 +282,12 @@ impl IntoDiagArg for ClosureKind { } } +impl IntoDiagArg for hir::def::Namespace { + fn into_diag_arg(self) -> DiagArgValue { + DiagArgValue::Str(Cow::Borrowed(self.descr())) + } +} + #[derive(Clone)] pub struct DiagSymbolList(Vec); diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index 12868a666056d..91af8758e5167 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -1364,18 +1364,15 @@ fn pretty_printing_compatibility_hack(item: &Item, sess: &Session) -> bool { }; if crate_matches { - // FIXME: make this translatable - #[allow(rustc::untranslatable_diagnostic)] - sess.psess.buffer_lint_with_diagnostic( - PROC_MACRO_BACK_COMPAT, - item.ident.span, - ast::CRATE_NODE_ID, - "using an old version of `rental`", - BuiltinLintDiag::ProcMacroBackCompat( - "older versions of the `rental` crate will stop compiling in future versions of Rust; \ - please update to `rental` v0.5.6, or switch to one of the `rental` alternatives".to_string() - ) - ); + sess.psess.buffer_lint( + PROC_MACRO_BACK_COMPAT, + item.ident.span, + ast::CRATE_NODE_ID, + BuiltinLintDiag::ProcMacroBackCompat { + crate_name: "rental".to_string(), + fixed_version: "0.5.6".to_string(), + }, + ); return true; } } diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index 35f0d8abffc57..badfa6d3aa323 100644 --- a/compiler/rustc_expand/src/config.rs +++ b/compiler/rustc_expand/src/config.rs @@ -14,6 +14,7 @@ use rustc_attr as attr; use rustc_data_structures::flat_map_in_place::FlatMapInPlace; use rustc_feature::Features; use rustc_feature::{ACCEPTED_FEATURES, REMOVED_FEATURES, UNSTABLE_FEATURES}; +use rustc_lint_defs::BuiltinLintDiag; use rustc_parse::validate_attr; use rustc_session::parse::feature_err; use rustc_session::Session; @@ -248,7 +249,6 @@ impl<'a> StripUnconfigured<'a> { /// Gives a compiler warning when the `cfg_attr` contains no attributes and /// is in the original source file. Gives a compiler error if the syntax of /// the attribute is incorrect. - #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable pub(crate) fn expand_cfg_attr(&self, attr: &Attribute, recursive: bool) -> Vec { let Some((cfg_predicate, expanded_attrs)) = rustc_parse::parse_cfg_attr(attr, &self.sess.psess) @@ -262,7 +262,7 @@ impl<'a> StripUnconfigured<'a> { rustc_lint_defs::builtin::UNUSED_ATTRIBUTES, attr.span, ast::CRATE_NODE_ID, - "`#[cfg_attr]` does not expand to any attributes", + BuiltinLintDiag::CfgAttrNoAttributes, ); } @@ -283,7 +283,6 @@ impl<'a> StripUnconfigured<'a> { } } - #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable fn expand_cfg_attr_item( &self, attr: &Attribute, @@ -346,7 +345,7 @@ impl<'a> StripUnconfigured<'a> { rustc_lint_defs::builtin::DEPRECATED_CFG_ATTR_CRATE_TYPE_NAME, attr.span, ast::CRATE_NODE_ID, - "`crate_type` within an `#![cfg_attr] attribute is deprecated`", + BuiltinLintDiag::CrateTypeInCfgAttr, ); } if attr.has_name(sym::crate_name) { @@ -354,7 +353,7 @@ impl<'a> StripUnconfigured<'a> { rustc_lint_defs::builtin::DEPRECATED_CFG_ATTR_CRATE_TYPE_NAME, attr.span, ast::CRATE_NODE_ID, - "`crate_name` within an `#![cfg_attr] attribute is deprecated`", + BuiltinLintDiag::CrateNameInCfgAttr, ); } attr diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index a049ac251e1e7..d8f0f221189ad 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -1799,11 +1799,10 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { } if attr.is_doc_comment() { - self.cx.sess.psess.buffer_lint_with_diagnostic( + self.cx.sess.psess.buffer_lint( UNUSED_DOC_COMMENTS, current_span, self.cx.current_expansion.lint_node_id, - "unused doc comment", BuiltinLintDiag::UnusedDocComment(attr.span), ); } else if rustc_attr::is_builtin_attr(attr) { @@ -1811,11 +1810,10 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { // `#[cfg]` and `#[cfg_attr]` are special - they are // eagerly evaluated. if attr_name != sym::cfg && attr_name != sym::cfg_attr { - self.cx.sess.psess.buffer_lint_with_diagnostic( + self.cx.sess.psess.buffer_lint( UNUSED_ATTRIBUTES, attr.span, self.cx.current_expansion.lint_node_id, - format!("unused attribute `{attr_name}`"), BuiltinLintDiag::UnusedBuiltinAttribute { attr_name, macro_name: pprust::path_to_string(&call.path), diff --git a/compiler/rustc_expand/src/mbe/macro_check.rs b/compiler/rustc_expand/src/mbe/macro_check.rs index dce8e0c36edf7..72dbbde54b310 100644 --- a/compiler/rustc_expand/src/mbe/macro_check.rs +++ b/compiler/rustc_expand/src/mbe/macro_check.rs @@ -110,7 +110,8 @@ use crate::mbe::{KleeneToken, TokenTree}; use rustc_ast::token::{Delimiter, IdentIsRaw, Token, TokenKind}; use rustc_ast::{NodeId, DUMMY_NODE_ID}; use rustc_data_structures::fx::FxHashMap; -use rustc_errors::{DiagMessage, MultiSpan}; +use rustc_errors::MultiSpan; +use rustc_lint_defs::BuiltinLintDiag; use rustc_session::lint::builtin::{META_VARIABLE_MISUSE, MISSING_FRAGMENT_SPECIFIER}; use rustc_session::parse::ParseSess; use rustc_span::symbol::kw; @@ -252,7 +253,7 @@ fn check_binders( // 1. The meta-variable is already bound in the current LHS: This is an error. let mut span = MultiSpan::from_span(span); span.push_span_label(prev_info.span, "previous declaration"); - buffer_lint(psess, span, node_id, "duplicate matcher binding"); + buffer_lint(psess, span, node_id, BuiltinLintDiag::DuplicateMatcherBinding); } else if get_binder_info(macros, binders, name).is_none() { // 2. The meta-variable is free: This is a binder. binders.insert(name, BinderInfo { span, ops: ops.into() }); @@ -271,7 +272,7 @@ fn check_binders( MISSING_FRAGMENT_SPECIFIER, span, node_id, - "missing fragment specifier", + BuiltinLintDiag::MissingFragmentSpecifier, ); } if !macros.is_empty() { @@ -595,7 +596,7 @@ fn check_ops_is_prefix( return; } } - buffer_lint(psess, span.into(), node_id, format!("unknown macro variable `{name}`")); + buffer_lint(psess, span.into(), node_id, BuiltinLintDiag::UnknownMacroVariable(name)); } /// Returns whether `binder_ops` is a prefix of `occurrence_ops`. @@ -628,8 +629,7 @@ fn ops_is_prefix( if i >= occurrence_ops.len() { let mut span = MultiSpan::from_span(span); span.push_span_label(binder.span, "expected repetition"); - let message = format!("variable '{name}' is still repeating at this depth"); - buffer_lint(psess, span, node_id, message); + buffer_lint(psess, span, node_id, BuiltinLintDiag::MetaVariableStillRepeating(name)); return; } let occurrence = &occurrence_ops[i]; @@ -637,21 +637,15 @@ fn ops_is_prefix( let mut span = MultiSpan::from_span(span); span.push_span_label(binder.span, "expected repetition"); span.push_span_label(occurrence.span, "conflicting repetition"); - let message = "meta-variable repeats with different Kleene operator"; - buffer_lint(psess, span, node_id, message); + buffer_lint(psess, span, node_id, BuiltinLintDiag::MetaVariableWrongOperator); return; } } } -fn buffer_lint( - psess: &ParseSess, - span: MultiSpan, - node_id: NodeId, - message: impl Into, -) { +fn buffer_lint(psess: &ParseSess, span: MultiSpan, node_id: NodeId, diag: BuiltinLintDiag) { // Macros loaded from other crates have dummy node ids. if node_id != DUMMY_NODE_ID { - psess.buffer_lint(META_VARIABLE_MISUSE, span, node_id, message); + psess.buffer_lint(META_VARIABLE_MISUSE, span, node_id, diag); } } diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index 8f18055f83817..d99ecb6108520 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -79,11 +79,10 @@ impl<'a> ParserAnyMacro<'a> { // but `m!()` is allowed in expression positions (cf. issue #34706). if kind == AstFragmentKind::Expr && parser.token == token::Semi { if is_local { - parser.psess.buffer_lint_with_diagnostic( + parser.psess.buffer_lint( SEMICOLON_IN_EXPRESSIONS_FROM_MACROS, parser.token.span, lint_node_id, - "trailing semicolon in macro used in expression position", BuiltinLintDiag::TrailingMacro(is_trailing_mac, macro_ident), ); } @@ -1154,11 +1153,10 @@ fn check_matcher_core<'tt>( name, Some(NonterminalKind::PatParam { inferred: false }), )); - sess.psess.buffer_lint_with_diagnostic( + sess.psess.buffer_lint( RUST_2021_INCOMPATIBLE_OR_PATTERNS, span, ast::CRATE_NODE_ID, - "the meaning of the `pat` fragment specifier is changing in Rust 2021, which may affect this macro", BuiltinLintDiag::OrPatternsBackCompat(span, suggestion), ); } diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index dbb88e42a3eb9..0b4a871dd50c2 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -515,12 +515,6 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ EncodeCrossCrate::Yes, experimental!(deprecated_safe), ), - // RFC 2397 - gated!( - do_not_recommend, Normal, template!(Word), WarnFollowing, - EncodeCrossCrate::Yes, experimental!(do_not_recommend) - ), - // `#[cfi_encoding = ""]` gated!( cfi_encoding, Normal, template!(NameValueStr: "encoding"), ErrorPreceding, @@ -899,10 +893,11 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ "the `#[rustc_main]` attribute is used internally to specify test entry point function", ), rustc_attr!( - rustc_skip_array_during_method_dispatch, Normal, template!(Word), - WarnFollowing, EncodeCrossCrate::No, - "the `#[rustc_skip_array_during_method_dispatch]` attribute is used to exclude a trait \ - from method dispatch when the receiver is an array, for compatibility in editions < 2021." + rustc_skip_during_method_dispatch, Normal, template!(List: "array, boxed_slice"), WarnFollowing, + EncodeCrossCrate::No, + "the `#[rustc_skip_during_method_dispatch]` attribute is used to exclude a trait \ + from method dispatch when the receiver is of the following type, for compatibility in \ + editions < 2021 (array) or editions < 2024 (boxed_slice)." ), rustc_attr!( rustc_must_implement_one_of, Normal, template!(List: "function1, function2, ..."), diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index c6fc9de119dc4..dc4807bab2d3d 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -489,6 +489,8 @@ declare_features! ( (incomplete, generic_const_exprs, "1.56.0", Some(76560)), /// Allows generic parameters and where-clauses on free & associated const items. (incomplete, generic_const_items, "1.73.0", Some(113521)), + /// Allows registering static items globally, possibly across crates, to iterate over at runtime. + (unstable, global_registration, "CURRENT_RUSTC_VERSION", Some(125119)), /// Allows using `..=X` as a patterns in slices. (unstable, half_open_range_patterns_in_slices, "1.66.0", Some(67264)), /// Allows `if let` guard in match arms. diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index d829e720d9e18..50fe20346cf45 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -607,6 +607,7 @@ pub fn check_intrinsic_type( | sym::simd_bitreverse | sym::simd_ctlz | sym::simd_cttz + | sym::simd_ctpop | sym::simd_fsqrt | sym::simd_fsin | sym::simd_fcos diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index e137aab210990..e8ede804c3f37 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -432,7 +432,7 @@ fn check_gat_where_clauses(tcx: TyCtxt<'_>, trait_def_id: LocalDefId) { } let gat_generics = tcx.generics_of(gat_def_id); // FIXME(jackh726): we can also warn in the more general case - if gat_generics.own_params.is_empty() { + if gat_generics.is_own_empty() { continue; } diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index aa28b2c8e2cfb..b760b86a7bfb4 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -1117,8 +1117,24 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef { let is_marker = tcx.has_attr(def_id, sym::marker); let rustc_coinductive = tcx.has_attr(def_id, sym::rustc_coinductive); - let skip_array_during_method_dispatch = - tcx.has_attr(def_id, sym::rustc_skip_array_during_method_dispatch); + + // FIXME: We could probably do way better attribute validation here. + let mut skip_array_during_method_dispatch = false; + let mut skip_boxed_slice_during_method_dispatch = false; + for attr in tcx.get_attrs(def_id, sym::rustc_skip_during_method_dispatch) { + if let Some(lst) = attr.meta_item_list() { + for item in lst { + if let Some(ident) = item.ident() { + match ident.as_str() { + "array" => skip_array_during_method_dispatch = true, + "boxed_slice" => skip_boxed_slice_during_method_dispatch = true, + _ => (), + } + } + } + } + } + let specialization_kind = if tcx.has_attr(def_id, sym::rustc_unsafe_specialization_marker) { ty::trait_def::TraitSpecializationKind::Marker } else if tcx.has_attr(def_id, sym::rustc_specialization_trait) { @@ -1253,6 +1269,7 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef { is_marker, is_coinductive: rustc_coinductive || is_auto, skip_array_during_method_dispatch, + skip_boxed_slice_during_method_dispatch, specialization_kind, must_implement_one_of, implement_via_object, 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 f19492e00b47e..00356ece58523 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs @@ -1409,7 +1409,7 @@ fn generics_args_err_extend<'a>( // it was done based on the end of assoc segment but that sometimes // led to impossible spans and caused issues like #116473 let args_span = args.span_ext.with_lo(args.span_ext.lo() - BytePos(2)); - if tcx.generics_of(adt_def.did()).count() == 0 { + if tcx.generics_of(adt_def.did()).is_empty() { // FIXME(estebank): we could also verify that the arguments being // work for the `enum`, instead of just looking if it takes *any*. err.span_suggestion_verbose( 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 39016d15236f4..8caeb85204b75 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -412,7 +412,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // Traits always have `Self` as a generic parameter, which means they will not return early // here and so associated type bindings will be handled regardless of whether there are any // non-`Self` generic parameters. - if generics.own_params.is_empty() { + if generics.is_own_empty() { return (tcx.mk_args(parent_args), arg_count); } diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs index 654ef4baeb39e..d1e50e13894a3 100644 --- a/compiler/rustc_hir_analysis/src/lib.rs +++ b/compiler/rustc_hir_analysis/src/lib.rs @@ -181,7 +181,7 @@ pub fn check_crate(tcx: TyCtxt<'_>) { let def_kind = tcx.def_kind(item_def_id); match def_kind { DefKind::Static { .. } => tcx.ensure().eval_static_initializer(item_def_id), - DefKind::Const if tcx.generics_of(item_def_id).own_params.is_empty() => { + DefKind::Const if tcx.generics_of(item_def_id).is_empty() => { let instance = ty::Instance::new(item_def_id.into(), ty::GenericArgs::empty()); let cid = GlobalId { instance, promoted: None }; let param_env = ty::ParamEnv::reveal_all(); diff --git a/compiler/rustc_hir_analysis/src/variance/constraints.rs b/compiler/rustc_hir_analysis/src/variance/constraints.rs index 730e989edaed8..0c436e21c16d9 100644 --- a/compiler/rustc_hir_analysis/src/variance/constraints.rs +++ b/compiler/rustc_hir_analysis/src/variance/constraints.rs @@ -99,7 +99,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { debug!("build_constraints_for_item({})", tcx.def_path_str(def_id)); // Skip items with no generics - there's nothing to infer in them. - if tcx.generics_of(def_id).count() == 0 { + if tcx.generics_of(def_id).is_empty() { return; } diff --git a/compiler/rustc_hir_analysis/src/variance/mod.rs b/compiler/rustc_hir_analysis/src/variance/mod.rs index cefd8fb1ac5f7..1977451f39e0a 100644 --- a/compiler/rustc_hir_analysis/src/variance/mod.rs +++ b/compiler/rustc_hir_analysis/src/variance/mod.rs @@ -41,7 +41,7 @@ fn crate_variances(tcx: TyCtxt<'_>, (): ()) -> CrateVariancesMap<'_> { fn variances_of(tcx: TyCtxt<'_>, item_def_id: LocalDefId) -> &[ty::Variance] { // Skip items with no generics - there's nothing to infer in them. - if tcx.generics_of(item_def_id).count() == 0 { + if tcx.generics_of(item_def_id).is_empty() { return &[]; } diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 29c7e576d2bb7..488537e81becf 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -1454,7 +1454,7 @@ impl<'a> State<'a> { self.word_space(":"); } // containing cbox, will be closed by print-block at `}` - self.cbox(INDENT_UNIT); + self.cbox(0); // head-box, will be closed by print-block after `{` self.ibox(0); self.print_block(blk); diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index d81dab2222a9c..33c24433ca341 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -1591,31 +1591,28 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { err.span_label(cause.span, "return type is not `()`"); } ObligationCauseCode::BlockTailExpression(blk_id, ..) => { - let parent_id = fcx.tcx.parent_hir_id(blk_id); err = self.report_return_mismatched_types( cause, expected, found, coercion_error, fcx, - parent_id, + blk_id, expression, - Some(blk_id), ); if !fcx.tcx.features().unsized_locals { unsized_return = self.is_return_ty_definitely_unsized(fcx); } } - ObligationCauseCode::ReturnValue(id) => { + ObligationCauseCode::ReturnValue(return_expr_id) => { err = self.report_return_mismatched_types( cause, expected, found, coercion_error, fcx, - id, + return_expr_id, expression, - None, ); if !fcx.tcx.features().unsized_locals { unsized_return = self.is_return_ty_definitely_unsized(fcx); @@ -1809,13 +1806,14 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { found: Ty<'tcx>, ty_err: TypeError<'tcx>, fcx: &FnCtxt<'a, 'tcx>, - id: hir::HirId, + block_or_return_id: hir::HirId, expression: Option<&'tcx hir::Expr<'tcx>>, - blk_id: Option, ) -> Diag<'a> { let mut err = fcx.err_ctxt().report_mismatched_types(cause, expected, found, ty_err); - let parent_id = fcx.tcx.parent_hir_id(id); + let due_to_block = matches!(fcx.tcx.hir_node(block_or_return_id), hir::Node::Block(..)); + + let parent_id = fcx.tcx.parent_hir_id(block_or_return_id); let parent = fcx.tcx.hir_node(parent_id); if let Some(expr) = expression && let hir::Node::Expr(hir::Expr { @@ -1829,72 +1827,64 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { // Verify that this is a tail expression of a function, otherwise the // label pointing out the cause for the type coercion will be wrong // as prior return coercions would not be relevant (#57664). - let fn_decl = if let (Some(expr), Some(blk_id)) = (expression, blk_id) { + if let Some(expr) = expression + && due_to_block + { fcx.suggest_missing_semicolon(&mut err, expr, expected, false); - let pointing_at_return_type = - fcx.suggest_mismatched_types_on_tail(&mut err, expr, expected, found, blk_id); - if let (Some(cond_expr), true, false) = ( - fcx.tcx.hir().get_if_cause(expr.hir_id), - expected.is_unit(), - pointing_at_return_type, - ) + let pointing_at_return_type = fcx.suggest_mismatched_types_on_tail( + &mut err, + expr, + expected, + found, + block_or_return_id, + ); + if let Some(cond_expr) = fcx.tcx.hir().get_if_cause(expr.hir_id) + && expected.is_unit() + && !pointing_at_return_type // If the block is from an external macro or try (`?`) desugaring, then // do not suggest adding a semicolon, because there's nowhere to put it. // See issues #81943 and #87051. && matches!( cond_expr.span.desugaring_kind(), None | Some(DesugaringKind::WhileLoop) - ) && !in_external_macro(fcx.tcx.sess, cond_expr.span) - && !matches!( - cond_expr.kind, - hir::ExprKind::Match(.., hir::MatchSource::TryDesugar(_)) - ) + ) + && !in_external_macro(fcx.tcx.sess, cond_expr.span) + && !matches!( + cond_expr.kind, + hir::ExprKind::Match(.., hir::MatchSource::TryDesugar(_)) + ) { err.span_label(cond_expr.span, "expected this to be `()`"); if expr.can_have_side_effects() { fcx.suggest_semicolon_at_end(cond_expr.span, &mut err); } } - fcx.get_node_fn_decl(parent) - .map(|(fn_id, fn_decl, _, is_main)| (fn_id, fn_decl, is_main)) - } else { - fcx.get_fn_decl(parent_id) }; - if let Some((fn_id, fn_decl, can_suggest)) = fn_decl { - if blk_id.is_none() { - fcx.suggest_missing_return_type( - &mut err, - fn_decl, - expected, - found, - can_suggest, - fn_id, - ); - } - } - - let mut parent_id = fcx.tcx.hir().get_parent_item(id).def_id; - let mut parent_item = fcx.tcx.hir_node_by_def_id(parent_id); - // When suggesting return, we need to account for closures and async blocks, not just items. - for (_, node) in fcx.tcx.hir().parent_iter(id) { - match node { - hir::Node::Expr(&hir::Expr { - kind: hir::ExprKind::Closure(hir::Closure { def_id, .. }), - .. - }) => { - parent_item = node; - parent_id = *def_id; - break; - } - hir::Node::Item(_) | hir::Node::TraitItem(_) | hir::Node::ImplItem(_) => break, - _ => {} - } + // If this is due to an explicit `return`, suggest adding a return type. + if let Some((fn_id, fn_decl, can_suggest)) = fcx.get_fn_decl(parent_id) + && !due_to_block + { + fcx.suggest_missing_return_type(&mut err, fn_decl, expected, found, can_suggest, fn_id); } - if let (Some(expr), Some(_), Some(fn_decl)) = (expression, blk_id, parent_item.fn_decl()) { + // If this is due to a block, then maybe we forgot a `return`/`break`. + if due_to_block + && let Some(expr) = expression + && let Some((parent_fn_decl, parent_id)) = fcx + .tcx + .hir() + .parent_iter(block_or_return_id) + .find_map(|(_, node)| Some((node.fn_decl()?, node.associated_body()?.0))) + { fcx.suggest_missing_break_or_return_expr( - &mut err, expr, fn_decl, expected, found, id, parent_id, + &mut err, + expr, + parent_fn_decl, + expected, + found, + block_or_return_id, + parent_id, ); } diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 5a9eab1ffeaf1..fade943c5ae3d 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -1346,6 +1346,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if segment.ident.name != kw::Empty { if let Some(err) = self.report_method_error( span, + Some(rcvr), rcvr_t, segment.ident, SelfSource::MethodCall(rcvr), @@ -3112,7 +3113,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let true_errors = ocx.select_where_possible(); - // Do a leak check -- we can't really report report a useful error here, + // Do a leak check -- we can't really report a useful error here, // but it at least avoids an ICE when the error has to do with higher-ranked // lifetimes. self.leak_check(outer_universe, Some(snapshot))?; diff --git a/compiler/rustc_hir_typeck/src/fallback.rs b/compiler/rustc_hir_typeck/src/fallback.rs index e456bd7fd4abe..e8533c68c7934 100644 --- a/compiler/rustc_hir_typeck/src/fallback.rs +++ b/compiler/rustc_hir_typeck/src/fallback.rs @@ -364,41 +364,11 @@ impl<'tcx> FnCtxt<'_, 'tcx> { }; let mut fallback_to = |ty| { - let unsafe_infer_vars = unsafe_infer_vars.get_or_init(|| { - let unsafe_infer_vars = compute_unsafe_infer_vars(self.root_ctxt, self.body_id); - debug!(?unsafe_infer_vars); - unsafe_infer_vars - }); - - let affected_unsafe_infer_vars = - graph::depth_first_search_as_undirected(&coercion_graph, root_vid) - .filter_map(|x| unsafe_infer_vars.get(&x).copied()) - .collect::>(); - - for (hir_id, span, reason) in affected_unsafe_infer_vars { - self.tcx.emit_node_span_lint( - lint::builtin::NEVER_TYPE_FALLBACK_FLOWING_INTO_UNSAFE, - hir_id, - span, - match reason { - UnsafeUseReason::Call => { - errors::NeverTypeFallbackFlowingIntoUnsafe::Call - } - UnsafeUseReason::Method => { - errors::NeverTypeFallbackFlowingIntoUnsafe::Method - } - UnsafeUseReason::Path => { - errors::NeverTypeFallbackFlowingIntoUnsafe::Path - } - UnsafeUseReason::UnionField => { - errors::NeverTypeFallbackFlowingIntoUnsafe::UnionField - } - UnsafeUseReason::Deref => { - errors::NeverTypeFallbackFlowingIntoUnsafe::Deref - } - }, - ); - } + self.lint_never_type_fallback_flowing_into_unsafe_code( + &unsafe_infer_vars, + &coercion_graph, + root_vid, + ); diverging_fallback.insert(diverging_ty, ty); }; @@ -464,6 +434,41 @@ impl<'tcx> FnCtxt<'_, 'tcx> { diverging_fallback } + fn lint_never_type_fallback_flowing_into_unsafe_code( + &self, + unsafe_infer_vars: &OnceCell>, + coercion_graph: &VecGraph, + root_vid: ty::TyVid, + ) { + let unsafe_infer_vars = unsafe_infer_vars.get_or_init(|| { + let unsafe_infer_vars = compute_unsafe_infer_vars(self.root_ctxt, self.body_id); + debug!(?unsafe_infer_vars); + unsafe_infer_vars + }); + + let affected_unsafe_infer_vars = + graph::depth_first_search_as_undirected(&coercion_graph, root_vid) + .filter_map(|x| unsafe_infer_vars.get(&x).copied()) + .collect::>(); + + for (hir_id, span, reason) in affected_unsafe_infer_vars { + self.tcx.emit_node_span_lint( + lint::builtin::NEVER_TYPE_FALLBACK_FLOWING_INTO_UNSAFE, + hir_id, + span, + match reason { + UnsafeUseReason::Call => errors::NeverTypeFallbackFlowingIntoUnsafe::Call, + UnsafeUseReason::Method => errors::NeverTypeFallbackFlowingIntoUnsafe::Method, + UnsafeUseReason::Path => errors::NeverTypeFallbackFlowingIntoUnsafe::Path, + UnsafeUseReason::UnionField => { + errors::NeverTypeFallbackFlowingIntoUnsafe::UnionField + } + UnsafeUseReason::Deref => errors::NeverTypeFallbackFlowingIntoUnsafe::Deref, + }, + ); + } + } + /// Returns a graph whose nodes are (unresolved) inference variables and where /// an edge `?A -> ?B` indicates that the variable `?A` is coerced to `?B`. fn create_coercion_graph(&self) -> VecGraph { diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 6e8ef04445215..77f90c0c1310c 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -33,7 +33,7 @@ use rustc_middle::{bug, span_bug}; use rustc_session::lint; use rustc_span::def_id::LocalDefId; use rustc_span::hygiene::DesugaringKind; -use rustc_span::symbol::{kw, sym, Ident}; +use rustc_span::symbol::{kw, sym}; use rustc_span::Span; use rustc_target::abi::FieldIdx; use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _; @@ -834,6 +834,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if item_name.name != kw::Empty { if let Some(e) = self.report_method_error( span, + None, ty.normalized, item_name, SelfSource::QPath(qself), @@ -866,76 +867,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) } - /// Given a function `Node`, return its `HirId` and `FnDecl` if it exists. Given a closure - /// that is the child of a function, return that function's `HirId` and `FnDecl` instead. - /// This may seem confusing at first, but this is used in diagnostics for `async fn`, - /// for example, where most of the type checking actually happens within a nested closure, - /// but we often want access to the parent function's signature. - /// - /// Otherwise, return false. - pub(crate) fn get_node_fn_decl( - &self, - node: Node<'tcx>, - ) -> Option<(LocalDefId, &'tcx hir::FnDecl<'tcx>, Ident, bool)> { - match node { - Node::Item(&hir::Item { - ident, - kind: hir::ItemKind::Fn(ref sig, ..), - owner_id, - .. - }) => { - // This is less than ideal, it will not suggest a return type span on any - // method called `main`, regardless of whether it is actually the entry point, - // but it will still present it as the reason for the expected type. - Some((owner_id.def_id, &sig.decl, ident, ident.name != sym::main)) - } - Node::TraitItem(&hir::TraitItem { - ident, - kind: hir::TraitItemKind::Fn(ref sig, ..), - owner_id, - .. - }) => Some((owner_id.def_id, &sig.decl, ident, true)), - Node::ImplItem(&hir::ImplItem { - ident, - kind: hir::ImplItemKind::Fn(ref sig, ..), - owner_id, - .. - }) => Some((owner_id.def_id, &sig.decl, ident, false)), - Node::Expr(&hir::Expr { - hir_id, - kind: - hir::ExprKind::Closure(hir::Closure { - kind: hir::ClosureKind::Coroutine(..), .. - }), - .. - }) => { - let (ident, sig, owner_id) = match self.tcx.parent_hir_node(hir_id) { - Node::Item(&hir::Item { - ident, - kind: hir::ItemKind::Fn(ref sig, ..), - owner_id, - .. - }) => (ident, sig, owner_id), - Node::TraitItem(&hir::TraitItem { - ident, - kind: hir::TraitItemKind::Fn(ref sig, ..), - owner_id, - .. - }) => (ident, sig, owner_id), - Node::ImplItem(&hir::ImplItem { - ident, - kind: hir::ImplItemKind::Fn(ref sig, ..), - owner_id, - .. - }) => (ident, sig, owner_id), - _ => return None, - }; - Some((owner_id.def_id, &sig.decl, ident, ident.name != sym::main)) - } - _ => None, - } - } - /// Given a `HirId`, return the `HirId` of the enclosing function, its `FnDecl`, and whether a /// suggestion can be made, `None` otherwise. pub fn get_fn_decl( @@ -944,10 +875,73 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) -> Option<(LocalDefId, &'tcx hir::FnDecl<'tcx>, bool)> { // Get enclosing Fn, if it is a function or a trait method, unless there's a `loop` or // `while` before reaching it, as block tail returns are not available in them. - self.tcx.hir().get_return_block(blk_id).and_then(|blk_id| { - let parent = self.tcx.hir_node(blk_id); - self.get_node_fn_decl(parent) - .map(|(fn_id, fn_decl, _, is_main)| (fn_id, fn_decl, is_main)) + self.tcx.hir().get_fn_id_for_return_block(blk_id).and_then(|item_id| { + match self.tcx.hir_node(item_id) { + Node::Item(&hir::Item { + ident, + kind: hir::ItemKind::Fn(ref sig, ..), + owner_id, + .. + }) => { + // This is less than ideal, it will not suggest a return type span on any + // method called `main`, regardless of whether it is actually the entry point, + // but it will still present it as the reason for the expected type. + Some((owner_id.def_id, sig.decl, ident.name != sym::main)) + } + Node::TraitItem(&hir::TraitItem { + kind: hir::TraitItemKind::Fn(ref sig, ..), + owner_id, + .. + }) => Some((owner_id.def_id, sig.decl, true)), + // FIXME: Suggestable if this is not a trait implementation + Node::ImplItem(&hir::ImplItem { + kind: hir::ImplItemKind::Fn(ref sig, ..), + owner_id, + .. + }) => Some((owner_id.def_id, sig.decl, false)), + Node::Expr(&hir::Expr { + hir_id, + kind: hir::ExprKind::Closure(&hir::Closure { def_id, kind, fn_decl, .. }), + .. + }) => { + match kind { + hir::ClosureKind::CoroutineClosure(_) => { + // FIXME(async_closures): Implement this. + return None; + } + hir::ClosureKind::Closure => Some((def_id, fn_decl, true)), + hir::ClosureKind::Coroutine(hir::CoroutineKind::Desugared( + _, + hir::CoroutineSource::Fn, + )) => { + let (ident, sig, owner_id) = match self.tcx.parent_hir_node(hir_id) { + Node::Item(&hir::Item { + ident, + kind: hir::ItemKind::Fn(ref sig, ..), + owner_id, + .. + }) => (ident, sig, owner_id), + Node::TraitItem(&hir::TraitItem { + ident, + kind: hir::TraitItemKind::Fn(ref sig, ..), + owner_id, + .. + }) => (ident, sig, owner_id), + Node::ImplItem(&hir::ImplItem { + ident, + kind: hir::ImplItemKind::Fn(ref sig, ..), + owner_id, + .. + }) => (ident, sig, owner_id), + _ => return None, + }; + Some((owner_id.def_id, sig.decl, ident.name != sym::main)) + } + _ => None, + } + } + _ => None, + } }) } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index aea34407a2d55..b8333d4749378 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -1774,7 +1774,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // that highlight errors inline. let mut sp = blk.span; let mut fn_span = None; - if let Some((decl, ident)) = self.get_parent_fn_decl(blk.hir_id) { + if let Some((fn_def_id, decl, _)) = self.get_fn_decl(blk.hir_id) { let ret_sp = decl.output.span(); if let Some(block_sp) = self.parent_item_span(blk.hir_id) { // HACK: on some cases (`ui/liveness/liveness-issue-2163.rs`) the @@ -1782,7 +1782,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // the span we're aiming at correspond to a `fn` body. if block_sp == blk.span { sp = ret_sp; - fn_span = Some(ident.span); + fn_span = self.tcx.def_ident_span(fn_def_id); } } } @@ -1897,15 +1897,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { None } - /// Given a function block's `HirId`, returns its `FnDecl` if it exists, or `None` otherwise. - pub(crate) fn get_parent_fn_decl( - &self, - blk_id: HirId, - ) -> Option<(&'tcx hir::FnDecl<'tcx>, Ident)> { - let parent = self.tcx.hir_node_by_def_id(self.tcx.hir().get_parent_item(blk_id).def_id); - self.get_node_fn_decl(parent).map(|(_, fn_decl, ident, _)| (fn_decl, ident)) - } - /// If `expr` is a `match` expression that has only one non-`!` arm, use that arm's tail /// expression's `Span`, otherwise return `expr.span`. This is done to give better errors /// when given code like the following: diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 56e13cd679d4f..cfd4dd4d1dd0d 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -800,6 +800,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { can_suggest: bool, fn_id: LocalDefId, ) -> bool { + // Can't suggest `->` on a block-like coroutine + if let Some(hir::CoroutineKind::Desugared(_, hir::CoroutineSource::Block)) = + self.tcx.coroutine_kind(fn_id) + { + return false; + } + let found = self.resolve_numeric_literals_with_default(self.resolve_vars_if_possible(found)); // Only suggest changing the return type for methods that @@ -1909,7 +1916,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let returned = matches!( self.tcx.parent_hir_node(expr.hir_id), hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Ret(_), .. }) - ) || map.get_return_block(expr.hir_id).is_some(); + ) || map.get_fn_id_for_return_block(expr.hir_id).is_some(); if returned && let ty::Adt(e, args_e) = expected.kind() && let ty::Adt(f, args_f) = found.kind() diff --git a/compiler/rustc_hir_typeck/src/method/confirm.rs b/compiler/rustc_hir_typeck/src/method/confirm.rs index 7425405853483..9c64f9475cf14 100644 --- a/compiler/rustc_hir_typeck/src/method/confirm.rs +++ b/compiler/rustc_hir_typeck/src/method/confirm.rs @@ -451,7 +451,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { // `foo.bar::(...)` -- the `Self` type here will be the // type of `foo` (possibly adjusted), but we don't want to // include that. We want just the `[_, u32]` part. - if !args.is_empty() && !generics.own_params.is_empty() { + if !args.is_empty() && !generics.is_own_empty() { let user_type_annotation = self.probe(|_| { let user_args = UserArgs { args: GenericArgs::for_item(self.tcx, pick.item.def_id, |param, _| { diff --git a/compiler/rustc_hir_typeck/src/method/mod.rs b/compiler/rustc_hir_typeck/src/method/mod.rs index a40fa600c19e2..4165ccb1b80ae 100644 --- a/compiler/rustc_hir_typeck/src/method/mod.rs +++ b/compiler/rustc_hir_typeck/src/method/mod.rs @@ -3,7 +3,7 @@ //! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/method-lookup.html mod confirm; -mod prelude2021; +mod prelude_edition_lints; pub mod probe; mod suggest; @@ -186,7 +186,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let pick = self.lookup_probe(segment.ident, self_ty, call_expr, ProbeScope::TraitsInScope)?; - self.lint_dot_call_from_2018(self_ty, segment, span, call_expr, self_expr, &pick, args); + self.lint_edition_dependent_dot_call( + self_ty, segment, span, call_expr, self_expr, &pick, args, + ); for &import_id in &pick.import_ids { debug!("used_trait_import: {:?}", import_id); diff --git a/compiler/rustc_hir_typeck/src/method/prelude2021.rs b/compiler/rustc_hir_typeck/src/method/prelude_edition_lints.rs similarity index 90% rename from compiler/rustc_hir_typeck/src/method/prelude2021.rs rename to compiler/rustc_hir_typeck/src/method/prelude_edition_lints.rs index a305461344d4d..e9eab6969b34e 100644 --- a/compiler/rustc_hir_typeck/src/method/prelude2021.rs +++ b/compiler/rustc_hir_typeck/src/method/prelude_edition_lints.rs @@ -1,12 +1,12 @@ -use crate::{ - method::probe::{self, Pick}, - FnCtxt, -}; +use crate::method::probe::{self, Pick}; +use crate::FnCtxt; + use hir::def_id::DefId; use hir::HirId; use hir::ItemKind; use rustc_errors::Applicability; use rustc_hir as hir; +use rustc_lint::{ARRAY_INTO_ITER, BOXED_SLICE_INTO_ITER}; use rustc_middle::span_bug; use rustc_middle::ty::{self, Ty}; use rustc_session::lint::builtin::RUST_2021_PRELUDE_COLLISIONS; @@ -17,7 +17,7 @@ use rustc_trait_selection::infer::InferCtxtExt; use std::fmt::Write; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { - pub(super) fn lint_dot_call_from_2018( + pub(super) fn lint_edition_dependent_dot_call( &self, self_ty: Ty<'tcx>, segment: &hir::PathSegment<'_>, @@ -32,22 +32,32 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { segment.ident, self_ty, call_expr, self_expr ); - // Rust 2021 and later is already using the new prelude - if span.at_least_rust_2021() { - return; - } - - let prelude_or_array_lint = match segment.ident.name { + let (prelude_or_array_lint, edition) = match segment.ident.name { // `try_into` was added to the prelude in Rust 2021. - sym::try_into => RUST_2021_PRELUDE_COLLISIONS, + sym::try_into if !span.at_least_rust_2021() => (RUST_2021_PRELUDE_COLLISIONS, "2021"), // `into_iter` wasn't added to the prelude, // but `[T; N].into_iter()` doesn't resolve to IntoIterator::into_iter // before Rust 2021, which results in the same problem. // It is only a problem for arrays. - sym::into_iter if let ty::Array(..) = self_ty.kind() => { - // In this case, it wasn't really a prelude addition that was the problem. - // Instead, the problem is that the array-into_iter hack will no longer apply in Rust 2021. - rustc_lint::ARRAY_INTO_ITER + sym::into_iter => { + if let ty::Array(..) = self_ty.kind() + && !span.at_least_rust_2021() + { + // In this case, it wasn't really a prelude addition that was the problem. + // Instead, the problem is that the array-into_iter hack will no longer + // apply in Rust 2021. + (ARRAY_INTO_ITER, "2021") + } else if self_ty.is_box() + && self_ty.boxed_ty().is_slice() + && !span.at_least_rust_2024() + { + // In this case, it wasn't really a prelude addition that was the problem. + // Instead, the problem is that the boxed-slice-into_iter hack will no + // longer apply in Rust 2024. + (BOXED_SLICE_INTO_ITER, "2024") + } else { + return; + } } _ => return, }; @@ -81,7 +91,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { prelude_or_array_lint, self_expr.hir_id, self_expr.span, - format!("trait method `{}` will become ambiguous in Rust 2021", segment.ident.name), + format!( + "trait method `{}` will become ambiguous in Rust {edition}", + segment.ident.name + ), |lint| { let sp = self_expr.span; @@ -131,7 +144,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { prelude_or_array_lint, call_expr.hir_id, call_expr.span, - format!("trait method `{}` will become ambiguous in Rust 2021", segment.ident.name), + format!( + "trait method `{}` will become ambiguous in Rust {edition}", + segment.ident.name + ), |lint| { let sp = call_expr.span; let trait_name = self.trait_path_or_bare_name( @@ -279,7 +295,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if !self_ty_name.contains('<') { if let ty::Adt(def, _) = self_ty.kind() { let generics = self.tcx.generics_of(def.did()); - if !generics.own_params.is_empty() { + if !generics.is_own_empty() { let counts = generics.own_counts(); self_ty_name += &format!( "<{}>", diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index fc652490a40aa..e0a60337c3ba1 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -1444,6 +1444,18 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { return ProbeResult::NoMatch; } } + + // Some trait methods are excluded for boxed slices before 2024. + // (`boxed_slice.into_iter()` wants a slice iterator for compatibility.) + if self_ty.is_box() + && self_ty.boxed_ty().is_slice() + && !method_name.span.at_least_rust_2024() + { + let trait_def = self.tcx.trait_def(poly_trait_ref.def_id()); + if trait_def.skip_boxed_slice_during_method_dispatch { + return ProbeResult::NoMatch; + } + } } let trait_ref = self.instantiate_binder_with_fresh_vars( @@ -1754,7 +1766,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { let generics = self.tcx.generics_of(method); assert_eq!(args.len(), generics.parent_count); - let xform_fn_sig = if generics.own_params.is_empty() { + let xform_fn_sig = if generics.is_own_empty() { fn_sig.instantiate(self.tcx, args) } else { let args = GenericArgs::for_item(self.tcx, method, |param, _| { diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index db510d44392cb..54af8354c4c72 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -7,6 +7,7 @@ use crate::errors::{self, CandidateTraitNote, NoAssociatedItem}; use crate::Expectation; use crate::FnCtxt; use core::ops::ControlFlow; +use hir::Expr; use rustc_ast::ast::Mutability; use rustc_attr::parse_confusables; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; @@ -19,7 +20,6 @@ use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_hir::lang_items::LangItem; -use rustc_hir::PatKind::Binding; use rustc_hir::PathSegment; use rustc_hir::{ExprKind, Node, QPath}; use rustc_infer::infer::{self, RegionVariableOrigin}; @@ -46,7 +46,7 @@ use std::borrow::Cow; use super::probe::{AutorefOrPtrAdjustment, IsSuggestion, Mode, ProbeScope}; use super::{CandidateSource, MethodError, NoMatchData}; -use rustc_hir::intravisit::Visitor; +use rustc_hir::intravisit::{self, Visitor}; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn is_fn_ty(&self, ty: Ty<'tcx>, span: Span) -> bool { @@ -188,6 +188,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn report_method_error( &self, span: Span, + rcvr_opt: Option<&'tcx hir::Expr<'tcx>>, rcvr_ty: Ty<'tcx>, item_name: Ident, source: SelfSource<'tcx>, @@ -212,6 +213,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { MethodError::NoMatch(mut no_match_data) => { return self.report_no_match_method_error( span, + rcvr_opt, rcvr_ty, item_name, source, @@ -356,9 +358,197 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err } + pub fn suggest_use_shadowed_binding_with_method( + &self, + rcvr_opt: Option<&'tcx hir::Expr<'tcx>>, + method_name: Ident, + ty_str_reported: &str, + err: &mut Diag<'_>, + ) { + #[derive(Debug)] + struct LetStmt { + ty_hir_id_opt: Option, + binding_id: hir::HirId, + span: Span, + init_hir_id: hir::HirId, + } + + // Used for finding suggest binding. + // ```rust + // earlier binding for suggesting: + // let y = vec![1, 2]; + // now binding: + // if let Some(y) = x { + // y.push(y); + // } + // ``` + struct LetVisitor<'a, 'tcx> { + // Error binding which don't have `method_name`. + binding_name: Symbol, + binding_id: hir::HirId, + // Used for check if the suggest binding has `method_name`. + fcx: &'a FnCtxt<'a, 'tcx>, + call_expr: &'tcx Expr<'tcx>, + method_name: Ident, + // Suggest the binding which is shallowed. + sugg_let: Option, + } + + impl<'a, 'tcx> LetVisitor<'a, 'tcx> { + // Check scope of binding. + fn is_sub_scope(&self, sub_id: hir::ItemLocalId, super_id: hir::ItemLocalId) -> bool { + let scope_tree = self.fcx.tcx.region_scope_tree(self.fcx.body_id); + if let Some(sub_var_scope) = scope_tree.var_scope(sub_id) + && let Some(super_var_scope) = scope_tree.var_scope(super_id) + && scope_tree.is_subscope_of(sub_var_scope, super_var_scope) + { + return true; + } + false + } + + // Check if an earlier shadowed binding make `the receiver` of a MethodCall has the method. + // If it does, record the earlier binding for subsequent notes. + fn check_and_add_sugg_binding(&mut self, binding: LetStmt) -> bool { + if !self.is_sub_scope(self.binding_id.local_id, binding.binding_id.local_id) { + return false; + } + + // Get the earlier shadowed binding'ty and use it to check the method. + if let Some(ty_hir_id) = binding.ty_hir_id_opt + && let Some(tyck_ty) = self.fcx.node_ty_opt(ty_hir_id) + { + if self + .fcx + .lookup_probe_for_diagnostic( + self.method_name, + tyck_ty, + self.call_expr, + ProbeScope::TraitsInScope, + None, + ) + .is_ok() + { + self.sugg_let = Some(binding); + return true; + } else { + return false; + } + } + + // If the shadowed binding has an an itializer expression, + // use the initializer expression'ty to try to find the method again. + // For example like: `let mut x = Vec::new();`, + // `Vec::new()` is the itializer expression. + if let Some(self_ty) = self.fcx.node_ty_opt(binding.init_hir_id) + && self + .fcx + .lookup_probe_for_diagnostic( + self.method_name, + self_ty, + self.call_expr, + ProbeScope::TraitsInScope, + None, + ) + .is_ok() + { + self.sugg_let = Some(binding); + return true; + } + return false; + } + } + + impl<'v> Visitor<'v> for LetVisitor<'_, '_> { + type Result = ControlFlow<()>; + fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) -> Self::Result { + if let hir::StmtKind::Let(&hir::LetStmt { pat, ty, init, .. }) = ex.kind + && let hir::PatKind::Binding(_, binding_id, binding_name, ..) = pat.kind + && let Some(init) = init + && binding_name.name == self.binding_name + && binding_id != self.binding_id + { + if self.check_and_add_sugg_binding(LetStmt { + ty_hir_id_opt: if let Some(ty) = ty { Some(ty.hir_id) } else { None }, + binding_id: binding_id, + span: pat.span, + init_hir_id: init.hir_id, + }) { + return ControlFlow::Break(()); + } + ControlFlow::Continue(()) + } else { + hir::intravisit::walk_stmt(self, ex) + } + } + + // Used for find the error binding. + // When the visitor reaches this point, all the shadowed bindings + // have been found, so the visitor ends. + fn visit_pat(&mut self, p: &'v hir::Pat<'v>) -> Self::Result { + match p.kind { + hir::PatKind::Binding(_, binding_id, binding_name, _) => { + if binding_name.name == self.binding_name && binding_id == self.binding_id { + return ControlFlow::Break(()); + } + } + _ => { + intravisit::walk_pat(self, p); + } + } + ControlFlow::Continue(()) + } + } + + if let Some(rcvr) = rcvr_opt + && let hir::ExprKind::Path(QPath::Resolved(_, path)) = rcvr.kind + && let hir::def::Res::Local(recv_id) = path.res + && let Some(segment) = path.segments.first() + { + let map = self.infcx.tcx.hir(); + let body_id = self.tcx.hir().body_owned_by(self.body_id); + let body = map.body(body_id); + + if let Node::Expr(call_expr) = self.tcx.parent_hir_node(rcvr.hir_id) { + let mut let_visitor = LetVisitor { + fcx: self, + call_expr, + binding_name: segment.ident.name, + binding_id: recv_id, + method_name, + sugg_let: None, + }; + let_visitor.visit_body(body); + if let Some(sugg_let) = let_visitor.sugg_let + && let Some(self_ty) = self.node_ty_opt(sugg_let.init_hir_id) + { + let _sm = self.infcx.tcx.sess.source_map(); + let rcvr_name = segment.ident.name; + let mut span = MultiSpan::from_span(sugg_let.span); + span.push_span_label(sugg_let.span, + format!("`{rcvr_name}` of type `{self_ty}` that has method `{method_name}` defined earlier here")); + span.push_span_label( + self.tcx.hir().span(recv_id), + format!( + "earlier `{rcvr_name}` shadowed here with type `{ty_str_reported}`" + ), + ); + err.span_note( + span, + format!( + "there's an earlier shadowed binding `{rcvr_name}` of type `{self_ty}` \ + that has method `{method_name}` available" + ), + ); + } + } + } + } + pub fn report_no_match_method_error( &self, mut span: Span, + rcvr_opt: Option<&'tcx hir::Expr<'tcx>>, rcvr_ty: Ty<'tcx>, item_name: Ident, source: SelfSource<'tcx>, @@ -451,7 +641,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut err = if is_write && let SelfSource::MethodCall(rcvr_expr) = source { self.suggest_missing_writer(rcvr_ty, rcvr_expr) } else { - tcx.dcx().create_err(NoAssociatedItem { + let mut err = tcx.dcx().create_err(NoAssociatedItem { span, item_kind, item_name, @@ -461,9 +651,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { rcvr_ty.prefix_string(self.tcx) }, - ty_str: ty_str_reported, + ty_str: ty_str_reported.clone(), trait_missing_method, - }) + }); + + if is_method { + self.suggest_use_shadowed_binding_with_method( + rcvr_opt, + item_name, + &ty_str_reported, + &mut err, + ); + } + + err }; if tcx.sess.source_map().is_multiline(sugg_span) { err.span_label(sugg_span.with_hi(span.lo()), ""); @@ -2240,7 +2441,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { type Result = ControlFlow>>; fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) -> Self::Result { if let hir::StmtKind::Let(&hir::LetStmt { pat, init, .. }) = ex.kind - && let Binding(_, _, ident, ..) = pat.kind + && let hir::PatKind::Binding(_, _, ident, ..) = pat.kind && ident.name == self.ident_name { ControlFlow::Break(init) diff --git a/compiler/rustc_infer/messages.ftl b/compiler/rustc_infer/messages.ftl index 64f52ea7ac196..8f1c4ad462a67 100644 --- a/compiler/rustc_infer/messages.ftl +++ b/compiler/rustc_infer/messages.ftl @@ -164,7 +164,10 @@ infer_label_bad = {$bad_kind -> infer_lf_bound_not_satisfied = lifetime bound not satisfied infer_lifetime_mismatch = lifetime mismatch -infer_lifetime_param_suggestion = consider introducing a named lifetime parameter{$is_impl -> +infer_lifetime_param_suggestion = consider {$is_reuse -> + [true] reusing + *[false] introducing +} a named lifetime parameter{$is_impl -> [true] {" "}and update trait if needed *[false] {""} } diff --git a/compiler/rustc_infer/src/errors/mod.rs b/compiler/rustc_infer/src/errors/mod.rs index 2acaeac24398d..8bb0dc39143ce 100644 --- a/compiler/rustc_infer/src/errors/mod.rs +++ b/compiler/rustc_infer/src/errors/mod.rs @@ -1,9 +1,11 @@ use hir::GenericParamKind; +use rustc_data_structures::fx::FxHashSet; use rustc_errors::{ codes::*, Applicability, Diag, DiagMessage, DiagStyledString, EmissionGuarantee, IntoDiagArg, MultiSpan, SubdiagMessageOp, Subdiagnostic, }; use rustc_hir as hir; +use rustc_hir::intravisit::{walk_ty, Visitor}; use rustc_hir::FnRetTy; use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_middle::ty::print::TraitRefPrintOnlyTraitPath; @@ -355,31 +357,33 @@ impl Subdiagnostic for AddLifetimeParamsSuggestion<'_> { _f: &F, ) { let mut mk_suggestion = || { - let ( - hir::Ty { kind: hir::TyKind::Ref(lifetime_sub, _), .. }, - hir::Ty { kind: hir::TyKind::Ref(lifetime_sup, _), .. }, - ) = (self.ty_sub, self.ty_sup) - else { - return false; - }; - - if !lifetime_sub.is_anonymous() || !lifetime_sup.is_anonymous() { - return false; - }; - let Some(anon_reg) = self.tcx.is_suitable_region(self.sub) else { return false; }; let node = self.tcx.hir_node_by_def_id(anon_reg.def_id); let is_impl = matches!(&node, hir::Node::ImplItem(_)); - let generics = match node { + let (generics, parent_generics) = match node { hir::Node::Item(&hir::Item { kind: hir::ItemKind::Fn(_, ref generics, ..), .. }) | hir::Node::TraitItem(&hir::TraitItem { ref generics, .. }) - | hir::Node::ImplItem(&hir::ImplItem { ref generics, .. }) => generics, + | hir::Node::ImplItem(&hir::ImplItem { ref generics, .. }) => ( + generics, + match self.tcx.parent_hir_node(self.tcx.local_def_id_to_hir_id(anon_reg.def_id)) + { + hir::Node::Item(hir::Item { + kind: hir::ItemKind::Trait(_, _, ref generics, ..), + .. + }) + | hir::Node::Item(hir::Item { + kind: hir::ItemKind::Impl(hir::Impl { ref generics, .. }), + .. + }) => Some(generics), + _ => None, + }, + ), _ => return false, }; @@ -390,24 +394,112 @@ impl Subdiagnostic for AddLifetimeParamsSuggestion<'_> { .map(|p| p.name.ident().name) .find(|i| *i != kw::UnderscoreLifetime); let introduce_new = suggestion_param_name.is_none(); + + let mut default = "'a".to_string(); + if let Some(parent_generics) = parent_generics { + let used: FxHashSet<_> = parent_generics + .params + .iter() + .filter(|p| matches!(p.kind, GenericParamKind::Lifetime { .. })) + .map(|p| p.name.ident().name) + .filter(|i| *i != kw::UnderscoreLifetime) + .map(|l| l.to_string()) + .collect(); + if let Some(lt) = + ('a'..='z').map(|it| format!("'{it}")).find(|it| !used.contains(it)) + { + // We want a lifetime that *isn't* present in the `trait` or `impl` that assoc + // `fn` belongs to. We could suggest reusing one of their lifetimes, but it is + // likely to be an over-constraining lifetime requirement, so we always add a + // lifetime to the `fn`. + default = lt; + } + } let suggestion_param_name = - suggestion_param_name.map(|n| n.to_string()).unwrap_or_else(|| "'a".to_owned()); - - debug!(?lifetime_sup.ident.span); - debug!(?lifetime_sub.ident.span); - let make_suggestion = |ident: Ident| { - let sugg = if ident.name == kw::Empty { - format!("{suggestion_param_name}, ") - } else if ident.name == kw::UnderscoreLifetime && ident.span.is_empty() { - format!("{suggestion_param_name} ") - } else { - suggestion_param_name.clone() - }; - (ident.span, sugg) - }; - let mut suggestions = - vec![make_suggestion(lifetime_sub.ident), make_suggestion(lifetime_sup.ident)]; + suggestion_param_name.map(|n| n.to_string()).unwrap_or_else(|| default); + + struct ImplicitLifetimeFinder { + suggestions: Vec<(Span, String)>, + suggestion_param_name: String, + } + impl<'v> Visitor<'v> for ImplicitLifetimeFinder { + fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) { + let make_suggestion = |ident: Ident| { + if ident.name == kw::Empty && ident.span.is_empty() { + format!("{}, ", self.suggestion_param_name) + } else if ident.name == kw::UnderscoreLifetime && ident.span.is_empty() { + format!("{} ", self.suggestion_param_name) + } else { + self.suggestion_param_name.clone() + } + }; + match ty.kind { + hir::TyKind::Path(hir::QPath::Resolved(_, path)) => { + for segment in path.segments { + if let Some(args) = segment.args { + if args.args.iter().all(|arg| { + matches!( + arg, + hir::GenericArg::Lifetime(lifetime) + if lifetime.ident.name == kw::Empty + ) + }) { + self.suggestions.push(( + segment.ident.span.shrink_to_hi(), + format!( + "<{}>", + args.args + .iter() + .map(|_| self.suggestion_param_name.clone()) + .collect::>() + .join(", ") + ), + )); + } else { + for arg in args.args { + if let hir::GenericArg::Lifetime(lifetime) = arg + && lifetime.is_anonymous() + { + self.suggestions.push(( + lifetime.ident.span, + make_suggestion(lifetime.ident), + )); + } + } + } + } + } + } + hir::TyKind::Ref(lifetime, ..) if lifetime.is_anonymous() => { + self.suggestions + .push((lifetime.ident.span, make_suggestion(lifetime.ident))); + } + _ => {} + } + walk_ty(self, ty); + } + } + let mut visitor = ImplicitLifetimeFinder { + suggestions: vec![], + suggestion_param_name: suggestion_param_name.clone(), + }; + if let Some(fn_decl) = node.fn_decl() + && let hir::FnRetTy::Return(ty) = fn_decl.output + { + visitor.visit_ty(ty); + } + if visitor.suggestions.is_empty() { + // Do not suggest constraining the `&self` param, but rather the return type. + // If that is wrong (because it is not sufficient), a follow up error will tell the + // user to fix it. This way we lower the chances of *over* constraining, but still + // get the cake of "correctly" contrained in two steps. + visitor.visit_ty(self.ty_sup); + } + visitor.visit_ty(self.ty_sub); + if visitor.suggestions.is_empty() { + return false; + } if introduce_new { let new_param_suggestion = if let Some(first) = generics.params.iter().find(|p| !p.name.ident().span.is_empty()) @@ -417,15 +509,16 @@ impl Subdiagnostic for AddLifetimeParamsSuggestion<'_> { (generics.span, format!("<{suggestion_param_name}>")) }; - suggestions.push(new_param_suggestion); + visitor.suggestions.push(new_param_suggestion); } - - diag.multipart_suggestion( + diag.multipart_suggestion_verbose( fluent::infer_lifetime_param_suggestion, - suggestions, + visitor.suggestions, Applicability::MaybeIncorrect, ); diag.arg("is_impl", is_impl); + diag.arg("is_reuse", !introduce_new); + true }; if mk_suggestion() && self.add_note { diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 723f4c81ca52e..8d4011421bd33 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -369,33 +369,43 @@ impl<'tcx> ty::InferCtxtLike for InferCtxt<'tcx> { } } - fn root_ty_var(&self, vid: TyVid) -> TyVid { - self.root_var(vid) + fn opportunistic_resolve_lt_var(&self, vid: ty::RegionVid) -> ty::Region<'tcx> { + self.inner.borrow_mut().unwrap_region_constraints().opportunistic_resolve_var(self.tcx, vid) } - fn probe_ty_var(&self, vid: TyVid) -> Option> { - self.probe_ty_var(vid).ok() + fn defining_opaque_types(&self) -> &'tcx ty::List { + self.defining_opaque_types } - fn opportunistic_resolve_lt_var(&self, vid: ty::RegionVid) -> Option> { - let re = self - .inner - .borrow_mut() - .unwrap_region_constraints() - .opportunistic_resolve_var(self.tcx, vid); - if *re == ty::ReVar(vid) { None } else { Some(re) } + fn opportunistic_resolve_ty_var(&self, vid: TyVid) -> Ty<'tcx> { + match self.probe_ty_var(vid) { + Ok(ty) => ty, + Err(_) => Ty::new_var(self.tcx, self.root_var(vid)), + } } - fn root_ct_var(&self, vid: ConstVid) -> ConstVid { - self.root_const_var(vid) + fn opportunistic_resolve_int_var(&self, vid: IntVid) -> Ty<'tcx> { + self.opportunistic_resolve_int_var(vid) } - fn probe_ct_var(&self, vid: ConstVid) -> Option> { - self.probe_const_var(vid).ok() + fn opportunistic_resolve_float_var(&self, vid: FloatVid) -> Ty<'tcx> { + self.opportunistic_resolve_float_var(vid) } - fn defining_opaque_types(&self) -> &'tcx ty::List { - self.defining_opaque_types + fn opportunistic_resolve_ct_var(&self, vid: ConstVid, ty: Ty<'tcx>) -> ty::Const<'tcx> { + match self.probe_const_var(vid) { + Ok(ct) => ct, + Err(_) => ty::Const::new_var(self.tcx, self.root_const_var(vid), ty), + } + } + + fn opportunistic_resolve_effect_var(&self, vid: EffectVid, ty: Ty<'tcx>) -> ty::Const<'tcx> { + match self.probe_effect_var(vid) { + Some(ct) => ct, + None => { + ty::Const::new_infer(self.tcx, InferConst::EffectVar(self.root_effect_var(vid)), ty) + } + } } } diff --git a/compiler/rustc_infer/src/infer/resolve.rs b/compiler/rustc_infer/src/infer/resolve.rs index 61b13dd9a54fd..21ef2e89523f5 100644 --- a/compiler/rustc_infer/src/infer/resolve.rs +++ b/compiler/rustc_infer/src/infer/resolve.rs @@ -173,84 +173,3 @@ impl<'a, 'tcx> FallibleTypeFolder> for FullTypeResolver<'a, 'tcx> { } } } - -/////////////////////////////////////////////////////////////////////////// -// EAGER RESOLUTION - -/// Resolves ty, region, and const vars to their inferred values or their root vars. -pub struct EagerResolver<'a, 'tcx> { - infcx: &'a InferCtxt<'tcx>, -} - -impl<'a, 'tcx> EagerResolver<'a, 'tcx> { - pub fn new(infcx: &'a InferCtxt<'tcx>) -> Self { - EagerResolver { infcx } - } -} - -impl<'tcx> TypeFolder> for EagerResolver<'_, 'tcx> { - fn interner(&self) -> TyCtxt<'tcx> { - self.infcx.tcx - } - - fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { - match *t.kind() { - ty::Infer(ty::TyVar(vid)) => match self.infcx.probe_ty_var(vid) { - Ok(t) => t.fold_with(self), - Err(_) => Ty::new_var(self.infcx.tcx, self.infcx.root_var(vid)), - }, - ty::Infer(ty::IntVar(vid)) => self.infcx.opportunistic_resolve_int_var(vid), - ty::Infer(ty::FloatVar(vid)) => self.infcx.opportunistic_resolve_float_var(vid), - _ => { - if t.has_infer() { - t.super_fold_with(self) - } else { - t - } - } - } - } - - fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { - match *r { - ty::ReVar(vid) => self - .infcx - .inner - .borrow_mut() - .unwrap_region_constraints() - .opportunistic_resolve_var(self.infcx.tcx, vid), - _ => r, - } - } - - fn fold_const(&mut self, c: ty::Const<'tcx>) -> ty::Const<'tcx> { - match c.kind() { - ty::ConstKind::Infer(ty::InferConst::Var(vid)) => { - // FIXME: we need to fold the ty too, I think. - match self.infcx.probe_const_var(vid) { - Ok(c) => c.fold_with(self), - Err(_) => { - ty::Const::new_var(self.infcx.tcx, self.infcx.root_const_var(vid), c.ty()) - } - } - } - ty::ConstKind::Infer(ty::InferConst::EffectVar(vid)) => { - debug_assert_eq!(c.ty(), self.infcx.tcx.types.bool); - self.infcx.probe_effect_var(vid).unwrap_or_else(|| { - ty::Const::new_infer( - self.infcx.tcx, - ty::InferConst::EffectVar(self.infcx.root_effect_var(vid)), - self.infcx.tcx.types.bool, - ) - }) - } - _ => { - if c.has_infer() { - c.super_fold_with(self) - } else { - c - } - } - } - } -} diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index 55304bbbd922f..d43be6cebcb20 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -389,6 +389,7 @@ pub fn run_compiler(config: Config, f: impl FnOnce(&Compiler) -> R + Se let hash_kind = config.opts.unstable_opts.src_hash_algorithm(&target); util::run_in_thread_pool_with_globals( + &early_dcx, config.opts.edition, config.opts.unstable_opts.threads, SourceMapInputs { file_loader, path_mapping, hash_kind }, diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index ce4d382501522..987e48a1a76ff 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -51,20 +51,38 @@ pub fn add_configuration(cfg: &mut Cfg, sess: &mut Session, codegen_backend: &dy pub static STACK_SIZE: OnceLock = OnceLock::new(); pub const DEFAULT_STACK_SIZE: usize = 8 * 1024 * 1024; -fn init_stack_size() -> usize { +fn init_stack_size(early_dcx: &EarlyDiagCtxt) -> usize { // Obey the environment setting or default *STACK_SIZE.get_or_init(|| { env::var_os("RUST_MIN_STACK") - .map(|os_str| os_str.to_string_lossy().into_owned()) - // ignore if it is set to nothing - .filter(|s| s.trim() != "") - .map(|s| s.trim().parse::().unwrap()) + .as_ref() + .map(|os_str| os_str.to_string_lossy()) + // if someone finds out `export RUST_MIN_STACK=640000` isn't enough stack + // they might try to "unset" it by running `RUST_MIN_STACK= rustc code.rs` + // this is wrong, but std would nonetheless "do what they mean", so let's do likewise + .filter(|s| !s.trim().is_empty()) + // rustc is a batch program, so error early on inputs which are unlikely to be intended + // so no one thinks we parsed them setting `RUST_MIN_STACK="64 megabytes"` + // FIXME: we could accept `RUST_MIN_STACK=64MB`, perhaps? + .map(|s| { + let s = s.trim(); + // FIXME(workingjubilee): add proper diagnostics when we factor out "pre-run" setup + #[allow(rustc::untranslatable_diagnostic, rustc::diagnostic_outside_of_impl)] + s.parse::().unwrap_or_else(|_| { + let mut err = early_dcx.early_struct_fatal(format!( + r#"`RUST_MIN_STACK` should be a number of bytes, but was "{s}""#, + )); + err.note("you can also unset `RUST_MIN_STACK` to use the default stack size"); + err.emit() + }) + }) // otherwise pick a consistent default .unwrap_or(DEFAULT_STACK_SIZE) }) } fn run_in_thread_with_globals R + Send, R: Send>( + thread_stack_size: usize, edition: Edition, sm_inputs: SourceMapInputs, f: F, @@ -75,7 +93,7 @@ fn run_in_thread_with_globals R + Send, R: Send>( // the parallel compiler, in particular to ensure there is no accidental // sharing of data between the main thread and the compilation thread // (which might cause problems for the parallel compiler). - let builder = thread::Builder::new().name("rustc".to_string()).stack_size(init_stack_size()); + let builder = thread::Builder::new().name("rustc".to_string()).stack_size(thread_stack_size); // We build the session globals and run `f` on the spawned thread, because // `SessionGlobals` does not impl `Send` in the non-parallel compiler. @@ -100,16 +118,19 @@ fn run_in_thread_with_globals R + Send, R: Send>( #[cfg(not(parallel_compiler))] pub(crate) fn run_in_thread_pool_with_globals R + Send, R: Send>( + thread_builder_diag: &EarlyDiagCtxt, edition: Edition, _threads: usize, sm_inputs: SourceMapInputs, f: F, ) -> R { - run_in_thread_with_globals(edition, sm_inputs, f) + let thread_stack_size = init_stack_size(thread_builder_diag); + run_in_thread_with_globals(thread_stack_size, edition, sm_inputs, f) } #[cfg(parallel_compiler)] pub(crate) fn run_in_thread_pool_with_globals R + Send, R: Send>( + thread_builder_diag: &EarlyDiagCtxt, edition: Edition, threads: usize, sm_inputs: SourceMapInputs, @@ -121,10 +142,12 @@ pub(crate) fn run_in_thread_pool_with_globals R + Send, use rustc_query_system::query::{break_query_cycles, QueryContext}; use std::process; + let thread_stack_size = init_stack_size(thread_builder_diag); + let registry = sync::Registry::new(std::num::NonZero::new(threads).unwrap()); if !sync::is_dyn_thread_safe() { - return run_in_thread_with_globals(edition, sm_inputs, |current_gcx| { + return run_in_thread_with_globals(thread_stack_size, edition, sm_inputs, |current_gcx| { // Register the thread for use with the `WorkerLocal` type. registry.register(); @@ -167,7 +190,7 @@ pub(crate) fn run_in_thread_pool_with_globals R + Send, }) .unwrap(); }) - .stack_size(init_stack_size()); + .stack_size(thread_stack_size); // We create the session globals on the main thread, then create the thread // pool. Upon creation, each worker thread created gets a copy of the @@ -376,31 +399,17 @@ pub(crate) fn check_attr_crate_type( if let ast::MetaItemKind::NameValue(spanned) = a.meta_kind().unwrap() { let span = spanned.span; - let lev_candidate = find_best_match_for_name( + let candidate = find_best_match_for_name( &CRATE_TYPES.iter().map(|(k, _)| *k).collect::>(), n, None, ); - if let Some(candidate) = lev_candidate { - lint_buffer.buffer_lint_with_diagnostic( - lint::builtin::UNKNOWN_CRATE_TYPES, - ast::CRATE_NODE_ID, - span, - "invalid `crate_type` value", - BuiltinLintDiag::UnknownCrateTypes( - span, - "did you mean".to_string(), - format!("\"{candidate}\""), - ), - ); - } else { - lint_buffer.buffer_lint( - lint::builtin::UNKNOWN_CRATE_TYPES, - ast::CRATE_NODE_ID, - span, - "invalid `crate_type` value", - ); - } + lint_buffer.buffer_lint( + lint::builtin::UNKNOWN_CRATE_TYPES, + ast::CRATE_NODE_ID, + span, + BuiltinLintDiag::UnknownCrateTypes { span, candidate }, + ); } } else { // This is here mainly to check for using a macro, such as diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 5180fce2eb378..6f6480a496413 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -1,13 +1,19 @@ +lint_abs_path_with_module = absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition + .suggestion = use `crate` + +lint_ambiguous_glob_reexport = ambiguous glob re-exports + .label_first_reexport = the name `{$name}` in the {$namespace} namespace is first re-exported here + .label_duplicate_reexport = but the name `{$name}` in the {$namespace} namespace is also re-exported here + lint_ambiguous_wide_pointer_comparisons = ambiguous wide pointer comparison, the comparison includes metadata which may not be expected .addr_metadata_suggestion = use explicit `std::ptr::eq` method to compare metadata and addresses .addr_suggestion = use `std::ptr::addr_eq` or untyped pointers to only compare their addresses -lint_array_into_iter = - this method call resolves to `<&{$target} as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to <{$target} as IntoIterator>::into_iter in Rust 2021 - .use_iter_suggestion = use `.iter()` instead of `.into_iter()` to avoid ambiguity - .remove_into_iter_suggestion = or remove `.into_iter()` to iterate by value - .use_explicit_into_iter_suggestion = - or use `IntoIterator::into_iter(..)` instead of `.into_iter()` to explicitly iterate by value +lint_associated_const_elided_lifetime = {$elided -> + [true] `&` without an explicit lifetime name cannot be used here + *[false] `'_` cannot be used here + } + .suggestion = use the `'static` lifetime lint_async_fn_in_trait = use of `async fn` in public traits is discouraged as auto trait bounds cannot be specified .note = you can suppress this lint if you plan to use the trait only in your own code, or do not care about auto traits like `Send` on the `Future` @@ -26,10 +32,19 @@ lint_atomic_ordering_load = atomic loads cannot have `Release` or `AcqRel` order lint_atomic_ordering_store = atomic stores cannot have `Acquire` or `AcqRel` ordering .help = consider using ordering modes `Release`, `SeqCst` or `Relaxed` +lint_avoid_att_syntax = + avoid using `.att_syntax`, prefer using `options(att_syntax)` instead + +lint_avoid_intel_syntax = + avoid using `.intel_syntax`, Intel syntax is the default + lint_bad_attribute_argument = bad attribute argument lint_bad_opt_access = {$msg} +lint_break_with_label_and_loop = this labeled break expression is easy to confuse with an unlabeled break with a labeled value expression + .suggestion = wrap this expression in parentheses + lint_builtin_allow_internal_unsafe = `allow_internal_unsafe` allows defining macros using unsafe without triggering the `unsafe_code` lint at their call site @@ -37,6 +52,8 @@ lint_builtin_anonymous_params = anonymous parameters are deprecated and will be .suggestion = try naming the parameter or explicitly ignoring it lint_builtin_asm_labels = avoid using named labels in inline assembly + .help = only local labels of the form `:` should be used in inline asm + .note = see the asm section of Rust By Example for more information lint_builtin_box_pointers = type uses owned (Box type) pointers: {$ty} @@ -161,6 +178,12 @@ lint_builtin_unused_doc_comment = unused doc comment lint_builtin_while_true = denote infinite loops with `loop {"{"} ... {"}"}` .suggestion = use `loop` +lint_byte_slice_in_packed_struct_with_derive = {$ty} slice in a packed struct that derives a built-in trait + .help = consider implementing the trait by hand, or remove the `packed` attribute + +lint_cfg_attr_no_attributes = + `#[cfg_attr]` does not expand to any attributes + lint_check_name_unknown_tool = unknown lint tool: `{$tool_name}` lint_command_line_source = `forbid` lint level was set on command line @@ -169,12 +192,20 @@ lint_confusable_identifier_pair = found both `{$existing_sym}` and `{$sym}` as i .current_use = this identifier can be confused with `{$existing_sym}` .other_use = other identifier used here +lint_crate_name_in_cfg_attr_deprecated = + `crate_name` within an `#![cfg_attr]` attribute is deprecated + +lint_crate_type_in_cfg_attr_deprecated = + `crate_type` within an `#![cfg_attr]` attribute is deprecated + lint_cstring_ptr = getting the inner pointer of a temporary `CString` .as_ptr_label = this pointer will be invalid .unwrap_label = this `CString` is deallocated at the end of the statement, bind it to a variable to extend its lifetime .note = pointers do not have a lifetime; when calling `as_ptr` the `CString` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned .help = for more information, see https://doc.rust-lang.org/reference/destructors.html +lint_custom_inner_attribute_unstable = custom inner attributes are unstable + lint_default_hash_types = prefer `{$preferred}` over `{$used}`, it has better performance .note = a `use rustc_data_structures::fx::{$preferred}` may be necessary @@ -185,6 +216,11 @@ lint_deprecated_lint_name = .suggestion = change it to .help = change it to {$replace} +lint_deprecated_where_clause_location = where clause not allowed here + .note = see issue #89122 for more information + .suggestion_move_to_end = move it to the end of the type declaration + .suggestion_remove_where = remove this `where` + lint_diag_out_of_impl = diagnostics should only be created in `Diagnostic`/`Subdiagnostic`/`LintDiagnostic` impls @@ -202,6 +238,11 @@ lint_dropping_references = calls to `std::mem::drop` with a reference instead of .label = argument has type `{$arg_ty}` .note = use `let _ = ...` to ignore the expression or result +lint_duplicate_macro_attribute = + duplicated attribute + +lint_duplicate_matcher_binding = duplicate matcher binding + lint_enum_intrinsics_mem_discriminant = the return value of `mem::discriminant` is unspecified when called with a non-enum type .note = the argument to `discriminant` should be a reference to an enum, but it was passed a reference to a `{$ty_param}`, which is not an enum. @@ -214,6 +255,13 @@ lint_expectation = this lint expectation is unfulfilled .note = the `unfulfilled_lint_expectations` lint can't be expected and will always produce this message .rationale = {$rationale} +lint_extern_crate_not_idiomatic = `extern crate` is not idiomatic in the new edition + .suggestion = convert it to a `use` + +lint_extern_without_abi = extern declarations without an explicit ABI are deprecated + .label = ABI should be specified here + .help = the default ABI is {$default_abi} + lint_for_loops_over_fallibles = for loop over {$article} `{$ty}`. This is more readably written as an `if let` statement .suggestion = consider using `if let` to clear intent @@ -228,6 +276,12 @@ lint_forgetting_references = calls to `std::mem::forget` with a reference instea .label = argument has type `{$arg_ty}` .note = use `let _ = ...` to ignore the expression or result +lint_hidden_glob_reexport = private item shadows public glob re-export + .note_glob_reexport = the name `{$name}` in the {$namespace} namespace is supposed to be publicly re-exported here + .note_private_item = but the private item here shadows it + +lint_hidden_lifetime_parameters = hidden lifetime parameters in types are deprecated + lint_hidden_unicode_codepoints = unicode codepoint changing visible direction of text present in {$label} .label = this {$label} contains {$count -> [one] an invisible @@ -269,6 +323,11 @@ lint_identifier_uncommon_codepoints = identifier contains {$codepoints_len -> lint_ignored_unless_crate_specified = {$level}({$name}) is ignored unless specified at crate level +lint_ill_formed_attribute_input = {$num_suggestions -> + [1] attribute must be of the form {$suggestions} + *[other] valid forms for the attribute are {$suggestions} + } + lint_impl_trait_overcaptures = `{$self_ty}` will capture more lifetimes than possibly intended in edition 2024 .note = specifically, {$num_captured -> [one] this lifetime is @@ -339,6 +398,14 @@ lint_improper_ctypes_union_layout_help = consider adding a `#[repr(C)]` or `#[re lint_improper_ctypes_union_layout_reason = this union has unspecified layout lint_improper_ctypes_union_non_exhaustive = this union is non-exhaustive +lint_incomplete_include = + include macro expected single expression in source + +lint_inner_macro_attribute_unstable = inner macro attributes are unstable + +lint_invalid_crate_type_value = invalid `crate_type` value + .suggestion = did you mean + # FIXME: we should ordinalize $valid_up_to when we add support for doing so lint_invalid_from_utf8_checked = calls to `{$method}` with a invalid literal always return an error .label = the literal was valid UTF-8 up to the {$valid_up_to} bytes @@ -367,9 +434,22 @@ lint_invalid_reference_casting_note_book = for more information, visit `, which means the only information carried by the iterator is the number of items .suggestion = you might have meant to use `Iterator::for_each` +lint_metavariable_still_repeating = variable '{$name}' is still repeating at this depth + +lint_metavariable_wrong_operator = meta-variable repeats with different Kleene operator + +lint_missing_fragment_specifier = missing fragment specifier + lint_mixed_script_confusables = the usage of Script Group `{$set}` in this crate consists solely of mixed script confusables .includes_note = the usage includes {$includes} @@ -386,6 +472,11 @@ lint_mixed_script_confusables = lint_multiple_supertrait_upcastable = `{$ident}` is object-safe and has multiple supertraits +lint_named_argument_used_positionally = named argument `{$named_arg_name}` is not used by name + .label_named_arg = this named argument is referred to by position in formatting string + .label_position_arg = this formatting argument uses named argument `{$named_arg_name}` by position + .suggestion = use the named argument by name to avoid ambiguity + lint_node_source = `forbid` level set here .note = {$reason} @@ -497,6 +588,9 @@ lint_opaque_hidden_inferred_bound = opaque type `{$ty}` does not satisfy its ass lint_opaque_hidden_inferred_bound_sugg = add this bound +lint_or_patterns_back_compat = the meaning of the `pat` fragment specifier is changing in Rust 2021, which may affect this macro + .suggestion = use pat_param to preserve semantics + lint_overflowing_bin_hex = literal out of range for `{$ty}` .negative_note = the literal `{$lit}` (decimal `{$dec}`) does not fit into the type `{$ty}` .negative_becomes_note = and the value `-{$lit}` will become `{$actually}{$ty}` @@ -526,6 +620,21 @@ lint_path_statement_drop = path statement drops value lint_path_statement_no_effect = path statement with no effect +lint_pattern_in_bodiless = patterns aren't allowed in functions without bodies + .label = pattern not allowed in function without body + +lint_pattern_in_foreign = patterns aren't allowed in foreign function declarations + .label = pattern not allowed in foreign function + +lint_private_extern_crate_reexport = + extern crate `{$ident}` is private, and cannot be re-exported (error E0365), consider declaring with `pub` + +lint_proc_macro_back_compat = using an old version of `{$crate_name}` + .note = older versions of the `{$crate_name}` crate will stop compiling in future versions of Rust; please update to `{$crate_name}` v{$fixed_version}, or switch to one of the `{$crate_name}` alternatives + +lint_proc_macro_derive_resolution_fallback = cannot find {$ns} `{$ident}` in this scope + .label = names from parent modules are not accessible without an explicit import + lint_ptr_null_checks_fn_ptr = function pointers are not nullable, so checking them for null will always return false .help = wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value .label = expression has type `{$orig_ty}` @@ -547,6 +656,16 @@ lint_reason_must_be_string_literal = reason must be a string literal lint_reason_must_come_last = reason in lint attribute must come last +lint_redundant_import = the item `{$ident}` is imported redundantly + .label_imported_here = the item `{ident}` is already imported here + .label_defined_here = the item `{ident}` is already defined here + .label_imported_prelude = the item `{ident}` is already imported by the extern prelude + .label_defined_prelude = the item `{ident}` is already defined by the extern prelude + +lint_redundant_import_visibility = glob import doesn't reexport anything with visibility `{$import_vis}` because no imported item is public enough + .note = the most public imported item is `{$max_vis}` + .help = reduce the glob import's visibility or increase visibility of imported items + lint_redundant_semicolons = unnecessary trailing {$multiple -> [true] semicolons @@ -557,6 +676,8 @@ lint_redundant_semicolons = *[false] this semicolon } +lint_remove_mut_from_pattern = remove `mut` from the parameter + lint_removed_lint = lint `{$name}` has been removed: {$reason} lint_renamed_lint = lint `{$name}` has been renamed to `{$replace}` @@ -565,6 +686,22 @@ lint_renamed_lint = lint `{$name}` has been renamed to `{$replace}` lint_requested_level = requested on the command line with `{$level} {$lint_name}` +lint_reserved_prefix = prefix `{$prefix}` is unknown + .label = unknown prefix + .suggestion = insert whitespace here to avoid this being parsed as a prefix in Rust 2021 + +lint_shadowed_into_iter = + this method call resolves to `<&{$target} as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to `<{$target} as IntoIterator>::into_iter` in Rust {$edition} + .use_iter_suggestion = use `.iter()` instead of `.into_iter()` to avoid ambiguity + .remove_into_iter_suggestion = or remove `.into_iter()` to iterate by value + .use_explicit_into_iter_suggestion = + or use `IntoIterator::into_iter(..)` instead of `.into_iter()` to explicitly iterate by value + +lint_single_use_lifetime = lifetime parameter `{$ident}` only used once + .label_param = this lifetime... + .label_use = ...is used only here + .suggestion = elide the single-use lifetime + lint_span_use_eq_ctxt = use `.eq_ctxt()` instead of `.ctxt() == .ctxt()` lint_supertrait_as_deref_target = this `Deref` implementation is covered by an implicit supertrait coercion @@ -578,6 +715,10 @@ lint_suspicious_double_ref_clone = lint_suspicious_double_ref_deref = using `.deref()` on a double reference, which returns `{$ty}` instead of dereferencing the inner type +lint_trailing_semi_macro = trailing semicolon in macro used in expression position + .note1 = macro invocations at the end of a block are treated as expressions + .note2 = to ignore the value produced by the macro, add a semicolon after the invocation of `{$name}` + lint_ty_qualified = usage of qualified `ty::{$ty}` .suggestion = try importing it and using it unqualified @@ -591,12 +732,64 @@ lint_undropped_manually_drops = calls to `std::mem::drop` with `std::mem::Manual .label = argument has type `{$arg_ty}` .suggestion = use `std::mem::ManuallyDrop::into_inner` to get the inner value +lint_unexpected_cfg_add_build_rs_println = or consider adding `{$build_rs_println}` to the top of the `build.rs` +lint_unexpected_cfg_add_cargo_feature = consider using a Cargo feature instead +lint_unexpected_cfg_add_cargo_toml_lint_cfg = or consider adding in `Cargo.toml` the `check-cfg` lint config for the lint:{$cargo_toml_lint_cfg} +lint_unexpected_cfg_add_cmdline_arg = to expect this configuration use `{$cmdline_arg}` +lint_unexpected_cfg_define_features = consider defining some features in `Cargo.toml` +lint_unexpected_cfg_doc_cargo = see for more information about checking conditional configuration +lint_unexpected_cfg_doc_rustc = see for more information about checking conditional configuration + +lint_unexpected_cfg_name = unexpected `cfg` condition name: `{$name}` +lint_unexpected_cfg_name_expected_names = expected names are: {$possibilities}{$and_more -> + [0] {""} + *[other] {" "}and {$and_more} more + } +lint_unexpected_cfg_name_expected_values = expected values for `{$best_match}` are: {$possibilities} +lint_unexpected_cfg_name_similar_name = there is a config with a similar name +lint_unexpected_cfg_name_similar_name_different_values = there is a config with a similar name and different values +lint_unexpected_cfg_name_similar_name_no_value = there is a config with a similar name and no value +lint_unexpected_cfg_name_similar_name_value = there is a config with a similar name and value +lint_unexpected_cfg_name_with_similar_value = found config with similar value + +lint_unexpected_cfg_value = unexpected `cfg` condition value: {$has_value -> + [true] `{$value}` + *[false] (none) + } +lint_unexpected_cfg_value_add_feature = consider adding `{$value}` as a feature in `Cargo.toml` +lint_unexpected_cfg_value_expected_values = expected values for `{$name}` are: {$have_none_possibility -> + [true] {"(none), "} + *[false] {""} + }{$possibilities}{$and_more -> + [0] {""} + *[other] {" "}and {$and_more} more + } +lint_unexpected_cfg_value_no_expected_value = no expected value for `{$name}` +lint_unexpected_cfg_value_no_expected_values = no expected values for `{$name}` +lint_unexpected_cfg_value_remove_condition = remove the condition +lint_unexpected_cfg_value_remove_value = remove the value +lint_unexpected_cfg_value_similar_name = there is a expected value with a similar name +lint_unexpected_cfg_value_specify_value = specify a config value + lint_ungated_async_fn_track_caller = `#[track_caller]` on async functions is a no-op .label = this function will not propagate the caller location +lint_unicode_text_flow = unicode codepoint changing visible direction of text present in comment + .label = {$num_codepoints -> + [1] this comment contains an invisible unicode text flow control codepoint + *[other] this comment contains invisible unicode text flow control codepoints + } + .note = these kind of unicode codepoints change the way text flows on applications that support them, but can cause confusion because they change the order of characters on the screen + .suggestion = if their presence wasn't intentional, you can remove them + .label_comment_char = {$c_debug} + + lint_unit_bindings = binding has unit type `()` .label = this pattern is inferred to be the unit type `()` +lint_unknown_diagnostic_attribute = unknown diagnostic attribute +lint_unknown_diagnostic_attribute_typo_sugg = an attribute with a similar name exists + lint_unknown_gated_lint = unknown lint: `{$name}` .note = the `{$name}` lint is unstable @@ -612,9 +805,16 @@ lint_unknown_lint = *[false] did you mean: `{$replace}` } +lint_unknown_macro_variable = unknown macro variable `{$name}` + lint_unknown_tool_in_scoped_lint = unknown tool name `{$tool_name}` found in scoped lint: `{$tool_name}::{$lint_name}` .help = add `#![register_tool({$tool_name})]` to the crate root +lint_unnameable_test_items = cannot test inner items + +lint_unnecessary_qualification = unnecessary qualification + .suggestion = remove the unnecessary path segments + lint_unsupported_group = `{$lint_group}` lint group is not supported with ´--force-warn´ lint_untranslatable_diag = diagnostics should be created using translatable messages @@ -622,6 +822,9 @@ lint_untranslatable_diag = diagnostics should be created using translatable mess lint_unused_allocation = unnecessary allocation, use `&` instead lint_unused_allocation_mut = unnecessary allocation, use `&mut` instead +lint_unused_builtin_attribute = unused attribute `{$attr_name}` + .note = the built-in attribute `{$attr_name}` will be ignored, since it's applied to the macro invocation `{$macro_name}` + lint_unused_closure = unused {$pre}{$count -> [one] closure @@ -638,14 +841,43 @@ lint_unused_coroutine = }{$post} that must be used .note = coroutines are lazy and do nothing unless resumed +lint_unused_crate_dependency = external crate `{$extern_crate}` unused in `{$local_crate}`: remove the dependency or add `use {$extern_crate} as _;` + lint_unused_def = unused {$pre}`{$def}`{$post} that must be used .suggestion = use `let _ = ...` to ignore the resulting value lint_unused_delim = unnecessary {$delim} around {$item} .suggestion = remove these {$delim} +lint_unused_doc_comment = unused doc comment + .label = rustdoc does not generate documentation for macro invocations + .help = to document an item produced by a macro, the macro must produce the documentation as part of its expansion + +lint_unused_extern_crate = unused extern crate + .suggestion = remove it + lint_unused_import_braces = braces around {$node} is unnecessary +lint_unused_imports = {$num_snippets -> + [one] unused import: {$span_snippets} + *[other] unused imports: {$span_snippets} + } + .suggestion_remove_whole_use = remove the whole `use` item + .suggestion_remove_imports = {$num_to_remove -> + [one] remove the unused import + *[other] remove the unused imports + } + .help = if this is a test module, consider adding a `#[cfg(test)]` to the containing module + +lint_unused_label = unused label + +lint_unused_lifetime = lifetime parameter `{$ident}` never used + .suggestion = elide the unused lifetime + +lint_unused_macro_definition = unused macro definition: `{$name}` + +lint_unused_macro_use = unused `#[macro_use]` import + lint_unused_op = unused {$op} that must be used .label = the {$op} produces a value .suggestion = use `let _ = ...` to ignore the resulting value @@ -654,3 +886,6 @@ lint_unused_result = unused result of type `{$ty}` lint_variant_size_differences = enum variant is more than three times larger ({$largest} bytes) than the next largest + +lint_wasm_c_abi = + older versions of the `wasm-bindgen` crate will be incompatible with future versions of Rust; please update to `wasm-bindgen` v0.2.88 diff --git a/compiler/rustc_lint/src/array_into_iter.rs b/compiler/rustc_lint/src/array_into_iter.rs deleted file mode 100644 index 8f4bae339573c..0000000000000 --- a/compiler/rustc_lint/src/array_into_iter.rs +++ /dev/null @@ -1,144 +0,0 @@ -use crate::{ - lints::{ArrayIntoIterDiag, ArrayIntoIterDiagSub}, - LateContext, LateLintPass, LintContext, -}; -use rustc_hir as hir; -use rustc_middle::bug; -use rustc_middle::ty; -use rustc_middle::ty::adjustment::{Adjust, Adjustment}; -use rustc_session::lint::FutureIncompatibilityReason; -use rustc_session::{declare_lint, impl_lint_pass}; -use rustc_span::edition::Edition; -use rustc_span::symbol::sym; -use rustc_span::Span; - -declare_lint! { - /// The `array_into_iter` lint detects calling `into_iter` on arrays. - /// - /// ### Example - /// - /// ```rust,edition2018 - /// # #![allow(unused)] - /// [1, 2, 3].into_iter().for_each(|n| { *n; }); - /// ``` - /// - /// {{produces}} - /// - /// ### Explanation - /// - /// Since Rust 1.53, arrays implement `IntoIterator`. However, to avoid - /// breakage, `array.into_iter()` in Rust 2015 and 2018 code will still - /// behave as `(&array).into_iter()`, returning an iterator over - /// references, just like in Rust 1.52 and earlier. - /// This only applies to the method call syntax `array.into_iter()`, not to - /// any other syntax such as `for _ in array` or `IntoIterator::into_iter(array)`. - pub ARRAY_INTO_ITER, - Warn, - "detects calling `into_iter` on arrays in Rust 2015 and 2018", - @future_incompatible = FutureIncompatibleInfo { - reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2021), - reference: "", - }; -} - -#[derive(Copy, Clone, Default)] -pub struct ArrayIntoIter { - for_expr_span: Span, -} - -impl_lint_pass!(ArrayIntoIter => [ARRAY_INTO_ITER]); - -impl<'tcx> LateLintPass<'tcx> for ArrayIntoIter { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) { - // Save the span of expressions in `for _ in expr` syntax, - // so we can give a better suggestion for those later. - if let hir::ExprKind::Match(arg, [_], hir::MatchSource::ForLoopDesugar) = &expr.kind { - if let hir::ExprKind::Call(path, [arg]) = &arg.kind { - if let hir::ExprKind::Path(hir::QPath::LangItem( - hir::LangItem::IntoIterIntoIter, - .., - )) = &path.kind - { - self.for_expr_span = arg.span; - } - } - } - - // We only care about method call expressions. - if let hir::ExprKind::MethodCall(call, receiver_arg, ..) = &expr.kind { - if call.ident.name != sym::into_iter { - return; - } - - // Check if the method call actually calls the libcore - // `IntoIterator::into_iter`. - let def_id = cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap(); - match cx.tcx.trait_of_item(def_id) { - Some(trait_id) if cx.tcx.is_diagnostic_item(sym::IntoIterator, trait_id) => {} - _ => return, - }; - - // As this is a method call expression, we have at least one argument. - let receiver_ty = cx.typeck_results().expr_ty(receiver_arg); - let adjustments = cx.typeck_results().expr_adjustments(receiver_arg); - - let Some(Adjustment { kind: Adjust::Borrow(_), target }) = adjustments.last() else { - return; - }; - - let types = - std::iter::once(receiver_ty).chain(adjustments.iter().map(|adj| adj.target)); - - let mut found_array = false; - - for ty in types { - match ty.kind() { - // If we run into a &[T; N] or &[T] first, there's nothing to warn about. - // It'll resolve to the reference version. - ty::Ref(_, inner_ty, _) if inner_ty.is_array() => return, - ty::Ref(_, inner_ty, _) if matches!(inner_ty.kind(), ty::Slice(..)) => return, - // Found an actual array type without matching a &[T; N] first. - // This is the problematic case. - ty::Array(..) => { - found_array = true; - break; - } - _ => {} - } - } - - if !found_array { - return; - } - - // Emit lint diagnostic. - let target = match *target.kind() { - ty::Ref(_, inner_ty, _) if inner_ty.is_array() => "[T; N]", - ty::Ref(_, inner_ty, _) if matches!(inner_ty.kind(), ty::Slice(..)) => "[T]", - // We know the original first argument type is an array type, - // we know that the first adjustment was an autoref coercion - // and we know that `IntoIterator` is the trait involved. The - // array cannot be coerced to something other than a reference - // to an array or to a slice. - _ => bug!("array type coerced to something other than array or slice"), - }; - let sub = if self.for_expr_span == expr.span { - Some(ArrayIntoIterDiagSub::RemoveIntoIter { - span: receiver_arg.span.shrink_to_hi().to(expr.span.shrink_to_hi()), - }) - } else if receiver_ty.is_array() { - Some(ArrayIntoIterDiagSub::UseExplicitIntoIter { - start_span: expr.span.shrink_to_lo(), - end_span: receiver_arg.span.shrink_to_hi().to(expr.span.shrink_to_hi()), - }) - } else { - None - }; - cx.emit_span_lint( - ARRAY_INTO_ITER, - call.ident.span, - ArrayIntoIterDiag { target, suggestion: call.ident.span, sub }, - ); - } - } -} diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 6b9f9d1531ea1..0f059bceae7cb 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -30,10 +30,10 @@ use crate::{ BuiltinExplicitOutlivesSuggestion, BuiltinFeatureIssueNote, BuiltinIncompleteFeatures, BuiltinIncompleteFeaturesHelp, BuiltinInternalFeatures, BuiltinKeywordIdents, BuiltinMissingCopyImpl, BuiltinMissingDebugImpl, BuiltinMissingDoc, - BuiltinMutablesTransmutes, BuiltinNoMangleGeneric, BuiltinNonShorthandFieldPatterns, - BuiltinSpecialModuleNameUsed, BuiltinTrivialBounds, BuiltinTypeAliasGenericBounds, - BuiltinTypeAliasGenericBoundsSuggestion, BuiltinTypeAliasWhereClause, - BuiltinUngatedAsyncFnTrackCaller, BuiltinUnpermittedTypeInit, + BuiltinMutablesTransmutes, BuiltinNamedAsmLabel, BuiltinNoMangleGeneric, + BuiltinNonShorthandFieldPatterns, BuiltinSpecialModuleNameUsed, BuiltinTrivialBounds, + BuiltinTypeAliasGenericBounds, BuiltinTypeAliasGenericBoundsSuggestion, + BuiltinTypeAliasWhereClause, BuiltinUngatedAsyncFnTrackCaller, BuiltinUnpermittedTypeInit, BuiltinUnpermittedTypeInitSub, BuiltinUnreachablePub, BuiltinUnsafe, BuiltinUnstableFeatures, BuiltinUnusedDocComment, BuiltinUnusedDocCommentSub, BuiltinWhileTrue, SuggestChangingAssocTypes, @@ -60,7 +60,7 @@ use rustc_middle::ty::GenericArgKind; use rustc_middle::ty::TypeVisitableExt; use rustc_middle::ty::Upcast; use rustc_middle::ty::{self, Ty, TyCtxt, VariantDef}; -use rustc_session::lint::{BuiltinLintDiag, FutureIncompatibilityReason}; +use rustc_session::lint::FutureIncompatibilityReason; use rustc_session::{declare_lint, declare_lint_pass, impl_lint_pass}; use rustc_span::edition::Edition; use rustc_span::source_map::Spanned; @@ -2882,16 +2882,7 @@ impl<'tcx> LateLintPass<'tcx> for NamedAsmLabels { let target_spans: MultiSpan = if spans.len() > 0 { spans.into() } else { (*template_span).into() }; - cx.span_lint_with_diagnostics( - NAMED_ASM_LABELS, - Some(target_spans), - fluent::lint_builtin_asm_labels, - |_| {}, - BuiltinLintDiag::NamedAsmLabel( - "only local labels of the form `:` should be used in inline asm" - .to_string(), - ), - ); + cx.emit_span_lint(NAMED_ASM_LABELS, target_spans, BuiltinNamedAsmLabel); } } } diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index 62ba9ef5c113a..deeb3ae090c57 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -527,30 +527,24 @@ pub struct EarlyContext<'a> { pub buffered: LintBuffer, } -pub trait LintContext { - fn sess(&self) -> &Session; - +impl EarlyContext<'_> { /// Emit a lint at the appropriate level, with an optional associated span and an existing /// diagnostic. /// /// [`lint_level`]: rustc_middle::lint::lint_level#decorate-signature #[rustc_lint_diagnostics] - fn span_lint_with_diagnostics( + pub fn span_lint_with_diagnostics( &self, lint: &'static Lint, - span: Option>, - msg: impl Into, - decorate: impl for<'a, 'b> FnOnce(&'b mut Diag<'a, ()>), + span: MultiSpan, diagnostic: BuiltinLintDiag, ) { - // We first generate a blank diagnostic. - self.opt_span_lint(lint, span, msg, |db| { - // Now, set up surrounding context. - diagnostics::builtin(self.sess(), diagnostic, db); - // Rewrap `db`, and pass control to the user. - decorate(db) - }); + diagnostics::emit_buffered_lint(self, lint, span, diagnostic) } +} + +pub trait LintContext { + fn sess(&self) -> &Session; // FIXME: These methods should not take an Into -- instead, callers should need to // set the span in their `decorate` function (preferably using set_span). diff --git a/compiler/rustc_lint/src/context/diagnostics.rs b/compiler/rustc_lint/src/context/diagnostics.rs index 5ad3ff71a6d4a..236eeee615218 100644 --- a/compiler/rustc_lint/src/context/diagnostics.rs +++ b/compiler/rustc_lint/src/context/diagnostics.rs @@ -1,58 +1,57 @@ #![allow(rustc::diagnostic_outside_of_impl)] #![allow(rustc::untranslatable_diagnostic)] +use std::borrow::Cow; + use rustc_ast::util::unicode::TEXT_FLOW_CONTROL_CHARS; -use rustc_errors::{elided_lifetime_in_path_suggestion, Diag}; -use rustc_errors::{Applicability, SuggestionStyle}; +use rustc_errors::Applicability; +use rustc_errors::{elided_lifetime_in_path_suggestion, DiagArgValue, MultiSpan}; use rustc_middle::middle::stability; -use rustc_session::lint::BuiltinLintDiag; -use rustc_session::Session; +use rustc_session::lint::{BuiltinLintDiag, Lint}; use rustc_span::BytePos; +use crate::{lints, EarlyContext, LintContext as _}; + mod check_cfg; -pub(super) fn builtin(sess: &Session, diagnostic: BuiltinLintDiag, diag: &mut Diag<'_, ()>) { +pub(super) fn emit_buffered_lint( + ctx: &EarlyContext<'_>, + lint: &'static Lint, + span: MultiSpan, + diagnostic: BuiltinLintDiag, +) { + let sess = ctx.sess(); match diagnostic { - BuiltinLintDiag::UnicodeTextFlow(span, content) => { + BuiltinLintDiag::UnicodeTextFlow(comment_span, content) => { let spans: Vec<_> = content .char_indices() .filter_map(|(i, c)| { TEXT_FLOW_CONTROL_CHARS.contains(&c).then(|| { - let lo = span.lo() + BytePos(2 + i as u32); - (c, span.with_lo(lo).with_hi(lo + BytePos(c.len_utf8() as u32))) + let lo = comment_span.lo() + BytePos(2 + i as u32); + (c, comment_span.with_lo(lo).with_hi(lo + BytePos(c.len_utf8() as u32))) }) }) .collect(); - let (an, s) = match spans.len() { - 1 => ("an ", ""), - _ => ("", "s"), - }; - diag.span_label( + let characters = spans + .iter() + .map(|&(c, span)| lints::UnicodeCharNoteSub { span, c_debug: format!("{c:?}") }) + .collect(); + let suggestions = (!spans.is_empty()).then_some(lints::UnicodeTextFlowSuggestion { + spans: spans.iter().map(|(_c, span)| *span).collect(), + }); + ctx.emit_span_lint( + lint, span, - format!( - "this comment contains {an}invisible unicode text flow control codepoint{s}", - ), - ); - for (c, span) in &spans { - diag.span_label(*span, format!("{c:?}")); - } - diag.note( - "these kind of unicode codepoints change the way text flows on \ - applications that support them, but can cause confusion because they \ - change the order of characters on the screen", - ); - if !spans.is_empty() { - diag.multipart_suggestion_with_style( - "if their presence wasn't intentional, you can remove them", - spans.into_iter().map(|(_, span)| (span, "".to_string())).collect(), - Applicability::MachineApplicable, - SuggestionStyle::HideCodeAlways, - ); - } - } - BuiltinLintDiag::Normal => (), - BuiltinLintDiag::AbsPathWithModule(span) => { - let (sugg, app) = match sess.source_map().span_to_snippet(span) { + lints::UnicodeTextFlow { + comment_span, + characters, + suggestions, + num_codepoints: spans.len(), + }, + ) + } + BuiltinLintDiag::AbsPathWithModule(mod_span) => { + let (replacement, applicability) = match sess.source_map().span_to_snippet(mod_span) { Ok(ref s) => { // FIXME(Manishearth) ideally the emitting code // can tell us whether or not this is global @@ -62,160 +61,207 @@ pub(super) fn builtin(sess: &Session, diagnostic: BuiltinLintDiag, diag: &mut Di } Err(_) => ("crate::".to_string(), Applicability::HasPlaceholders), }; - diag.span_suggestion(span, "use `crate`", sugg, app); - } - BuiltinLintDiag::ProcMacroDeriveResolutionFallback(span) => { - diag.span_label( + ctx.emit_span_lint( + lint, span, - "names from parent modules are not accessible without an explicit import", + lints::AbsPathWithModule { + sugg: lints::AbsPathWithModuleSugg { + span: mod_span, + applicability, + replacement, + }, + }, ); } - BuiltinLintDiag::MacroExpandedMacroExportsAccessedByAbsolutePaths(span_def) => { - diag.span_note(span_def, "the macro is defined here"); - } + BuiltinLintDiag::ProcMacroDeriveResolutionFallback { span: macro_span, ns, ident } => ctx + .emit_span_lint( + lint, + span, + lints::ProcMacroDeriveResolutionFallback { span: macro_span, ns, ident }, + ), + BuiltinLintDiag::MacroExpandedMacroExportsAccessedByAbsolutePaths(span_def) => ctx + .emit_span_lint( + lint, + span, + lints::MacroExpandedMacroExportsAccessedByAbsolutePaths { definition: span_def }, + ), BuiltinLintDiag::ElidedLifetimesInPaths(n, path_span, incl_angl_brckt, insertion_span) => { - diag.subdiagnostic( - sess.dcx(), - elided_lifetime_in_path_suggestion( - sess.source_map(), - n, - path_span, - incl_angl_brckt, - insertion_span, - ), + ctx.emit_span_lint( + lint, + span, + lints::ElidedLifetimesInPaths { + subdiag: elided_lifetime_in_path_suggestion( + sess.source_map(), + n, + path_span, + incl_angl_brckt, + insertion_span, + ), + }, ); } - BuiltinLintDiag::UnknownCrateTypes(span, note, sugg) => { - diag.span_suggestion(span, note, sugg, Applicability::MaybeIncorrect); - } - BuiltinLintDiag::UnusedImports(message, replaces, in_test_module) => { - if !replaces.is_empty() { - diag.tool_only_multipart_suggestion( - message, - replaces, - Applicability::MachineApplicable, - ); - } + BuiltinLintDiag::UnknownCrateTypes { span, candidate } => { + let sugg = candidate.map(|candidate| lints::UnknownCrateTypesSub { span, candidate }); + ctx.emit_span_lint(lint, span, lints::UnknownCrateTypes { sugg }); + } + BuiltinLintDiag::UnusedImports { + remove_whole_use, + num_to_remove, + remove_spans, + test_module_span, + span_snippets, + } => { + let sugg = if remove_whole_use { + lints::UnusedImportsSugg::RemoveWholeUse { span: remove_spans[0] } + } else { + lints::UnusedImportsSugg::RemoveImports { remove_spans, num_to_remove } + }; + let test_module_span = + test_module_span.map(|span| sess.source_map().guess_head_span(span)); - if let Some(span) = in_test_module { - diag.span_help( - sess.source_map().guess_head_span(span), - "if this is a test module, consider adding a `#[cfg(test)]` to the containing module", - ); - } + ctx.emit_span_lint( + lint, + span, + lints::UnusedImports { + sugg, + test_module_span, + num_snippets: span_snippets.len(), + span_snippets: DiagArgValue::StrListSepByAnd( + span_snippets.into_iter().map(Cow::Owned).collect(), + ), + }, + ); } BuiltinLintDiag::RedundantImport(spans, ident) => { - for (span, is_imported) in spans { - let introduced = if is_imported { "imported" } else { "defined" }; - let span_msg = if span.is_dummy() { "by the extern prelude" } else { "here" }; - diag.span_label( - span, - format!("the item `{ident}` is already {introduced} {span_msg}"), - ); - } - } - BuiltinLintDiag::DeprecatedMacro(suggestion, span) => { - stability::deprecation_suggestion(diag, "macro", suggestion, span) - } - BuiltinLintDiag::UnusedDocComment(span) => { - diag.span_label(span, "rustdoc does not generate documentation for macro invocations"); - diag.help("to document an item produced by a macro, \ - the macro must produce the documentation as part of its expansion"); - } - BuiltinLintDiag::PatternsInFnsWithoutBody(span, ident) => { - diag.span_suggestion( + let subs = spans + .into_iter() + .map(|(span, is_imported)| { + (match (span.is_dummy(), is_imported) { + (false, true) => lints::RedundantImportSub::ImportedHere, + (false, false) => lints::RedundantImportSub::DefinedHere, + (true, true) => lints::RedundantImportSub::ImportedPrelude, + (true, false) => lints::RedundantImportSub::DefinedPrelude, + })(span) + }) + .collect(); + ctx.emit_span_lint(lint, span, lints::RedundantImport { subs, ident }); + } + BuiltinLintDiag::DeprecatedMacro { + suggestion, + suggestion_span, + note, + path, + since_kind, + } => { + let sub = suggestion.map(|suggestion| stability::DeprecationSuggestion { + span: suggestion_span, + kind: "macro".to_owned(), + suggestion, + }); + ctx.emit_span_lint( + lint, span, - "remove `mut` from the parameter", - ident, - Applicability::MachineApplicable, + stability::Deprecated { sub, kind: "macro".to_owned(), path, note, since_kind }, ); } - BuiltinLintDiag::MissingAbi(span, default_abi) => { - diag.span_label(span, "ABI should be specified here"); - diag.help(format!("the default ABI is {}", default_abi.name())); + BuiltinLintDiag::UnusedDocComment(attr_span) => { + ctx.emit_span_lint(lint, span, lints::UnusedDocComment { span: attr_span }); } - BuiltinLintDiag::LegacyDeriveHelpers(span) => { - diag.span_label(span, "the attribute is introduced here"); + BuiltinLintDiag::PatternsInFnsWithoutBody { span: remove_span, ident, is_foreign } => { + let sub = lints::PatternsInFnsWithoutBodySub { ident, span: remove_span }; + + ctx.emit_span_lint( + lint, + span, + if is_foreign { + lints::PatternsInFnsWithoutBody::Foreign { sub } + } else { + lints::PatternsInFnsWithoutBody::Bodiless { sub } + }, + ); } - BuiltinLintDiag::ProcMacroBackCompat(note) => { - diag.note(note); + BuiltinLintDiag::MissingAbi(label_span, default_abi) => { + ctx.emit_span_lint( + lint, + span, + lints::MissingAbi { span: label_span, default_abi: default_abi.name() }, + ); } - BuiltinLintDiag::OrPatternsBackCompat(span, suggestion) => { - diag.span_suggestion( + BuiltinLintDiag::LegacyDeriveHelpers(label_span) => { + ctx.emit_span_lint(lint, span, lints::LegacyDeriveHelpers { span: label_span }); + } + BuiltinLintDiag::ProcMacroBackCompat { crate_name, fixed_version } => { + ctx.emit_span_lint( + lint, span, - "use pat_param to preserve semantics", - suggestion, - Applicability::MachineApplicable, + lints::ProcMacroBackCompat { crate_name, fixed_version }, + ); + } + BuiltinLintDiag::OrPatternsBackCompat(suggestion_span, suggestion) => { + ctx.emit_span_lint( + lint, + span, + lints::OrPatternsBackCompat { span: suggestion_span, suggestion }, ); } - BuiltinLintDiag::ReservedPrefix(span) => { - diag.span_label(span, "unknown prefix"); - diag.span_suggestion_verbose( - span.shrink_to_hi(), - "insert whitespace here to avoid this being parsed as a prefix in Rust 2021", - " ", - Applicability::MachineApplicable, + BuiltinLintDiag::ReservedPrefix(label_span, prefix) => { + ctx.emit_span_lint( + lint, + span, + lints::ReservedPrefix { + label: label_span, + suggestion: label_span.shrink_to_hi(), + prefix, + }, ); } BuiltinLintDiag::UnusedBuiltinAttribute { attr_name, macro_name, invoc_span } => { - diag.span_note( - invoc_span, - format!("the built-in attribute `{attr_name}` will be ignored, since it's applied to the macro invocation `{macro_name}`") - ); + ctx.emit_span_lint( + lint, + span, + lints::UnusedBuiltinAttribute { invoc_span, attr_name, macro_name }, + ); } BuiltinLintDiag::TrailingMacro(is_trailing, name) => { - if is_trailing { - diag.note("macro invocations at the end of a block are treated as expressions"); - diag.note(format!("to ignore the value produced by the macro, add a semicolon after the invocation of `{name}`")); - } - } - BuiltinLintDiag::BreakWithLabelAndLoop(span) => { - diag.multipart_suggestion( - "wrap this expression in parentheses", - vec![ - (span.shrink_to_lo(), "(".to_string()), - (span.shrink_to_hi(), ")".to_string()), - ], - Applicability::MachineApplicable, - ); + ctx.emit_span_lint(lint, span, lints::TrailingMacro { is_trailing, name }); } - BuiltinLintDiag::NamedAsmLabel(help) => { - diag.help(help); - diag.note("see the asm section of Rust By Example for more information"); + BuiltinLintDiag::BreakWithLabelAndLoop(sugg_span) => { + ctx.emit_span_lint( + lint, + span, + lints::BreakWithLabelAndLoop { + sub: lints::BreakWithLabelAndLoopSub { + left: sugg_span.shrink_to_lo(), + right: sugg_span.shrink_to_hi(), + }, + }, + ); } BuiltinLintDiag::UnexpectedCfgName(name, value) => { - check_cfg::unexpected_cfg_name(sess, diag, name, value) + ctx.emit_span_lint(lint, span, check_cfg::unexpected_cfg_name(sess, name, value)); } BuiltinLintDiag::UnexpectedCfgValue(name, value) => { - check_cfg::unexpected_cfg_value(sess, diag, name, value) - } - BuiltinLintDiag::DeprecatedWhereclauseLocation(sugg) => { - let left_sp = diag.span.primary_span().unwrap(); - match sugg { - Some((right_sp, sugg)) => diag.multipart_suggestion( - "move it to the end of the type declaration", - vec![(left_sp, String::new()), (right_sp, sugg)], - Applicability::MachineApplicable, - ), - None => diag.span_suggestion( - left_sp, - "remove this `where`", - "", - Applicability::MachineApplicable, - ), + ctx.emit_span_lint(lint, span, check_cfg::unexpected_cfg_value(sess, name, value)); + } + BuiltinLintDiag::DeprecatedWhereclauseLocation(left_sp, sugg) => { + let suggestion = match sugg { + Some((right_sp, sugg)) => lints::DeprecatedWhereClauseLocationSugg::MoveToEnd { + left: left_sp, + right: right_sp, + sugg, + }, + None => lints::DeprecatedWhereClauseLocationSugg::RemoveWhere { span: left_sp }, }; - diag.note("see issue #89122 for more information"); + ctx.emit_span_lint(lint, span, lints::DeprecatedWhereClauseLocation { suggestion }); } BuiltinLintDiag::SingleUseLifetime { param_span, use_span: Some((use_span, elide)), deletion_span, + ident, } => { debug!(?param_span, ?use_span, ?deletion_span); - diag.span_label(param_span, "this lifetime..."); - diag.span_label(use_span, "...is used only here"); - if let Some(deletion_span) = deletion_span { - let msg = "elide the single-use lifetime"; + let suggestion = if let Some(deletion_span) = deletion_span { let (use_span, replace_lt) = if elide { let use_span = sess.source_map().span_extend_while_whitespace(use_span); (use_span, String::new()) @@ -226,24 +272,22 @@ pub(super) fn builtin(sess: &Session, diagnostic: BuiltinLintDiag, diag: &mut Di // issue 107998 for the case such as a wrong function pointer type // `deletion_span` is empty and there is no need to report lifetime uses here - let suggestions = if deletion_span.is_empty() { - vec![(use_span, replace_lt)] - } else { - vec![(deletion_span, String::new()), (use_span, replace_lt)] - }; - diag.multipart_suggestion(msg, suggestions, Applicability::MachineApplicable); - } + let deletion_span = + if deletion_span.is_empty() { None } else { Some(deletion_span) }; + Some(lints::SingleUseLifetimeSugg { deletion_span, use_span, replace_lt }) + } else { + None + }; + + ctx.emit_span_lint( + lint, + span, + lints::SingleUseLifetime { suggestion, param_span, use_span, ident }, + ); } - BuiltinLintDiag::SingleUseLifetime { param_span: _, use_span: None, deletion_span } => { + BuiltinLintDiag::SingleUseLifetime { use_span: None, deletion_span, ident, .. } => { debug!(?deletion_span); - if let Some(deletion_span) = deletion_span { - diag.span_suggestion( - deletion_span, - "elide the unused lifetime", - "", - Applicability::MachineApplicable, - ); - } + ctx.emit_span_lint(lint, span, lints::UnusedLifetime { deletion_span, ident }); } BuiltinLintDiag::NamedArgumentUsedPositionally { position_sp_to_replace, @@ -252,19 +296,12 @@ pub(super) fn builtin(sess: &Session, diagnostic: BuiltinLintDiag, diag: &mut Di named_arg_name, is_formatting_arg, } => { - diag.span_label( - named_arg_sp, - "this named argument is referred to by position in formatting string", - ); - if let Some(positional_arg_for_msg) = position_sp_for_msg { - let msg = format!( - "this formatting argument uses named argument `{named_arg_name}` by position" - ); - diag.span_label(positional_arg_for_msg, msg); - } - - if let Some(positional_arg_to_replace) = position_sp_to_replace { - let name = if is_formatting_arg { named_arg_name + "$" } else { named_arg_name }; + let (suggestion, name) = if let Some(positional_arg_to_replace) = position_sp_to_replace + { + let mut name = named_arg_name.clone(); + if is_formatting_arg { + name.push('$') + }; let span_to_replace = if let Ok(positional_arg_content) = sess.source_map().span_to_snippet(positional_arg_to_replace) && positional_arg_content.starts_with(':') @@ -273,31 +310,40 @@ pub(super) fn builtin(sess: &Session, diagnostic: BuiltinLintDiag, diag: &mut Di } else { positional_arg_to_replace }; - diag.span_suggestion_verbose( - span_to_replace, - "use the named argument by name to avoid ambiguity", + (Some(span_to_replace), name) + } else { + (None, String::new()) + }; + + ctx.emit_span_lint( + lint, + span, + lints::NamedArgumentUsedPositionally { + named_arg_sp, + position_label_sp: position_sp_for_msg, + suggestion, name, - Applicability::MaybeIncorrect, - ); - } + named_arg_name, + }, + ) } - BuiltinLintDiag::ByteSliceInPackedStructWithDerive => { - diag.help("consider implementing the trait by hand, or remove the `packed` attribute"); + BuiltinLintDiag::ByteSliceInPackedStructWithDerive { ty } => { + ctx.emit_span_lint(lint, span, lints::ByteSliceInPackedStructWithDerive { ty }) } BuiltinLintDiag::UnusedExternCrate { removal_span } => { - diag.span_suggestion(removal_span, "remove it", "", Applicability::MachineApplicable); + ctx.emit_span_lint(lint, span, lints::UnusedExternCrate { removal_span }) } BuiltinLintDiag::ExternCrateNotIdiomatic { vis_span, ident_span } => { let suggestion_span = vis_span.between(ident_span); - diag.span_suggestion_verbose( - suggestion_span, - "convert it to a `use`", - if vis_span.is_empty() { "use " } else { " use " }, - Applicability::MachineApplicable, + let code = if vis_span.is_empty() { "use " } else { " use " }; + ctx.emit_span_lint( + lint, + span, + lints::ExternCrateNotIdiomatic { span: suggestion_span, code }, ); } BuiltinLintDiag::AmbiguousGlobImports { diag: ambiguity } => { - rustc_errors::report_ambiguity_error(diag, ambiguity); + ctx.emit_span_lint(lint, span, lints::AmbiguousGlobImports { ambiguity }); } BuiltinLintDiag::AmbiguousGlobReexports { name, @@ -305,15 +351,15 @@ pub(super) fn builtin(sess: &Session, diagnostic: BuiltinLintDiag, diag: &mut Di first_reexport_span, duplicate_reexport_span, } => { - diag.span_label( - first_reexport_span, - format!("the name `{name}` in the {namespace} namespace is first re-exported here"), - ); - diag.span_label( - duplicate_reexport_span, - format!( - "but the name `{name}` in the {namespace} namespace is also re-exported here" - ), + ctx.emit_span_lint( + lint, + span, + lints::AmbiguousGlobReexports { + first_reexport: first_reexport_span, + duplicate_reexport: duplicate_reexport_span, + name, + namespace, + }, ); } BuiltinLintDiag::HiddenGlobReexports { @@ -322,38 +368,127 @@ pub(super) fn builtin(sess: &Session, diagnostic: BuiltinLintDiag, diag: &mut Di glob_reexport_span, private_item_span, } => { - diag.span_note(glob_reexport_span, format!("the name `{name}` in the {namespace} namespace is supposed to be publicly re-exported here")); - diag.span_note(private_item_span, "but the private item here shadows it".to_owned()); - } - BuiltinLintDiag::UnusedQualifications { removal_span } => { - diag.span_suggestion_verbose( - removal_span, - "remove the unnecessary path segments", - "", - Applicability::MachineApplicable, + ctx.emit_span_lint( + lint, + span, + lints::HiddenGlobReexports { + glob_reexport: glob_reexport_span, + private_item: private_item_span, + + name, + namespace, + }, ); } - BuiltinLintDiag::AssociatedConstElidedLifetime { elided, span } => { - diag.span_suggestion_verbose( - if elided { span.shrink_to_hi() } else { span }, - "use the `'static` lifetime", - if elided { "'static " } else { "'static" }, - Applicability::MachineApplicable, - ); + BuiltinLintDiag::UnusedQualifications { removal_span } => { + ctx.emit_span_lint(lint, span, lints::UnusedQualifications { removal_span }); } - BuiltinLintDiag::RedundantImportVisibility { max_vis, span } => { - diag.span_note(span, format!("the most public imported item is `{max_vis}`")); - diag.help( - "reduce the glob import's visibility or increase visibility of imported items", + BuiltinLintDiag::AssociatedConstElidedLifetime { elided, span: lt_span } => { + let lt_span = if elided { lt_span.shrink_to_hi() } else { lt_span }; + let code = if elided { "'static " } else { "'static" }; + ctx.emit_span_lint( + lint, + span, + lints::AssociatedConstElidedLifetime { span: lt_span, code, elided }, ); } - BuiltinLintDiag::MaybeTypo { span, name } => { - diag.span_suggestion_verbose( + BuiltinLintDiag::RedundantImportVisibility { max_vis, span: vis_span, import_vis } => { + ctx.emit_span_lint( + lint, span, - "an attribute with a similar name exists", - name, - Applicability::MachineApplicable, + lints::RedundantImportVisibility { span: vis_span, help: (), max_vis, import_vis }, ); } + BuiltinLintDiag::UnknownDiagnosticAttribute { span: typo_span, typo_name } => { + let typo = typo_name.map(|typo_name| lints::UnknownDiagnosticAttributeTypoSugg { + span: typo_span, + typo_name, + }); + ctx.emit_span_lint(lint, span, lints::UnknownDiagnosticAttribute { typo }); + } + BuiltinLintDiag::MacroUseDeprecated => { + ctx.emit_span_lint(lint, span, lints::MacroUseDeprecated) + } + BuiltinLintDiag::UnusedMacroUse => ctx.emit_span_lint(lint, span, lints::UnusedMacroUse), + BuiltinLintDiag::PrivateExternCrateReexport(ident) => { + ctx.emit_span_lint(lint, span, lints::PrivateExternCrateReexport { ident }) + } + BuiltinLintDiag::UnusedLabel => ctx.emit_span_lint(lint, span, lints::UnusedLabel), + BuiltinLintDiag::MacroIsPrivate(ident) => { + ctx.emit_span_lint(lint, span, lints::MacroIsPrivate { ident }) + } + BuiltinLintDiag::UnusedMacroDefinition(name) => { + ctx.emit_span_lint(lint, span, lints::UnusedMacroDefinition { name }) + } + BuiltinLintDiag::MacroRuleNeverUsed(n, name) => { + ctx.emit_span_lint(lint, span, lints::MacroRuleNeverUsed { n: n + 1, name }) + } + BuiltinLintDiag::UnstableFeature(msg) => { + ctx.emit_span_lint(lint, span, lints::UnstableFeature { msg }) + } + BuiltinLintDiag::AvoidUsingIntelSyntax => { + ctx.emit_span_lint(lint, span, lints::AvoidIntelSyntax) + } + BuiltinLintDiag::AvoidUsingAttSyntax => { + ctx.emit_span_lint(lint, span, lints::AvoidAttSyntax) + } + BuiltinLintDiag::IncompleteInclude => { + ctx.emit_span_lint(lint, span, lints::IncompleteInclude) + } + BuiltinLintDiag::UnnameableTestItems => { + ctx.emit_span_lint(lint, span, lints::UnnameableTestItems) + } + BuiltinLintDiag::DuplicateMacroAttribute => { + ctx.emit_span_lint(lint, span, lints::DuplicateMacroAttribute) + } + BuiltinLintDiag::CfgAttrNoAttributes => { + ctx.emit_span_lint(lint, span, lints::CfgAttrNoAttributes) + } + BuiltinLintDiag::CrateTypeInCfgAttr => { + ctx.emit_span_lint(lint, span, lints::CrateTypeInCfgAttr) + } + BuiltinLintDiag::CrateNameInCfgAttr => { + ctx.emit_span_lint(lint, span, lints::CrateNameInCfgAttr) + } + BuiltinLintDiag::MissingFragmentSpecifier => { + ctx.emit_span_lint(lint, span, lints::MissingFragmentSpecifier) + } + BuiltinLintDiag::MetaVariableStillRepeating(name) => { + ctx.emit_span_lint(lint, span, lints::MetaVariableStillRepeating { name }) + } + BuiltinLintDiag::MetaVariableWrongOperator => { + ctx.emit_span_lint(lint, span, lints::MetaVariableWrongOperator) + } + BuiltinLintDiag::DuplicateMatcherBinding => { + ctx.emit_span_lint(lint, span, lints::DuplicateMatcherBinding) + } + BuiltinLintDiag::UnknownMacroVariable(name) => { + ctx.emit_span_lint(lint, span, lints::UnknownMacroVariable { name }) + } + BuiltinLintDiag::UnusedCrateDependency { extern_crate, local_crate } => ctx.emit_span_lint( + lint, + span, + lints::UnusedCrateDependency { extern_crate, local_crate }, + ), + BuiltinLintDiag::WasmCAbi => ctx.emit_span_lint(lint, span, lints::WasmCAbi), + BuiltinLintDiag::IllFormedAttributeInput { suggestions } => ctx.emit_span_lint( + lint, + span, + lints::IllFormedAttributeInput { + num_suggestions: suggestions.len(), + suggestions: DiagArgValue::StrListSepByAnd( + suggestions.into_iter().map(|s| format!("`{s}`").into()).collect(), + ), + }, + ), + BuiltinLintDiag::InnerAttributeUnstable { is_macro } => ctx.emit_span_lint( + lint, + span, + if is_macro { + lints::InnerAttributeUnstable::InnerMacroAttribute + } else { + lints::InnerAttributeUnstable::CustomInnerAttribute + }, + ), } } diff --git a/compiler/rustc_lint/src/context/diagnostics/check_cfg.rs b/compiler/rustc_lint/src/context/diagnostics/check_cfg.rs index 3c423b4e2aa6d..020ca1753cf0b 100644 --- a/compiler/rustc_lint/src/context/diagnostics/check_cfg.rs +++ b/compiler/rustc_lint/src/context/diagnostics/check_cfg.rs @@ -1,52 +1,52 @@ -use rustc_errors::{Applicability, Diag}; use rustc_middle::bug; use rustc_session::{config::ExpectedValues, Session}; use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::{sym, Span, Symbol}; +use crate::lints; + const MAX_CHECK_CFG_NAMES_OR_VALUES: usize = 35; -fn check_cfg_expected_note( +fn sort_and_truncate_possibilities( sess: &Session, - possibilities: &[Symbol], - type_: &str, - name: Option, - suffix: &str, -) -> String { - use std::fmt::Write; - + mut possibilities: Vec, +) -> (Vec, usize) { let n_possibilities = if sess.opts.unstable_opts.check_cfg_all_expected { possibilities.len() } else { std::cmp::min(possibilities.len(), MAX_CHECK_CFG_NAMES_OR_VALUES) }; - let mut possibilities = possibilities.iter().map(Symbol::as_str).collect::>(); - possibilities.sort(); + possibilities.sort_by(|s1, s2| s1.as_str().cmp(s2.as_str())); let and_more = possibilities.len().saturating_sub(n_possibilities); - let possibilities = possibilities[..n_possibilities].join("`, `"); + possibilities.truncate(n_possibilities); + (possibilities, and_more) +} - let mut note = String::with_capacity(50 + possibilities.len()); +enum EscapeQuotes { + Yes, + No, +} - write!(&mut note, "expected {type_}").unwrap(); - if let Some(name) = name { - write!(&mut note, " for `{name}`").unwrap(); - } - write!(&mut note, " are: {suffix}`{possibilities}`").unwrap(); - if and_more > 0 { - write!(&mut note, " and {and_more} more").unwrap(); +fn to_check_cfg_arg(name: Symbol, value: Option, quotes: EscapeQuotes) -> String { + if let Some(value) = value { + let value = str::escape_debug(value.as_str()).to_string(); + let values = match quotes { + EscapeQuotes::Yes => format!("\\\"{}\\\"", value.replace("\"", "\\\\\\\\\"")), + EscapeQuotes::No => format!("\"{value}\""), + }; + format!("cfg({name}, values({values}))") + } else { + format!("cfg({name})") } - - note } pub(super) fn unexpected_cfg_name( sess: &Session, - diag: &mut Diag<'_, ()>, (name, name_span): (Symbol, Span), value: Option<(Symbol, Span)>, -) { +) -> lints::UnexpectedCfgName { #[allow(rustc::potential_query_instability)] let possibilities: Vec = sess.psess.check_config.expecteds.keys().copied().collect(); @@ -69,116 +69,122 @@ pub(super) fn unexpected_cfg_name( let is_from_cargo = rustc_session::utils::was_invoked_from_cargo(); let mut is_feature_cfg = name == sym::feature; - if is_feature_cfg && is_from_cargo { - diag.help("consider defining some features in `Cargo.toml`"); + let code_sugg = if is_feature_cfg && is_from_cargo { + lints::unexpected_cfg_name::CodeSuggestion::DefineFeatures // Suggest the most probable if we found one } else if let Some(best_match) = find_best_match_for_name(&possibilities, name, None) { + is_feature_cfg |= best_match == sym::feature; + if let Some(ExpectedValues::Some(best_match_values)) = sess.psess.check_config.expecteds.get(&best_match) { // We will soon sort, so the initial order does not matter. #[allow(rustc::potential_query_instability)] - let mut possibilities = - best_match_values.iter().flatten().map(Symbol::as_str).collect::>(); - possibilities.sort(); + let mut possibilities = best_match_values.iter().flatten().collect::>(); + possibilities.sort_by_key(|s| s.as_str()); + + let get_possibilities_sub = || { + if !possibilities.is_empty() { + let possibilities = + possibilities.iter().copied().cloned().collect::>().into(); + Some(lints::unexpected_cfg_name::ExpectedValues { best_match, possibilities }) + } else { + None + } + }; - let mut should_print_possibilities = true; if let Some((value, value_span)) = value { if best_match_values.contains(&Some(value)) { - diag.span_suggestion( - name_span, - "there is a config with a similar name and value", - best_match, - Applicability::MaybeIncorrect, - ); - should_print_possibilities = false; + lints::unexpected_cfg_name::CodeSuggestion::SimilarNameAndValue { + span: name_span, + code: best_match.to_string(), + } } else if best_match_values.contains(&None) { - diag.span_suggestion( - name_span.to(value_span), - "there is a config with a similar name and no value", - best_match, - Applicability::MaybeIncorrect, - ); - should_print_possibilities = false; + lints::unexpected_cfg_name::CodeSuggestion::SimilarNameNoValue { + span: name_span.to(value_span), + code: best_match.to_string(), + } } else if let Some(first_value) = possibilities.first() { - diag.span_suggestion( - name_span.to(value_span), - "there is a config with a similar name and different values", - format!("{best_match} = \"{first_value}\""), - Applicability::MaybeIncorrect, - ); + lints::unexpected_cfg_name::CodeSuggestion::SimilarNameDifferentValues { + span: name_span.to(value_span), + code: format!("{best_match} = \"{first_value}\""), + expected: get_possibilities_sub(), + } } else { - diag.span_suggestion( - name_span.to(value_span), - "there is a config with a similar name and different values", - best_match, - Applicability::MaybeIncorrect, - ); - }; + lints::unexpected_cfg_name::CodeSuggestion::SimilarNameDifferentValues { + span: name_span.to(value_span), + code: best_match.to_string(), + expected: get_possibilities_sub(), + } + } } else { - diag.span_suggestion( - name_span, - "there is a config with a similar name", - best_match, - Applicability::MaybeIncorrect, - ); - } - - if !possibilities.is_empty() && should_print_possibilities { - let possibilities = possibilities.join("`, `"); - diag.help(format!("expected values for `{best_match}` are: `{possibilities}`")); + lints::unexpected_cfg_name::CodeSuggestion::SimilarName { + span: name_span, + code: best_match.to_string(), + expected: get_possibilities_sub(), + } } } else { - diag.span_suggestion( - name_span, - "there is a config with a similar name", - best_match, - Applicability::MaybeIncorrect, - ); + lints::unexpected_cfg_name::CodeSuggestion::SimilarName { + span: name_span, + code: best_match.to_string(), + expected: None, + } } - - is_feature_cfg |= best_match == sym::feature; } else { - if !names_possibilities.is_empty() && names_possibilities.len() <= 3 { + let similar_values = if !names_possibilities.is_empty() && names_possibilities.len() <= 3 { names_possibilities.sort(); - for cfg_name in names_possibilities.iter() { - diag.span_suggestion( - name_span, - "found config with similar value", - format!("{cfg_name} = \"{name}\""), - Applicability::MaybeIncorrect, - ); - } - } - if !possibilities.is_empty() { - diag.help_once(check_cfg_expected_note(sess, &possibilities, "names", None, "")); + names_possibilities + .iter() + .map(|cfg_name| lints::unexpected_cfg_name::FoundWithSimilarValue { + span: name_span, + code: format!("{cfg_name} = \"{name}\""), + }) + .collect() + } else { + vec![] + }; + let expected_names = if !possibilities.is_empty() { + let (possibilities, and_more) = sort_and_truncate_possibilities(sess, possibilities); + Some(lints::unexpected_cfg_name::ExpectedNames { + possibilities: possibilities.into(), + and_more, + }) + } else { + None + }; + lints::unexpected_cfg_name::CodeSuggestion::SimilarValues { + with_similar_values: similar_values, + expected_names, } - } + }; + + let inst = |escape_quotes| to_check_cfg_arg(name, value.map(|(v, _s)| v), escape_quotes); - let inst = if let Some((value, _value_span)) = value { - let pre = if is_from_cargo { "\\" } else { "" }; - format!("cfg({name}, values({pre}\"{value}{pre}\"))") + let invocation_help = if is_from_cargo { + let sub = if !is_feature_cfg { + Some(lints::UnexpectedCfgCargoHelp::new( + &inst(EscapeQuotes::No), + &inst(EscapeQuotes::Yes), + )) + } else { + None + }; + lints::unexpected_cfg_name::InvocationHelp::Cargo { sub } } else { - format!("cfg({name})") + lints::unexpected_cfg_name::InvocationHelp::Rustc(lints::UnexpectedCfgRustcHelp::new( + &inst(EscapeQuotes::No), + )) }; - if is_from_cargo { - if !is_feature_cfg { - diag.help(format!("consider using a Cargo feature instead or adding `println!(\"cargo::rustc-check-cfg={inst}\");` to the top of the `build.rs`")); - } - diag.note("see for more information about checking conditional configuration"); - } else { - diag.help(format!("to expect this configuration use `--check-cfg={inst}`")); - diag.note("see for more information about checking conditional configuration"); - } + lints::UnexpectedCfgName { code_sugg, invocation_help, name } } pub(super) fn unexpected_cfg_value( sess: &Session, - diag: &mut Diag<'_, ()>, (name, name_span): (Symbol, Span), value: Option<(Symbol, Span)>, -) { +) -> lints::UnexpectedCfgValue { let Some(ExpectedValues::Some(values)) = &sess.psess.check_config.expecteds.get(&name) else { bug!( "it shouldn't be possible to have a diagnostic on a value whose name is not in values" @@ -198,81 +204,90 @@ pub(super) fn unexpected_cfg_value( // Show the full list if all possible values for a given name, but don't do it // for names as the possibilities could be very long - if !possibilities.is_empty() { - diag.note(check_cfg_expected_note( - sess, - &possibilities, - "values", - Some(name), - if have_none_possibility { "(none), " } else { "" }, - )); + let code_sugg = if !possibilities.is_empty() { + let expected_values = { + let (possibilities, and_more) = + sort_and_truncate_possibilities(sess, possibilities.clone()); + lints::unexpected_cfg_value::ExpectedValues { + name, + have_none_possibility, + possibilities: possibilities.into(), + and_more, + } + }; - if let Some((value, value_span)) = value { + let suggestion = if let Some((value, value_span)) = value { // Suggest the most probable if we found one if let Some(best_match) = find_best_match_for_name(&possibilities, value, None) { - diag.span_suggestion( - value_span, - "there is a expected value with a similar name", - format!("\"{best_match}\""), - Applicability::MaybeIncorrect, - ); + Some(lints::unexpected_cfg_value::ChangeValueSuggestion::SimilarName { + span: value_span, + best_match, + }) + } else { + None } } else if let &[first_possibility] = &possibilities[..] { - diag.span_suggestion( - name_span.shrink_to_hi(), - "specify a config value", - format!(" = \"{first_possibility}\""), - Applicability::MaybeIncorrect, - ); - } + Some(lints::unexpected_cfg_value::ChangeValueSuggestion::SpecifyValue { + span: name_span.shrink_to_hi(), + first_possibility, + }) + } else { + None + }; + + lints::unexpected_cfg_value::CodeSuggestion::ChangeValue { expected_values, suggestion } } else if have_none_possibility { - diag.note(format!("no expected value for `{name}`")); - if let Some((_value, value_span)) = value { - diag.span_suggestion( - name_span.shrink_to_hi().to(value_span), - "remove the value", - "", - Applicability::MaybeIncorrect, - ); - } + let suggestion = + value.map(|(_value, value_span)| lints::unexpected_cfg_value::RemoveValueSuggestion { + span: name_span.shrink_to_hi().to(value_span), + }); + lints::unexpected_cfg_value::CodeSuggestion::RemoveValue { suggestion, name } } else { - diag.note(format!("no expected values for `{name}`")); - - let sp = if let Some((_value, value_span)) = value { + let span = if let Some((_value, value_span)) = value { name_span.to(value_span) } else { name_span }; - diag.span_suggestion(sp, "remove the condition", "", Applicability::MaybeIncorrect); - } + let suggestion = lints::unexpected_cfg_value::RemoveConditionSuggestion { span }; + lints::unexpected_cfg_value::CodeSuggestion::RemoveCondition { suggestion, name } + }; // We don't want to suggest adding values to well known names // since those are defined by rustc it-self. Users can still // do it if they want, but should not encourage them. let is_cfg_a_well_know_name = sess.psess.check_config.well_known_names.contains(&name); - let inst = if let Some((value, _value_span)) = value { - let pre = if is_from_cargo { "\\" } else { "" }; - format!("cfg({name}, values({pre}\"{value}{pre}\"))") - } else { - format!("cfg({name})") - }; + let inst = |escape_quotes| to_check_cfg_arg(name, value.map(|(v, _s)| v), escape_quotes); - if is_from_cargo { - if name == sym::feature { + let invocation_help = if is_from_cargo { + let help = if name == sym::feature { if let Some((value, _value_span)) = value { - diag.help(format!("consider adding `{value}` as a feature in `Cargo.toml`")); + Some(lints::unexpected_cfg_value::CargoHelp::AddFeature { value }) } else { - diag.help("consider defining some features in `Cargo.toml`"); + Some(lints::unexpected_cfg_value::CargoHelp::DefineFeatures) } } else if !is_cfg_a_well_know_name { - diag.help(format!("consider using a Cargo feature instead or adding `println!(\"cargo::rustc-check-cfg={inst}\");` to the top of the `build.rs`")); - } - diag.note("see for more information about checking conditional configuration"); + Some(lints::unexpected_cfg_value::CargoHelp::Other(lints::UnexpectedCfgCargoHelp::new( + &inst(EscapeQuotes::No), + &inst(EscapeQuotes::Yes), + ))) + } else { + None + }; + lints::unexpected_cfg_value::InvocationHelp::Cargo(help) } else { - if !is_cfg_a_well_know_name { - diag.help(format!("to expect this configuration use `--check-cfg={inst}`")); - } - diag.note("see for more information about checking conditional configuration"); + let help = if !is_cfg_a_well_know_name { + Some(lints::UnexpectedCfgRustcHelp::new(&inst(EscapeQuotes::No))) + } else { + None + }; + lints::unexpected_cfg_value::InvocationHelp::Rustc(help) + }; + + lints::UnexpectedCfgValue { + code_sugg, + invocation_help, + has_value: value.is_some(), + value: value.map_or_else(String::new, |(v, _span)| v.to_string()), } } diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs index 3f627baf7704f..736c7a1106932 100644 --- a/compiler/rustc_lint/src/early.rs +++ b/compiler/rustc_lint/src/early.rs @@ -14,7 +14,7 @@ //! upon. As the ast is traversed, this keeps track of the current lint level //! for all lint attributes. -use crate::context::{EarlyContext, LintContext, LintStore}; +use crate::context::{EarlyContext, LintStore}; use crate::passes::{EarlyLintPass, EarlyLintPassObject}; use rustc_ast::ptr::P; use rustc_ast::visit::{self as ast_visit, walk_list, Visitor}; @@ -44,14 +44,8 @@ impl<'a, T: EarlyLintPass> EarlyContextAndPass<'a, T> { #[allow(rustc::diagnostic_outside_of_impl)] fn inlined_check_id(&mut self, id: ast::NodeId) { for early_lint in self.context.buffered.take(id) { - let BufferedEarlyLint { span, msg, node_id: _, lint_id, diagnostic } = early_lint; - self.context.span_lint_with_diagnostics( - lint_id.lint, - Some(span), - msg, - |_| {}, - diagnostic, - ); + let BufferedEarlyLint { span, node_id: _, lint_id, diagnostic } = early_lint; + self.context.span_lint_with_diagnostics(lint_id.lint, span, diagnostic); } } diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index d93edadcfbcc2..64fcc7e46e0e0 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -42,7 +42,6 @@ #[macro_use] extern crate tracing; -mod array_into_iter; mod async_fn_in_trait; pub mod builtin; mod context; @@ -76,18 +75,18 @@ mod passes; mod ptr_nulls; mod redundant_semicolon; mod reference_casting; +mod shadowed_into_iter; mod traits; mod types; mod unit_bindings; mod unused; -pub use array_into_iter::ARRAY_INTO_ITER; +pub use shadowed_into_iter::{ARRAY_INTO_ITER, BOXED_SLICE_INTO_ITER}; use rustc_hir::def_id::LocalModDefId; use rustc_middle::query::Providers; use rustc_middle::ty::TyCtxt; -use array_into_iter::ArrayIntoIter; use async_fn_in_trait::AsyncFnInTrait; use builtin::*; use deref_into_dyn_supertrait::*; @@ -112,6 +111,7 @@ use pass_by_value::*; use ptr_nulls::*; use redundant_semicolon::*; use reference_casting::*; +use shadowed_into_iter::ShadowedIntoIter; use traits::*; use types::*; use unit_bindings::*; @@ -215,7 +215,7 @@ late_lint_methods!( DerefNullPtr: DerefNullPtr, UnstableFeatures: UnstableFeatures, UngatedAsyncFnTrackCaller: UngatedAsyncFnTrackCaller, - ArrayIntoIter: ArrayIntoIter::default(), + ShadowedIntoIter: ShadowedIntoIter, DropTraitConstraints: DropTraitConstraints, TemporaryCStringAsPtr: TemporaryCStringAsPtr, NonPanicFmt: NonPanicFmt, diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 7efa5245baa20..3bd6faca37963 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -5,16 +5,22 @@ use std::num::NonZero; use crate::errors::RequestedLevel; use crate::fluent_generated as fluent; use rustc_errors::{ - codes::*, Applicability, Diag, DiagMessage, DiagStyledString, EmissionGuarantee, - LintDiagnostic, SubdiagMessageOp, Subdiagnostic, SuggestionStyle, + codes::*, Applicability, Diag, DiagArgValue, DiagMessage, DiagStyledString, + ElidedLifetimeInPathSubdiag, EmissionGuarantee, LintDiagnostic, SubdiagMessageOp, + Subdiagnostic, SuggestionStyle, }; -use rustc_hir::def_id::DefId; +use rustc_hir::{def::Namespace, def_id::DefId}; use rustc_macros::{LintDiagnostic, Subdiagnostic}; use rustc_middle::ty::{ inhabitedness::InhabitedPredicate, Clause, PolyExistentialTraitRef, Ty, TyCtxt, }; -use rustc_session::Session; -use rustc_span::{edition::Edition, sym, symbol::Ident, Span, Symbol}; +use rustc_session::{lint::AmbiguityErrorDiag, Session}; +use rustc_span::{ + edition::Edition, + sym, + symbol::{Ident, MacroRulesNormalizedIdent}, + Span, Symbol, +}; use crate::{ builtin::InitError, builtin::TypeAliasBounds, errors::OverruledAttributeSub, LateContext, @@ -22,17 +28,18 @@ use crate::{ // array_into_iter.rs #[derive(LintDiagnostic)] -#[diag(lint_array_into_iter)] -pub struct ArrayIntoIterDiag<'a> { - pub target: &'a str, +#[diag(lint_shadowed_into_iter)] +pub struct ShadowedIntoIterDiag { + pub target: &'static str, + pub edition: &'static str, #[suggestion(lint_use_iter_suggestion, code = "iter", applicability = "machine-applicable")] pub suggestion: Span, #[subdiagnostic] - pub sub: Option, + pub sub: Option, } #[derive(Subdiagnostic)] -pub enum ArrayIntoIterDiagSub { +pub enum ShadowedIntoIterDiagSub { #[suggestion(lint_remove_into_iter_suggestion, code = "", applicability = "maybe-incorrect")] RemoveIntoIter { #[primary_span] @@ -1946,3 +1953,837 @@ pub struct UnitBindingsDiag { #[label] pub label: Span, } + +#[derive(LintDiagnostic)] +#[diag(lint_builtin_asm_labels)] +#[help] +#[note] +pub struct BuiltinNamedAsmLabel; + +#[derive(Subdiagnostic)] +#[help(lint_unexpected_cfg_add_cargo_feature)] +#[help(lint_unexpected_cfg_add_cargo_toml_lint_cfg)] +#[help(lint_unexpected_cfg_add_build_rs_println)] +pub struct UnexpectedCfgCargoHelp { + pub build_rs_println: String, + pub cargo_toml_lint_cfg: String, +} + +impl UnexpectedCfgCargoHelp { + pub fn new(unescaped: &str, escaped: &str) -> Self { + Self { + cargo_toml_lint_cfg: format!( + "\n [lints.rust]\n unexpected_cfgs = {{ level = \"warn\", check-cfg = ['{unescaped}'] }}", + ), + build_rs_println: format!("println!(\"cargo::rustc-check-cfg={escaped}\");",), + } + } +} + +#[derive(Subdiagnostic)] +#[help(lint_unexpected_cfg_add_cmdline_arg)] +pub struct UnexpectedCfgRustcHelp { + pub cmdline_arg: String, +} + +impl UnexpectedCfgRustcHelp { + pub fn new(unescaped: &str) -> Self { + Self { cmdline_arg: format!("--check-cfg={unescaped}") } + } +} + +#[derive(LintDiagnostic)] +#[diag(lint_unexpected_cfg_name)] +pub struct UnexpectedCfgName { + #[subdiagnostic] + pub code_sugg: unexpected_cfg_name::CodeSuggestion, + #[subdiagnostic] + pub invocation_help: unexpected_cfg_name::InvocationHelp, + + pub name: Symbol, +} + +pub mod unexpected_cfg_name { + use rustc_errors::DiagSymbolList; + use rustc_macros::Subdiagnostic; + use rustc_span::{Span, Symbol}; + + #[derive(Subdiagnostic)] + pub enum CodeSuggestion { + #[help(lint_unexpected_cfg_define_features)] + DefineFeatures, + #[suggestion( + lint_unexpected_cfg_name_similar_name_value, + applicability = "maybe-incorrect", + code = "{code}" + )] + SimilarNameAndValue { + #[primary_span] + span: Span, + code: String, + }, + #[suggestion( + lint_unexpected_cfg_name_similar_name_no_value, + applicability = "maybe-incorrect", + code = "{code}" + )] + SimilarNameNoValue { + #[primary_span] + span: Span, + code: String, + }, + #[suggestion( + lint_unexpected_cfg_name_similar_name_different_values, + applicability = "maybe-incorrect", + code = "{code}" + )] + SimilarNameDifferentValues { + #[primary_span] + span: Span, + code: String, + #[subdiagnostic] + expected: Option, + }, + #[suggestion( + lint_unexpected_cfg_name_similar_name, + applicability = "maybe-incorrect", + code = "{code}" + )] + SimilarName { + #[primary_span] + span: Span, + code: String, + #[subdiagnostic] + expected: Option, + }, + SimilarValues { + #[subdiagnostic] + with_similar_values: Vec, + #[subdiagnostic] + expected_names: Option, + }, + } + + #[derive(Subdiagnostic)] + #[help(lint_unexpected_cfg_name_expected_values)] + pub struct ExpectedValues { + pub best_match: Symbol, + pub possibilities: DiagSymbolList, + } + + #[derive(Subdiagnostic)] + #[suggestion( + lint_unexpected_cfg_name_with_similar_value, + applicability = "maybe-incorrect", + code = "{code}" + )] + pub struct FoundWithSimilarValue { + #[primary_span] + pub span: Span, + pub code: String, + } + + #[derive(Subdiagnostic)] + #[help_once(lint_unexpected_cfg_name_expected_names)] + pub struct ExpectedNames { + pub possibilities: DiagSymbolList, + pub and_more: usize, + } + + #[derive(Subdiagnostic)] + pub enum InvocationHelp { + #[note(lint_unexpected_cfg_doc_cargo)] + Cargo { + #[subdiagnostic] + sub: Option, + }, + #[note(lint_unexpected_cfg_doc_rustc)] + Rustc(#[subdiagnostic] super::UnexpectedCfgRustcHelp), + } +} + +#[derive(LintDiagnostic)] +#[diag(lint_unexpected_cfg_value)] +pub struct UnexpectedCfgValue { + #[subdiagnostic] + pub code_sugg: unexpected_cfg_value::CodeSuggestion, + #[subdiagnostic] + pub invocation_help: unexpected_cfg_value::InvocationHelp, + + pub has_value: bool, + pub value: String, +} + +pub mod unexpected_cfg_value { + use rustc_errors::DiagSymbolList; + use rustc_macros::Subdiagnostic; + use rustc_span::{Span, Symbol}; + + #[derive(Subdiagnostic)] + pub enum CodeSuggestion { + ChangeValue { + #[subdiagnostic] + expected_values: ExpectedValues, + #[subdiagnostic] + suggestion: Option, + }, + #[note(lint_unexpected_cfg_value_no_expected_value)] + RemoveValue { + #[subdiagnostic] + suggestion: Option, + + name: Symbol, + }, + #[note(lint_unexpected_cfg_value_no_expected_values)] + RemoveCondition { + #[subdiagnostic] + suggestion: RemoveConditionSuggestion, + + name: Symbol, + }, + } + + #[derive(Subdiagnostic)] + pub enum ChangeValueSuggestion { + #[suggestion( + lint_unexpected_cfg_value_similar_name, + code = r#""{best_match}""#, + applicability = "maybe-incorrect" + )] + SimilarName { + #[primary_span] + span: Span, + best_match: Symbol, + }, + #[suggestion( + lint_unexpected_cfg_value_specify_value, + code = r#" = "{first_possibility}""#, + applicability = "maybe-incorrect" + )] + SpecifyValue { + #[primary_span] + span: Span, + first_possibility: Symbol, + }, + } + + #[derive(Subdiagnostic)] + #[suggestion( + lint_unexpected_cfg_value_remove_value, + code = "", + applicability = "maybe-incorrect" + )] + pub struct RemoveValueSuggestion { + #[primary_span] + pub span: Span, + } + + #[derive(Subdiagnostic)] + #[suggestion( + lint_unexpected_cfg_value_remove_condition, + code = "", + applicability = "maybe-incorrect" + )] + pub struct RemoveConditionSuggestion { + #[primary_span] + pub span: Span, + } + + #[derive(Subdiagnostic)] + #[note(lint_unexpected_cfg_value_expected_values)] + pub struct ExpectedValues { + pub name: Symbol, + pub have_none_possibility: bool, + pub possibilities: DiagSymbolList, + pub and_more: usize, + } + + #[derive(Subdiagnostic)] + pub enum InvocationHelp { + #[note(lint_unexpected_cfg_doc_cargo)] + Cargo(#[subdiagnostic] Option), + #[note(lint_unexpected_cfg_doc_rustc)] + Rustc(#[subdiagnostic] Option), + } + + #[derive(Subdiagnostic)] + pub enum CargoHelp { + #[help(lint_unexpected_cfg_value_add_feature)] + AddFeature { + value: Symbol, + }, + #[help(lint_unexpected_cfg_define_features)] + DefineFeatures, + Other(#[subdiagnostic] super::UnexpectedCfgCargoHelp), + } +} + +#[derive(LintDiagnostic)] +#[diag(lint_macro_use_deprecated)] +pub struct MacroUseDeprecated; + +#[derive(LintDiagnostic)] +#[diag(lint_unused_macro_use)] +pub struct UnusedMacroUse; + +#[derive(LintDiagnostic)] +#[diag(lint_private_extern_crate_reexport)] +pub struct PrivateExternCrateReexport { + pub ident: Ident, +} + +#[derive(LintDiagnostic)] +#[diag(lint_unused_label)] +pub struct UnusedLabel; + +#[derive(LintDiagnostic)] +#[diag(lint_macro_is_private)] +pub struct MacroIsPrivate { + pub ident: Ident, +} + +#[derive(LintDiagnostic)] +#[diag(lint_unused_macro_definition)] +pub struct UnusedMacroDefinition { + pub name: Symbol, +} + +#[derive(LintDiagnostic)] +#[diag(lint_macro_rule_never_used)] +pub struct MacroRuleNeverUsed { + pub n: usize, + pub name: Symbol, +} + +pub struct UnstableFeature { + pub msg: DiagMessage, +} + +impl<'a> LintDiagnostic<'a, ()> for UnstableFeature { + fn decorate_lint<'b>(self, _diag: &'b mut Diag<'a, ()>) {} + + fn msg(&self) -> DiagMessage { + self.msg.clone() + } +} + +#[derive(LintDiagnostic)] +#[diag(lint_avoid_intel_syntax)] +pub struct AvoidIntelSyntax; + +#[derive(LintDiagnostic)] +#[diag(lint_avoid_att_syntax)] +pub struct AvoidAttSyntax; + +#[derive(LintDiagnostic)] +#[diag(lint_incomplete_include)] +pub struct IncompleteInclude; + +#[derive(LintDiagnostic)] +#[diag(lint_unnameable_test_items)] +pub struct UnnameableTestItems; + +#[derive(LintDiagnostic)] +#[diag(lint_duplicate_macro_attribute)] +pub struct DuplicateMacroAttribute; + +#[derive(LintDiagnostic)] +#[diag(lint_cfg_attr_no_attributes)] +pub struct CfgAttrNoAttributes; + +#[derive(LintDiagnostic)] +#[diag(lint_crate_type_in_cfg_attr_deprecated)] +pub struct CrateTypeInCfgAttr; + +#[derive(LintDiagnostic)] +#[diag(lint_crate_name_in_cfg_attr_deprecated)] +pub struct CrateNameInCfgAttr; + +#[derive(LintDiagnostic)] +#[diag(lint_missing_fragment_specifier)] +pub struct MissingFragmentSpecifier; + +#[derive(LintDiagnostic)] +#[diag(lint_metavariable_still_repeating)] +pub struct MetaVariableStillRepeating { + pub name: MacroRulesNormalizedIdent, +} + +#[derive(LintDiagnostic)] +#[diag(lint_metavariable_wrong_operator)] +pub struct MetaVariableWrongOperator; + +#[derive(LintDiagnostic)] +#[diag(lint_duplicate_matcher_binding)] +pub struct DuplicateMatcherBinding; + +#[derive(LintDiagnostic)] +#[diag(lint_unknown_macro_variable)] +pub struct UnknownMacroVariable { + pub name: MacroRulesNormalizedIdent, +} + +#[derive(LintDiagnostic)] +#[diag(lint_unused_crate_dependency)] +pub struct UnusedCrateDependency { + pub extern_crate: Symbol, + pub local_crate: Symbol, +} + +#[derive(LintDiagnostic)] +#[diag(lint_wasm_c_abi)] +pub struct WasmCAbi; + +#[derive(LintDiagnostic)] +#[diag(lint_ill_formed_attribute_input)] +pub struct IllFormedAttributeInput { + pub num_suggestions: usize, + pub suggestions: DiagArgValue, +} + +#[derive(LintDiagnostic)] +pub enum InnerAttributeUnstable { + #[diag(lint_inner_macro_attribute_unstable)] + InnerMacroAttribute, + #[diag(lint_custom_inner_attribute_unstable)] + CustomInnerAttribute, +} + +#[derive(LintDiagnostic)] +#[diag(lint_unknown_diagnostic_attribute)] +pub struct UnknownDiagnosticAttribute { + #[subdiagnostic] + pub typo: Option, +} + +#[derive(Subdiagnostic)] +#[suggestion( + lint_unknown_diagnostic_attribute_typo_sugg, + style = "verbose", + code = "{typo_name}", + applicability = "machine-applicable" +)] +pub struct UnknownDiagnosticAttributeTypoSugg { + #[primary_span] + pub span: Span, + pub typo_name: Symbol, +} + +#[derive(LintDiagnostic)] +#[diag(lint_unicode_text_flow)] +#[note] +pub struct UnicodeTextFlow { + #[label] + pub comment_span: Span, + #[subdiagnostic] + pub characters: Vec, + #[subdiagnostic] + pub suggestions: Option, + + pub num_codepoints: usize, +} + +#[derive(Subdiagnostic)] +#[label(lint_label_comment_char)] +pub struct UnicodeCharNoteSub { + #[primary_span] + pub span: Span, + pub c_debug: String, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion(lint_suggestion, applicability = "machine-applicable", style = "hidden")] +pub struct UnicodeTextFlowSuggestion { + #[suggestion_part(code = "")] + pub spans: Vec, +} + +#[derive(LintDiagnostic)] +#[diag(lint_abs_path_with_module)] +pub struct AbsPathWithModule { + #[subdiagnostic] + pub sugg: AbsPathWithModuleSugg, +} + +#[derive(Subdiagnostic)] +#[suggestion(lint_suggestion, code = "{replacement}")] +pub struct AbsPathWithModuleSugg { + #[primary_span] + pub span: Span, + #[applicability] + pub applicability: Applicability, + pub replacement: String, +} + +#[derive(LintDiagnostic)] +#[diag(lint_proc_macro_derive_resolution_fallback)] +pub struct ProcMacroDeriveResolutionFallback { + #[label] + pub span: Span, + pub ns: Namespace, + pub ident: Ident, +} + +#[derive(LintDiagnostic)] +#[diag(lint_macro_expanded_macro_exports_accessed_by_absolute_paths)] +pub struct MacroExpandedMacroExportsAccessedByAbsolutePaths { + #[note] + pub definition: Span, +} + +#[derive(LintDiagnostic)] +#[diag(lint_hidden_lifetime_parameters)] +pub struct ElidedLifetimesInPaths { + #[subdiagnostic] + pub subdiag: ElidedLifetimeInPathSubdiag, +} + +#[derive(LintDiagnostic)] +#[diag(lint_invalid_crate_type_value)] +pub struct UnknownCrateTypes { + #[subdiagnostic] + pub sugg: Option, +} + +#[derive(Subdiagnostic)] +#[suggestion(lint_suggestion, code = r#""{candidate}""#, applicability = "maybe-incorrect")] +pub struct UnknownCrateTypesSub { + #[primary_span] + pub span: Span, + pub candidate: Symbol, +} + +#[derive(LintDiagnostic)] +#[diag(lint_unused_imports)] +pub struct UnusedImports { + #[subdiagnostic] + pub sugg: UnusedImportsSugg, + #[help] + pub test_module_span: Option, + + pub span_snippets: DiagArgValue, + pub num_snippets: usize, +} + +#[derive(Subdiagnostic)] +pub enum UnusedImportsSugg { + #[suggestion( + lint_suggestion_remove_whole_use, + applicability = "machine-applicable", + code = "", + style = "tool-only" + )] + RemoveWholeUse { + #[primary_span] + span: Span, + }, + #[multipart_suggestion( + lint_suggestion_remove_imports, + applicability = "machine-applicable", + style = "tool-only" + )] + RemoveImports { + #[suggestion_part(code = "")] + remove_spans: Vec, + num_to_remove: usize, + }, +} + +#[derive(LintDiagnostic)] +#[diag(lint_redundant_import)] +pub struct RedundantImport { + #[subdiagnostic] + pub subs: Vec, + + pub ident: Ident, +} + +#[derive(Subdiagnostic)] +pub enum RedundantImportSub { + #[label(lint_label_imported_here)] + ImportedHere(#[primary_span] Span), + #[label(lint_label_defined_here)] + DefinedHere(#[primary_span] Span), + #[label(lint_label_imported_prelude)] + ImportedPrelude(#[primary_span] Span), + #[label(lint_label_defined_prelude)] + DefinedPrelude(#[primary_span] Span), +} + +#[derive(LintDiagnostic)] +#[diag(lint_unused_doc_comment)] +#[help] +pub struct UnusedDocComment { + #[label] + pub span: Span, +} + +#[derive(LintDiagnostic)] +pub enum PatternsInFnsWithoutBody { + #[diag(lint_pattern_in_foreign)] + Foreign { + #[subdiagnostic] + sub: PatternsInFnsWithoutBodySub, + }, + #[diag(lint_pattern_in_bodiless)] + Bodiless { + #[subdiagnostic] + sub: PatternsInFnsWithoutBodySub, + }, +} + +#[derive(Subdiagnostic)] +#[suggestion(lint_remove_mut_from_pattern, code = "{ident}", applicability = "machine-applicable")] +pub struct PatternsInFnsWithoutBodySub { + #[primary_span] + pub span: Span, + + pub ident: Ident, +} + +#[derive(LintDiagnostic)] +#[diag(lint_extern_without_abi)] +#[help] +pub struct MissingAbi { + #[label] + pub span: Span, + + pub default_abi: &'static str, +} + +#[derive(LintDiagnostic)] +#[diag(lint_legacy_derive_helpers)] +pub struct LegacyDeriveHelpers { + #[label] + pub span: Span, +} + +#[derive(LintDiagnostic)] +#[diag(lint_proc_macro_back_compat)] +#[note] +pub struct ProcMacroBackCompat { + pub crate_name: String, + pub fixed_version: String, +} + +#[derive(LintDiagnostic)] +#[diag(lint_or_patterns_back_compat)] +pub struct OrPatternsBackCompat { + #[suggestion(code = "{suggestion}", applicability = "machine-applicable")] + pub span: Span, + pub suggestion: String, +} + +#[derive(LintDiagnostic)] +#[diag(lint_reserved_prefix)] +pub struct ReservedPrefix { + #[label] + pub label: Span, + #[suggestion(code = " ", applicability = "machine-applicable")] + pub suggestion: Span, + + pub prefix: String, +} + +#[derive(LintDiagnostic)] +#[diag(lint_unused_builtin_attribute)] +pub struct UnusedBuiltinAttribute { + #[note] + pub invoc_span: Span, + + pub attr_name: Symbol, + pub macro_name: String, +} + +#[derive(LintDiagnostic)] +#[diag(lint_trailing_semi_macro)] +pub struct TrailingMacro { + #[note(lint_note1)] + #[note(lint_note2)] + pub is_trailing: bool, + + pub name: Ident, +} + +#[derive(LintDiagnostic)] +#[diag(lint_break_with_label_and_loop)] +pub struct BreakWithLabelAndLoop { + #[subdiagnostic] + pub sub: BreakWithLabelAndLoopSub, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion(lint_suggestion, applicability = "machine-applicable")] +pub struct BreakWithLabelAndLoopSub { + #[suggestion_part(code = "(")] + pub left: Span, + #[suggestion_part(code = ")")] + pub right: Span, +} + +#[derive(LintDiagnostic)] +#[diag(lint_deprecated_where_clause_location)] +#[note] +pub struct DeprecatedWhereClauseLocation { + #[subdiagnostic] + pub suggestion: DeprecatedWhereClauseLocationSugg, +} + +#[derive(Subdiagnostic)] +pub enum DeprecatedWhereClauseLocationSugg { + #[multipart_suggestion(lint_suggestion_move_to_end, applicability = "machine-applicable")] + MoveToEnd { + #[suggestion_part(code = "")] + left: Span, + #[suggestion_part(code = "{sugg}")] + right: Span, + + sugg: String, + }, + #[suggestion(lint_suggestion_remove_where, code = "", applicability = "machine-applicable")] + RemoveWhere { + #[primary_span] + span: Span, + }, +} + +#[derive(LintDiagnostic)] +#[diag(lint_single_use_lifetime)] +pub struct SingleUseLifetime { + #[label(lint_label_param)] + pub param_span: Span, + #[label(lint_label_use)] + pub use_span: Span, + #[subdiagnostic] + pub suggestion: Option, + + pub ident: Ident, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion(lint_suggestion, applicability = "machine-applicable")] +pub struct SingleUseLifetimeSugg { + #[suggestion_part(code = "")] + pub deletion_span: Option, + #[suggestion_part(code = "{replace_lt}")] + pub use_span: Span, + + pub replace_lt: String, +} + +#[derive(LintDiagnostic)] +#[diag(lint_unused_lifetime)] +pub struct UnusedLifetime { + #[suggestion(code = "", applicability = "machine-applicable")] + pub deletion_span: Option, + + pub ident: Ident, +} + +#[derive(LintDiagnostic)] +#[diag(lint_named_argument_used_positionally)] +pub struct NamedArgumentUsedPositionally { + #[label(lint_label_named_arg)] + pub named_arg_sp: Span, + #[label(lint_label_position_arg)] + pub position_label_sp: Option, + #[suggestion(style = "verbose", code = "{name}", applicability = "maybe-incorrect")] + pub suggestion: Option, + + pub name: String, + pub named_arg_name: String, +} + +#[derive(LintDiagnostic)] +#[diag(lint_byte_slice_in_packed_struct_with_derive)] +#[help] +pub struct ByteSliceInPackedStructWithDerive { + // FIXME: make this translatable + pub ty: String, +} + +#[derive(LintDiagnostic)] +#[diag(lint_unused_extern_crate)] +pub struct UnusedExternCrate { + #[suggestion(code = "", applicability = "machine-applicable")] + pub removal_span: Span, +} + +#[derive(LintDiagnostic)] +#[diag(lint_extern_crate_not_idiomatic)] +pub struct ExternCrateNotIdiomatic { + #[suggestion(style = "verbose", code = "{code}", applicability = "machine-applicable")] + pub span: Span, + + pub code: &'static str, +} + +// FIXME: make this translatable +pub struct AmbiguousGlobImports { + pub ambiguity: AmbiguityErrorDiag, +} + +impl<'a, G: EmissionGuarantee> LintDiagnostic<'a, G> for AmbiguousGlobImports { + fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, G>) { + rustc_errors::report_ambiguity_error(diag, self.ambiguity); + } + + fn msg(&self) -> DiagMessage { + DiagMessage::Str(self.ambiguity.msg.clone().into()) + } +} + +#[derive(LintDiagnostic)] +#[diag(lint_ambiguous_glob_reexport)] +pub struct AmbiguousGlobReexports { + #[label(lint_label_first_reexport)] + pub first_reexport: Span, + #[label(lint_label_duplicate_reexport)] + pub duplicate_reexport: Span, + + pub name: String, + // FIXME: make this translatable + pub namespace: String, +} + +#[derive(LintDiagnostic)] +#[diag(lint_hidden_glob_reexport)] +pub struct HiddenGlobReexports { + #[note(lint_note_glob_reexport)] + pub glob_reexport: Span, + #[note(lint_note_private_item)] + pub private_item: Span, + + pub name: String, + // FIXME: make this translatable + pub namespace: String, +} + +#[derive(LintDiagnostic)] +#[diag(lint_unnecessary_qualification)] +pub struct UnusedQualifications { + #[suggestion(style = "verbose", code = "", applicability = "machine-applicable")] + pub removal_span: Span, +} + +#[derive(LintDiagnostic)] +#[diag(lint_associated_const_elided_lifetime)] +pub struct AssociatedConstElidedLifetime { + #[suggestion(style = "verbose", code = "{code}", applicability = "machine-applicable")] + pub span: Span, + + pub code: &'static str, + pub elided: bool, +} + +#[derive(LintDiagnostic)] +#[diag(lint_redundant_import_visibility)] +pub struct RedundantImportVisibility { + #[note] + pub span: Span, + #[help] + pub help: (), + + pub import_vis: String, + pub max_vis: String, +} diff --git a/compiler/rustc_lint/src/non_local_def.rs b/compiler/rustc_lint/src/non_local_def.rs index 885c0bb3a89cc..b3e93748a1665 100644 --- a/compiler/rustc_lint/src/non_local_def.rs +++ b/compiler/rustc_lint/src/non_local_def.rs @@ -38,7 +38,7 @@ declare_lint! { /// /// Creating non-local definitions go against expectation and can create discrepancies /// in tooling. It should be avoided. It may become deny-by-default in edition 2024 - /// and higher, see see the tracking issue . + /// and higher, see the tracking issue . /// /// An `impl` definition is non-local if it is nested inside an item and neither /// the type nor the trait are at the same nesting level as the `impl` block. diff --git a/compiler/rustc_lint/src/shadowed_into_iter.rs b/compiler/rustc_lint/src/shadowed_into_iter.rs new file mode 100644 index 0000000000000..41ec84faa7833 --- /dev/null +++ b/compiler/rustc_lint/src/shadowed_into_iter.rs @@ -0,0 +1,157 @@ +use crate::lints::{ShadowedIntoIterDiag, ShadowedIntoIterDiagSub}; +use crate::{LateContext, LateLintPass, LintContext}; +use rustc_hir as hir; +use rustc_middle::ty::{self, Ty}; +use rustc_session::lint::FutureIncompatibilityReason; +use rustc_session::{declare_lint, impl_lint_pass}; +use rustc_span::edition::Edition; + +declare_lint! { + /// The `array_into_iter` lint detects calling `into_iter` on arrays. + /// + /// ### Example + /// + /// ```rust,edition2018 + /// # #![allow(unused)] + /// [1, 2, 3].into_iter().for_each(|n| { *n; }); + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Since Rust 1.53, arrays implement `IntoIterator`. However, to avoid + /// breakage, `array.into_iter()` in Rust 2015 and 2018 code will still + /// behave as `(&array).into_iter()`, returning an iterator over + /// references, just like in Rust 1.52 and earlier. + /// This only applies to the method call syntax `array.into_iter()`, not to + /// any other syntax such as `for _ in array` or `IntoIterator::into_iter(array)`. + pub ARRAY_INTO_ITER, + Warn, + "detects calling `into_iter` on arrays in Rust 2015 and 2018", + @future_incompatible = FutureIncompatibleInfo { + reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2021), + reference: "", + }; +} + +declare_lint! { + /// The `boxed_slice_into_iter` lint detects calling `into_iter` on boxed slices. + /// + /// ### Example + /// + /// ```rust,edition2021 + /// # #![allow(unused)] + /// vec![1, 2, 3].into_boxed_slice().into_iter().for_each(|n| { *n; }); + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Since Rust CURRENT_RUSTC_VERSION, boxed slices implement `IntoIterator`. However, to avoid + /// breakage, `boxed_slice.into_iter()` in Rust 2015, 2018, and 2021 code will still + /// behave as `(&boxed_slice).into_iter()`, returning an iterator over + /// references, just like in Rust CURRENT_RUSTC_VERSION and earlier. + /// This only applies to the method call syntax `boxed_slice.into_iter()`, not to + /// any other syntax such as `for _ in boxed_slice` or `IntoIterator::into_iter(boxed_slice)`. + pub BOXED_SLICE_INTO_ITER, + Warn, + "detects calling `into_iter` on boxed slices in Rust 2015, 2018, and 2021", + @future_incompatible = FutureIncompatibleInfo { + reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2024), + }; +} + +#[derive(Copy, Clone)] +pub struct ShadowedIntoIter; + +impl_lint_pass!(ShadowedIntoIter => [ARRAY_INTO_ITER, BOXED_SLICE_INTO_ITER]); + +impl<'tcx> LateLintPass<'tcx> for ShadowedIntoIter { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) { + let hir::ExprKind::MethodCall(call, receiver_arg, ..) = &expr.kind else { + return; + }; + + // Check if the method call actually calls the libcore + // `IntoIterator::into_iter`. + let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) else { + return; + }; + if Some(method_def_id) != cx.tcx.lang_items().into_iter_fn() { + return; + } + + // As this is a method call expression, we have at least one argument. + let receiver_ty = cx.typeck_results().expr_ty(receiver_arg); + let adjustments = cx.typeck_results().expr_adjustments(receiver_arg); + + let adjusted_receiver_tys: Vec<_> = + [receiver_ty].into_iter().chain(adjustments.iter().map(|adj| adj.target)).collect(); + + fn is_ref_to_array(ty: Ty<'_>) -> bool { + if let ty::Ref(_, pointee_ty, _) = *ty.kind() { pointee_ty.is_array() } else { false } + } + fn is_boxed_slice(ty: Ty<'_>) -> bool { + ty.is_box() && ty.boxed_ty().is_slice() + } + fn is_ref_to_boxed_slice(ty: Ty<'_>) -> bool { + if let ty::Ref(_, pointee_ty, _) = *ty.kind() { + is_boxed_slice(pointee_ty) + } else { + false + } + } + + let (lint, target, edition, can_suggest_ufcs) = + if is_ref_to_array(*adjusted_receiver_tys.last().unwrap()) + && let Some(idx) = adjusted_receiver_tys + .iter() + .copied() + .take_while(|ty| !is_ref_to_array(*ty)) + .position(|ty| ty.is_array()) + { + (ARRAY_INTO_ITER, "[T; N]", "2021", idx == 0) + } else if is_ref_to_boxed_slice(*adjusted_receiver_tys.last().unwrap()) + && let Some(idx) = adjusted_receiver_tys + .iter() + .copied() + .take_while(|ty| !is_ref_to_boxed_slice(*ty)) + .position(|ty| is_boxed_slice(ty)) + { + (BOXED_SLICE_INTO_ITER, "Box<[T]>", "2024", idx == 0) + } else { + return; + }; + + // If this expression comes from the `IntoIter::into_iter` inside of a for loop, + // we should just suggest removing the `.into_iter()` or changing it to `.iter()` + // to disambiguate if we want to iterate by-value or by-ref. + let sub = if let Some((_, hir::Node::Expr(parent_expr))) = + cx.tcx.hir().parent_iter(expr.hir_id).nth(1) + && let hir::ExprKind::Match(arg, [_], hir::MatchSource::ForLoopDesugar) = + &parent_expr.kind + && let hir::ExprKind::Call(path, [_]) = &arg.kind + && let hir::ExprKind::Path(hir::QPath::LangItem(hir::LangItem::IntoIterIntoIter, ..)) = + &path.kind + { + Some(ShadowedIntoIterDiagSub::RemoveIntoIter { + span: receiver_arg.span.shrink_to_hi().to(expr.span.shrink_to_hi()), + }) + } else if can_suggest_ufcs { + Some(ShadowedIntoIterDiagSub::UseExplicitIntoIter { + start_span: expr.span.shrink_to_lo(), + end_span: receiver_arg.span.shrink_to_hi().to(expr.span.shrink_to_hi()), + }) + } else { + None + }; + + cx.emit_span_lint( + lint, + call.ident.span, + ShadowedIntoIterDiag { target, edition, suggestion: call.ident.span, sub }, + ); + } +} diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 5369454577249..82f16b31a6673 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -3339,11 +3339,14 @@ declare_lint! { /// /// ### Explanation /// - /// This lint is only active when `--check-cfg` arguments are being passed - /// to the compiler and triggers whenever an unexpected condition name or value is used. + /// This lint is only active when [`--check-cfg`][check-cfg] arguments are being + /// passed to the compiler and triggers whenever an unexpected condition name or value is + /// used. + /// + /// See the [Checking Conditional Configurations][check-cfg] section for more + /// details. /// - /// The known condition include names or values passed in `--check-cfg`, and some - /// well-knows names and values built into the compiler. + /// [check-cfg]: https://doc.rust-lang.org/nightly/rustc/check-cfg.html pub UNEXPECTED_CFGS, Warn, "detects unexpected names and values in `#[cfg]` conditions", @@ -4263,8 +4266,7 @@ declare_lint! { /// /// // where absurd is a function with the following signature /// // (it's sound, because `!` always marks unreachable code): - /// fn absurd(_: !) -> T { ... } - // FIXME: use `core::convert::absurd` here instead, once it's merged + /// fn absurd(never: !) -> T { ... } /// ``` /// /// While it's convenient to be able to use non-diverging code in one of the branches (like @@ -4321,7 +4323,12 @@ declare_lint! { /// [`()`]: https://doc.rust-lang.org/core/primitive.unit.html pub NEVER_TYPE_FALLBACK_FLOWING_INTO_UNSAFE, Warn, - "never type fallback affecting unsafe function calls" + "never type fallback affecting unsafe function calls", + @future_incompatible = FutureIncompatibleInfo { + reason: FutureIncompatibilityReason::FutureReleaseSemanticsChange, + reference: "issue #123748 ", + }; + report_in_external_macro } declare_lint! { diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index e06e3e9b805f7..1941b0b12643f 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -6,10 +6,12 @@ use rustc_data_structures::stable_hasher::{ HashStable, StableCompare, StableHasher, ToStableHashKey, }; use rustc_error_messages::{DiagMessage, MultiSpan}; +use rustc_hir::def::Namespace; use rustc_hir::HashStableContext; use rustc_hir::HirId; use rustc_macros::{Decodable, Encodable, HashStable_Generic}; use rustc_span::edition::Edition; +use rustc_span::symbol::MacroRulesNormalizedIdent; use rustc_span::{sym, symbol::Ident, Span, Symbol}; use rustc_target::spec::abi::Abi; @@ -565,19 +567,44 @@ pub struct AmbiguityErrorDiag { pub b2_help_msgs: Vec, } +#[derive(Debug, Clone)] +pub enum DeprecatedSinceKind { + InEffect, + InFuture, + InVersion(String), +} + // This could be a closure, but then implementing derive trait // becomes hacky (and it gets allocated). #[derive(Debug)] pub enum BuiltinLintDiag { - Normal, AbsPathWithModule(Span), - ProcMacroDeriveResolutionFallback(Span), + ProcMacroDeriveResolutionFallback { + span: Span, + ns: Namespace, + ident: Ident, + }, MacroExpandedMacroExportsAccessedByAbsolutePaths(Span), ElidedLifetimesInPaths(usize, Span, bool, Span), - UnknownCrateTypes(Span, String, String), - UnusedImports(String, Vec<(Span, String)>, Option), + UnknownCrateTypes { + span: Span, + candidate: Option, + }, + UnusedImports { + remove_whole_use: bool, + num_to_remove: usize, + remove_spans: Vec, + test_module_span: Option, + span_snippets: Vec, + }, RedundantImport(Vec<(Span, bool)>, Ident), - DeprecatedMacro(Option, Span), + DeprecatedMacro { + suggestion: Option, + suggestion_span: Span, + note: Option, + path: String, + since_kind: DeprecatedSinceKind, + }, MissingAbi(Span, Abi), UnusedDocComment(Span), UnusedBuiltinAttribute { @@ -585,18 +612,24 @@ pub enum BuiltinLintDiag { macro_name: String, invoc_span: Span, }, - PatternsInFnsWithoutBody(Span, Ident), + PatternsInFnsWithoutBody { + span: Span, + ident: Ident, + is_foreign: bool, + }, LegacyDeriveHelpers(Span), - ProcMacroBackCompat(String), + ProcMacroBackCompat { + crate_name: String, + fixed_version: String, + }, OrPatternsBackCompat(Span, String), - ReservedPrefix(Span), + ReservedPrefix(Span, String), TrailingMacro(bool, Ident), BreakWithLabelAndLoop(Span), - NamedAsmLabel(String), UnicodeTextFlow(Span, String), UnexpectedCfgName((Symbol, Span), Option<(Symbol, Span)>), UnexpectedCfgValue((Symbol, Span), Option<(Symbol, Span)>), - DeprecatedWhereclauseLocation(Option<(Span, String)>), + DeprecatedWhereclauseLocation(Span, Option<(Span, String)>), SingleUseLifetime { /// Span of the parameter which declares this lifetime. param_span: Span, @@ -606,6 +639,7 @@ pub enum BuiltinLintDiag { /// Span of the single use, or None if the lifetime is never used. /// If true, the lifetime will be fully elided. use_span: Option<(Span, bool)>, + ident: Ident, }, NamedArgumentUsedPositionally { /// Span where the named argument is used by position and will be replaced with the named @@ -620,7 +654,10 @@ pub enum BuiltinLintDiag { /// Indicates if the named argument is used as a width/precision for formatting is_formatting_arg: bool, }, - ByteSliceInPackedStructWithDerive, + ByteSliceInPackedStructWithDerive { + // FIXME: enum of byte/string + ty: String, + }, UnusedExternCrate { removal_span: Span, }, @@ -662,10 +699,43 @@ pub enum BuiltinLintDiag { RedundantImportVisibility { span: Span, max_vis: String, + import_vis: String, }, - MaybeTypo { + UnknownDiagnosticAttribute { span: Span, - name: Symbol, + typo_name: Option, + }, + MacroUseDeprecated, + UnusedMacroUse, + PrivateExternCrateReexport(Ident), + UnusedLabel, + MacroIsPrivate(Ident), + UnusedMacroDefinition(Symbol), + MacroRuleNeverUsed(usize, Symbol), + UnstableFeature(DiagMessage), + AvoidUsingIntelSyntax, + AvoidUsingAttSyntax, + IncompleteInclude, + UnnameableTestItems, + DuplicateMacroAttribute, + CfgAttrNoAttributes, + CrateTypeInCfgAttr, + CrateNameInCfgAttr, + MissingFragmentSpecifier, + MetaVariableStillRepeating(MacroRulesNormalizedIdent), + MetaVariableWrongOperator, + DuplicateMatcherBinding, + UnknownMacroVariable(MacroRulesNormalizedIdent), + UnusedCrateDependency { + extern_crate: Symbol, + local_crate: Symbol, + }, + WasmCAbi, + IllFormedAttributeInput { + suggestions: Vec, + }, + InnerAttributeUnstable { + is_macro: bool, }, } @@ -676,9 +746,6 @@ pub struct BufferedEarlyLint { /// The span of code that we are linting on. pub span: MultiSpan, - /// The lint message. - pub msg: DiagMessage, - /// The `NodeId` of the AST node that generated the lint. pub node_id: NodeId, @@ -706,12 +773,10 @@ impl LintBuffer { lint: &'static Lint, node_id: NodeId, span: MultiSpan, - msg: impl Into, diagnostic: BuiltinLintDiag, ) { let lint_id = LintId::of(lint); - let msg = msg.into(); - self.add_early_lint(BufferedEarlyLint { lint_id, node_id, span, msg, diagnostic }); + self.add_early_lint(BufferedEarlyLint { lint_id, node_id, span, diagnostic }); } pub fn take(&mut self, id: NodeId) -> Vec { @@ -724,20 +789,9 @@ impl LintBuffer { lint: &'static Lint, id: NodeId, sp: impl Into, - msg: impl Into, - ) { - self.add_lint(lint, id, sp.into(), msg, BuiltinLintDiag::Normal) - } - - pub fn buffer_lint_with_diagnostic( - &mut self, - lint: &'static Lint, - id: NodeId, - sp: impl Into, - msg: impl Into, diagnostic: BuiltinLintDiag, ) { - self.add_lint(lint, id, sp.into(), msg, diagnostic) + self.add_lint(lint, id, sp.into(), diagnostic) } } diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs index ae481efb263df..38d4a5ee61ce4 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs @@ -158,7 +158,9 @@ impl DiagnosticDeriveVariantBuilder { let slug = subdiag.slug.unwrap_or_else(|| match subdiag.kind { SubdiagnosticKind::Label => parse_quote! { _subdiag::label }, SubdiagnosticKind::Note => parse_quote! { _subdiag::note }, + SubdiagnosticKind::NoteOnce => parse_quote! { _subdiag::note_once }, SubdiagnosticKind::Help => parse_quote! { _subdiag::help }, + SubdiagnosticKind::HelpOnce => parse_quote! { _subdiag::help_once }, SubdiagnosticKind::Warn => parse_quote! { _subdiag::warn }, SubdiagnosticKind::Suggestion { .. } => parse_quote! { _subdiag::suggestion }, SubdiagnosticKind::MultipartSuggestion { .. } => unreachable!(), @@ -233,9 +235,11 @@ impl DiagnosticDeriveVariantBuilder { }; let fn_ident = format_ident!("{}", subdiag); match subdiag { - SubdiagnosticKind::Note | SubdiagnosticKind::Help | SubdiagnosticKind::Warn => { - Ok(self.add_subdiagnostic(&fn_ident, slug)) - } + SubdiagnosticKind::Note + | SubdiagnosticKind::NoteOnce + | SubdiagnosticKind::Help + | SubdiagnosticKind::HelpOnce + | SubdiagnosticKind::Warn => Ok(self.add_subdiagnostic(&fn_ident, slug)), SubdiagnosticKind::Label | SubdiagnosticKind::Suggestion { .. } => { throw_invalid_attr!(attr, |diag| diag .help("`#[label]` and `#[suggestion]` can only be applied to fields")); @@ -347,7 +351,11 @@ impl DiagnosticDeriveVariantBuilder { report_error_if_not_applied_to_span(attr, &info)?; Ok(self.add_spanned_subdiagnostic(binding, &fn_ident, slug)) } - SubdiagnosticKind::Note | SubdiagnosticKind::Help | SubdiagnosticKind::Warn => { + SubdiagnosticKind::Note + | SubdiagnosticKind::NoteOnce + | SubdiagnosticKind::Help + | SubdiagnosticKind::HelpOnce + | SubdiagnosticKind::Warn => { let inner = info.ty.inner_type(); if type_matches_path(inner, &["rustc_span", "Span"]) || type_matches_path(inner, &["rustc_span", "MultiSpan"]) diff --git a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs index 45236771bce63..69014f39925ab 100644 --- a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs @@ -510,11 +510,11 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { .map(|binding| self.generate_field_attr_code(binding, kind_stats)) .collect(); - if kind_slugs.is_empty() { + if kind_slugs.is_empty() && !self.has_subdiagnostic { if self.is_enum { // It's okay for a variant to not be a subdiagnostic at all.. return Ok(quote! {}); - } else if !self.has_subdiagnostic { + } else { // ..but structs should always be _something_. throw_span_err!( self.variant.ast().ident.span().unwrap(), diff --git a/compiler/rustc_macros/src/diagnostics/utils.rs b/compiler/rustc_macros/src/diagnostics/utils.rs index 4684306e23592..05a5a32514bb7 100644 --- a/compiler/rustc_macros/src/diagnostics/utils.rs +++ b/compiler/rustc_macros/src/diagnostics/utils.rs @@ -575,8 +575,12 @@ pub(super) enum SubdiagnosticKind { Label, /// `#[note(...)]` Note, + /// `#[note_once(...)]` + NoteOnce, /// `#[help(...)]` Help, + /// `#[help_once(...)]` + HelpOnce, /// `#[warning(...)]` Warn, /// `#[suggestion{,_short,_hidden,_verbose}]` @@ -624,7 +628,9 @@ impl SubdiagnosticVariant { let mut kind = match name { "label" => SubdiagnosticKind::Label, "note" => SubdiagnosticKind::Note, + "note_once" => SubdiagnosticKind::NoteOnce, "help" => SubdiagnosticKind::Help, + "help_once" => SubdiagnosticKind::HelpOnce, "warning" => SubdiagnosticKind::Warn, _ => { // Recover old `#[(multipart_)suggestion_*]` syntaxes @@ -682,7 +688,9 @@ impl SubdiagnosticVariant { match kind { SubdiagnosticKind::Label | SubdiagnosticKind::Note + | SubdiagnosticKind::NoteOnce | SubdiagnosticKind::Help + | SubdiagnosticKind::HelpOnce | SubdiagnosticKind::Warn | SubdiagnosticKind::MultipartSuggestion { .. } => { return Ok(Some(SubdiagnosticVariant { kind, slug: None, no_span: false })); @@ -836,7 +844,9 @@ impl SubdiagnosticVariant { } SubdiagnosticKind::Label | SubdiagnosticKind::Note + | SubdiagnosticKind::NoteOnce | SubdiagnosticKind::Help + | SubdiagnosticKind::HelpOnce | SubdiagnosticKind::Warn => {} } @@ -849,7 +859,9 @@ impl quote::IdentFragment for SubdiagnosticKind { match self { SubdiagnosticKind::Label => write!(f, "label"), SubdiagnosticKind::Note => write!(f, "note"), + SubdiagnosticKind::NoteOnce => write!(f, "note_once"), SubdiagnosticKind::Help => write!(f, "help"), + SubdiagnosticKind::HelpOnce => write!(f, "help_once"), SubdiagnosticKind::Warn => write!(f, "warn"), SubdiagnosticKind::Suggestion { .. } => write!(f, "suggestions_with_style"), SubdiagnosticKind::MultipartSuggestion { .. } => { diff --git a/compiler/rustc_macros/src/lib.rs b/compiler/rustc_macros/src/lib.rs index c7b7eadbd9d6b..de9c916b4f04f 100644 --- a/compiler/rustc_macros/src/lib.rs +++ b/compiler/rustc_macros/src/lib.rs @@ -108,7 +108,9 @@ decl_derive!( // struct attributes diag, help, + help_once, note, + note_once, warning, // field attributes skip_arg, @@ -125,7 +127,9 @@ decl_derive!( // struct attributes diag, help, + help_once, note, + note_once, warning, // field attributes skip_arg, @@ -142,7 +146,9 @@ decl_derive!( // struct/variant attributes label, help, + help_once, note, + note_once, warning, subdiagnostic, suggestion, diff --git a/compiler/rustc_metadata/messages.ftl b/compiler/rustc_metadata/messages.ftl index 3d0846ae6deab..2f5dfad265c80 100644 --- a/compiler/rustc_metadata/messages.ftl +++ b/compiler/rustc_metadata/messages.ftl @@ -284,8 +284,6 @@ metadata_unsupported_abi = metadata_unsupported_abi_i686 = ABI not supported by `#[link(kind = "raw-dylib")]` on i686 -metadata_wasm_c_abi = - older versions of the `wasm-bindgen` crate will be incompatible with future versions of Rust; please update to `wasm-bindgen` v0.2.88 metadata_wasm_import_form = wasm import module must be of the form `wasm_import_module = "string"` diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs index e3205fc1d30e1..be1a73ef0a7d9 100644 --- a/compiler/rustc_metadata/src/creader.rs +++ b/compiler/rustc_metadata/src/creader.rs @@ -20,7 +20,7 @@ use rustc_middle::bug; use rustc_middle::ty::{TyCtxt, TyCtxtFeed}; use rustc_session::config::{self, CrateType, ExternLocation}; use rustc_session::cstore::{CrateDepKind, CrateSource, ExternCrate, ExternCrateSource}; -use rustc_session::lint; +use rustc_session::lint::{self, BuiltinLintDiag}; use rustc_session::output::validate_crate_name; use rustc_session::search_paths::PathKind; use rustc_span::edition::Edition; @@ -975,15 +975,14 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> { } self.sess.psess.buffer_lint( - lint::builtin::UNUSED_CRATE_DEPENDENCIES, - span, - ast::CRATE_NODE_ID, - format!( - "external crate `{}` unused in `{}`: remove the dependency or add `use {} as _;`", - name, - self.tcx.crate_name(LOCAL_CRATE), - name), - ); + lint::builtin::UNUSED_CRATE_DEPENDENCIES, + span, + ast::CRATE_NODE_ID, + BuiltinLintDiag::UnusedCrateDependency { + extern_crate: name_interned, + local_crate: self.tcx.crate_name(LOCAL_CRATE), + }, + ); } } @@ -1020,7 +1019,7 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> { lint::builtin::WASM_C_ABI, span, ast::CRATE_NODE_ID, - crate::fluent_generated::metadata_wasm_c_abi, + BuiltinLintDiag::WasmCAbi, ); } } diff --git a/compiler/rustc_metadata/src/lib.rs b/compiler/rustc_metadata/src/lib.rs index 99584845e49ae..00bb4c435c8b6 100644 --- a/compiler/rustc_metadata/src/lib.rs +++ b/compiler/rustc_metadata/src/lib.rs @@ -1,21 +1,22 @@ +// tidy-alphabetical-start +#![allow(internal_features)] +#![allow(rustc::potential_query_instability)] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] -#![feature(rustdoc_internals)] -#![allow(internal_features)] +#![feature(coroutines)] #![feature(decl_macro)] #![feature(error_iter)] #![feature(extract_if)] -#![feature(coroutines)] +#![feature(if_let_guard)] #![feature(iter_from_coroutine)] #![feature(let_chains)] -#![feature(if_let_guard)] -#![feature(proc_macro_internals)] #![feature(macro_metavar_expr)] #![feature(min_specialization)] -#![feature(trusted_len)] -#![feature(try_blocks)] #![feature(never_type)] -#![allow(rustc::potential_query_instability)] +#![feature(proc_macro_internals)] +#![feature(rustdoc_internals)] +#![feature(trusted_len)] +// tidy-alphabetical-end extern crate proc_macro; diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 79e4ff81093b0..825034cf96a91 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -1,15 +1,13 @@ use crate::creader::CrateMetadataRef; -use decoder::Metadata; +pub(crate) use decoder::{CrateMetadata, CrateNumMap, MetadataBlob}; +use decoder::{DecodeContext, Metadata}; use def_path_hash_map::DefPathHashMapRef; -use rustc_data_structures::fx::FxHashMap; -use rustc_macros::{Decodable, Encodable, TyDecodable, TyEncodable}; -use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile; -use rustc_middle::middle::lib_features::FeatureStability; -use table::TableBuilder; - +use encoder::EncodeContext; +pub use encoder::{encode_metadata, rendered_const, EncodedMetadata}; use rustc_ast as ast; use rustc_ast::expand::StrippedCfgItem; use rustc_attr as attr; +use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::svh::Svh; use rustc_hir as hir; use rustc_hir::def::{CtorKind, DefKind, DocLinkResMap}; @@ -18,10 +16,13 @@ use rustc_hir::definitions::DefKey; use rustc_hir::lang_items::LangItem; use rustc_index::bit_set::BitSet; use rustc_index::IndexVec; +use rustc_macros::{Decodable, Encodable, TyDecodable, TyEncodable}; use rustc_macros::{MetadataDecodable, MetadataEncodable}; use rustc_middle::metadata::ModChild; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs; +use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile; use rustc_middle::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo}; +use rustc_middle::middle::lib_features::FeatureStability; use rustc_middle::middle::resolve_bound_vars::ObjectLifetimeDefault; use rustc_middle::mir; use rustc_middle::trivially_parameterized_over_tcx; @@ -33,20 +34,14 @@ use rustc_serialize::opaque::FileEncoder; use rustc_session::config::SymbolManglingVersion; use rustc_session::cstore::{CrateDepKind, ForeignModule, LinkagePreference, NativeLib}; use rustc_span::edition::Edition; -use rustc_span::hygiene::{ExpnIndex, MacroKind}; +use rustc_span::hygiene::{ExpnIndex, MacroKind, SyntaxContextData}; use rustc_span::symbol::{Ident, Symbol}; use rustc_span::{self, ExpnData, ExpnHash, ExpnId, Span}; use rustc_target::abi::{FieldIdx, VariantIdx}; use rustc_target::spec::{PanicStrategy, TargetTriple}; - use std::marker::PhantomData; use std::num::NonZero; - -use decoder::DecodeContext; -pub(crate) use decoder::{CrateMetadata, CrateNumMap, MetadataBlob}; -use encoder::EncodeContext; -pub use encoder::{encode_metadata, rendered_const, EncodedMetadata}; -use rustc_span::hygiene::SyntaxContextData; +use table::TableBuilder; mod decoder; mod def_path_hash_map; diff --git a/compiler/rustc_middle/messages.ftl b/compiler/rustc_middle/messages.ftl index 27d555d7e26c7..f4d619329eb9c 100644 --- a/compiler/rustc_middle/messages.ftl +++ b/compiler/rustc_middle/messages.ftl @@ -50,6 +50,20 @@ middle_const_not_used_in_type_alias = middle_cycle = a cycle occurred during layout computation +middle_deprecated = use of deprecated {$kind} `{$path}`{$has_note -> + [true] : {$note} + *[other] {""} + } +middle_deprecated_in_future = use of {$kind} `{$path}` that will be deprecated in a future Rust version{$has_note -> + [true] : {$note} + *[other] {""} + } +middle_deprecated_in_version = use of {$kind} `{$path}` that will be deprecated in future version {$version}{$has_note -> + [true] : {$note} + *[other] {""} + } +middle_deprecated_suggestion = replace the use of the deprecated {$kind} + middle_drop_check_overflow = overflow while adding drop-check rules for {$ty} .note = overflowed on {$overflow_ty} diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs index c7aea137b6841..f3f24f7717701 100644 --- a/compiler/rustc_middle/src/hir/map/mod.rs +++ b/compiler/rustc_middle/src/hir/map/mod.rs @@ -511,14 +511,14 @@ impl<'hir> Map<'hir> { self.body_const_context(self.enclosing_body_owner(hir_id)).is_some() } - /// Retrieves the `HirId` for `id`'s enclosing method, unless there's a - /// `while` or `loop` before reaching it, as block tail returns are not - /// available in them. + /// Retrieves the `HirId` for `id`'s enclosing function *if* the `id` block or return is + /// in the "tail" position of the function, in other words if it's likely to correspond + /// to the return type of the function. /// /// ``` /// fn foo(x: usize) -> bool { /// if x == 1 { - /// true // If `get_return_block` gets passed the `id` corresponding + /// true // If `get_fn_id_for_return_block` gets passed the `id` corresponding /// } else { // to this, it will return `foo`'s `HirId`. /// false /// } @@ -528,12 +528,12 @@ impl<'hir> Map<'hir> { /// ```compile_fail,E0308 /// fn foo(x: usize) -> bool { /// loop { - /// true // If `get_return_block` gets passed the `id` corresponding + /// true // If `get_fn_id_for_return_block` gets passed the `id` corresponding /// } // to this, it will return `None`. /// false /// } /// ``` - pub fn get_return_block(self, id: HirId) -> Option { + pub fn get_fn_id_for_return_block(self, id: HirId) -> Option { let mut iter = self.parent_iter(id).peekable(); let mut ignore_tail = false; if let Node::Expr(Expr { kind: ExprKind::Ret(_), .. }) = self.tcx.hir_node(id) { @@ -549,6 +549,11 @@ impl<'hir> Map<'hir> { Node::Block(Block { expr: None, .. }) => return None, // The current node is not the tail expression of its parent. Node::Block(Block { expr: Some(e), .. }) if hir_id != e.hir_id => return None, + Node::Block(Block { expr: Some(e), .. }) + if matches!(e.kind, ExprKind::If(_, _, None)) => + { + return None; + } _ => {} } } diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs index 04fd4c8d0f7b9..70437fdcb6ff1 100644 --- a/compiler/rustc_middle/src/lib.rs +++ b/compiler/rustc_middle/src/lib.rs @@ -22,46 +22,46 @@ //! //! This API is completely unstable and subject to change. +// tidy-alphabetical-start +#![allow(internal_features)] +#![allow(rustc::diagnostic_outside_of_impl)] +#![allow(rustc::potential_query_instability)] +#![allow(rustc::untranslatable_diagnostic)] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] -#![feature(min_exhaustive_patterns)] -#![feature(rustdoc_internals)] #![feature(allocator_api)] #![feature(array_windows)] #![feature(assert_matches)] #![feature(box_patterns)] #![feature(closure_track_caller)] -#![feature(core_intrinsics)] +#![feature(const_option)] #![feature(const_type_name)] -#![feature(discriminant_kind)] +#![feature(core_intrinsics)] #![feature(coroutines)] -#![feature(stmt_expr_attributes)] +#![feature(decl_macro)] +#![feature(discriminant_kind)] +#![feature(extern_types)] +#![feature(extract_if)] #![feature(if_let_guard)] +#![feature(intra_doc_pointers)] #![feature(iter_from_coroutine)] +#![feature(let_chains)] +#![feature(macro_metavar_expr)] +#![feature(min_exhaustive_patterns)] +#![feature(min_specialization)] #![feature(negative_impls)] #![feature(never_type)] -#![feature(extern_types)] #![feature(new_uninit)] -#![feature(let_chains)] -#![feature(min_specialization)] -#![feature(trusted_len)] -#![feature(type_alias_impl_trait)] -#![feature(strict_provenance)] +#![feature(ptr_alignment_type)] #![feature(rustc_attrs)] -#![feature(control_flow_enum)] +#![feature(rustdoc_internals)] +#![feature(strict_provenance)] #![feature(trait_upcasting)] +#![feature(trusted_len)] #![feature(try_blocks)] -#![feature(decl_macro)] -#![feature(extract_if)] -#![feature(intra_doc_pointers)] +#![feature(type_alias_impl_trait)] #![feature(yeet_expr)] -#![feature(const_option)] -#![feature(ptr_alignment_type)] -#![feature(macro_metavar_expr)] -#![allow(internal_features)] -#![allow(rustc::potential_query_instability)] -#![allow(rustc::diagnostic_outside_of_impl)] -#![allow(rustc::untranslatable_diagnostic)] +// tidy-alphabetical-end #[macro_use] extern crate tracing; diff --git a/compiler/rustc_middle/src/middle/stability.rs b/compiler/rustc_middle/src/middle/stability.rs index 67bd53f53daeb..e5df05763b022 100644 --- a/compiler/rustc_middle/src/middle/stability.rs +++ b/compiler/rustc_middle/src/middle/stability.rs @@ -9,15 +9,15 @@ use rustc_attr::{ self as attr, ConstStability, DefaultBodyStability, DeprecatedSince, Deprecation, Stability, }; use rustc_data_structures::unord::UnordMap; -use rustc_errors::{Applicability, Diag}; +use rustc_errors::{Applicability, Diag, EmissionGuarantee}; use rustc_feature::GateIssue; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId, LocalDefIdMap}; use rustc_hir::{self as hir, HirId}; -use rustc_macros::{Decodable, Encodable, HashStable}; +use rustc_macros::{Decodable, Encodable, HashStable, Subdiagnostic}; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_session::lint::builtin::{DEPRECATED, DEPRECATED_IN_FUTURE, SOFT_UNSTABLE}; -use rustc_session::lint::{BuiltinLintDiag, Level, Lint, LintBuffer}; +use rustc_session::lint::{BuiltinLintDiag, DeprecatedSinceKind, Level, Lint, LintBuffer}; use rustc_session::parse::feature_err_issue; use rustc_session::Session; use rustc_span::symbol::{sym, Symbol}; @@ -125,90 +125,107 @@ pub fn report_unstable( } } -pub fn deprecation_suggestion( - diag: &mut Diag<'_, ()>, - kind: &str, - suggestion: Option, - span: Span, -) { - if let Some(suggestion) = suggestion { - diag.span_suggestion_verbose( - span, - format!("replace the use of the deprecated {kind}"), - suggestion, - Applicability::MachineApplicable, - ); - } -} - fn deprecation_lint(is_in_effect: bool) -> &'static Lint { if is_in_effect { DEPRECATED } else { DEPRECATED_IN_FUTURE } } -fn deprecation_message( - is_in_effect: bool, - since: DeprecatedSince, - note: Option, - kind: &str, - path: &str, -) -> String { - let message = if is_in_effect { - format!("use of deprecated {kind} `{path}`") +#[derive(Subdiagnostic)] +#[suggestion( + middle_deprecated_suggestion, + code = "{suggestion}", + style = "verbose", + applicability = "machine-applicable" +)] +pub struct DeprecationSuggestion { + #[primary_span] + pub span: Span, + + pub kind: String, + pub suggestion: Symbol, +} + +pub struct Deprecated { + pub sub: Option, + + // FIXME: make this translatable + pub kind: String, + pub path: String, + pub note: Option, + pub since_kind: DeprecatedSinceKind, +} + +impl<'a, G: EmissionGuarantee> rustc_errors::LintDiagnostic<'a, G> for Deprecated { + fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, G>) { + diag.arg("kind", self.kind); + diag.arg("path", self.path); + if let DeprecatedSinceKind::InVersion(version) = self.since_kind { + diag.arg("version", version); + } + if let Some(note) = self.note { + diag.arg("has_note", true); + diag.arg("note", note); + } else { + diag.arg("has_note", false); + } + if let Some(sub) = self.sub { + diag.subdiagnostic(diag.dcx, sub); + } + } + + fn msg(&self) -> rustc_errors::DiagMessage { + match &self.since_kind { + DeprecatedSinceKind::InEffect => crate::fluent_generated::middle_deprecated, + DeprecatedSinceKind::InFuture => crate::fluent_generated::middle_deprecated_in_future, + DeprecatedSinceKind::InVersion(_) => { + crate::fluent_generated::middle_deprecated_in_version + } + } + } +} + +fn deprecated_since_kind(is_in_effect: bool, since: DeprecatedSince) -> DeprecatedSinceKind { + if is_in_effect { + DeprecatedSinceKind::InEffect } else { match since { - DeprecatedSince::RustcVersion(version) => format!( - "use of {kind} `{path}` that will be deprecated in future version {version}" - ), - DeprecatedSince::Future => { - format!("use of {kind} `{path}` that will be deprecated in a future Rust version") + DeprecatedSince::RustcVersion(version) => { + DeprecatedSinceKind::InVersion(version.to_string()) } + DeprecatedSince::Future => DeprecatedSinceKind::InFuture, DeprecatedSince::NonStandard(_) | DeprecatedSince::Unspecified | DeprecatedSince::Err => { unreachable!("this deprecation is always in effect; {since:?}") } } - }; - - match note { - Some(reason) => format!("{message}: {reason}"), - None => message, } } -pub fn deprecation_message_and_lint( - depr: &Deprecation, - kind: &str, - path: &str, -) -> (String, &'static Lint) { - let is_in_effect = depr.is_in_effect(); - ( - deprecation_message(is_in_effect, depr.since, depr.note, kind, path), - deprecation_lint(is_in_effect), - ) -} - -pub fn early_report_deprecation( +pub fn early_report_macro_deprecation( lint_buffer: &mut LintBuffer, - message: String, - suggestion: Option, - lint: &'static Lint, + depr: &Deprecation, span: Span, node_id: NodeId, + path: String, ) { if span.in_derive_expansion() { return; } - let diag = BuiltinLintDiag::DeprecatedMacro(suggestion, span); - lint_buffer.buffer_lint_with_diagnostic(lint, node_id, span, message, diag); + let is_in_effect = depr.is_in_effect(); + let diag = BuiltinLintDiag::DeprecatedMacro { + suggestion: depr.suggestion, + suggestion_span: span, + note: depr.note, + path, + since_kind: deprecated_since_kind(is_in_effect, depr.since.clone()), + }; + lint_buffer.buffer_lint(deprecation_lint(is_in_effect), node_id, span, diag); } fn late_report_deprecation( tcx: TyCtxt<'_>, - message: String, - suggestion: Option, - lint: &'static Lint, + depr: &Deprecation, span: Span, method_span: Option, hir_id: HirId, @@ -217,13 +234,26 @@ fn late_report_deprecation( if span.in_derive_expansion() { return; } + + let def_path = with_no_trimmed_paths!(tcx.def_path_str(def_id)); + let def_kind = tcx.def_descr(def_id); + let is_in_effect = depr.is_in_effect(); + let method_span = method_span.unwrap_or(span); - tcx.node_span_lint(lint, hir_id, method_span, message, |diag| { - if let hir::Node::Expr(_) = tcx.hir_node(hir_id) { - let kind = tcx.def_descr(def_id); - deprecation_suggestion(diag, kind, suggestion, method_span); - } - }); + let suggestion = + if let hir::Node::Expr(_) = tcx.hir_node(hir_id) { depr.suggestion } else { None }; + let diag = Deprecated { + sub: suggestion.map(|suggestion| DeprecationSuggestion { + span: method_span, + kind: def_kind.to_owned(), + suggestion, + }), + kind: def_kind.to_owned(), + path: def_path, + note: depr.note, + since_kind: deprecated_since_kind(is_in_effect, depr.since), + }; + tcx.emit_node_span_lint(deprecation_lint(is_in_effect), hir_id, method_span, diag); } /// Result of `TyCtxt::eval_stability`. @@ -352,28 +382,9 @@ impl<'tcx> TyCtxt<'tcx> { // Calculating message for lint involves calling `self.def_path_str`. // Which by default to calculate visible path will invoke expensive `visible_parent_map` query. // So we skip message calculation altogether, if lint is allowed. - let is_in_effect = depr_attr.is_in_effect(); - let lint = deprecation_lint(is_in_effect); + let lint = deprecation_lint(depr_attr.is_in_effect()); if self.lint_level_at_node(lint, id).0 != Level::Allow { - let def_path = with_no_trimmed_paths!(self.def_path_str(def_id)); - let def_kind = self.def_descr(def_id); - - late_report_deprecation( - self, - deprecation_message( - is_in_effect, - depr_attr.since, - depr_attr.note, - def_kind, - &def_path, - ), - depr_attr.suggestion, - lint, - span, - method_span, - id, - def_id, - ); + late_report_deprecation(self, depr_attr, span, method_span, id, def_id); } } }; diff --git a/compiler/rustc_middle/src/mir/coverage.rs b/compiler/rustc_middle/src/mir/coverage.rs index 7f9a5a366d74e..4155c61e96d04 100644 --- a/compiler/rustc_middle/src/mir/coverage.rs +++ b/compiler/rustc_middle/src/mir/coverage.rs @@ -181,8 +181,8 @@ impl Debug for CodeRegion { } } -#[derive(Copy, Clone, Debug, PartialEq, TyEncodable, TyDecodable, Hash, HashStable)] -#[derive(TypeFoldable, TypeVisitable)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, HashStable)] +#[derive(TyEncodable, TyDecodable, TypeFoldable, TypeVisitable)] pub enum Op { Subtract, Add, diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index 5aaa1c30cade0..7e8598b49df43 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -971,9 +971,6 @@ impl<'tcx> Debug for Rvalue<'tcx> { with_no_trimmed_paths!(write!(fmt, "{place:?} as {ty} ({kind:?})")) } BinaryOp(ref op, box (ref a, ref b)) => write!(fmt, "{op:?}({a:?}, {b:?})"), - CheckedBinaryOp(ref op, box (ref a, ref b)) => { - write!(fmt, "Checked{op:?}({a:?}, {b:?})") - } UnaryOp(ref op, ref a) => write!(fmt, "{op:?}({a:?})"), Discriminant(ref place) => write!(fmt, "discriminant({place:?})"), NullaryOp(ref op, ref t) => { diff --git a/compiler/rustc_middle/src/mir/statement.rs b/compiler/rustc_middle/src/mir/statement.rs index 375f1f15a39ed..4d9a931d69791 100644 --- a/compiler/rustc_middle/src/mir/statement.rs +++ b/compiler/rustc_middle/src/mir/statement.rs @@ -438,7 +438,6 @@ impl<'tcx> Rvalue<'tcx> { _, ) | Rvalue::BinaryOp(_, _) - | Rvalue::CheckedBinaryOp(_, _) | Rvalue::NullaryOp(_, _) | Rvalue::UnaryOp(_, _) | Rvalue::Discriminant(_) diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index e124b478f4193..2b28496faec73 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -1295,18 +1295,12 @@ pub enum Rvalue<'tcx> { /// truncated as needed. /// * The `Bit*` operations accept signed integers, unsigned integers, or bools with matching /// types and return a value of that type. + /// * The `FooWithOverflow` are like the `Foo`, but returning `(T, bool)` instead of just `T`, + /// where the `bool` is true if the result is not equal to the infinite-precision result. /// * The remaining operations accept signed integers, unsigned integers, or floats with /// matching types and return a value of that type. BinaryOp(BinOp, Box<(Operand<'tcx>, Operand<'tcx>)>), - /// Same as `BinaryOp`, but yields `(T, bool)` with a `bool` indicating an error condition. - /// - /// For addition, subtraction, and multiplication on integers the error condition is set when - /// the infinite precision result would not be equal to the actual result. - /// - /// Other combinations of types and operators are unsupported. - CheckedBinaryOp(BinOp, Box<(Operand<'tcx>, Operand<'tcx>)>), - /// Computes a value as described by the operation. NullaryOp(NullOp<'tcx>, Ty<'tcx>), @@ -1449,14 +1443,23 @@ pub enum BinOp { Add, /// Like `Add`, but with UB on overflow. (Integers only.) AddUnchecked, + /// Like `Add`, but returns `(T, bool)` of both the wrapped result + /// and a bool indicating whether it overflowed. + AddWithOverflow, /// The `-` operator (subtraction) Sub, /// Like `Sub`, but with UB on overflow. (Integers only.) SubUnchecked, + /// Like `Sub`, but returns `(T, bool)` of both the wrapped result + /// and a bool indicating whether it overflowed. + SubWithOverflow, /// The `*` operator (multiplication) Mul, /// Like `Mul`, but with UB on overflow. (Integers only.) MulUnchecked, + /// Like `Mul`, but returns `(T, bool)` of both the wrapped result + /// and a bool indicating whether it overflowed. + MulWithOverflow, /// The `/` operator (division) /// /// For integer types, division by zero is UB, as is `MIN / -1` for signed. diff --git a/compiler/rustc_middle/src/mir/tcx.rs b/compiler/rustc_middle/src/mir/tcx.rs index 4994679ad5dad..e1ae2e0866675 100644 --- a/compiler/rustc_middle/src/mir/tcx.rs +++ b/compiler/rustc_middle/src/mir/tcx.rs @@ -179,12 +179,6 @@ impl<'tcx> Rvalue<'tcx> { let rhs_ty = rhs.ty(local_decls, tcx); op.ty(tcx, lhs_ty, rhs_ty) } - Rvalue::CheckedBinaryOp(op, box (ref lhs, ref rhs)) => { - let lhs_ty = lhs.ty(local_decls, tcx); - let rhs_ty = rhs.ty(local_decls, tcx); - let ty = op.ty(tcx, lhs_ty, rhs_ty); - Ty::new_tup(tcx, &[ty, tcx.types.bool]) - } Rvalue::UnaryOp(UnOp::Not | UnOp::Neg, ref operand) => operand.ty(local_decls, tcx), Rvalue::Discriminant(ref place) => place.ty(local_decls, tcx).ty.discriminant_ty(tcx), Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(..), _) => { @@ -263,6 +257,11 @@ impl<'tcx> BinOp { assert_eq!(lhs_ty, rhs_ty); lhs_ty } + &BinOp::AddWithOverflow | &BinOp::SubWithOverflow | &BinOp::MulWithOverflow => { + // these should be integers of the same size. + assert_eq!(lhs_ty, rhs_ty); + Ty::new_tup(tcx, &[lhs_ty, tcx.types.bool]) + } &BinOp::Shl | &BinOp::ShlUnchecked | &BinOp::Shr @@ -315,6 +314,9 @@ impl BinOp { BinOp::Le => hir::BinOpKind::Le, BinOp::Ge => hir::BinOpKind::Ge, BinOp::Cmp + | BinOp::AddWithOverflow + | BinOp::SubWithOverflow + | BinOp::MulWithOverflow | BinOp::AddUnchecked | BinOp::SubUnchecked | BinOp::MulUnchecked @@ -325,4 +327,24 @@ impl BinOp { } } } + + /// If this is a `FooWithOverflow`, return `Some(Foo)`. + pub fn overflowing_to_wrapping(self) -> Option { + Some(match self { + BinOp::AddWithOverflow => BinOp::Add, + BinOp::SubWithOverflow => BinOp::Sub, + BinOp::MulWithOverflow => BinOp::Mul, + _ => return None, + }) + } + + /// If this is a `Foo`, return `Some(FooWithOverflow)`. + pub fn wrapping_to_overflowing(self) -> Option { + Some(match self { + BinOp::Add => BinOp::AddWithOverflow, + BinOp::Sub => BinOp::SubWithOverflow, + BinOp::Mul => BinOp::MulWithOverflow, + _ => return None, + }) + } } diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index d97abc3f1904f..8901fd42d9366 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -696,8 +696,7 @@ macro_rules! make_mir_visitor { self.visit_ty($(& $mutability)? *ty, TyContext::Location(location)); } - Rvalue::BinaryOp(_bin_op, box(lhs, rhs)) - | Rvalue::CheckedBinaryOp(_bin_op, box(lhs, rhs)) => { + Rvalue::BinaryOp(_bin_op, box(lhs, rhs)) => { self.visit_operand(lhs, location); self.visit_operand(rhs, location); } diff --git a/compiler/rustc_middle/src/traits/solve/cache.rs b/compiler/rustc_middle/src/traits/solve/cache.rs index 03ce7cf98cf7e..2ff4ade21d087 100644 --- a/compiler/rustc_middle/src/traits/solve/cache.rs +++ b/compiler/rustc_middle/src/traits/solve/cache.rs @@ -14,11 +14,11 @@ pub struct EvaluationCache<'tcx> { map: Lock, CacheEntry<'tcx>>>, } -#[derive(PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq)] pub struct CacheData<'tcx> { pub result: QueryResult<'tcx>, pub proof_tree: Option<&'tcx [inspect::GoalEvaluationStep>]>, - pub reached_depth: usize, + pub additional_depth: usize, pub encountered_overflow: bool, } @@ -29,7 +29,7 @@ impl<'tcx> EvaluationCache<'tcx> { tcx: TyCtxt<'tcx>, key: CanonicalInput<'tcx>, proof_tree: Option<&'tcx [inspect::GoalEvaluationStep>]>, - reached_depth: usize, + additional_depth: usize, encountered_overflow: bool, cycle_participants: FxHashSet>, dep_node: DepNodeIndex, @@ -40,17 +40,17 @@ impl<'tcx> EvaluationCache<'tcx> { let data = WithDepNode::new(dep_node, QueryData { result, proof_tree }); entry.cycle_participants.extend(cycle_participants); if encountered_overflow { - entry.with_overflow.insert(reached_depth, data); + entry.with_overflow.insert(additional_depth, data); } else { - entry.success = Some(Success { data, reached_depth }); + entry.success = Some(Success { data, additional_depth }); } if cfg!(debug_assertions) { drop(map); - if Some(CacheData { result, proof_tree, reached_depth, encountered_overflow }) - != self.get(tcx, key, |_| false, Limit(reached_depth)) - { - bug!("unable to retrieve inserted element from cache: {key:?}"); + let expected = CacheData { result, proof_tree, additional_depth, encountered_overflow }; + let actual = self.get(tcx, key, [], Limit(additional_depth)); + if !actual.as_ref().is_some_and(|actual| expected == *actual) { + bug!("failed to lookup inserted element for {key:?}: {expected:?} != {actual:?}"); } } } @@ -63,23 +63,25 @@ impl<'tcx> EvaluationCache<'tcx> { &self, tcx: TyCtxt<'tcx>, key: CanonicalInput<'tcx>, - cycle_participant_in_stack: impl FnOnce(&FxHashSet>) -> bool, + stack_entries: impl IntoIterator>, available_depth: Limit, ) -> Option> { let map = self.map.borrow(); let entry = map.get(&key)?; - if cycle_participant_in_stack(&entry.cycle_participants) { - return None; + for stack_entry in stack_entries { + if entry.cycle_participants.contains(&stack_entry) { + return None; + } } if let Some(ref success) = entry.success { - if available_depth.value_within_limit(success.reached_depth) { + if available_depth.value_within_limit(success.additional_depth) { let QueryData { result, proof_tree } = success.data.get(tcx); return Some(CacheData { result, proof_tree, - reached_depth: success.reached_depth, + additional_depth: success.additional_depth, encountered_overflow: false, }); } @@ -90,7 +92,7 @@ impl<'tcx> EvaluationCache<'tcx> { CacheData { result, proof_tree, - reached_depth: available_depth.0, + additional_depth: available_depth.0, encountered_overflow: true, } }) @@ -99,7 +101,7 @@ impl<'tcx> EvaluationCache<'tcx> { struct Success<'tcx> { data: WithDepNode>, - reached_depth: usize, + additional_depth: usize, } #[derive(Clone, Copy)] diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs index 9dc30447f0e57..f7cc055be11c2 100644 --- a/compiler/rustc_middle/src/ty/consts.rs +++ b/compiler/rustc_middle/src/ty/consts.rs @@ -175,6 +175,14 @@ impl<'tcx> Const<'tcx> { } impl<'tcx> rustc_type_ir::inherent::Const> for Const<'tcx> { + fn new_infer(tcx: TyCtxt<'tcx>, infer: ty::InferConst, ty: Ty<'tcx>) -> Self { + Const::new_infer(tcx, infer, ty) + } + + fn new_var(tcx: TyCtxt<'tcx>, vid: ty::ConstVid, ty: Ty<'tcx>) -> Self { + Const::new_var(tcx, vid, ty) + } + fn new_anon_bound( tcx: TyCtxt<'tcx>, debruijn: ty::DebruijnIndex, diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 69681930be657..8185c99c2fd20 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -134,7 +134,6 @@ impl<'tcx> Interner for TyCtxt<'tcx> { type EarlyParamRegion = ty::EarlyParamRegion; type LateParamRegion = ty::LateParamRegion; type BoundRegion = ty::BoundRegion; - type InferRegion = ty::RegionVid; type PlaceholderRegion = ty::PlaceholderRegion; type ParamEnv = ty::ParamEnv<'tcx>; @@ -233,6 +232,10 @@ impl<'tcx> Interner for TyCtxt<'tcx> { fn parent(self, def_id: Self::DefId) -> Self::DefId { self.parent(def_id) } + + fn recursion_limit(self) -> usize { + self.recursion_limit().0 + } } impl<'tcx> rustc_type_ir::inherent::Abi> for abi::Abi { diff --git a/compiler/rustc_middle/src/ty/generic_args.rs b/compiler/rustc_middle/src/ty/generic_args.rs index 38b2987399a9a..3d263e62de66e 100644 --- a/compiler/rustc_middle/src/ty/generic_args.rs +++ b/compiler/rustc_middle/src/ty/generic_args.rs @@ -27,6 +27,7 @@ use std::ops::Deref; use std::ptr::NonNull; pub type GenericArgKind<'tcx> = rustc_type_ir::GenericArgKind>; +pub type TermKind<'tcx> = rustc_type_ir::TermKind>; /// An entity in the Rust type system, which can be one of /// several kinds (types, lifetimes, and consts). diff --git a/compiler/rustc_middle/src/ty/generics.rs b/compiler/rustc_middle/src/ty/generics.rs index 04655c5d20bad..cfaca05c2f06b 100644 --- a/compiler/rustc_middle/src/ty/generics.rs +++ b/compiler/rustc_middle/src/ty/generics.rs @@ -391,6 +391,14 @@ impl<'tcx> Generics { } false } + + pub fn is_empty(&'tcx self) -> bool { + self.count() == 0 + } + + pub fn is_own_empty(&'tcx self) -> bool { + self.own_params.is_empty() + } } /// Bounds on generics. diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index ce63fc20ddb20..f27409894fa59 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -28,7 +28,7 @@ use crate::ty::fast_reject::SimplifiedType; use crate::ty::util::Discr; pub use adt::*; pub use assoc::*; -pub use generic_args::{GenericArgKind, *}; +pub use generic_args::{GenericArgKind, TermKind, *}; pub use generics::*; pub use intrinsic::IntrinsicDef; use rustc_ast as ast; @@ -48,7 +48,8 @@ use rustc_hir::def::{CtorKind, CtorOf, DefKind, DocLinkResMap, LifetimeRes, Res} use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, LocalDefIdMap}; use rustc_index::IndexVec; use rustc_macros::{ - Decodable, Encodable, HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable, + extension, Decodable, Encodable, HashStable, TyDecodable, TyEncodable, TypeFoldable, + TypeVisitable, }; use rustc_query_system::ich::StableHashingContext; use rustc_serialize::{Decodable, Encodable}; @@ -521,6 +522,14 @@ pub struct Term<'tcx> { marker: PhantomData<(Ty<'tcx>, Const<'tcx>)>, } +impl<'tcx> rustc_type_ir::inherent::IntoKind for Term<'tcx> { + type Kind = TermKind<'tcx>; + + fn kind(self) -> Self::Kind { + self.unpack() + } +} + #[cfg(parallel_compiler)] unsafe impl<'tcx> rustc_data_structures::sync::DynSend for Term<'tcx> where &'tcx (Ty<'tcx>, Const<'tcx>): rustc_data_structures::sync::DynSend @@ -536,14 +545,10 @@ unsafe impl<'tcx> Sync for Term<'tcx> where &'tcx (Ty<'tcx>, Const<'tcx>): Sync impl Debug for Term<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let data = if let Some(ty) = self.ty() { - format!("Term::Ty({ty:?})") - } else if let Some(ct) = self.ct() { - format!("Term::Ct({ct:?})") - } else { - unreachable!() - }; - f.write_str(&data) + match self.unpack() { + TermKind::Ty(ty) => write!(f, "Term::Ty({ty:?})"), + TermKind::Const(ct) => write!(f, "Term::Const({ct:?})"), + } } } @@ -570,13 +575,19 @@ impl<'tcx> TypeFoldable> for Term<'tcx> { self, folder: &mut F, ) -> Result { - Ok(self.unpack().try_fold_with(folder)?.pack()) + match self.unpack() { + ty::TermKind::Ty(ty) => ty.try_fold_with(folder).map(Into::into), + ty::TermKind::Const(ct) => ct.try_fold_with(folder).map(Into::into), + } } } impl<'tcx> TypeVisitable> for Term<'tcx> { fn visit_with>>(&self, visitor: &mut V) -> V::Result { - self.unpack().visit_with(visitor) + match self.unpack() { + ty::TermKind::Ty(ty) => ty.visit_with(visitor), + ty::TermKind::Const(ct) => ct.visit_with(visitor), + } } } @@ -654,13 +665,7 @@ const TAG_MASK: usize = 0b11; const TYPE_TAG: usize = 0b00; const CONST_TAG: usize = 0b01; -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable)] -#[derive(HashStable, TypeFoldable, TypeVisitable)] -pub enum TermKind<'tcx> { - Ty(Ty<'tcx>), - Const(Const<'tcx>), -} - +#[extension(pub trait TermKindPackExt<'tcx>)] impl<'tcx> TermKind<'tcx> { #[inline] fn pack(self) -> Term<'tcx> { @@ -1809,6 +1814,11 @@ impl<'tcx> TyCtxt<'tcx> { self.get_attrs(did, attr).next().is_some() } + /// Determines whether an item is annotated with a multi-segement attribute + pub fn has_attrs_with_path(self, did: impl Into, attrs: &[Symbol]) -> bool { + self.get_attrs_by_path(did.into(), attrs).next().is_some() + } + /// Returns `true` if this is an `auto trait`. pub fn trait_is_auto(self, trait_def_id: DefId) -> bool { self.trait_def(trait_def_id).has_auto_impl diff --git a/compiler/rustc_middle/src/ty/predicate.rs b/compiler/rustc_middle/src/ty/predicate.rs index 644fca7c5fee9..be91249a25f8d 100644 --- a/compiler/rustc_middle/src/ty/predicate.rs +++ b/compiler/rustc_middle/src/ty/predicate.rs @@ -37,7 +37,11 @@ pub struct Predicate<'tcx>( pub(super) Interned<'tcx, WithCachedTypeInfo>>>, ); -impl<'tcx> rustc_type_ir::inherent::Predicate> for Predicate<'tcx> {} +impl<'tcx> rustc_type_ir::inherent::Predicate> for Predicate<'tcx> { + fn is_coinductive(self, interner: TyCtxt<'tcx>) -> bool { + self.is_coinductive(interner) + } +} impl<'tcx> rustc_type_ir::visit::Flags for Predicate<'tcx> { fn flags(&self) -> TypeFlags { diff --git a/compiler/rustc_middle/src/ty/print/mod.rs b/compiler/rustc_middle/src/ty/print/mod.rs index e7589737d64b9..dc77f59f3d0fa 100644 --- a/compiler/rustc_middle/src/ty/print/mod.rs +++ b/compiler/rustc_middle/src/ty/print/mod.rs @@ -160,7 +160,7 @@ pub trait Printer<'tcx>: Sized { // If we have any generic arguments to print, we do that // on top of the same path, but without its own generics. _ => { - if !generics.own_params.is_empty() && args.len() >= generics.count() { + if !generics.is_own_empty() && args.len() >= generics.count() { let args = generics.own_args_no_defaults(self.tcx(), args); return self.path_generic_args( |cx| cx.print_def_path(def_id, parent_args), diff --git a/compiler/rustc_middle/src/ty/region.rs b/compiler/rustc_middle/src/ty/region.rs index 7540f0ab83ff0..551e2ea229561 100644 --- a/compiler/rustc_middle/src/ty/region.rs +++ b/compiler/rustc_middle/src/ty/region.rs @@ -1,13 +1,12 @@ -use polonius_engine::Atom; use rustc_data_structures::intern::Interned; use rustc_errors::MultiSpan; use rustc_hir::def_id::DefId; -use rustc_index::Idx; use rustc_macros::{HashStable, TyDecodable, TyEncodable}; use rustc_span::symbol::sym; use rustc_span::symbol::{kw, Symbol}; use rustc_span::{ErrorGuaranteed, DUMMY_SP}; use rustc_type_ir::RegionKind as IrRegionKind; +pub use rustc_type_ir::RegionVid; use std::ops::Deref; use crate::ty::{self, BoundVar, TyCtxt, TypeFlags}; @@ -348,21 +347,6 @@ impl std::fmt::Debug for EarlyParamRegion { } } -rustc_index::newtype_index! { - /// A **region** (lifetime) **v**ariable **ID**. - #[derive(HashStable)] - #[encodable] - #[orderable] - #[debug_format = "'?{}"] - pub struct RegionVid {} -} - -impl Atom for RegionVid { - fn index(self) -> usize { - Idx::index(self) - } -} - #[derive(Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable, Copy)] #[derive(HashStable)] /// The parameter representation of late-bound function parameters, "some region diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index 7d24824d568f4..81d92a2a448e4 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -259,18 +259,6 @@ impl<'tcx> DebugWithInfcx> for Region<'tcx> { } } -impl<'tcx> DebugWithInfcx> for ty::RegionVid { - fn fmt>>( - this: WithInfcx<'_, Infcx, &Self>, - f: &mut core::fmt::Formatter<'_>, - ) -> core::fmt::Result { - match this.infcx.universe_of_lt(*this.data) { - Some(universe) => write!(f, "'?{}_{}", this.data.index(), universe.index()), - None => write!(f, "{:?}", this.data), - } - } -} - impl<'tcx, T: DebugWithInfcx>> DebugWithInfcx> for ty::Binder<'tcx, T> { fn fmt>>( this: WithInfcx<'_, Infcx, &Self>, @@ -383,13 +371,10 @@ impl<'tcx, T: Lift>> Lift> for Option { impl<'a, 'tcx> Lift> for Term<'a> { type Lifted = ty::Term<'tcx>; fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option { - Some( - match self.unpack() { - TermKind::Ty(ty) => TermKind::Ty(tcx.lift(ty)?), - TermKind::Const(c) => TermKind::Const(tcx.lift(c)?), - } - .pack(), - ) + match self.unpack() { + TermKind::Ty(ty) => tcx.lift(ty).map(Into::into), + TermKind::Const(c) => tcx.lift(c).map(Into::into), + } } } diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 7c41c5f512e6e..2d9d178449e09 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -1611,6 +1611,14 @@ impl<'tcx> rustc_type_ir::inherent::Ty> for Ty<'tcx> { tcx.types.bool } + fn new_infer(tcx: TyCtxt<'tcx>, infer: ty::InferTy) -> Self { + Ty::new_infer(tcx, infer) + } + + fn new_var(tcx: TyCtxt<'tcx>, vid: ty::TyVid) -> Self { + Ty::new_var(tcx, vid) + } + fn new_anon_bound(tcx: TyCtxt<'tcx>, debruijn: ty::DebruijnIndex, var: ty::BoundVar) -> Self { Ty::new_bound(tcx, debruijn, ty::BoundTy { var, kind: ty::BoundTyKind::Anon }) } diff --git a/compiler/rustc_middle/src/ty/trait_def.rs b/compiler/rustc_middle/src/ty/trait_def.rs index c5b3de17bcb30..cf1cbb934105b 100644 --- a/compiler/rustc_middle/src/ty/trait_def.rs +++ b/compiler/rustc_middle/src/ty/trait_def.rs @@ -39,11 +39,16 @@ pub struct TraitDef { /// also have already switched to the new trait solver. pub is_coinductive: bool, - /// If `true`, then this trait has the `#[rustc_skip_array_during_method_dispatch]` + /// If `true`, then this trait has the `#[rustc_skip_during_method_dispatch(array)]` /// attribute, indicating that editions before 2021 should not consider this trait /// during method dispatch if the receiver is an array. pub skip_array_during_method_dispatch: bool, + /// If `true`, then this trait has the `#[rustc_skip_during_method_dispatch(boxed_slice)]` + /// attribute, indicating that editions before 2024 should not consider this trait + /// during method dispatch if the receiver is a boxed slice. + pub skip_boxed_slice_during_method_dispatch: bool, + /// Used to determine whether the standard library is allowed to specialize /// on this trait. pub specialization_kind: TraitSpecializationKind, diff --git a/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs b/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs index c669d3fd6230d..6f8cfc3af4473 100644 --- a/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs +++ b/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs @@ -195,9 +195,15 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> { }, @call(mir_checked, args) => { parse_by_kind!(self, args[0], _, "binary op", - ExprKind::Binary { op, lhs, rhs } => Ok(Rvalue::CheckedBinaryOp( - *op, Box::new((self.parse_operand(*lhs)?, self.parse_operand(*rhs)?)) - )), + ExprKind::Binary { op, lhs, rhs } => { + if let Some(op_with_overflow) = op.wrapping_to_overflowing() { + Ok(Rvalue::BinaryOp( + op_with_overflow, Box::new((self.parse_operand(*lhs)?, self.parse_operand(*rhs)?)) + )) + } else { + Err(self.expr_error(expr_id, "No WithOverflow form of this operator")) + } + }, ) }, @call(mir_offset, args) => { 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 0b2248d049afc..60a2827a3e292 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs @@ -568,11 +568,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let result_tup = Ty::new_tup(self.tcx, &[ty, bool_ty]); let result_value = self.temp(result_tup, span); + let op_with_overflow = op.wrapping_to_overflowing().unwrap(); + self.cfg.push_assign( block, source_info, result_value, - Rvalue::CheckedBinaryOp(op, Box::new((lhs.to_copy(), rhs.to_copy()))), + Rvalue::BinaryOp(op_with_overflow, Box::new((lhs.to_copy(), rhs.to_copy()))), ); let val_fld = FieldIdx::ZERO; let of_fld = FieldIdx::new(1); diff --git a/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs b/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs index bdc70de58e84b..706bb796349fb 100644 --- a/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs +++ b/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs @@ -109,7 +109,6 @@ where | Rvalue::Repeat(..) | Rvalue::Len(..) | Rvalue::BinaryOp(..) - | Rvalue::CheckedBinaryOp(..) | Rvalue::NullaryOp(..) | Rvalue::UnaryOp(..) | Rvalue::Discriminant(..) diff --git a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs index 6ae7df79d3094..521ecb1b9a594 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs @@ -420,8 +420,7 @@ impl<'b, 'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> Gatherer<'b, 'a, 'tcx, F> { | Rvalue::Cast(_, ref operand, _) | Rvalue::ShallowInitBox(ref operand, _) | Rvalue::UnaryOp(_, ref operand) => self.gather_operand(operand), - Rvalue::BinaryOp(ref _binop, box (ref lhs, ref rhs)) - | Rvalue::CheckedBinaryOp(ref _binop, box (ref lhs, ref rhs)) => { + Rvalue::BinaryOp(ref _binop, box (ref lhs, ref rhs)) => { self.gather_operand(lhs); self.gather_operand(rhs); } diff --git a/compiler/rustc_mir_dataflow/src/value_analysis.rs b/compiler/rustc_mir_dataflow/src/value_analysis.rs index 807bef0741178..1e5322dd99b8a 100644 --- a/compiler/rustc_mir_dataflow/src/value_analysis.rs +++ b/compiler/rustc_mir_dataflow/src/value_analysis.rs @@ -184,7 +184,6 @@ pub trait ValueAnalysis<'tcx> { | Rvalue::Len(..) | Rvalue::Cast(..) | Rvalue::BinaryOp(..) - | Rvalue::CheckedBinaryOp(..) | Rvalue::NullaryOp(..) | Rvalue::UnaryOp(..) | Rvalue::Discriminant(..) diff --git a/compiler/rustc_mir_transform/src/coverage/counters.rs b/compiler/rustc_mir_transform/src/coverage/counters.rs index b98554ec00fa2..b5968517d772d 100644 --- a/compiler/rustc_mir_transform/src/coverage/counters.rs +++ b/compiler/rustc_mir_transform/src/coverage/counters.rs @@ -11,7 +11,7 @@ use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph, TraverseCoverage /// The coverage counter or counter expression associated with a particular /// BCB node or BCB edge. -#[derive(Clone, Copy)] +#[derive(Clone, Copy, PartialEq, Eq, Hash)] pub(super) enum BcbCounter { Counter { id: CounterId }, Expression { id: ExpressionId }, @@ -35,6 +35,13 @@ impl Debug for BcbCounter { } } +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +struct BcbExpression { + lhs: BcbCounter, + op: Op, + rhs: BcbCounter, +} + #[derive(Debug)] pub(super) enum CounterIncrementSite { Node { bcb: BasicCoverageBlock }, @@ -56,9 +63,13 @@ pub(super) struct CoverageCounters { /// We currently don't iterate over this map, but if we do in the future, /// switch it back to `FxIndexMap` to avoid query stability hazards. bcb_edge_counters: FxHashMap<(BasicCoverageBlock, BasicCoverageBlock), BcbCounter>, + /// Table of expression data, associating each expression ID with its /// corresponding operator (+ or -) and its LHS/RHS operands. - expressions: IndexVec, + expressions: IndexVec, + /// Remember expressions that have already been created (or simplified), + /// so that we don't create unnecessary duplicates. + expressions_memo: FxHashMap, } impl CoverageCounters { @@ -76,6 +87,7 @@ impl CoverageCounters { bcb_counters: IndexVec::from_elem_n(None, num_bcbs), bcb_edge_counters: FxHashMap::default(), expressions: IndexVec::new(), + expressions_memo: FxHashMap::default(), }; MakeBcbCounters::new(&mut this, basic_coverage_blocks) @@ -90,8 +102,57 @@ impl CoverageCounters { } fn make_expression(&mut self, lhs: BcbCounter, op: Op, rhs: BcbCounter) -> BcbCounter { - let expression = Expression { lhs: lhs.as_term(), op, rhs: rhs.as_term() }; - let id = self.expressions.push(expression); + let new_expr = BcbExpression { lhs, op, rhs }; + *self + .expressions_memo + .entry(new_expr) + .or_insert_with(|| Self::make_expression_inner(&mut self.expressions, new_expr)) + } + + /// This is an associated function so that we can call it while borrowing + /// `&mut self.expressions_memo`. + fn make_expression_inner( + expressions: &mut IndexVec, + new_expr: BcbExpression, + ) -> BcbCounter { + // Simplify expressions using basic algebra. + // + // Some of these cases might not actually occur in practice, depending + // on the details of how the instrumentor builds expressions. + let BcbExpression { lhs, op, rhs } = new_expr; + + if let BcbCounter::Expression { id } = lhs { + let lhs_expr = &expressions[id]; + + // Simplify `(a - b) + b` to `a`. + if lhs_expr.op == Op::Subtract && op == Op::Add && lhs_expr.rhs == rhs { + return lhs_expr.lhs; + } + // Simplify `(a + b) - b` to `a`. + if lhs_expr.op == Op::Add && op == Op::Subtract && lhs_expr.rhs == rhs { + return lhs_expr.lhs; + } + // Simplify `(a + b) - a` to `b`. + if lhs_expr.op == Op::Add && op == Op::Subtract && lhs_expr.lhs == rhs { + return lhs_expr.rhs; + } + } + + if let BcbCounter::Expression { id } = rhs { + let rhs_expr = &expressions[id]; + + // Simplify `a + (b - a)` to `b`. + if op == Op::Add && rhs_expr.op == Op::Subtract && lhs == rhs_expr.rhs { + return rhs_expr.lhs; + } + // Simplify `a - (a - b)` to `b`. + if op == Op::Subtract && rhs_expr.op == Op::Subtract && lhs == rhs_expr.lhs { + return rhs_expr.rhs; + } + } + + // Simplification failed, so actually create the new expression. + let id = expressions.push(new_expr); BcbCounter::Expression { id } } @@ -166,7 +227,21 @@ impl CoverageCounters { } pub(super) fn into_expressions(self) -> IndexVec { - self.expressions + let old_len = self.expressions.len(); + let expressions = self + .expressions + .into_iter() + .map(|BcbExpression { lhs, op, rhs }| Expression { + lhs: lhs.as_term(), + op, + rhs: rhs.as_term(), + }) + .collect::>(); + + // Expression IDs are indexes into this vector, so make sure we didn't + // accidentally invalidate them by changing its length. + assert_eq!(old_len, expressions.len()); + expressions } } diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs index a42d64f86be18..3d24a56cdd7c6 100644 --- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs +++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs @@ -165,7 +165,9 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, 'tcx> { } } } - Rvalue::CheckedBinaryOp(op, box (left, right)) => { + Rvalue::BinaryOp(overflowing_op, box (left, right)) + if let Some(op) = overflowing_op.overflowing_to_wrapping() => + { // Flood everything now, so we can use `insert_value_idx` directly later. state.flood(target.as_ref(), self.map()); @@ -175,7 +177,7 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, 'tcx> { let overflow_target = self.map().apply(target, TrackElem::Field(1_u32.into())); if value_target.is_some() || overflow_target.is_some() { - let (val, overflow) = self.binary_op(state, *op, left, right); + let (val, overflow) = self.binary_op(state, op, left, right); if let Some(value_target) = value_target { // We have flooded `target` earlier. diff --git a/compiler/rustc_mir_transform/src/dest_prop.rs b/compiler/rustc_mir_transform/src/dest_prop.rs index 1bc383fccc72b..b1016c0867c62 100644 --- a/compiler/rustc_mir_transform/src/dest_prop.rs +++ b/compiler/rustc_mir_transform/src/dest_prop.rs @@ -564,7 +564,7 @@ impl WriteInfo { | Rvalue::ShallowInitBox(op, _) => { self.add_operand(op); } - Rvalue::BinaryOp(_, ops) | Rvalue::CheckedBinaryOp(_, ops) => { + Rvalue::BinaryOp(_, ops) => { for op in [&ops.0, &ops.1] { self.add_operand(op); } diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index 123166a764d8d..1f3e407180b5f 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -831,23 +831,18 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { // on both operands for side effect. let lhs = lhs?; let rhs = rhs?; - if let Some(value) = self.simplify_binary(op, false, ty, lhs, rhs) { - return Some(value); - } - Value::BinaryOp(op, lhs, rhs) - } - Rvalue::CheckedBinaryOp(op, box (ref mut lhs, ref mut rhs)) => { - let ty = lhs.ty(self.local_decls, self.tcx); - let lhs = self.simplify_operand(lhs, location); - let rhs = self.simplify_operand(rhs, location); - // Only short-circuit options after we called `simplify_operand` - // on both operands for side effect. - let lhs = lhs?; - let rhs = rhs?; - if let Some(value) = self.simplify_binary(op, true, ty, lhs, rhs) { - return Some(value); + + if let Some(op) = op.overflowing_to_wrapping() { + if let Some(value) = self.simplify_binary(op, true, ty, lhs, rhs) { + return Some(value); + } + Value::CheckedBinaryOp(op, lhs, rhs) + } else { + if let Some(value) = self.simplify_binary(op, false, ty, lhs, rhs) { + return Some(value); + } + Value::BinaryOp(op, lhs, rhs) } - Value::CheckedBinaryOp(op, lhs, rhs) } Rvalue::UnaryOp(op, ref mut arg) => { let arg = self.simplify_operand(arg, location)?; diff --git a/compiler/rustc_mir_transform/src/known_panics_lint.rs b/compiler/rustc_mir_transform/src/known_panics_lint.rs index d0a5a6cada8fa..38fc37a3a3131 100644 --- a/compiler/rustc_mir_transform/src/known_panics_lint.rs +++ b/compiler/rustc_mir_transform/src/known_panics_lint.rs @@ -399,16 +399,8 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { } Rvalue::BinaryOp(op, box (left, right)) => { trace!("checking BinaryOp(op = {:?}, left = {:?}, right = {:?})", op, left, right); - self.check_binary_op(*op, left, right, location)?; - } - Rvalue::CheckedBinaryOp(op, box (left, right)) => { - trace!( - "checking CheckedBinaryOp(op = {:?}, left = {:?}, right = {:?})", - op, - left, - right - ); - self.check_binary_op(*op, left, right, location)?; + let op = op.overflowing_to_wrapping().unwrap_or(*op); + self.check_binary_op(op, left, right, location)?; } // Do not try creating references (#67862) @@ -555,24 +547,18 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { let right = self.eval_operand(right)?; let right = self.use_ecx(|this| this.ecx.read_immediate(&right))?; - let val = - self.use_ecx(|this| this.ecx.wrapping_binary_op(bin_op, &left, &right))?; - val.into() - } - - CheckedBinaryOp(bin_op, box (ref left, ref right)) => { - let left = self.eval_operand(left)?; - let left = self.use_ecx(|this| this.ecx.read_immediate(&left))?; - - let right = self.eval_operand(right)?; - let right = self.use_ecx(|this| this.ecx.read_immediate(&right))?; - - let (val, overflowed) = - self.use_ecx(|this| this.ecx.overflowing_binary_op(bin_op, &left, &right))?; - let overflowed = ImmTy::from_bool(overflowed, self.tcx); - Value::Aggregate { - variant: VariantIdx::ZERO, - fields: [Value::from(val), overflowed.into()].into_iter().collect(), + if let Some(bin_op) = bin_op.overflowing_to_wrapping() { + let (val, overflowed) = + self.use_ecx(|this| this.ecx.overflowing_binary_op(bin_op, &left, &right))?; + let overflowed = ImmTy::from_bool(overflowed, self.tcx); + Value::Aggregate { + variant: VariantIdx::ZERO, + fields: [Value::from(val), overflowed.into()].into_iter().collect(), + } + } else { + let val = + self.use_ecx(|this| this.ecx.wrapping_binary_op(bin_op, &left, &right))?; + val.into() } } diff --git a/compiler/rustc_mir_transform/src/lower_intrinsics.rs b/compiler/rustc_mir_transform/src/lower_intrinsics.rs index 43d8c45bb2dd9..221301b2ceb04 100644 --- a/compiler/rustc_mir_transform/src/lower_intrinsics.rs +++ b/compiler/rustc_mir_transform/src/lower_intrinsics.rs @@ -140,16 +140,16 @@ impl<'tcx> MirPass<'tcx> for LowerIntrinsics { rhs = args.next().unwrap(); } let bin_op = match intrinsic.name { - sym::add_with_overflow => BinOp::Add, - sym::sub_with_overflow => BinOp::Sub, - sym::mul_with_overflow => BinOp::Mul, + sym::add_with_overflow => BinOp::AddWithOverflow, + sym::sub_with_overflow => BinOp::SubWithOverflow, + sym::mul_with_overflow => BinOp::MulWithOverflow, _ => bug!("unexpected intrinsic"), }; block.statements.push(Statement { source_info: terminator.source_info, kind: StatementKind::Assign(Box::new(( *destination, - Rvalue::CheckedBinaryOp(bin_op, Box::new((lhs.node, rhs.node))), + Rvalue::BinaryOp(bin_op, Box::new((lhs.node, rhs.node))), ))), }); terminator.kind = TerminatorKind::Goto { target }; diff --git a/compiler/rustc_mir_transform/src/promote_consts.rs b/compiler/rustc_mir_transform/src/promote_consts.rs index b3116c002d3ce..34aa31baab79d 100644 --- a/compiler/rustc_mir_transform/src/promote_consts.rs +++ b/compiler/rustc_mir_transform/src/promote_consts.rs @@ -470,7 +470,7 @@ impl<'tcx> Validator<'_, 'tcx> { self.validate_operand(operand)?; } - Rvalue::BinaryOp(op, box (lhs, rhs)) | Rvalue::CheckedBinaryOp(op, box (lhs, rhs)) => { + Rvalue::BinaryOp(op, box (lhs, rhs)) => { let op = *op; let lhs_ty = lhs.ty(self.body, self.tcx); @@ -539,10 +539,13 @@ impl<'tcx> Validator<'_, 'tcx> { | BinOp::Offset | BinOp::Add | BinOp::AddUnchecked + | BinOp::AddWithOverflow | BinOp::Sub | BinOp::SubUnchecked + | BinOp::SubWithOverflow | BinOp::Mul | BinOp::MulUnchecked + | BinOp::MulWithOverflow | BinOp::BitXor | BinOp::BitAnd | BinOp::BitOr diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index dd1065590b306..4b435649216b9 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -1429,7 +1429,7 @@ impl<'v> RootCollector<'_, 'v> { match self.tcx.def_kind(id.owner_id) { DefKind::Enum | DefKind::Struct | DefKind::Union => { if self.strategy == MonoItemCollectionStrategy::Eager - && self.tcx.generics_of(id.owner_id).count() == 0 + && self.tcx.generics_of(id.owner_id).is_empty() { debug!("RootCollector: ADT drop-glue for `{id:?}`",); diff --git a/compiler/rustc_monomorphize/src/polymorphize.rs b/compiler/rustc_monomorphize/src/polymorphize.rs index 6487169d17398..14ebe27ac23c5 100644 --- a/compiler/rustc_monomorphize/src/polymorphize.rs +++ b/compiler/rustc_monomorphize/src/polymorphize.rs @@ -51,7 +51,7 @@ fn unused_generic_params<'tcx>( debug!(?generics); // Exit early when there are no parameters to be unused. - if generics.count() == 0 { + if generics.is_empty() { return UnusedGenericParams::new_all_used(); } diff --git a/compiler/rustc_next_trait_solver/src/canonicalizer.rs b/compiler/rustc_next_trait_solver/src/canonicalizer.rs index 755f5cfa5a378..696639e9c1b65 100644 --- a/compiler/rustc_next_trait_solver/src/canonicalizer.rs +++ b/compiler/rustc_next_trait_solver/src/canonicalizer.rs @@ -268,7 +268,7 @@ impl, I: Interner> TypeFolder ty::ReVar(vid) => { assert_eq!( self.infcx.opportunistic_resolve_lt_var(vid), - None, + r, "region vid should have been resolved fully before canonicalization" ); match self.canonicalize_mode { @@ -302,13 +302,8 @@ impl, I: Interner> TypeFolder ty::Infer(i) => match i { ty::TyVar(vid) => { assert_eq!( - self.infcx.root_ty_var(vid), - vid, - "ty vid should have been resolved fully before canonicalization" - ); - assert_eq!( - self.infcx.probe_ty_var(vid), - None, + self.infcx.opportunistic_resolve_ty_var(vid), + t, "ty vid should have been resolved fully before canonicalization" ); @@ -318,10 +313,24 @@ impl, I: Interner> TypeFolder .unwrap_or_else(|| panic!("ty var should have been resolved: {t:?}")), )) } - ty::IntVar(_) => CanonicalVarKind::Ty(CanonicalTyVarKind::Int), - ty::FloatVar(_) => CanonicalVarKind::Ty(CanonicalTyVarKind::Float), + ty::IntVar(vid) => { + assert_eq!( + self.infcx.opportunistic_resolve_int_var(vid), + t, + "ty vid should have been resolved fully before canonicalization" + ); + CanonicalVarKind::Ty(CanonicalTyVarKind::Int) + } + ty::FloatVar(vid) => { + assert_eq!( + self.infcx.opportunistic_resolve_float_var(vid), + t, + "ty vid should have been resolved fully before canonicalization" + ); + CanonicalVarKind::Ty(CanonicalTyVarKind::Float) + } ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_) => { - todo!() + panic!("fresh vars not expected in canonicalization") } }, ty::Placeholder(placeholder) => match self.canonicalize_mode { @@ -387,14 +396,11 @@ impl, I: Interner> TypeFolder let kind = match c.kind() { ty::ConstKind::Infer(i) => match i { ty::InferConst::Var(vid) => { + // We compare `kind`s here because we've folded the `ty` with `RegionsToStatic` + // so we'll get a mismatch in types if it actually changed any regions. assert_eq!( - self.infcx.root_ct_var(vid), - vid, - "region vid should have been resolved fully before canonicalization" - ); - assert_eq!( - self.infcx.probe_ct_var(vid), - None, + self.infcx.opportunistic_resolve_ct_var(vid, ty).kind(), + c.kind(), "region vid should have been resolved fully before canonicalization" ); CanonicalVarKind::Const(self.infcx.universe_of_ct(vid).unwrap(), ty) diff --git a/compiler/rustc_next_trait_solver/src/lib.rs b/compiler/rustc_next_trait_solver/src/lib.rs index 4202dc39fb2fb..b913a05095c2b 100644 --- a/compiler/rustc_next_trait_solver/src/lib.rs +++ b/compiler/rustc_next_trait_solver/src/lib.rs @@ -1,2 +1,3 @@ pub mod canonicalizer; +pub mod resolve; pub mod solve; diff --git a/compiler/rustc_next_trait_solver/src/resolve.rs b/compiler/rustc_next_trait_solver/src/resolve.rs new file mode 100644 index 0000000000000..1333b4aa7d83a --- /dev/null +++ b/compiler/rustc_next_trait_solver/src/resolve.rs @@ -0,0 +1,83 @@ +use rustc_type_ir::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; +use rustc_type_ir::inherent::*; +use rustc_type_ir::visit::TypeVisitableExt; +use rustc_type_ir::{self as ty, InferCtxtLike, Interner}; + +/////////////////////////////////////////////////////////////////////////// +// EAGER RESOLUTION + +/// Resolves ty, region, and const vars to their inferred values or their root vars. +pub struct EagerResolver< + 'a, + Infcx: InferCtxtLike, + I: Interner = ::Interner, +> { + infcx: &'a Infcx, +} + +impl<'a, Infcx: InferCtxtLike> EagerResolver<'a, Infcx> { + pub fn new(infcx: &'a Infcx) -> Self { + EagerResolver { infcx } + } +} + +impl, I: Interner> TypeFolder for EagerResolver<'_, Infcx> { + fn interner(&self) -> I { + self.infcx.interner() + } + + fn fold_ty(&mut self, t: I::Ty) -> I::Ty { + match t.kind() { + ty::Infer(ty::TyVar(vid)) => { + let resolved = self.infcx.opportunistic_resolve_ty_var(vid); + if t != resolved && resolved.has_infer() { + resolved.fold_with(self) + } else { + resolved + } + } + ty::Infer(ty::IntVar(vid)) => self.infcx.opportunistic_resolve_int_var(vid), + ty::Infer(ty::FloatVar(vid)) => self.infcx.opportunistic_resolve_float_var(vid), + _ => { + if t.has_infer() { + t.super_fold_with(self) + } else { + t + } + } + } + } + + fn fold_region(&mut self, r: I::Region) -> I::Region { + match r.kind() { + ty::ReVar(vid) => self.infcx.opportunistic_resolve_lt_var(vid), + _ => r, + } + } + + fn fold_const(&mut self, c: I::Const) -> I::Const { + match c.kind() { + ty::ConstKind::Infer(ty::InferConst::Var(vid)) => { + let ty = c.ty().fold_with(self); + let resolved = self.infcx.opportunistic_resolve_ct_var(vid, ty); + if c != resolved && resolved.has_infer() { + resolved.fold_with(self) + } else { + resolved + } + } + ty::ConstKind::Infer(ty::InferConst::EffectVar(vid)) => { + let bool = Ty::new_bool(self.infcx.interner()); + debug_assert_eq!(c.ty(), bool); + self.infcx.opportunistic_resolve_effect_var(vid, bool) + } + _ => { + if c.has_infer() { + c.super_fold_with(self) + } else { + c + } + } + } + } +} diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs index d2d200a91aff2..6eb8bed6a8c4e 100644 --- a/compiler/rustc_parse/src/lexer/mod.rs +++ b/compiler/rustc_parse/src/lexer/mod.rs @@ -370,11 +370,10 @@ impl<'psess, 'src> StringReader<'psess, 'src> { let content = self.str_from(content_start); if contains_text_flow_control_chars(content) { let span = self.mk_sp(start, self.pos); - self.psess.buffer_lint_with_diagnostic( + self.psess.buffer_lint( TEXT_DIRECTION_CODEPOINT_IN_COMMENT, span, ast::CRATE_NODE_ID, - "unicode codepoint changing visible direction of text present in comment", BuiltinLintDiag::UnicodeTextFlow(span, content.to_string()), ); } @@ -723,12 +722,11 @@ impl<'psess, 'src> StringReader<'psess, 'src> { self.dcx().emit_err(errors::UnknownPrefix { span: prefix_span, prefix, sugg }); } else { // Before Rust 2021, only emit a lint for migration. - self.psess.buffer_lint_with_diagnostic( + self.psess.buffer_lint( RUST_2021_PREFIXES_INCOMPATIBLE_SYNTAX, prefix_span, ast::CRATE_NODE_ID, - format!("prefix `{prefix}` is unknown"), - BuiltinLintDiag::ReservedPrefix(prefix_span), + BuiltinLintDiag::ReservedPrefix(prefix_span, prefix.to_string()), ); } } diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index d2d21624150bf..fd3f63a04de55 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -1913,11 +1913,10 @@ impl<'a> Parser<'a> { | ExprKind::Block(_, None) ) { - self.psess.buffer_lint_with_diagnostic( + self.psess.buffer_lint( BREAK_WITH_LABEL_AND_LOOP, lo.to(expr.span), ast::CRATE_NODE_ID, - "this labeled break expression is easy to confuse with an unlabeled break with a labeled value expression", BuiltinLintDiag::BreakWithLabelAndLoop(expr.span), ); } diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index a46c104b6d9ca..f43ddadc2ea02 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -58,9 +58,15 @@ impl<'a> Parser<'a> { let attrs = self.parse_inner_attributes()?; let post_attr_lo = self.token.span; - let mut items = ThinVec::new(); - while let Some(item) = self.parse_item(ForceCollect::No)? { - self.maybe_consume_incorrect_semicolon(Some(&item)); + let mut items: ThinVec> = ThinVec::new(); + + // There shouldn't be any stray semicolons before or after items. + // `parse_item` consumes the appropriate semicolons so any leftover is an error. + loop { + while self.maybe_consume_incorrect_semicolon(items.last().map(|x| &**x)) {} // Eat all bad semicolons + let Some(item) = self.parse_item(ForceCollect::No)? else { + break; + }; items.push(item); } diff --git a/compiler/rustc_parse/src/validate_attr.rs b/compiler/rustc_parse/src/validate_attr.rs index f88edf29dcebe..b91ef1ae1f320 100644 --- a/compiler/rustc_parse/src/validate_attr.rs +++ b/compiler/rustc_parse/src/validate_attr.rs @@ -10,6 +10,7 @@ use rustc_errors::{Applicability, FatalError, PResult}; use rustc_feature::{AttributeTemplate, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP}; use rustc_session::errors::report_lit_error; use rustc_session::lint::builtin::ILL_FORMED_ATTRIBUTE_INPUT; +use rustc_session::lint::BuiltinLintDiag; use rustc_session::parse::ParseSess; use rustc_span::{sym, Span, Symbol}; @@ -176,37 +177,26 @@ fn emit_malformed_attribute( }; let error_msg = format!("malformed `{name}` attribute input"); - let mut msg = "attribute must be of the form ".to_owned(); let mut suggestions = vec![]; - let mut first = true; let inner = if style == ast::AttrStyle::Inner { "!" } else { "" }; if template.word { - first = false; - let code = format!("#{inner}[{name}]"); - msg.push_str(&format!("`{code}`")); - suggestions.push(code); + suggestions.push(format!("#{inner}[{name}]")); } if let Some(descr) = template.list { - if !first { - msg.push_str(" or "); - } - first = false; - let code = format!("#{inner}[{name}({descr})]"); - msg.push_str(&format!("`{code}`")); - suggestions.push(code); + suggestions.push(format!("#{inner}[{name}({descr})]")); } if let Some(descr) = template.name_value_str { - if !first { - msg.push_str(" or "); - } - let code = format!("#{inner}[{name} = \"{descr}\"]"); - msg.push_str(&format!("`{code}`")); - suggestions.push(code); + suggestions.push(format!("#{inner}[{name} = \"{descr}\"]")); } - suggestions.sort(); if should_warn(name) { - psess.buffer_lint(ILL_FORMED_ATTRIBUTE_INPUT, span, ast::CRATE_NODE_ID, msg); + psess.buffer_lint( + ILL_FORMED_ATTRIBUTE_INPUT, + span, + ast::CRATE_NODE_ID, + BuiltinLintDiag::IllFormedAttributeInput { suggestions: suggestions.clone() }, + ); } else { + suggestions.sort(); psess .dcx .struct_span_err(span, error_msg) diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index 3deefcaa06c3d..d850644bb452f 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -341,7 +341,7 @@ passes_implied_feature_not_exist = feature `{$implied_by}` implying `{$feature}` does not exist passes_incorrect_do_not_recommend_location = - `#[do_not_recommend]` can only be placed on trait implementations + `#[diagnostic::do_not_recommend]` can only be placed on trait implementations passes_incorrect_meta_item = expected a quoted string literal passes_incorrect_meta_item_suggestion = consider surrounding this with quotes diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index aadbd74731392..1924533e28049 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -42,12 +42,9 @@ use std::collections::hash_map::Entry; #[derive(LintDiagnostic)] #[diag(passes_diagnostic_diagnostic_on_unimplemented_only_for_traits)] -pub struct DiagnosticOnUnimplementedOnlyForTraits; +struct DiagnosticOnUnimplementedOnlyForTraits; -pub(crate) fn target_from_impl_item<'tcx>( - tcx: TyCtxt<'tcx>, - impl_item: &hir::ImplItem<'_>, -) -> Target { +fn target_from_impl_item<'tcx>(tcx: TyCtxt<'tcx>, impl_item: &hir::ImplItem<'_>) -> Target { match impl_item.kind { hir::ImplItemKind::Const(..) => Target::AssocConst, hir::ImplItemKind::Fn(..) => { @@ -99,7 +96,7 @@ struct CheckAttrVisitor<'tcx> { } impl<'tcx> CheckAttrVisitor<'tcx> { - pub fn dcx(&self) -> &'tcx DiagCtxt { + fn dcx(&self) -> &'tcx DiagCtxt { self.tcx.dcx() } @@ -116,92 +113,96 @@ impl<'tcx> CheckAttrVisitor<'tcx> { let mut seen = FxHashMap::default(); let attrs = self.tcx.hir().attrs(hir_id); for attr in attrs { - if attr.path_matches(&[sym::diagnostic, sym::on_unimplemented]) { - self.check_diagnostic_on_unimplemented(attr.span, hir_id, target); - } - match attr.name_or_empty() { - sym::do_not_recommend => self.check_do_not_recommend(attr.span, target), - sym::inline => self.check_inline(hir_id, attr, span, target), - sym::coverage => self.check_coverage(hir_id, attr, span, target), - sym::non_exhaustive => self.check_non_exhaustive(hir_id, attr, span, target), - sym::marker => self.check_marker(hir_id, attr, span, target), - sym::target_feature => self.check_target_feature(hir_id, attr, span, target, attrs), - sym::thread_local => self.check_thread_local(attr, span, target), - sym::track_caller => { + match attr.path().as_slice() { + [sym::diagnostic, sym::do_not_recommend] => { + self.check_do_not_recommend(attr.span, hir_id, target) + } + [sym::diagnostic, sym::on_unimplemented] => { + self.check_diagnostic_on_unimplemented(attr.span, hir_id, target) + } + [sym::inline] => self.check_inline(hir_id, attr, span, target), + [sym::coverage] => self.check_coverage(hir_id, attr, span, target), + [sym::non_exhaustive] => self.check_non_exhaustive(hir_id, attr, span, target), + [sym::marker] => self.check_marker(hir_id, attr, span, target), + [sym::target_feature] => { + self.check_target_feature(hir_id, attr, span, target, attrs) + } + [sym::thread_local] => self.check_thread_local(attr, span, target), + [sym::track_caller] => { self.check_track_caller(hir_id, attr.span, attrs, span, target) } - sym::doc => self.check_doc_attrs( + [sym::doc] => self.check_doc_attrs( attr, hir_id, target, &mut specified_inline, &mut doc_aliases, ), - sym::no_link => self.check_no_link(hir_id, attr, span, target), - sym::export_name => self.check_export_name(hir_id, attr, span, target), - sym::rustc_layout_scalar_valid_range_start - | sym::rustc_layout_scalar_valid_range_end => { + [sym::no_link] => self.check_no_link(hir_id, attr, span, target), + [sym::export_name] => self.check_export_name(hir_id, attr, span, target), + [sym::rustc_layout_scalar_valid_range_start] + | [sym::rustc_layout_scalar_valid_range_end] => { self.check_rustc_layout_scalar_valid_range(attr, span, target) } - sym::allow_internal_unstable => { + [sym::allow_internal_unstable] => { self.check_allow_internal_unstable(hir_id, attr, span, target, attrs) } - sym::debugger_visualizer => self.check_debugger_visualizer(attr, target), - sym::rustc_allow_const_fn_unstable => { + [sym::debugger_visualizer] => self.check_debugger_visualizer(attr, target), + [sym::rustc_allow_const_fn_unstable] => { self.check_rustc_allow_const_fn_unstable(hir_id, attr, span, target) } - sym::rustc_std_internal_symbol => { + [sym::rustc_std_internal_symbol] => { self.check_rustc_std_internal_symbol(attr, span, target) } - sym::naked => self.check_naked(hir_id, attr, span, target), - sym::rustc_never_returns_null_ptr => { + [sym::naked] => self.check_naked(hir_id, attr, span, target), + [sym::rustc_never_returns_null_ptr] => { self.check_applied_to_fn_or_method(hir_id, attr, span, target) } - sym::rustc_legacy_const_generics => { + [sym::rustc_legacy_const_generics] => { self.check_rustc_legacy_const_generics(hir_id, attr, span, target, item) } - sym::rustc_lint_query_instability => { + [sym::rustc_lint_query_instability] => { self.check_rustc_lint_query_instability(hir_id, attr, span, target) } - sym::rustc_lint_diagnostics => { + [sym::rustc_lint_diagnostics] => { self.check_rustc_lint_diagnostics(hir_id, attr, span, target) } - sym::rustc_lint_opt_ty => self.check_rustc_lint_opt_ty(attr, span, target), - sym::rustc_lint_opt_deny_field_access => { + [sym::rustc_lint_opt_ty] => self.check_rustc_lint_opt_ty(attr, span, target), + [sym::rustc_lint_opt_deny_field_access] => { self.check_rustc_lint_opt_deny_field_access(attr, span, target) } - sym::rustc_clean - | sym::rustc_dirty - | sym::rustc_if_this_changed - | sym::rustc_then_this_would_need => self.check_rustc_dirty_clean(attr), - sym::rustc_coinductive - | sym::rustc_must_implement_one_of - | sym::rustc_deny_explicit_impl - | sym::const_trait => self.check_must_be_applied_to_trait(attr, span, target), - sym::cmse_nonsecure_entry => { + [sym::rustc_clean] + | [sym::rustc_dirty] + | [sym::rustc_if_this_changed] + | [sym::rustc_then_this_would_need] => self.check_rustc_dirty_clean(attr), + [sym::rustc_coinductive] + | [sym::rustc_must_implement_one_of] + | [sym::rustc_deny_explicit_impl] + | [sym::const_trait] => self.check_must_be_applied_to_trait(attr, span, target), + [sym::cmse_nonsecure_entry] => { self.check_cmse_nonsecure_entry(hir_id, attr, span, target) } - sym::collapse_debuginfo => self.check_collapse_debuginfo(attr, span, target), - sym::must_not_suspend => self.check_must_not_suspend(attr, span, target), - sym::must_use => self.check_must_use(hir_id, attr, target), - sym::rustc_pass_by_value => self.check_pass_by_value(attr, span, target), - sym::rustc_allow_incoherent_impl => { + [sym::collapse_debuginfo] => self.check_collapse_debuginfo(attr, span, target), + [sym::must_not_suspend] => self.check_must_not_suspend(attr, span, target), + [sym::must_use] => self.check_must_use(hir_id, attr, target), + [sym::rustc_pass_by_value] => self.check_pass_by_value(attr, span, target), + [sym::rustc_allow_incoherent_impl] => { self.check_allow_incoherent_impl(attr, span, target) } - sym::rustc_has_incoherent_inherent_impls => { + [sym::rustc_has_incoherent_inherent_impls] => { self.check_has_incoherent_inherent_impls(attr, span, target) } - sym::ffi_pure => self.check_ffi_pure(attr.span, attrs, target), - sym::ffi_const => self.check_ffi_const(attr.span, target), - sym::rustc_const_unstable - | sym::rustc_const_stable - | sym::unstable - | sym::stable - | sym::rustc_allowed_through_unstable_modules - | sym::rustc_promotable => self.check_stability_promotable(attr, target), - sym::link_ordinal => self.check_link_ordinal(attr, span, target), - sym::rustc_confusables => self.check_confusables(attr, target), - sym::rustc_safe_intrinsic => { + [sym::ffi_pure] => self.check_ffi_pure(attr.span, attrs, target), + [sym::ffi_const] => self.check_ffi_const(attr.span, target), + [sym::rustc_const_unstable] + | [sym::rustc_const_stable] + | [sym::unstable] + | [sym::stable] + | [sym::rustc_allowed_through_unstable_modules] + | [sym::rustc_promotable] => self.check_stability_promotable(attr, target), + [sym::link_ordinal] => self.check_link_ordinal(attr, span, target), + [sym::rustc_confusables] => self.check_confusables(attr, target), + [sym::rustc_safe_intrinsic] => { self.check_rustc_safe_intrinsic(hir_id, attr, span, target) } _ => true, @@ -293,18 +294,26 @@ impl<'tcx> CheckAttrVisitor<'tcx> { ); } - /// Checks if `#[do_not_recommend]` is applied on a trait impl. - fn check_do_not_recommend(&self, attr_span: Span, target: Target) -> bool { - if let Target::Impl = target { - true - } else { - self.dcx().emit_err(errors::IncorrectDoNotRecommendLocation { span: attr_span }); - false + /// Checks if `#[diagnostic::do_not_recommend]` is applied on a trait impl. + fn check_do_not_recommend(&self, attr_span: Span, hir_id: HirId, target: Target) -> bool { + if !matches!(target, Target::Impl) { + self.tcx.emit_node_span_lint( + UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, + hir_id, + attr_span, + errors::IncorrectDoNotRecommendLocation, + ); } + true } /// Checks if `#[diagnostic::on_unimplemented]` is applied to a trait definition - fn check_diagnostic_on_unimplemented(&self, attr_span: Span, hir_id: HirId, target: Target) { + fn check_diagnostic_on_unimplemented( + &self, + attr_span: Span, + hir_id: HirId, + target: Target, + ) -> bool { if !matches!(target, Target::Trait) { self.tcx.emit_node_span_lint( UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, @@ -313,6 +322,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { DiagnosticOnUnimplementedOnlyForTraits, ); } + true } /// Checks if an `#[inline]` is applied to a function or a closure. Returns `true` if valid. diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index b8586e7e974ad..180552785fe75 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -17,12 +17,9 @@ use rustc_span::{Span, Symbol, DUMMY_SP}; use crate::check_attr::ProcMacroKind; use crate::lang_items::Duplicate; -#[derive(Diagnostic)] +#[derive(LintDiagnostic)] #[diag(passes_incorrect_do_not_recommend_location)] -pub struct IncorrectDoNotRecommendLocation { - #[primary_span] - pub span: Span, -} +pub struct IncorrectDoNotRecommendLocation; #[derive(LintDiagnostic)] #[diag(passes_outer_crate_level_attr)] diff --git a/compiler/rustc_passes/src/lang_items.rs b/compiler/rustc_passes/src/lang_items.rs index a5fa94faeadcd..b3722e99e1684 100644 --- a/compiler/rustc_passes/src/lang_items.rs +++ b/compiler/rustc_passes/src/lang_items.rs @@ -149,8 +149,9 @@ impl<'ast, 'tcx> LanguageItemCollector<'ast, 'tcx> { } }; - // When there's a duplicate lang item, something went very wrong and there's no value in recovering or doing anything. - // Give the user the one message to let them debug the mess they created and then wish them farewell. + // When there's a duplicate lang item, something went very wrong and there's no value + // in recovering or doing anything. Give the user the one message to let them debug the + // mess they created and then wish them farewell. self.tcx.dcx().emit_fatal(DuplicateLangItem { local_span: item_span, lang_item_name, diff --git a/compiler/rustc_resolve/src/check_unused.rs b/compiler/rustc_resolve/src/check_unused.rs index 5fe68085d6537..fc3669fecc2ad 100644 --- a/compiler/rustc_resolve/src/check_unused.rs +++ b/compiler/rustc_resolve/src/check_unused.rs @@ -32,7 +32,7 @@ use rustc_ast as ast; use rustc_ast::visit::{self, Visitor}; use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet}; use rustc_data_structures::unord::UnordSet; -use rustc_errors::{pluralize, MultiSpan}; +use rustc_errors::MultiSpan; use rustc_hir::def::{DefKind, Res}; use rustc_session::lint::builtin::{MACRO_USE_EXTERN_CRATE, UNUSED_EXTERN_CRATES}; use rustc_session::lint::builtin::{UNUSED_IMPORTS, UNUSED_QUALIFICATIONS}; @@ -151,11 +151,10 @@ impl<'a, 'b, 'tcx> UnusedImportCheckVisitor<'a, 'b, 'tcx> { // We do this in any edition. if warn_if_unused { if let Some(&span) = maybe_unused_extern_crates.get(&extern_crate.id) { - self.r.lint_buffer.buffer_lint_with_diagnostic( + self.r.lint_buffer.buffer_lint( UNUSED_EXTERN_CRATES, extern_crate.id, span, - "unused extern crate", BuiltinLintDiag::UnusedExternCrate { removal_span: extern_crate.span_with_attributes, }, @@ -204,11 +203,10 @@ impl<'a, 'b, 'tcx> UnusedImportCheckVisitor<'a, 'b, 'tcx> { .span .find_ancestor_inside(extern_crate.span) .unwrap_or(extern_crate.ident.span); - self.r.lint_buffer.buffer_lint_with_diagnostic( + self.r.lint_buffer.buffer_lint( UNUSED_EXTERN_CRATES, extern_crate.id, extern_crate.span, - "`extern crate` is not idiomatic in the new edition", BuiltinLintDiag::ExternCrateNotIdiomatic { vis_span, ident_span }, ); } @@ -299,13 +297,13 @@ fn calc_unused_spans( let mut unused_spans = Vec::new(); let mut to_remove = Vec::new(); - let mut used_childs = 0; + let mut used_children = 0; let mut contains_self = false; let mut previous_unused = false; for (pos, (use_tree, use_tree_id)) in nested.iter().enumerate() { let remove = match calc_unused_spans(unused_import, use_tree, *use_tree_id) { UnusedSpanResult::Used => { - used_childs += 1; + used_children += 1; None } UnusedSpanResult::Unused { mut spans, remove } => { @@ -313,7 +311,7 @@ fn calc_unused_spans( Some(remove) } UnusedSpanResult::PartialUnused { mut spans, remove: mut to_remove_extra } => { - used_childs += 1; + used_children += 1; unused_spans.append(&mut spans); to_remove.append(&mut to_remove_extra); None @@ -322,7 +320,7 @@ fn calc_unused_spans( if let Some(remove) = remove { let remove_span = if nested.len() == 1 { remove - } else if pos == nested.len() - 1 || used_childs > 0 { + } else if pos == nested.len() - 1 || used_children > 0 { // Delete everything from the end of the last import, to delete the // previous comma nested[pos - 1].0.span.shrink_to_hi().to(use_tree.span) @@ -346,7 +344,7 @@ fn calc_unused_spans( } if unused_spans.is_empty() { UnusedSpanResult::Used - } else if used_childs == 0 { + } else if used_children == 0 { UnusedSpanResult::Unused { spans: unused_spans, remove: full_span } } else { // If there is only one remaining child that is used, the braces around the use @@ -360,7 +358,7 @@ fn calc_unused_spans( // `self`: `use foo::{self};` is valid Rust syntax, while `use foo::self;` errors // out. We also cannot turn `use foo::{self}` into `use foo`, as the former doesn't // import types with the same name as the module. - if used_childs == 1 && !contains_self { + if used_children == 1 && !contains_self { // Left brace, from the start of the nested group to the first item. to_remove.push( tree_span.shrink_to_lo().to(nested.first().unwrap().0.span.shrink_to_lo()), @@ -394,10 +392,7 @@ impl Resolver<'_, '_> { MACRO_USE_EXTERN_CRATE, import.root_id, import.span, - "deprecated `#[macro_use]` attribute used to \ - import macros should be replaced at use sites \ - with a `use` item to import the macro \ - instead", + BuiltinLintDiag::MacroUseDeprecated, ); } } @@ -414,8 +409,12 @@ impl Resolver<'_, '_> { } } ImportKind::MacroUse { .. } => { - let msg = "unused `#[macro_use]` import"; - self.lint_buffer.buffer_lint(UNUSED_IMPORTS, import.root_id, import.span, msg); + self.lint_buffer.buffer_lint( + UNUSED_IMPORTS, + import.root_id, + import.span, + BuiltinLintDiag::UnusedMacroUse, + ); } _ => {} } @@ -434,20 +433,12 @@ impl Resolver<'_, '_> { visitor.report_unused_extern_crate_items(maybe_unused_extern_crates); for unused in visitor.unused_imports.values() { - let mut fixes = Vec::new(); - let spans = match calc_unused_spans(unused, &unused.use_tree, unused.use_tree_id) { - UnusedSpanResult::Used => continue, - UnusedSpanResult::Unused { spans, remove } => { - fixes.push((remove, String::new())); - spans - } - UnusedSpanResult::PartialUnused { spans, remove } => { - for fix in &remove { - fixes.push((*fix, String::new())); - } - spans - } - }; + let (spans, remove_spans) = + match calc_unused_spans(unused, &unused.use_tree, unused.use_tree_id) { + UnusedSpanResult::Used => continue, + UnusedSpanResult::Unused { spans, remove } => (spans, vec![remove]), + UnusedSpanResult::PartialUnused { spans, remove } => (spans, remove), + }; let ms = MultiSpan::from_spans(spans); @@ -459,23 +450,8 @@ impl Resolver<'_, '_> { .collect::>(); span_snippets.sort(); - let msg = format!( - "unused import{}{}", - pluralize!(ms.primary_spans().len()), - if !span_snippets.is_empty() { - format!(": {}", span_snippets.join(", ")) - } else { - String::new() - } - ); - - let fix_msg = if fixes.len() == 1 && fixes[0].0 == unused.item_span { - "remove the whole `use` item" - } else if ms.primary_spans().len() > 1 { - "remove the unused imports" - } else { - "remove the unused import" - }; + let remove_whole_use = remove_spans.len() == 1 && remove_spans[0] == unused.item_span; + let num_to_remove = ms.primary_spans().len(); // If we are in the `--test` mode, suppress a help that adds the `#[cfg(test)]` // attribute; however, if not, suggest adding the attribute. There is no way to @@ -501,12 +477,17 @@ impl Resolver<'_, '_> { } }; - visitor.r.lint_buffer.buffer_lint_with_diagnostic( + visitor.r.lint_buffer.buffer_lint( UNUSED_IMPORTS, unused.use_tree_id, ms, - msg, - BuiltinLintDiag::UnusedImports(fix_msg.into(), fixes, test_module_span), + BuiltinLintDiag::UnusedImports { + remove_whole_use, + num_to_remove, + remove_spans, + test_module_span, + span_snippets, + }, ); } @@ -552,11 +533,10 @@ impl Resolver<'_, '_> { continue; } - self.lint_buffer.buffer_lint_with_diagnostic( + self.lint_buffer.buffer_lint( UNUSED_QUALIFICATIONS, unn_qua.node_id, unn_qua.path_span, - "unnecessary qualification", BuiltinLintDiag::UnusedQualifications { removal_span: unn_qua.removal_span }, ); } diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index b28312fa473a7..856cfbc01e8cd 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -128,13 +128,10 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { self.report_with_use_injections(krate); for &(span_use, span_def) in &self.macro_expanded_macro_export_errors { - let msg = "macro-expanded `macro_export` macros from the current crate \ - cannot be referred to by absolute paths"; - self.lint_buffer.buffer_lint_with_diagnostic( + self.lint_buffer.buffer_lint( MACRO_EXPANDED_MACRO_EXPORTS_ACCESSED_BY_ABSOLUTE_PATHS, CRATE_NODE_ID, span_use, - msg, BuiltinLintDiag::MacroExpandedMacroExportsAccessedByAbsolutePaths(span_def), ); } @@ -145,11 +142,10 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { let NameBindingKind::Import { import, .. } = ambiguity_error.b1.0.kind else { unreachable!() }; - self.lint_buffer.buffer_lint_with_diagnostic( + self.lint_buffer.buffer_lint( AMBIGUOUS_GLOB_IMPORTS, import.root_id, ambiguity_error.ident.span, - diag.msg.to_string(), BuiltinLintDiag::AmbiguousGlobImports { diag }, ); } else { @@ -526,12 +522,10 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } let diag = BuiltinLintDiag::AbsPathWithModule(root_span); - self.lint_buffer.buffer_lint_with_diagnostic( + self.lint_buffer.buffer_lint( ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE, node_id, root_span, - "absolute paths must start with `self`, `super`, \ - `crate`, or an external crate name in the 2018 edition", diag, ); } diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index f88725830f1e7..57db765c07e55 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -524,18 +524,15 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { match binding { Ok(binding) => { if let Some(lint_id) = derive_fallback_lint_id { - this.lint_buffer.buffer_lint_with_diagnostic( + this.lint_buffer.buffer_lint( PROC_MACRO_DERIVE_RESOLUTION_FALLBACK, lint_id, orig_ident.span, - format!( - "cannot find {} `{}` in this scope", - ns.descr(), - ident - ), - BuiltinLintDiag::ProcMacroDeriveResolutionFallback( - orig_ident.span, - ), + BuiltinLintDiag::ProcMacroDeriveResolutionFallback { + span: orig_ident.span, + ns, + ident, + }, ); } let misc_flags = if module == this.graph_root { diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index f53bcb0e9d0de..51b87c5a9b038 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -619,11 +619,10 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { && binding.res() != Res::Err && exported_ambiguities.contains(&binding) { - self.lint_buffer.buffer_lint_with_diagnostic( + self.lint_buffer.buffer_lint( AMBIGUOUS_GLOB_REEXPORTS, import.root_id, import.root_span, - "ambiguous glob re-exports", BuiltinLintDiag::AmbiguousGlobReexports { name: key.ident.to_string(), namespace: key.ns.descr().to_string(), @@ -655,11 +654,10 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { && glob_binding.vis.is_public() && !binding.vis.is_public() { - self.lint_buffer.buffer_lint_with_diagnostic( + self.lint_buffer.buffer_lint( HIDDEN_GLOB_REEXPORTS, binding_id, binding.span, - "private item shadows public glob re-export", BuiltinLintDiag::HiddenGlobReexports { name: key.ident.name.to_string(), namespace: key.ns.descr().to_owned(), @@ -1015,17 +1013,13 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { && !max_vis.is_at_least(import_vis, self.tcx) { let def_id = self.local_def_id(id); - let msg = format!( - "glob import doesn't reexport anything with visibility `{}` because no imported item is public enough", - import_vis.to_string(def_id, self.tcx) - ); - self.lint_buffer.buffer_lint_with_diagnostic( + self.lint_buffer.buffer_lint( UNUSED_IMPORTS, id, import.span, - msg, BuiltinLintDiag::RedundantImportVisibility { max_vis: max_vis.to_string(def_id, self.tcx), + import_vis: import_vis.to_string(def_id, self.tcx), span: import.span, }, ); @@ -1252,16 +1246,11 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { if !any_successful_reexport { let (ns, binding) = reexport_error.unwrap(); if pub_use_of_private_extern_crate_hack(import, binding) { - let msg = format!( - "extern crate `{ident}` is private, and cannot be \ - re-exported (error E0365), consider declaring with \ - `pub`" - ); self.lint_buffer.buffer_lint( PUB_USE_OF_PRIVATE_EXTERN_CRATE, import_id, import.span, - msg, + BuiltinLintDiag::PrivateExternCrateReexport(ident), ); } else { if ns == TypeNS { @@ -1397,7 +1386,6 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { UNUSED_IMPORTS, id, import.span, - format!("the item `{source}` is imported redundantly"), BuiltinLintDiag::RedundantImport(redundant_spans, source), ); */ diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 0f585aafdd5cf..d1d0e336cfe05 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -24,7 +24,7 @@ use rustc_middle::middle::resolve_bound_vars::Set1; use rustc_middle::ty::DelegationFnSig; use rustc_middle::{bug, span_bug}; use rustc_session::config::{CrateType, ResolveDocLinks}; -use rustc_session::lint; +use rustc_session::lint::{self, BuiltinLintDiag}; use rustc_session::parse::feature_err; use rustc_span::source_map::{respan, Spanned}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; @@ -1675,16 +1675,10 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { return; } LifetimeRibKind::AnonymousWarn(node_id) => { - let msg = if elided { - "`&` without an explicit lifetime name cannot be used here" - } else { - "`'_` cannot be used here" - }; - self.r.lint_buffer.buffer_lint_with_diagnostic( + self.r.lint_buffer.buffer_lint( lint::builtin::ELIDED_LIFETIMES_IN_ASSOCIATED_CONSTANT, node_id, lifetime.ident.span, - msg, lint::BuiltinLintDiag::AssociatedConstElidedLifetime { elided, span: lifetime.ident.span, @@ -1966,11 +1960,10 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { } if should_lint { - self.r.lint_buffer.buffer_lint_with_diagnostic( + self.r.lint_buffer.buffer_lint( lint::builtin::ELIDED_LIFETIMES_IN_PATHS, segment_id, elided_lifetime_span, - "hidden lifetime parameters in types are deprecated", lint::BuiltinLintDiag::ElidedLifetimesInPaths( expected_lifetimes, path_span, @@ -4822,7 +4815,12 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { late_resolution_visitor.resolve_doc_links(&krate.attrs, MaybeExported::Ok(CRATE_NODE_ID)); visit::walk_crate(&mut late_resolution_visitor, krate); for (id, span) in late_resolution_visitor.diag_metadata.unused_labels.iter() { - self.lint_buffer.buffer_lint(lint::builtin::UNUSED_LABELS, *id, *span, "unused label"); + self.lint_buffer.buffer_lint( + lint::builtin::UNUSED_LABELS, + *id, + *span, + BuiltinLintDiag::UnusedLabel, + ); } } } diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 1958fdf1cbc46..9daa22f89d2c4 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -17,6 +17,7 @@ use rustc_ast::{ }; use rustc_ast_pretty::pprust::where_bound_predicate_to_string; use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::fx::FxIndexSet; use rustc_errors::{ codes::*, pluralize, struct_span_code_err, Applicability, Diag, ErrorGuaranteed, MultiSpan, SuggestionStyle, @@ -31,7 +32,7 @@ use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::edition::Edition; use rustc_span::hygiene::MacroKind; use rustc_span::symbol::{kw, sym, Ident, Symbol}; -use rustc_span::Span; +use rustc_span::{Span, DUMMY_SP}; use rustc_middle::ty; @@ -2651,15 +2652,15 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { let deletion_span = if param.bounds.is_empty() { deletion_span() } else { None }; - self.r.lint_buffer.buffer_lint_with_diagnostic( + self.r.lint_buffer.buffer_lint( lint::builtin::SINGLE_USE_LIFETIMES, param.id, param.ident.span, - format!("lifetime parameter `{}` only used once", param.ident), lint::BuiltinLintDiag::SingleUseLifetime { param_span: param.ident.span, use_span: Some((use_span, elidable)), deletion_span, + ident: param.ident, }, ); } @@ -2669,15 +2670,15 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { // if the lifetime originates from expanded code, we won't be able to remove it #104432 if deletion_span.is_some_and(|sp| !sp.in_derive_expansion()) { - self.r.lint_buffer.buffer_lint_with_diagnostic( + self.r.lint_buffer.buffer_lint( lint::builtin::UNUSED_LIFETIMES, param.id, param.ident.span, - format!("lifetime parameter `{}` never used", param.ident), lint::BuiltinLintDiag::SingleUseLifetime { param_span: param.ident.span, use_span: None, deletion_span, + ident: param.ident, }, ); } @@ -2714,8 +2715,17 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { self.suggest_introducing_lifetime( &mut err, Some(lifetime_ref.ident.name.as_str()), - |err, _, span, message, suggestion| { - err.span_suggestion(span, message, suggestion, Applicability::MaybeIncorrect); + |err, _, span, message, suggestion, span_suggs| { + err.multipart_suggestion_with_style( + message, + std::iter::once((span, suggestion)).chain(span_suggs.clone()).collect(), + Applicability::MaybeIncorrect, + if span_suggs.is_empty() { + SuggestionStyle::ShowCode + } else { + SuggestionStyle::ShowAlways + }, + ); true }, ); @@ -2726,13 +2736,20 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { &self, err: &mut Diag<'_>, name: Option<&str>, - suggest: impl Fn(&mut Diag<'_>, bool, Span, Cow<'static, str>, String) -> bool, + suggest: impl Fn( + &mut Diag<'_>, + bool, + Span, + Cow<'static, str>, + String, + Vec<(Span, String)>, + ) -> bool, ) { let mut suggest_note = true; for rib in self.lifetime_ribs.iter().rev() { let mut should_continue = true; match rib.kind { - LifetimeRibKind::Generics { binder: _, span, kind } => { + LifetimeRibKind::Generics { binder, span, kind } => { // Avoid suggesting placing lifetime parameters on constant items unless the relevant // feature is enabled. Suggest the parent item as a possible location if applicable. if let LifetimeBinderKind::ConstItem = kind @@ -2761,11 +2778,53 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { | LifetimeBinderKind::PolyTrait | LifetimeBinderKind::WhereBound ); + + let mut rm_inner_binders: FxIndexSet = Default::default(); let (span, sugg) = if span.is_empty() { + let mut binder_idents: FxIndexSet = Default::default(); + binder_idents.insert(Ident::from_str(name.unwrap_or("'a"))); + + // We need to special case binders in the following situation: + // Change `T: for<'a> Trait + 'b` to `for<'a, 'b> T: Trait + 'b` + // T: for<'a> Trait + 'b + // ^^^^^^^ remove existing inner binder `for<'a>` + // for<'a, 'b> T: Trait + 'b + // ^^^^^^^^^^^ suggest outer binder `for<'a, 'b>` + if let LifetimeBinderKind::WhereBound = kind + && let Some(ast::WherePredicate::BoundPredicate( + ast::WhereBoundPredicate { bounded_ty, bounds, .. }, + )) = self.diag_metadata.current_where_predicate + && bounded_ty.id == binder + { + for bound in bounds { + if let ast::GenericBound::Trait(poly_trait_ref, _) = bound + && let span = poly_trait_ref + .span + .with_hi(poly_trait_ref.trait_ref.path.span.lo()) + && !span.is_empty() + { + rm_inner_binders.insert(span); + poly_trait_ref.bound_generic_params.iter().for_each(|v| { + binder_idents.insert(v.ident); + }); + } + } + } + + let binders_sugg = binder_idents.into_iter().enumerate().fold( + "".to_string(), + |mut binders, (i, x)| { + if i != 0 { + binders += ", "; + } + binders += x.as_str(); + binders + }, + ); let sugg = format!( "{}<{}>{}", if higher_ranked { "for" } else { "" }, - name.unwrap_or("'a"), + binders_sugg, if higher_ranked { " " } else { "" }, ); (span, sugg) @@ -2780,13 +2839,28 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { let sugg = format!("{}, ", name.unwrap_or("'a")); (span, sugg) }; + if higher_ranked { let message = Cow::from(format!( "consider making the {} lifetime-generic with a new `{}` lifetime", kind.descr(), name.unwrap_or("'a"), )); - should_continue = suggest(err, true, span, message, sugg); + should_continue = suggest( + err, + true, + span, + message, + sugg, + if !rm_inner_binders.is_empty() { + rm_inner_binders + .into_iter() + .map(|v| (v, "".to_string())) + .collect::>() + } else { + vec![] + }, + ); err.note_once( "for more information on higher-ranked polymorphism, visit \ https://doc.rust-lang.org/nomicon/hrtb.html", @@ -2794,10 +2868,10 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { } else if let Some(name) = name { let message = Cow::from(format!("consider introducing lifetime `{name}` here")); - should_continue = suggest(err, false, span, message, sugg); + should_continue = suggest(err, false, span, message, sugg, vec![]); } else { let message = Cow::from("consider introducing a named lifetime parameter"); - should_continue = suggest(err, false, span, message, sugg); + should_continue = suggest(err, false, span, message, sugg, vec![]); } } LifetimeRibKind::Item | LifetimeRibKind::ConstParamTy => break, @@ -3033,11 +3107,11 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { self.suggest_introducing_lifetime( err, None, - |err, higher_ranked, span, message, intro_sugg| { + |err, higher_ranked, span, message, intro_sugg, _| { err.multipart_suggestion_verbose( message, std::iter::once((span, intro_sugg)) - .chain(spans_suggs.iter().cloned()) + .chain(spans_suggs.clone()) .collect(), Applicability::MaybeIncorrect, ); @@ -3161,11 +3235,11 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { self.suggest_introducing_lifetime( err, None, - |err, higher_ranked, span, message, intro_sugg| { + |err, higher_ranked, span, message, intro_sugg, _| { err.multipart_suggestion_verbose( message, std::iter::once((span, intro_sugg)) - .chain(spans_suggs.iter().cloned()) + .chain(spans_suggs.clone()) .collect(), Applicability::MaybeIncorrect, ); @@ -3309,7 +3383,6 @@ fn mk_where_bound_predicate( poly_trait_ref: &ast::PolyTraitRef, ty: &Ty, ) -> Option { - use rustc_span::DUMMY_SP; let modified_segments = { let mut segments = path.segments.clone(); let [preceding @ .., second_last, last] = segments.as_mut_slice() else { diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 01bcfec4bdc0b..f4c5ad8f6721a 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -51,7 +51,7 @@ use rustc_middle::ty::{self, DelegationFnSig, Feed, MainDefinition, RegisteredTo use rustc_middle::ty::{ResolverGlobalCtxt, ResolverOutputs, TyCtxt, TyCtxtFeed}; use rustc_query_system::ich::StableHashingContext; use rustc_session::lint::builtin::PRIVATE_MACRO_USE; -use rustc_session::lint::LintBuffer; +use rustc_session::lint::{BuiltinLintDiag, LintBuffer}; use rustc_span::hygiene::{ExpnId, LocalExpnId, MacroKind, SyntaxContext, Transparency}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{Span, DUMMY_SP}; @@ -1860,8 +1860,12 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } if let NameBindingKind::Import { import, binding } = used_binding.kind { if let ImportKind::MacroUse { warn_private: true } = import.kind { - let msg = format!("macro `{ident}` is private"); - self.lint_buffer().buffer_lint(PRIVATE_MACRO_USE, import.root_id, ident.span, msg); + self.lint_buffer().buffer_lint( + PRIVATE_MACRO_USE, + import.root_id, + ident.span, + BuiltinLintDiag::MacroIsPrivate(ident), + ); } // Avoid marking `extern crate` items that refer to a name from extern prelude, // but not introduce it, as used if they are accessed from lexical scope. diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index f8d245f94e53a..268e7f06d0423 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -315,7 +315,7 @@ impl<'a, 'tcx> ResolverExpand for Resolver<'a, 'tcx> { UNUSED_MACROS, node_id, ident.span, - format!("unused macro definition: `{}`", ident.name), + BuiltinLintDiag::UnusedMacroDefinition(ident.name), ); } for (&(def_id, arm_i), &(ident, rule_span)) in self.unused_macro_rules.iter() { @@ -328,7 +328,7 @@ impl<'a, 'tcx> ResolverExpand for Resolver<'a, 'tcx> { UNUSED_MACRO_RULES, node_id, rule_span, - format!("rule #{} of macro `{}` is never used", arm_i + 1, ident.name), + BuiltinLintDiag::MacroRuleNeverUsed(arm_i, ident.name), ); } } @@ -552,14 +552,25 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { // We are trying to avoid reporting this error if other related errors were reported. if res != Res::Err && inner_attr && !self.tcx.features().custom_inner_attributes { - let msg = match res { - Res::Def(..) => "inner macro attributes are unstable", - Res::NonMacroAttr(..) => "custom inner attributes are unstable", + let is_macro = match res { + Res::Def(..) => true, + Res::NonMacroAttr(..) => false, _ => unreachable!(), }; if soft_custom_inner_attributes_gate { - self.tcx.sess.psess.buffer_lint(SOFT_UNSTABLE, path.span, node_id, msg); + self.tcx.sess.psess.buffer_lint( + SOFT_UNSTABLE, + path.span, + node_id, + BuiltinLintDiag::InnerAttributeUnstable { is_macro }, + ); } else { + // FIXME: deduplicate with rustc_lint (`BuiltinLintDiag::InnerAttributeUnstable`) + let msg = if is_macro { + "inner macro attributes are unstable" + } else { + "custom inner attributes are unstable" + }; feature_err(&self.tcx.sess, sym::custom_inner_attributes, path.span, msg).emit(); } } @@ -567,22 +578,20 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { if res == Res::NonMacroAttr(NonMacroAttrKind::Tool) && let [namespace, attribute, ..] = &*path.segments && namespace.ident.name == sym::diagnostic - && attribute.ident.name != sym::on_unimplemented + && !(attribute.ident.name == sym::on_unimplemented + || (attribute.ident.name == sym::do_not_recommend + && self.tcx.features().do_not_recommend)) { let distance = edit_distance(attribute.ident.name.as_str(), sym::on_unimplemented.as_str(), 5); - let help = if distance.is_some() { - BuiltinLintDiag::MaybeTypo { span: attribute.span(), name: sym::on_unimplemented } - } else { - BuiltinLintDiag::Normal - }; - self.tcx.sess.psess.buffer_lint_with_diagnostic( + let typo_name = distance.map(|_| sym::on_unimplemented); + + self.tcx.sess.psess.buffer_lint( UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, attribute.span(), node_id, - "unknown diagnostic attribute", - help, + BuiltinLintDiag::UnknownDiagnosticAttribute { span: attribute.span(), typo_name }, ); } @@ -782,11 +791,10 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { .invocation_parents .get(&parent_scope.expansion) .map_or(ast::CRATE_NODE_ID, |id| self.def_id_to_node_id[id.0]); - self.lint_buffer.buffer_lint_with_diagnostic( + self.lint_buffer.buffer_lint( LEGACY_DERIVE_HELPERS, node_id, ident.span, - "derive helper attribute is used before it is introduced", BuiltinLintDiag::LegacyDeriveHelpers(binding.span), ); } @@ -836,8 +844,17 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { let allowed_by_implication = implied_by.is_some_and(|feature| is_allowed(feature)); if !is_allowed(feature) && !allowed_by_implication { let lint_buffer = &mut self.lint_buffer; - let soft_handler = - |lint, span, msg: String| lint_buffer.buffer_lint(lint, node_id, span, msg); + let soft_handler = |lint, span, msg: String| { + lint_buffer.buffer_lint( + lint, + node_id, + span, + BuiltinLintDiag::UnstableFeature( + // FIXME make this translatable + msg.into(), + ), + ) + }; stability::report_unstable( self.tcx.sess, feature, @@ -853,14 +870,12 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } if let Some(depr) = &ext.deprecation { let path = pprust::path_to_string(path); - let (message, lint) = stability::deprecation_message_and_lint(depr, "macro", &path); - stability::early_report_deprecation( + stability::early_report_macro_deprecation( &mut self.lint_buffer, - message, - depr.suggestion, - lint, + depr, span, node_id, + path, ); } } diff --git a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs index 21433cfdb613e..62eb07e828714 100644 --- a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs +++ b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs @@ -269,7 +269,7 @@ fn trait_object_ty<'tcx>(tcx: TyCtxt<'tcx>, poly_trait_ref: ty::PolyTraitRef<'tc /// if a function is member of the group derived from this type id. Therefore, in the first call to /// typeid_for_fnabi (when type ids are attached to functions and methods), it can only include at /// most as much information that would be available in the second call (i.e., during code -/// generation at call sites); otherwise, the type ids would not not match. +/// generation at call sites); otherwise, the type ids would not match. /// /// For this, it: /// diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs index f6053f43fbd19..df07f81bc4573 100644 --- a/compiler/rustc_session/src/parse.rs +++ b/compiler/rustc_session/src/parse.rs @@ -305,32 +305,12 @@ impl ParseSess { lint: &'static Lint, span: impl Into, node_id: NodeId, - msg: impl Into, - ) { - self.buffered_lints.with_lock(|buffered_lints| { - buffered_lints.push(BufferedEarlyLint { - span: span.into(), - node_id, - msg: msg.into(), - lint_id: LintId::of(lint), - diagnostic: BuiltinLintDiag::Normal, - }); - }); - } - - pub fn buffer_lint_with_diagnostic( - &self, - lint: &'static Lint, - span: impl Into, - node_id: NodeId, - msg: impl Into, diagnostic: BuiltinLintDiag, ) { self.buffered_lints.with_lock(|buffered_lints| { buffered_lints.push(BufferedEarlyLint { span: span.into(), node_id, - msg: msg.into(), lint_id: LintId::of(lint), diagnostic, }); diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index db01bb90d6fac..a2872fc1661af 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -1183,9 +1183,9 @@ fn validate_commandline_args_with_session_available(sess: &Session) { }); } } - // Cannot mix and match sanitizers. - let mut sanitizer_iter = sess.opts.unstable_opts.sanitizer.into_iter(); - if let (Some(first), Some(second)) = (sanitizer_iter.next(), sanitizer_iter.next()) { + + // Cannot mix and match mutually-exclusive sanitizers. + if let Some((first, second)) = sess.opts.unstable_opts.sanitizer.mutually_exclusive() { sess.dcx().emit_err(errors::CannotMixAndMatchSanitizers { first: first.to_string(), second: second.to_string(), @@ -1220,14 +1220,6 @@ fn validate_commandline_args_with_session_available(sess: &Session) { sess.dcx().emit_err(errors::SanitizerCfiRequiresSingleCodegenUnit); } - // LLVM CFI is incompatible with LLVM KCFI. - if sess.is_sanitizer_cfi_enabled() && sess.is_sanitizer_kcfi_enabled() { - sess.dcx().emit_err(errors::CannotMixAndMatchSanitizers { - first: "cfi".to_string(), - second: "kcfi".to_string(), - }); - } - // Canonical jump tables requires CFI. if sess.is_sanitizer_cfi_canonical_jump_tables_disabled() { if !sess.is_sanitizer_cfi_enabled() { diff --git a/compiler/rustc_session/src/version.rs b/compiler/rustc_session/src/version.rs index 39e4541349ecd..e244c77f7f95b 100644 --- a/compiler/rustc_session/src/version.rs +++ b/compiler/rustc_session/src/version.rs @@ -1,5 +1,10 @@ use rustc_macros::{current_rustc_version, Decodable, Encodable, HashStable_Generic}; -use std::fmt::{self, Display}; +use std::{ + borrow::Cow, + fmt::{self, Display}, +}; + +use rustc_errors::IntoDiagArg; #[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(HashStable_Generic)] @@ -18,3 +23,9 @@ impl Display for RustcVersion { write!(formatter, "{}.{}.{}", self.major, self.minor, self.patch) } } + +impl IntoDiagArg for RustcVersion { + fn into_diag_arg(self) -> rustc_errors::DiagArgValue { + rustc_errors::DiagArgValue::Str(Cow::Owned(self.to_string())) + } +} diff --git a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs index 452ab04c44c54..d89caabab3e1a 100644 --- a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs +++ b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs @@ -1,5 +1,6 @@ //! Conversion of internal Rust compiler `mir` items to stable ones. +use rustc_middle::bug; use rustc_middle::mir; use rustc_middle::mir::interpret::alloc_range; use rustc_middle::mir::mono::MonoItem; @@ -183,16 +184,21 @@ impl<'tcx> Stable<'tcx> for mir::Rvalue<'tcx> { op.stable(tables), ty.stable(tables), ), - BinaryOp(bin_op, ops) => stable_mir::mir::Rvalue::BinaryOp( - bin_op.stable(tables), - ops.0.stable(tables), - ops.1.stable(tables), - ), - CheckedBinaryOp(bin_op, ops) => stable_mir::mir::Rvalue::CheckedBinaryOp( - bin_op.stable(tables), - ops.0.stable(tables), - ops.1.stable(tables), - ), + BinaryOp(bin_op, ops) => { + if let Some(bin_op) = bin_op.overflowing_to_wrapping() { + stable_mir::mir::Rvalue::CheckedBinaryOp( + bin_op.stable(tables), + ops.0.stable(tables), + ops.1.stable(tables), + ) + } else { + stable_mir::mir::Rvalue::BinaryOp( + bin_op.stable(tables), + ops.0.stable(tables), + ops.1.stable(tables), + ) + } + } NullaryOp(null_op, ty) => { stable_mir::mir::Rvalue::NullaryOp(null_op.stable(tables), ty.stable(tables)) } @@ -485,10 +491,13 @@ impl<'tcx> Stable<'tcx> for mir::BinOp { match self { BinOp::Add => stable_mir::mir::BinOp::Add, BinOp::AddUnchecked => stable_mir::mir::BinOp::AddUnchecked, + BinOp::AddWithOverflow => bug!("AddWithOverflow should have been translated already"), BinOp::Sub => stable_mir::mir::BinOp::Sub, BinOp::SubUnchecked => stable_mir::mir::BinOp::SubUnchecked, + BinOp::SubWithOverflow => bug!("AddWithOverflow should have been translated already"), BinOp::Mul => stable_mir::mir::BinOp::Mul, BinOp::MulUnchecked => stable_mir::mir::BinOp::MulUnchecked, + BinOp::MulWithOverflow => bug!("AddWithOverflow should have been translated already"), BinOp::Div => stable_mir::mir::BinOp::Div, BinOp::Rem => stable_mir::mir::BinOp::Rem, BinOp::BitXor => stable_mir::mir::BinOp::BitXor, diff --git a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs index d59318be72005..15447983abbc2 100644 --- a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs +++ b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs @@ -505,6 +505,7 @@ impl<'tcx> Stable<'tcx> for ty::TraitDef { is_marker: self.is_marker, is_coinductive: self.is_coinductive, skip_array_during_method_dispatch: self.skip_array_during_method_dispatch, + skip_boxed_slice_during_method_dispatch: self.skip_boxed_slice_during_method_dispatch, specialization_kind: self.specialization_kind.stable(tables), must_implement_one_of: self .must_implement_one_of diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 68b1b32baf2dc..ace4dff46aa0a 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -930,6 +930,7 @@ symbols! { global_alloc_ty, global_allocator, global_asm, + global_registration, globs, gt, half_open_range_patterns, @@ -1632,7 +1633,7 @@ symbols! { rustc_reservation_impl, rustc_safe_intrinsic, rustc_serialize, - rustc_skip_array_during_method_dispatch, + rustc_skip_during_method_dispatch, rustc_specialization_trait, rustc_std_internal_symbol, rustc_strict_coherence, @@ -1681,6 +1682,7 @@ symbols! { simd_cast_ptr, simd_ceil, simd_ctlz, + simd_ctpop, simd_cttz, simd_div, simd_eq, diff --git a/compiler/rustc_target/src/spec/base/apple/mod.rs b/compiler/rustc_target/src/spec/base/apple/mod.rs index c5b2065080b20..055420090835d 100644 --- a/compiler/rustc_target/src/spec/base/apple/mod.rs +++ b/compiler/rustc_target/src/spec/base/apple/mod.rs @@ -272,6 +272,7 @@ fn macos_default_deployment_target(arch: Arch) -> (u32, u32) { fn macos_deployment_target(arch: Arch) -> (u32, u32) { // If you are looking for the default deployment target, prefer `rustc --print deployment-target`. + // Note: If bumping this version, remember to update it in the rustc/platform-support docs. from_set_deployment_target("MACOSX_DEPLOYMENT_TARGET") .unwrap_or_else(|| macos_default_deployment_target(arch)) } @@ -320,6 +321,7 @@ fn link_env_remove(os: &'static str) -> StaticCow<[StaticCow]> { fn ios_deployment_target(arch: Arch, abi: &str) -> (u32, u32) { // If you are looking for the default deployment target, prefer `rustc --print deployment-target`. + // Note: If bumping this version, remember to update it in the rustc/platform-support docs. let (major, minor) = match (arch, abi) { (Arm64e, _) => (14, 0), // Mac Catalyst defaults to 13.1 in Clang. @@ -352,6 +354,7 @@ pub fn ios_sim_llvm_target(arch: Arch) -> String { fn tvos_deployment_target() -> (u32, u32) { // If you are looking for the default deployment target, prefer `rustc --print deployment-target`. + // Note: If bumping this version, remember to update it in the rustc platform-support docs. from_set_deployment_target("TVOS_DEPLOYMENT_TARGET").unwrap_or((10, 0)) } @@ -367,6 +370,7 @@ pub fn tvos_sim_llvm_target(arch: Arch) -> String { fn watchos_deployment_target() -> (u32, u32) { // If you are looking for the default deployment target, prefer `rustc --print deployment-target`. + // Note: If bumping this version, remember to update it in the rustc platform-support docs. from_set_deployment_target("WATCHOS_DEPLOYMENT_TARGET").unwrap_or((5, 0)) } @@ -382,6 +386,7 @@ pub fn watchos_sim_llvm_target(arch: Arch) -> String { fn visionos_deployment_target() -> (u32, u32) { // If you are looking for the default deployment target, prefer `rustc --print deployment-target`. + // Note: If bumping this version, remember to update it in the rustc platform-support docs. from_set_deployment_target("XROS_DEPLOYMENT_TARGET").unwrap_or((1, 0)) } diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 910c6aeb7d66d..83ee63e2cf286 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -1317,6 +1317,34 @@ bitflags::bitflags! { rustc_data_structures::external_bitflags_debug! { SanitizerSet } impl SanitizerSet { + // Taken from LLVM's sanitizer compatibility logic: + // https://github.com/llvm/llvm-project/blob/release/18.x/clang/lib/Driver/SanitizerArgs.cpp#L512 + const MUTUALLY_EXCLUSIVE: &'static [(SanitizerSet, SanitizerSet)] = &[ + (SanitizerSet::ADDRESS, SanitizerSet::MEMORY), + (SanitizerSet::ADDRESS, SanitizerSet::THREAD), + (SanitizerSet::ADDRESS, SanitizerSet::HWADDRESS), + (SanitizerSet::ADDRESS, SanitizerSet::MEMTAG), + (SanitizerSet::ADDRESS, SanitizerSet::KERNELADDRESS), + (SanitizerSet::ADDRESS, SanitizerSet::SAFESTACK), + (SanitizerSet::LEAK, SanitizerSet::MEMORY), + (SanitizerSet::LEAK, SanitizerSet::THREAD), + (SanitizerSet::LEAK, SanitizerSet::KERNELADDRESS), + (SanitizerSet::LEAK, SanitizerSet::SAFESTACK), + (SanitizerSet::MEMORY, SanitizerSet::THREAD), + (SanitizerSet::MEMORY, SanitizerSet::HWADDRESS), + (SanitizerSet::MEMORY, SanitizerSet::KERNELADDRESS), + (SanitizerSet::MEMORY, SanitizerSet::SAFESTACK), + (SanitizerSet::THREAD, SanitizerSet::HWADDRESS), + (SanitizerSet::THREAD, SanitizerSet::KERNELADDRESS), + (SanitizerSet::THREAD, SanitizerSet::SAFESTACK), + (SanitizerSet::HWADDRESS, SanitizerSet::MEMTAG), + (SanitizerSet::HWADDRESS, SanitizerSet::KERNELADDRESS), + (SanitizerSet::HWADDRESS, SanitizerSet::SAFESTACK), + (SanitizerSet::CFI, SanitizerSet::KCFI), + (SanitizerSet::MEMTAG, SanitizerSet::KERNELADDRESS), + (SanitizerSet::KERNELADDRESS, SanitizerSet::SAFESTACK), + ]; + /// Return sanitizer's name /// /// Returns none if the flags is a set of sanitizers numbering not exactly one. @@ -1337,6 +1365,13 @@ impl SanitizerSet { _ => return None, }) } + + pub fn mutually_exclusive(self) -> Option<(SanitizerSet, SanitizerSet)> { + Self::MUTUALLY_EXCLUSIVE + .into_iter() + .find(|&(a, b)| self.contains(*a) && self.contains(*b)) + .copied() + } } /// Formats a sanitizer set as a comma separated list of sanitizers' names. diff --git a/compiler/rustc_trait_selection/src/solve/alias_relate.rs b/compiler/rustc_trait_selection/src/solve/alias_relate.rs index 65cc0a4585732..43e61de955af7 100644 --- a/compiler/rustc_trait_selection/src/solve/alias_relate.rs +++ b/compiler/rustc_trait_selection/src/solve/alias_relate.rs @@ -16,10 +16,11 @@ //! relate them structurally. use super::EvalCtxt; +use rustc_infer::infer::InferCtxt; use rustc_middle::traits::solve::{Certainty, Goal, QueryResult}; use rustc_middle::ty; -impl<'tcx> EvalCtxt<'_, 'tcx> { +impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> { #[instrument(level = "trace", skip(self), ret)] pub(super) fn compute_alias_relate_goal( &mut self, diff --git a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs index b1dd6ae6611e0..1152441069280 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs @@ -1,8 +1,7 @@ //! Code shared by trait and projection goals for candidate assembly. -use crate::solve::GoalSource; -use crate::solve::{EvalCtxt, SolverMode}; use rustc_hir::def_id::DefId; +use rustc_infer::infer::InferCtxt; use rustc_infer::traits::query::NoSolution; use rustc_middle::bug; use rustc_middle::traits::solve::inspect::ProbeKind; @@ -17,6 +16,9 @@ use rustc_middle::ty::{TypeVisitableExt, Upcast}; use rustc_span::{ErrorGuaranteed, DUMMY_SP}; use std::fmt::Debug; +use crate::solve::GoalSource; +use crate::solve::{EvalCtxt, SolverMode}; + pub(super) mod structural_traits; /// A candidate is a possible way to prove a goal. @@ -46,18 +48,18 @@ pub(super) trait GoalKind<'tcx>: /// work, then produce a response (typically by executing /// [`EvalCtxt::evaluate_added_goals_and_make_canonical_response`]). fn probe_and_match_goal_against_assumption( - ecx: &mut EvalCtxt<'_, 'tcx>, + ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>, source: CandidateSource<'tcx>, goal: Goal<'tcx, Self>, assumption: ty::Clause<'tcx>, - then: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> QueryResult<'tcx>, + then: impl FnOnce(&mut EvalCtxt<'_, InferCtxt<'tcx>>) -> QueryResult<'tcx>, ) -> Result, NoSolution>; /// Consider a clause, which consists of a "assumption" and some "requirements", /// to satisfy a goal. If the requirements hold, then attempt to satisfy our /// goal by equating it with the assumption. fn probe_and_consider_implied_clause( - ecx: &mut EvalCtxt<'_, 'tcx>, + ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>, parent_source: CandidateSource<'tcx>, goal: Goal<'tcx, Self>, assumption: ty::Clause<'tcx>, @@ -75,7 +77,7 @@ pub(super) trait GoalKind<'tcx>: /// additionally checking all of the supertraits and object bounds to hold, /// since they're not implied by the well-formedness of the object type. fn probe_and_consider_object_bound_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, + ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>, source: CandidateSource<'tcx>, goal: Goal<'tcx, Self>, assumption: ty::Clause<'tcx>, @@ -99,7 +101,7 @@ pub(super) trait GoalKind<'tcx>: } fn consider_impl_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, + ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>, goal: Goal<'tcx, Self>, impl_def_id: DefId, ) -> Result, NoSolution>; @@ -111,7 +113,7 @@ pub(super) trait GoalKind<'tcx>: /// Trait goals always hold while projection goals never do. This is a bit arbitrary /// but prevents incorrect normalization while hiding any trait errors. fn consider_error_guaranteed_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, + ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>, guar: ErrorGuaranteed, ) -> Result, NoSolution>; @@ -120,13 +122,13 @@ pub(super) trait GoalKind<'tcx>: /// These components are given by built-in rules from /// [`structural_traits::instantiate_constituent_tys_for_auto_trait`]. fn consider_auto_trait_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, + ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>, goal: Goal<'tcx, Self>, ) -> Result, NoSolution>; /// A trait alias holds if the RHS traits and `where` clauses hold. fn consider_trait_alias_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, + ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>, goal: Goal<'tcx, Self>, ) -> Result, NoSolution>; @@ -135,7 +137,7 @@ pub(super) trait GoalKind<'tcx>: /// These components are given by built-in rules from /// [`structural_traits::instantiate_constituent_tys_for_sized_trait`]. fn consider_builtin_sized_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, + ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>, goal: Goal<'tcx, Self>, ) -> Result, NoSolution>; @@ -144,27 +146,27 @@ pub(super) trait GoalKind<'tcx>: /// These components are given by built-in rules from /// [`structural_traits::instantiate_constituent_tys_for_copy_clone_trait`]. fn consider_builtin_copy_clone_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, + ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>, goal: Goal<'tcx, Self>, ) -> Result, NoSolution>; /// A type is `PointerLike` if we can compute its layout, and that layout /// matches the layout of `usize`. fn consider_builtin_pointer_like_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, + ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>, goal: Goal<'tcx, Self>, ) -> Result, NoSolution>; /// A type is a `FnPtr` if it is of `FnPtr` type. fn consider_builtin_fn_ptr_trait_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, + ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>, goal: Goal<'tcx, Self>, ) -> Result, NoSolution>; /// A callable type (a closure, fn def, or fn ptr) is known to implement the `Fn` /// family of traits where `A` is given by the signature of the type. fn consider_builtin_fn_trait_candidates( - ecx: &mut EvalCtxt<'_, 'tcx>, + ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>, goal: Goal<'tcx, Self>, kind: ty::ClosureKind, ) -> Result, NoSolution>; @@ -172,7 +174,7 @@ pub(super) trait GoalKind<'tcx>: /// An async closure is known to implement the `AsyncFn` family of traits /// where `A` is given by the signature of the type. fn consider_builtin_async_fn_trait_candidates( - ecx: &mut EvalCtxt<'_, 'tcx>, + ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>, goal: Goal<'tcx, Self>, kind: ty::ClosureKind, ) -> Result, NoSolution>; @@ -181,13 +183,13 @@ pub(super) trait GoalKind<'tcx>: /// is used internally to delay computation for async closures until after /// upvar analysis is performed in HIR typeck. fn consider_builtin_async_fn_kind_helper_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, + ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>, goal: Goal<'tcx, Self>, ) -> Result, NoSolution>; /// `Tuple` is implemented if the `Self` type is a tuple. fn consider_builtin_tuple_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, + ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>, goal: Goal<'tcx, Self>, ) -> Result, NoSolution>; @@ -197,7 +199,7 @@ pub(super) trait GoalKind<'tcx>: /// the built-in types. For structs, the metadata type is given by the struct /// tail. fn consider_builtin_pointee_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, + ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>, goal: Goal<'tcx, Self>, ) -> Result, NoSolution>; @@ -205,7 +207,7 @@ pub(super) trait GoalKind<'tcx>: /// `Future`, where `O` is given by the coroutine's return type /// that was computed during type-checking. fn consider_builtin_future_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, + ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>, goal: Goal<'tcx, Self>, ) -> Result, NoSolution>; @@ -213,19 +215,19 @@ pub(super) trait GoalKind<'tcx>: /// `Iterator`, where `O` is given by the generator's yield type /// that was computed during type-checking. fn consider_builtin_iterator_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, + ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>, goal: Goal<'tcx, Self>, ) -> Result, NoSolution>; /// A coroutine (that comes from a `gen` desugaring) is known to implement /// `FusedIterator` fn consider_builtin_fused_iterator_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, + ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>, goal: Goal<'tcx, Self>, ) -> Result, NoSolution>; fn consider_builtin_async_iterator_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, + ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>, goal: Goal<'tcx, Self>, ) -> Result, NoSolution>; @@ -233,27 +235,27 @@ pub(super) trait GoalKind<'tcx>: /// implement `Coroutine`, given the resume, yield, /// and return types of the coroutine computed during type-checking. fn consider_builtin_coroutine_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, + ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>, goal: Goal<'tcx, Self>, ) -> Result, NoSolution>; fn consider_builtin_discriminant_kind_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, + ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>, goal: Goal<'tcx, Self>, ) -> Result, NoSolution>; fn consider_builtin_async_destruct_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, + ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>, goal: Goal<'tcx, Self>, ) -> Result, NoSolution>; fn consider_builtin_destruct_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, + ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>, goal: Goal<'tcx, Self>, ) -> Result, NoSolution>; fn consider_builtin_transmute_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, + ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>, goal: Goal<'tcx, Self>, ) -> Result, NoSolution>; @@ -265,12 +267,12 @@ pub(super) trait GoalKind<'tcx>: /// otherwise recompute this for codegen. This is a bit of a mess but the /// easiest way to maintain the existing behavior for now. fn consider_structural_builtin_unsize_candidates( - ecx: &mut EvalCtxt<'_, 'tcx>, + ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>, goal: Goal<'tcx, Self>, ) -> Vec>; } -impl<'tcx> EvalCtxt<'_, 'tcx> { +impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> { pub(super) fn assemble_and_evaluate_candidates>( &mut self, goal: Goal<'tcx, G>, diff --git a/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs b/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs index cf826596392ce..930ae5af811b4 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs @@ -3,6 +3,7 @@ use rustc_data_structures::fx::FxHashMap; use rustc_hir::LangItem; use rustc_hir::{def_id::DefId, Movability, Mutability}; +use rustc_infer::infer::InferCtxt; use rustc_infer::traits::query::NoSolution; use rustc_macros::{TypeFoldable, TypeVisitable}; use rustc_middle::bug; @@ -18,7 +19,7 @@ use crate::solve::EvalCtxt; // instantiate the binder with placeholders eagerly. #[instrument(level = "trace", skip(ecx), ret)] pub(in crate::solve) fn instantiate_constituent_tys_for_auto_trait<'tcx>( - ecx: &EvalCtxt<'_, 'tcx>, + ecx: &EvalCtxt<'_, InferCtxt<'tcx>>, ty: Ty<'tcx>, ) -> Result>>, NoSolution> { let tcx = ecx.tcx(); @@ -97,7 +98,7 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_auto_trait<'tcx>( #[instrument(level = "trace", skip(ecx), ret)] pub(in crate::solve) fn instantiate_constituent_tys_for_sized_trait<'tcx>( - ecx: &EvalCtxt<'_, 'tcx>, + ecx: &EvalCtxt<'_, InferCtxt<'tcx>>, ty: Ty<'tcx>, ) -> Result>>, NoSolution> { match *ty.kind() { @@ -161,7 +162,7 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_sized_trait<'tcx>( #[instrument(level = "trace", skip(ecx), ret)] pub(in crate::solve) fn instantiate_constituent_tys_for_copy_clone_trait<'tcx>( - ecx: &EvalCtxt<'_, 'tcx>, + ecx: &EvalCtxt<'_, InferCtxt<'tcx>>, ty: Ty<'tcx>, ) -> Result>>, NoSolution> { match *ty.kind() { @@ -663,7 +664,7 @@ fn coroutine_closure_to_ambiguous_coroutine<'tcx>( // normalize eagerly here. See https://github.com/lcnr/solver-woes/issues/9 // for more details. pub(in crate::solve) fn predicates_for_object_candidate<'tcx>( - ecx: &EvalCtxt<'_, 'tcx>, + ecx: &EvalCtxt<'_, InferCtxt<'tcx>>, param_env: ty::ParamEnv<'tcx>, trait_ref: ty::TraitRef<'tcx>, object_bound: &'tcx ty::List>, @@ -716,7 +717,7 @@ pub(in crate::solve) fn predicates_for_object_candidate<'tcx>( } struct ReplaceProjectionWith<'a, 'tcx> { - ecx: &'a EvalCtxt<'a, 'tcx>, + ecx: &'a EvalCtxt<'a, InferCtxt<'tcx>>, param_env: ty::ParamEnv<'tcx>, mapping: FxHashMap>, nested: Vec>>, diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs index 52deb22098f4b..9590a82c0677f 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs @@ -18,7 +18,6 @@ use rustc_index::IndexVec; use rustc_infer::infer::canonical::query_response::make_query_region_constraints; use rustc_infer::infer::canonical::CanonicalVarValues; use rustc_infer::infer::canonical::{CanonicalExt, QueryRegionConstraints}; -use rustc_infer::infer::resolve::EagerResolver; use rustc_infer::infer::RegionVariableOrigin; use rustc_infer::infer::{InferCtxt, InferOk}; use rustc_infer::traits::solve::NestedNormalizationGoals; @@ -31,6 +30,7 @@ use rustc_middle::traits::solve::{ use rustc_middle::traits::ObligationCause; use rustc_middle::ty::{self, BoundVar, GenericArgKind, Ty, TyCtxt, TypeFoldable}; use rustc_next_trait_solver::canonicalizer::{CanonicalizeMode, Canonicalizer}; +use rustc_next_trait_solver::resolve::EagerResolver; use rustc_span::{Span, DUMMY_SP}; use std::assert_matches::assert_matches; use std::iter; @@ -52,7 +52,7 @@ impl<'tcx, T> ResponseT<'tcx> for inspect::State, T> { } } -impl<'tcx> EvalCtxt<'_, 'tcx> { +impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> { /// Canonicalizes the goal remembering the original values /// for each bound variable. pub(super) fn canonicalize_goal>>( diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs index 70308d4359d22..7e1d7d73e0b3f 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs @@ -4,7 +4,6 @@ use std::ops::ControlFlow; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_hir::def_id::DefId; use rustc_infer::infer::at::ToTrace; -use rustc_infer::infer::canonical::CanonicalVarValues; use rustc_infer::infer::{ BoundRegionConversionTime, DefineOpaqueTypes, InferCtxt, InferOk, TyCtxtInferExt, }; @@ -13,10 +12,8 @@ use rustc_infer::traits::solve::{MaybeCause, NestedNormalizationGoals}; use rustc_infer::traits::ObligationCause; use rustc_macros::{extension, HashStable, HashStable_NoContext, TyDecodable, TyEncodable}; use rustc_middle::bug; -use rustc_middle::infer::canonical::CanonicalVarInfos; use rustc_middle::traits::solve::{ - inspect, CanonicalInput, CanonicalResponse, Certainty, PredefinedOpaques, - PredefinedOpaquesData, QueryResult, + inspect, CanonicalInput, CanonicalResponse, Certainty, PredefinedOpaquesData, QueryResult, }; use rustc_middle::traits::specialization_graph; use rustc_middle::ty::{ @@ -25,7 +22,7 @@ use rustc_middle::ty::{ }; use rustc_session::config::DumpSolverProofTree; use rustc_span::DUMMY_SP; -use rustc_type_ir::{self as ir, Interner}; +use rustc_type_ir::{self as ir, CanonicalVarValues, Interner}; use rustc_type_ir_macros::{Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic}; use crate::traits::coherence; @@ -41,7 +38,11 @@ pub(super) mod canonical; mod probe; mod select; -pub struct EvalCtxt<'a, 'tcx> { +pub struct EvalCtxt< + 'a, + Infcx: InferCtxtLike, + I: Interner = ::Interner, +> { /// The inference context that backs (mostly) inference and placeholder terms /// instantiated while solving goals. /// @@ -57,11 +58,11 @@ pub struct EvalCtxt<'a, 'tcx> { /// If some `InferCtxt` method is missing, please first think defensively about /// the method's compatibility with this solver, or if an existing one does /// the job already. - infcx: &'a InferCtxt<'tcx>, + infcx: &'a Infcx, /// The variable info for the `var_values`, only used to make an ambiguous response /// with no constraints. - variables: CanonicalVarInfos<'tcx>, + variables: I::CanonicalVars, /// Whether we're currently computing a `NormalizesTo` goal. Unlike other goals, /// `NormalizesTo` goals act like functions with the expected term always being /// fully unconstrained. This would weaken inference however, as the nested goals @@ -70,9 +71,9 @@ pub struct EvalCtxt<'a, 'tcx> { /// when then adds these to its own context. The caller is always an `AliasRelate` /// goal so this never leaks out of the solver. is_normalizes_to_goal: bool, - pub(super) var_values: CanonicalVarValues<'tcx>, + pub(super) var_values: CanonicalVarValues, - predefined_opaques_in_body: PredefinedOpaques<'tcx>, + predefined_opaques_in_body: I::PredefinedOpaques, /// The highest universe index nameable by the caller. /// @@ -85,9 +86,9 @@ pub struct EvalCtxt<'a, 'tcx> { /// new placeholders to the caller. pub(super) max_input_universe: ty::UniverseIndex, - pub(super) search_graph: &'a mut SearchGraph<'tcx>, + pub(super) search_graph: &'a mut SearchGraph, - nested_goals: NestedGoals>, + nested_goals: NestedGoals, // Has this `EvalCtxt` errored out with `NoSolution` in `try_evaluate_added_goals`? // @@ -97,7 +98,7 @@ pub struct EvalCtxt<'a, 'tcx> { // evaluation code. tainted: Result<(), NoSolution>, - pub(super) inspect: ProofTreeBuilder>, + pub(super) inspect: ProofTreeBuilder, } #[derive(derivative::Derivative)] @@ -157,7 +158,7 @@ impl<'tcx> InferCtxt<'tcx> { } } -impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { +impl<'a, 'tcx> EvalCtxt<'a, InferCtxt<'tcx>> { pub(super) fn solver_mode(&self) -> SolverMode { self.search_graph.solver_mode() } @@ -172,7 +173,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { pub(super) fn enter_root( infcx: &InferCtxt<'tcx>, generate_proof_tree: GenerateProofTree, - f: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> R, + f: impl FnOnce(&mut EvalCtxt<'_, InferCtxt<'tcx>>) -> R, ) -> (R, Option>>) { let mode = if infcx.intercrate { SolverMode::Coherence } else { SolverMode::Normal }; let mut search_graph = search_graph::SearchGraph::new(mode); @@ -225,10 +226,10 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { /// and registering opaques from the canonicalized input. fn enter_canonical( tcx: TyCtxt<'tcx>, - search_graph: &'a mut search_graph::SearchGraph<'tcx>, + search_graph: &'a mut search_graph::SearchGraph>, canonical_input: CanonicalInput<'tcx>, canonical_goal_evaluation: &mut ProofTreeBuilder>, - f: impl FnOnce(&mut EvalCtxt<'_, 'tcx>, Goal<'tcx, ty::Predicate<'tcx>>) -> R, + f: impl FnOnce(&mut EvalCtxt<'_, InferCtxt<'tcx>>, Goal<'tcx, ty::Predicate<'tcx>>) -> R, ) -> R { let intercrate = match search_graph.solver_mode() { SolverMode::Normal => false, @@ -287,7 +288,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { #[instrument(level = "debug", skip(tcx, search_graph, goal_evaluation), ret)] fn evaluate_canonical_goal( tcx: TyCtxt<'tcx>, - search_graph: &'a mut search_graph::SearchGraph<'tcx>, + search_graph: &'a mut search_graph::SearchGraph>, canonical_input: CanonicalInput<'tcx>, goal_evaluation: &mut ProofTreeBuilder>, ) -> QueryResult<'tcx> { @@ -600,7 +601,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { } } -impl<'tcx> EvalCtxt<'_, 'tcx> { +impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> { pub(super) fn tcx(&self) -> TyCtxt<'tcx> { self.infcx.tcx } diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs index 9edc489754ca9..1748c9be9275e 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs @@ -1,6 +1,7 @@ use crate::solve::assembly::Candidate; use super::EvalCtxt; +use rustc_infer::infer::InferCtxt; use rustc_infer::traits::BuiltinImplSource; use rustc_middle::traits::query::NoSolution; use rustc_middle::traits::solve::{inspect, CandidateSource, QueryResult}; @@ -8,7 +9,7 @@ use rustc_middle::ty::TyCtxt; use std::marker::PhantomData; pub(in crate::solve) struct ProbeCtxt<'me, 'a, 'tcx, F, T> { - ecx: &'me mut EvalCtxt<'a, 'tcx>, + ecx: &'me mut EvalCtxt<'a, InferCtxt<'tcx>>, probe_kind: F, _result: PhantomData, } @@ -17,7 +18,10 @@ impl<'tcx, F, T> ProbeCtxt<'_, '_, 'tcx, F, T> where F: FnOnce(&T) -> inspect::ProbeKind>, { - pub(in crate::solve) fn enter(self, f: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> T) -> T { + pub(in crate::solve) fn enter( + self, + f: impl FnOnce(&mut EvalCtxt<'_, InferCtxt<'tcx>>) -> T, + ) -> T { let ProbeCtxt { ecx: outer_ecx, probe_kind, _result } = self; let infcx = outer_ecx.infcx; @@ -60,13 +64,13 @@ where #[instrument(level = "debug", skip_all, fields(source = ?self.source))] pub(in crate::solve) fn enter( self, - f: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> QueryResult<'tcx>, + f: impl FnOnce(&mut EvalCtxt<'_, InferCtxt<'tcx>>) -> QueryResult<'tcx>, ) -> Result, NoSolution> { self.cx.enter(|ecx| f(ecx)).map(|result| Candidate { source: self.source, result }) } } -impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { +impl<'a, 'tcx> EvalCtxt<'a, InferCtxt<'tcx>> { /// `probe_kind` is only called when proof tree building is enabled so it can be /// as expensive as necessary to output the desired information. pub(in crate::solve) fn probe(&mut self, probe_kind: F) -> ProbeCtxt<'_, 'a, 'tcx, F, T> diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs index 4933080451daa..7291eb00e7271 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs @@ -380,7 +380,10 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> { source: CandidateSource::Impl(impl_def_id), result: _, } = candidate.kind() - && goal.infcx().tcx.has_attr(impl_def_id, sym::do_not_recommend) + && goal + .infcx() + .tcx + .has_attrs_with_path(impl_def_id, &[sym::diagnostic, sym::do_not_recommend]) { return ControlFlow::Break(self.obligation.clone()); } diff --git a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs index e12c66b692850..737d03f73f00b 100644 --- a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs +++ b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs @@ -11,7 +11,6 @@ use rustc_ast_ir::try_visit; use rustc_ast_ir::visit::VisitorResult; -use rustc_infer::infer::resolve::EagerResolver; use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, InferOk}; use rustc_macros::extension; use rustc_middle::traits::query::NoSolution; @@ -20,6 +19,7 @@ use rustc_middle::traits::solve::{Certainty, Goal}; use rustc_middle::traits::ObligationCause; use rustc_middle::ty::{TyCtxt, TypeFoldable}; use rustc_middle::{bug, ty}; +use rustc_next_trait_solver::resolve::EagerResolver; use rustc_span::{Span, DUMMY_SP}; use crate::solve::eval_ctxt::canonical; diff --git a/compiler/rustc_trait_selection/src/solve/mod.rs b/compiler/rustc_trait_selection/src/solve/mod.rs index b085d009d75af..60722d3618f9a 100644 --- a/compiler/rustc_trait_selection/src/solve/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/mod.rs @@ -15,6 +15,7 @@ //! about it on zulip. use rustc_hir::def_id::DefId; use rustc_infer::infer::canonical::{Canonical, CanonicalVarValues}; +use rustc_infer::infer::InferCtxt; use rustc_infer::traits::query::NoSolution; use rustc_macros::extension; use rustc_middle::bug; @@ -82,7 +83,7 @@ impl<'tcx> Canonical<'tcx, Response>> { } } -impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { +impl<'a, 'tcx> EvalCtxt<'a, InferCtxt<'tcx>> { #[instrument(level = "trace", skip(self))] fn compute_type_outlives_goal( &mut self, @@ -201,7 +202,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { } } -impl<'tcx> EvalCtxt<'_, 'tcx> { +impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> { #[instrument(level = "trace", skip(self, goals))] fn add_goals( &mut self, diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/anon_const.rs b/compiler/rustc_trait_selection/src/solve/normalizes_to/anon_const.rs index 94e078f56159e..c9621e705e575 100644 --- a/compiler/rustc_trait_selection/src/solve/normalizes_to/anon_const.rs +++ b/compiler/rustc_trait_selection/src/solve/normalizes_to/anon_const.rs @@ -1,8 +1,9 @@ use crate::solve::EvalCtxt; +use rustc_infer::infer::InferCtxt; use rustc_middle::traits::solve::{Certainty, Goal, QueryResult}; use rustc_middle::ty; -impl<'tcx> EvalCtxt<'_, 'tcx> { +impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> { #[instrument(level = "trace", skip(self), ret)] pub(super) fn normalize_anon_const( &mut self, diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/inherent.rs b/compiler/rustc_trait_selection/src/solve/normalizes_to/inherent.rs index 353bdb9caff8f..2146a2c2f0819 100644 --- a/compiler/rustc_trait_selection/src/solve/normalizes_to/inherent.rs +++ b/compiler/rustc_trait_selection/src/solve/normalizes_to/inherent.rs @@ -4,12 +4,13 @@ //! 1. instantiate generic parameters, //! 2. equate the self type, and //! 3. instantiate and register where clauses. +use rustc_infer::infer::InferCtxt; use rustc_middle::traits::solve::{Certainty, Goal, GoalSource, QueryResult}; use rustc_middle::ty; use crate::solve::EvalCtxt; -impl<'tcx> EvalCtxt<'_, 'tcx> { +impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> { pub(super) fn normalize_inherent_associated_type( &mut self, goal: Goal<'tcx, ty::NormalizesTo<'tcx>>, diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs b/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs index 8c492b62c1ade..7ef8373663ba5 100644 --- a/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs @@ -5,6 +5,7 @@ use super::assembly::{self, structural_traits, Candidate}; use super::{EvalCtxt, GoalSource}; use rustc_hir::def_id::DefId; use rustc_hir::LangItem; +use rustc_infer::infer::InferCtxt; use rustc_infer::traits::query::NoSolution; use rustc_infer::traits::solve::inspect::ProbeKind; use rustc_infer::traits::solve::MaybeCause; @@ -24,7 +25,7 @@ mod inherent; mod opaque_types; mod weak_types; -impl<'tcx> EvalCtxt<'_, 'tcx> { +impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> { #[instrument(level = "trace", skip(self), ret)] pub(super) fn compute_normalizes_to_goal( &mut self, @@ -98,11 +99,11 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { } fn probe_and_match_goal_against_assumption( - ecx: &mut EvalCtxt<'_, 'tcx>, + ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>, source: CandidateSource<'tcx>, goal: Goal<'tcx, Self>, assumption: ty::Clause<'tcx>, - then: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> QueryResult<'tcx>, + then: impl FnOnce(&mut EvalCtxt<'_, InferCtxt<'tcx>>) -> QueryResult<'tcx>, ) -> Result, NoSolution> { if let Some(projection_pred) = assumption.as_projection_clause() { if projection_pred.projection_def_id() == goal.predicate.def_id() { @@ -137,7 +138,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { } fn consider_impl_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, + ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>, goal: Goal<'tcx, NormalizesTo<'tcx>>, impl_def_id: DefId, ) -> Result, NoSolution> { @@ -199,7 +200,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS); }; - let error_response = |ecx: &mut EvalCtxt<'_, 'tcx>, reason| { + let error_response = |ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>, reason| { let guar = tcx.dcx().span_delayed_bug(tcx.def_span(assoc_def.item.def_id), reason); let error_term = match assoc_def.item.kind { ty::AssocKind::Const => ty::Const::new_error( @@ -279,14 +280,14 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { /// Fail to normalize if the predicate contains an error, alternatively, we could normalize to `ty::Error` /// and succeed. Can experiment with this to figure out what results in better error messages. fn consider_error_guaranteed_candidate( - _ecx: &mut EvalCtxt<'_, 'tcx>, + _ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>, _guar: ErrorGuaranteed, ) -> Result, NoSolution> { Err(NoSolution) } fn consider_auto_trait_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, + ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>, goal: Goal<'tcx, Self>, ) -> Result, NoSolution> { ecx.tcx().dcx().span_delayed_bug( @@ -297,42 +298,42 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { } fn consider_trait_alias_candidate( - _ecx: &mut EvalCtxt<'_, 'tcx>, + _ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>, goal: Goal<'tcx, Self>, ) -> Result, NoSolution> { bug!("trait aliases do not have associated types: {:?}", goal); } fn consider_builtin_sized_candidate( - _ecx: &mut EvalCtxt<'_, 'tcx>, + _ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>, goal: Goal<'tcx, Self>, ) -> Result, NoSolution> { bug!("`Sized` does not have an associated type: {:?}", goal); } fn consider_builtin_copy_clone_candidate( - _ecx: &mut EvalCtxt<'_, 'tcx>, + _ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>, goal: Goal<'tcx, Self>, ) -> Result, NoSolution> { bug!("`Copy`/`Clone` does not have an associated type: {:?}", goal); } fn consider_builtin_pointer_like_candidate( - _ecx: &mut EvalCtxt<'_, 'tcx>, + _ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>, goal: Goal<'tcx, Self>, ) -> Result, NoSolution> { bug!("`PointerLike` does not have an associated type: {:?}", goal); } fn consider_builtin_fn_ptr_trait_candidate( - _ecx: &mut EvalCtxt<'_, 'tcx>, + _ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>, goal: Goal<'tcx, Self>, ) -> Result, NoSolution> { bug!("`FnPtr` does not have an associated type: {:?}", goal); } fn consider_builtin_fn_trait_candidates( - ecx: &mut EvalCtxt<'_, 'tcx>, + ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>, goal: Goal<'tcx, Self>, goal_kind: ty::ClosureKind, ) -> Result, NoSolution> { @@ -375,7 +376,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { } fn consider_builtin_async_fn_trait_candidates( - ecx: &mut EvalCtxt<'_, 'tcx>, + ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>, goal: Goal<'tcx, Self>, goal_kind: ty::ClosureKind, ) -> Result, NoSolution> { @@ -460,7 +461,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { } fn consider_builtin_async_fn_kind_helper_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, + ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>, goal: Goal<'tcx, Self>, ) -> Result, NoSolution> { let [ @@ -507,14 +508,14 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { } fn consider_builtin_tuple_candidate( - _ecx: &mut EvalCtxt<'_, 'tcx>, + _ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>, goal: Goal<'tcx, Self>, ) -> Result, NoSolution> { bug!("`Tuple` does not have an associated type: {:?}", goal); } fn consider_builtin_pointee_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, + ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>, goal: Goal<'tcx, Self>, ) -> Result, NoSolution> { let tcx = ecx.tcx(); @@ -596,7 +597,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { } fn consider_builtin_future_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, + ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>, goal: Goal<'tcx, Self>, ) -> Result, NoSolution> { let self_ty = goal.predicate.self_ty(); @@ -628,7 +629,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { } fn consider_builtin_iterator_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, + ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>, goal: Goal<'tcx, Self>, ) -> Result, NoSolution> { let self_ty = goal.predicate.self_ty(); @@ -660,14 +661,14 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { } fn consider_builtin_fused_iterator_candidate( - _ecx: &mut EvalCtxt<'_, 'tcx>, + _ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>, goal: Goal<'tcx, Self>, ) -> Result, NoSolution> { bug!("`FusedIterator` does not have an associated type: {:?}", goal); } fn consider_builtin_async_iterator_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, + ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>, goal: Goal<'tcx, Self>, ) -> Result, NoSolution> { let self_ty = goal.predicate.self_ty(); @@ -703,7 +704,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { } fn consider_builtin_coroutine_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, + ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>, goal: Goal<'tcx, Self>, ) -> Result, NoSolution> { let self_ty = goal.predicate.self_ty(); @@ -748,14 +749,14 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { } fn consider_structural_builtin_unsize_candidates( - _ecx: &mut EvalCtxt<'_, 'tcx>, + _ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>, goal: Goal<'tcx, Self>, ) -> Vec> { bug!("`Unsize` does not have an associated type: {:?}", goal); } fn consider_builtin_discriminant_kind_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, + ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>, goal: Goal<'tcx, Self>, ) -> Result, NoSolution> { let self_ty = goal.predicate.self_ty(); @@ -807,7 +808,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { } fn consider_builtin_async_destruct_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, + ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>, goal: Goal<'tcx, Self>, ) -> Result, NoSolution> { let self_ty = goal.predicate.self_ty(); @@ -860,14 +861,14 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { } fn consider_builtin_destruct_candidate( - _ecx: &mut EvalCtxt<'_, 'tcx>, + _ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>, goal: Goal<'tcx, Self>, ) -> Result, NoSolution> { bug!("`Destruct` does not have an associated type: {:?}", goal); } fn consider_builtin_transmute_candidate( - _ecx: &mut EvalCtxt<'_, 'tcx>, + _ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>, goal: Goal<'tcx, Self>, ) -> Result, NoSolution> { bug!("`BikeshedIntrinsicFrom` does not have an associated type: {:?}", goal) @@ -880,7 +881,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { /// diverge. #[instrument(level = "trace", skip(ecx, param_env), ret)] fn fetch_eligible_assoc_item_def<'tcx>( - ecx: &EvalCtxt<'_, 'tcx>, + ecx: &EvalCtxt<'_, InferCtxt<'tcx>>, param_env: ty::ParamEnv<'tcx>, goal_trait_ref: ty::TraitRef<'tcx>, trait_assoc_def_id: DefId, diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/opaque_types.rs b/compiler/rustc_trait_selection/src/solve/normalizes_to/opaque_types.rs index 9fdb280cdc6da..3b83d347276f7 100644 --- a/compiler/rustc_trait_selection/src/solve/normalizes_to/opaque_types.rs +++ b/compiler/rustc_trait_selection/src/solve/normalizes_to/opaque_types.rs @@ -1,6 +1,7 @@ //! Computes a normalizes-to (projection) goal for opaque types. This goal //! behaves differently depending on the param-env's reveal mode and whether //! the opaque is in a defining scope. +use rustc_infer::infer::InferCtxt; use rustc_middle::traits::query::NoSolution; use rustc_middle::traits::solve::{Certainty, Goal, QueryResult}; use rustc_middle::traits::Reveal; @@ -9,7 +10,7 @@ use rustc_middle::ty::util::NotUniqueParam; use crate::solve::{EvalCtxt, SolverMode}; -impl<'tcx> EvalCtxt<'_, 'tcx> { +impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> { pub(super) fn normalize_opaque_type( &mut self, goal: Goal<'tcx, ty::NormalizesTo<'tcx>>, diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/weak_types.rs b/compiler/rustc_trait_selection/src/solve/normalizes_to/weak_types.rs index 13af5068b6c99..109a9e9671f08 100644 --- a/compiler/rustc_trait_selection/src/solve/normalizes_to/weak_types.rs +++ b/compiler/rustc_trait_selection/src/solve/normalizes_to/weak_types.rs @@ -3,12 +3,13 @@ //! //! Since a weak alias is never ambiguous, this just computes the `type_of` of //! the alias and registers the where-clauses of the type alias. +use rustc_infer::infer::InferCtxt; use rustc_middle::traits::solve::{Certainty, Goal, GoalSource, QueryResult}; use rustc_middle::ty; use crate::solve::EvalCtxt; -impl<'tcx> EvalCtxt<'_, 'tcx> { +impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> { pub(super) fn normalize_weak_type( &mut self, goal: Goal<'tcx, ty::NormalizesTo<'tcx>>, diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs index 0f1be1072a8f1..8fa78e49dc6e6 100644 --- a/compiler/rustc_trait_selection/src/solve/project_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs @@ -1,10 +1,11 @@ use crate::solve::GoalSource; use super::EvalCtxt; +use rustc_infer::infer::InferCtxt; use rustc_middle::traits::solve::{Certainty, Goal, QueryResult}; use rustc_middle::ty::{self, ProjectionPredicate}; -impl<'tcx> EvalCtxt<'_, 'tcx> { +impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> { #[instrument(level = "trace", skip(self), ret)] pub(super) fn compute_projection_goal( &mut self, diff --git a/compiler/rustc_trait_selection/src/solve/search_graph.rs b/compiler/rustc_trait_selection/src/solve/search_graph.rs index 0164d44667c3f..bcd210f789bfe 100644 --- a/compiler/rustc_trait_selection/src/solve/search_graph.rs +++ b/compiler/rustc_trait_selection/src/solve/search_graph.rs @@ -1,18 +1,21 @@ -use crate::solve::FIXPOINT_STEP_LIMIT; +use std::mem; -use super::inspect; -use super::inspect::ProofTreeBuilder; -use super::SolverMode; -use rustc_data_structures::fx::FxHashMap; -use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_index::Idx; use rustc_index::IndexVec; use rustc_middle::dep_graph::dep_kinds; use rustc_middle::traits::solve::CacheData; -use rustc_middle::traits::solve::{CanonicalInput, Certainty, EvaluationCache, QueryResult}; +use rustc_middle::traits::solve::EvaluationCache; use rustc_middle::ty::TyCtxt; +use rustc_next_trait_solver::solve::{CanonicalInput, Certainty, QueryResult}; use rustc_session::Limit; -use std::mem; +use rustc_type_ir::inherent::*; +use rustc_type_ir::Interner; + +use super::inspect; +use super::inspect::ProofTreeBuilder; +use super::SolverMode; +use crate::solve::FIXPOINT_STEP_LIMIT; rustc_index::newtype_index! { #[orderable] @@ -30,9 +33,10 @@ bitflags::bitflags! { } } -#[derive(Debug)] -struct StackEntry<'tcx> { - input: CanonicalInput<'tcx>, +#[derive(derivative::Derivative)] +#[derivative(Debug(bound = ""))] +struct StackEntry { + input: CanonicalInput, available_depth: Limit, @@ -43,21 +47,40 @@ struct StackEntry<'tcx> { /// Whether this entry is a non-root cycle participant. /// /// We must not move the result of non-root cycle participants to the - /// global cache. See [SearchGraph::cycle_participants] for more details. - /// We store the highest stack depth of a head of a cycle this goal is involved - /// in. This necessary to soundly cache its provisional result. + /// global cache. We store the highest stack depth of a head of a cycle + /// this goal is involved in. This necessary to soundly cache its + /// provisional result. non_root_cycle_participant: Option, encountered_overflow: bool, has_been_used: HasBeenUsed, + + /// We put only the root goal of a coinductive cycle into the global cache. + /// + /// If we were to use that result when later trying to prove another cycle + /// participant, we can end up with unstable query results. + /// + /// See tests/ui/next-solver/coinduction/incompleteness-unstable-result.rs for + /// an example of where this is needed. + /// + /// There can be multiple roots on the same stack, so we need to track + /// cycle participants per root: + /// ```plain + /// A :- B + /// B :- A, C + /// C :- D + /// D :- C + /// ``` + cycle_participants: FxHashSet>, /// Starts out as `None` and gets set when rerunning this /// goal in case we encounter a cycle. - provisional_result: Option>, + provisional_result: Option>, } /// The provisional result for a goal which is not on the stack. -struct DetachedEntry<'tcx> { +#[derive(Debug)] +struct DetachedEntry { /// The head of the smallest non-trivial cycle involving this entry. /// /// Given the following rules, when proving `A` the head for @@ -68,7 +91,7 @@ struct DetachedEntry<'tcx> { /// C :- A + B + C /// ``` head: StackDepth, - result: QueryResult<'tcx>, + result: QueryResult, } /// Stores the stack depth of a currently evaluated goal *and* already @@ -83,14 +106,15 @@ struct DetachedEntry<'tcx> { /// /// The provisional cache can theoretically result in changes to the observable behavior, /// see tests/ui/traits/next-solver/cycles/provisional-cache-impacts-behavior.rs. -#[derive(Default)] -struct ProvisionalCacheEntry<'tcx> { +#[derive(derivative::Derivative)] +#[derivative(Default(bound = ""))] +struct ProvisionalCacheEntry { stack_depth: Option, - with_inductive_stack: Option>, - with_coinductive_stack: Option>, + with_inductive_stack: Option>, + with_coinductive_stack: Option>, } -impl<'tcx> ProvisionalCacheEntry<'tcx> { +impl ProvisionalCacheEntry { fn is_empty(&self) -> bool { self.stack_depth.is_none() && self.with_inductive_stack.is_none() @@ -98,53 +122,30 @@ impl<'tcx> ProvisionalCacheEntry<'tcx> { } } -pub(super) struct SearchGraph<'tcx> { +pub(super) struct SearchGraph { mode: SolverMode, /// The stack of goals currently being computed. /// /// An element is *deeper* in the stack if its index is *lower*. - stack: IndexVec>, - provisional_cache: FxHashMap, ProvisionalCacheEntry<'tcx>>, - /// We put only the root goal of a coinductive cycle into the global cache. - /// - /// If we were to use that result when later trying to prove another cycle - /// participant, we can end up with unstable query results. - /// - /// See tests/ui/next-solver/coinduction/incompleteness-unstable-result.rs for - /// an example of where this is needed. - cycle_participants: FxHashSet>, + stack: IndexVec>, + provisional_cache: FxHashMap, ProvisionalCacheEntry>, } -impl<'tcx> SearchGraph<'tcx> { - pub(super) fn new(mode: SolverMode) -> SearchGraph<'tcx> { - Self { - mode, - stack: Default::default(), - provisional_cache: Default::default(), - cycle_participants: Default::default(), - } +impl SearchGraph { + pub(super) fn new(mode: SolverMode) -> SearchGraph { + Self { mode, stack: Default::default(), provisional_cache: Default::default() } } pub(super) fn solver_mode(&self) -> SolverMode { self.mode } - /// Update the stack and reached depths on cache hits. - #[instrument(level = "trace", skip(self))] - fn on_cache_hit(&mut self, additional_depth: usize, encountered_overflow: bool) { - let reached_depth = self.stack.next_index().plus(additional_depth); - if let Some(last) = self.stack.raw.last_mut() { - last.reached_depth = last.reached_depth.max(reached_depth); - last.encountered_overflow |= encountered_overflow; - } - } - /// Pops the highest goal from the stack, lazily updating the /// the next goal in the stack. /// /// Directly popping from the stack instead of using this method /// would cause us to not track overflow and recursion depth correctly. - fn pop_stack(&mut self) -> StackEntry<'tcx> { + fn pop_stack(&mut self) -> StackEntry { let elem = self.stack.pop().unwrap(); if let Some(last) = self.stack.raw.last_mut() { last.reached_depth = last.reached_depth.max(elem.reached_depth); @@ -153,25 +154,8 @@ impl<'tcx> SearchGraph<'tcx> { elem } - /// The trait solver behavior is different for coherence - /// so we use a separate cache. Alternatively we could use - /// a single cache and share it between coherence and ordinary - /// trait solving. - pub(super) fn global_cache(&self, tcx: TyCtxt<'tcx>) -> &'tcx EvaluationCache<'tcx> { - match self.mode { - SolverMode::Normal => &tcx.new_solver_evaluation_cache, - SolverMode::Coherence => &tcx.new_solver_coherence_evaluation_cache, - } - } - pub(super) fn is_empty(&self) -> bool { - if self.stack.is_empty() { - debug_assert!(self.provisional_cache.is_empty()); - debug_assert!(self.cycle_participants.is_empty()); - true - } else { - false - } + self.stack.is_empty() } /// Returns the remaining depth allowed for nested goals. @@ -181,8 +165,8 @@ impl<'tcx> SearchGraph<'tcx> { /// the remaining depth of all nested goals to prevent hangs /// in case there is exponential blowup. fn allowed_depth_for_nested( - tcx: TyCtxt<'tcx>, - stack: &IndexVec>, + tcx: I, + stack: &IndexVec>, ) -> Option { if let Some(last) = stack.raw.last() { if last.available_depth.0 == 0 { @@ -195,13 +179,13 @@ impl<'tcx> SearchGraph<'tcx> { Limit(last.available_depth.0 - 1) }) } else { - Some(tcx.recursion_limit()) + Some(Limit(tcx.recursion_limit())) } } fn stack_coinductive_from( - tcx: TyCtxt<'tcx>, - stack: &IndexVec>, + tcx: I, + stack: &IndexVec>, head: StackDepth, ) -> bool { stack.raw[head.index()..] @@ -220,21 +204,32 @@ impl<'tcx> SearchGraph<'tcx> { // we reach a fixpoint and all other cycle participants to make sure that // their result does not get moved to the global cache. fn tag_cycle_participants( - stack: &mut IndexVec>, - cycle_participants: &mut FxHashSet>, + stack: &mut IndexVec>, usage_kind: HasBeenUsed, head: StackDepth, ) { stack[head].has_been_used |= usage_kind; debug_assert!(!stack[head].has_been_used.is_empty()); - for entry in &mut stack.raw[head.index() + 1..] { + + // The current root of these cycles. Note that this may not be the final + // root in case a later goal depends on a goal higher up the stack. + let mut current_root = head; + while let Some(parent) = stack[current_root].non_root_cycle_participant { + current_root = parent; + debug_assert!(!stack[current_root].has_been_used.is_empty()); + } + + let (stack, cycle_participants) = stack.raw.split_at_mut(head.index() + 1); + let current_cycle_root = &mut stack[current_root.as_usize()]; + for entry in cycle_participants { entry.non_root_cycle_participant = entry.non_root_cycle_participant.max(Some(head)); - cycle_participants.insert(entry.input); + current_cycle_root.cycle_participants.insert(entry.input); + current_cycle_root.cycle_participants.extend(mem::take(&mut entry.cycle_participants)); } } fn clear_dependent_provisional_results( - provisional_cache: &mut FxHashMap, ProvisionalCacheEntry<'tcx>>, + provisional_cache: &mut FxHashMap, ProvisionalCacheEntry>, head: StackDepth, ) { #[allow(rustc::potential_query_instability)] @@ -244,6 +239,19 @@ impl<'tcx> SearchGraph<'tcx> { !entry.is_empty() }); } +} + +impl<'tcx> SearchGraph> { + /// The trait solver behavior is different for coherence + /// so we use a separate cache. Alternatively we could use + /// a single cache and share it between coherence and ordinary + /// trait solving. + pub(super) fn global_cache(&self, tcx: TyCtxt<'tcx>) -> &'tcx EvaluationCache<'tcx> { + match self.mode { + SolverMode::Normal => &tcx.new_solver_evaluation_cache, + SolverMode::Coherence => &tcx.new_solver_coherence_evaluation_cache, + } + } /// Probably the most involved method of the whole solver. /// @@ -252,10 +260,14 @@ impl<'tcx> SearchGraph<'tcx> { pub(super) fn with_new_goal( &mut self, tcx: TyCtxt<'tcx>, - input: CanonicalInput<'tcx>, + input: CanonicalInput>, inspect: &mut ProofTreeBuilder>, - mut prove_goal: impl FnMut(&mut Self, &mut ProofTreeBuilder>) -> QueryResult<'tcx>, - ) -> QueryResult<'tcx> { + mut prove_goal: impl FnMut( + &mut Self, + &mut ProofTreeBuilder>, + ) -> QueryResult>, + ) -> QueryResult> { + self.check_invariants(); // Check for overflow. let Some(available_depth) = Self::allowed_depth_for_nested(tcx, &self.stack) else { if let Some(last) = self.stack.raw.last_mut() { @@ -266,37 +278,7 @@ impl<'tcx> SearchGraph<'tcx> { return Self::response_no_constraints(tcx, input, Certainty::overflow(true)); }; - // Try to fetch the goal from the global cache. - 'global: { - let Some(CacheData { result, proof_tree, reached_depth, encountered_overflow }) = - self.global_cache(tcx).get( - tcx, - input, - |cycle_participants| { - self.stack.iter().any(|entry| cycle_participants.contains(&entry.input)) - }, - available_depth, - ) - else { - break 'global; - }; - - // If we're building a proof tree and the current cache entry does not - // contain a proof tree, we do not use the entry but instead recompute - // the goal. We simply overwrite the existing entry once we're done, - // caching the proof tree. - if !inspect.is_noop() { - if let Some(revisions) = proof_tree { - inspect.goal_evaluation_kind( - inspect::WipCanonicalGoalEvaluationKind::Interned { revisions }, - ); - } else { - break 'global; - } - } - - self.on_cache_hit(reached_depth, encountered_overflow); - debug!("global cache hit"); + if let Some(result) = self.lookup_global_cache(tcx, input, available_depth, inspect) { return result; } @@ -322,12 +304,7 @@ impl<'tcx> SearchGraph<'tcx> { // already set correctly while computing the cache entry. inspect .goal_evaluation_kind(inspect::WipCanonicalGoalEvaluationKind::ProvisionalCacheHit); - Self::tag_cycle_participants( - &mut self.stack, - &mut self.cycle_participants, - HasBeenUsed::empty(), - entry.head, - ); + Self::tag_cycle_participants(&mut self.stack, HasBeenUsed::empty(), entry.head); return entry.result; } else if let Some(stack_depth) = cache_entry.stack_depth { debug!("encountered cycle with depth {stack_depth:?}"); @@ -344,12 +321,7 @@ impl<'tcx> SearchGraph<'tcx> { } else { HasBeenUsed::INDUCTIVE_CYCLE }; - Self::tag_cycle_participants( - &mut self.stack, - &mut self.cycle_participants, - usage_kind, - stack_depth, - ); + Self::tag_cycle_participants(&mut self.stack, usage_kind, stack_depth); // Return the provisional result or, if we're in the first iteration, // start with no constraints. @@ -370,6 +342,7 @@ impl<'tcx> SearchGraph<'tcx> { non_root_cycle_participant: None, encountered_overflow: false, has_been_used: HasBeenUsed::empty(), + cycle_participants: Default::default(), provisional_result: None, }; assert_eq!(self.stack.push(entry), depth); @@ -378,63 +351,16 @@ impl<'tcx> SearchGraph<'tcx> { // This is for global caching, so we properly track query dependencies. // Everything that affects the `result` should be performed within this - // `with_anon_task` closure. + // `with_anon_task` closure. If computing this goal depends on something + // not tracked by the cache key and from outside of this anon task, it + // must not be added to the global cache. Notably, this is the case for + // trait solver cycles participants. let ((final_entry, result), dep_node) = tcx.dep_graph.with_anon_task(tcx, dep_kinds::TraitSelect, || { - // When we encounter a coinductive cycle, we have to fetch the - // result of that cycle while we are still computing it. Because - // of this we continuously recompute the cycle until the result - // of the previous iteration is equal to the final result, at which - // point we are done. for _ in 0..FIXPOINT_STEP_LIMIT { - let result = prove_goal(self, inspect); - let stack_entry = self.pop_stack(); - debug_assert_eq!(stack_entry.input, input); - - // If the current goal is not the root of a cycle, we are done. - if stack_entry.has_been_used.is_empty() { - return (stack_entry, result); - } - - // If it is a cycle head, we have to keep trying to prove it until - // we reach a fixpoint. We need to do so for all cycle heads, - // not only for the root. - // - // See tests/ui/traits/next-solver/cycles/fixpoint-rerun-all-cycle-heads.rs - // for an example. - - // Start by clearing all provisional cache entries which depend on this - // the current goal. - Self::clear_dependent_provisional_results( - &mut self.provisional_cache, - self.stack.next_index(), - ); - - // Check whether we reached a fixpoint, either because the final result - // is equal to the provisional result of the previous iteration, or because - // this was only the root of either coinductive or inductive cycles, and the - // final result is equal to the initial response for that case. - let reached_fixpoint = if let Some(r) = stack_entry.provisional_result { - r == result - } else if stack_entry.has_been_used == HasBeenUsed::COINDUCTIVE_CYCLE { - Self::response_no_constraints(tcx, input, Certainty::Yes) == result - } else if stack_entry.has_been_used == HasBeenUsed::INDUCTIVE_CYCLE { - Self::response_no_constraints(tcx, input, Certainty::overflow(false)) - == result - } else { - false - }; - - // If we did not reach a fixpoint, update the provisional result and reevaluate. - if reached_fixpoint { - return (stack_entry, result); - } else { - let depth = self.stack.push(StackEntry { - has_been_used: HasBeenUsed::empty(), - provisional_result: Some(result), - ..stack_entry - }); - debug_assert_eq!(self.provisional_cache[&input].stack_depth, Some(depth)); + match self.fixpoint_step_in_task(tcx, input, inspect, &mut prove_goal) { + StepResult::Done(final_entry, result) => return (final_entry, result), + StepResult::HasChanged => {} } } @@ -463,14 +389,13 @@ impl<'tcx> SearchGraph<'tcx> { } else { self.provisional_cache.remove(&input); let reached_depth = final_entry.reached_depth.as_usize() - self.stack.len(); - let cycle_participants = mem::take(&mut self.cycle_participants); // When encountering a cycle, both inductive and coinductive, we only // move the root into the global cache. We also store all other cycle // participants involved. // // We must not use the global cache entry of a root goal if a cycle // participant is on the stack. This is necessary to prevent unstable - // results. See the comment of `SearchGraph::cycle_participants` for + // results. See the comment of `StackEntry::cycle_participants` for // more details. self.global_cache(tcx).insert( tcx, @@ -478,20 +403,208 @@ impl<'tcx> SearchGraph<'tcx> { proof_tree, reached_depth, final_entry.encountered_overflow, - cycle_participants, + final_entry.cycle_participants, dep_node, result, ) } + self.check_invariants(); + result } + /// Try to fetch a previously computed result from the global cache, + /// making sure to only do so if it would match the result of reevaluating + /// this goal. + fn lookup_global_cache( + &mut self, + tcx: TyCtxt<'tcx>, + input: CanonicalInput>, + available_depth: Limit, + inspect: &mut ProofTreeBuilder>, + ) -> Option>> { + let CacheData { result, proof_tree, additional_depth, encountered_overflow } = self + .global_cache(tcx) + .get(tcx, input, self.stack.iter().map(|e| e.input), available_depth)?; + + // If we're building a proof tree and the current cache entry does not + // contain a proof tree, we do not use the entry but instead recompute + // the goal. We simply overwrite the existing entry once we're done, + // caching the proof tree. + if !inspect.is_noop() { + if let Some(revisions) = proof_tree { + let kind = inspect::WipCanonicalGoalEvaluationKind::Interned { revisions }; + inspect.goal_evaluation_kind(kind); + } else { + return None; + } + } + + // Update the reached depth of the current goal to make sure + // its state is the same regardless of whether we've used the + // global cache or not. + let reached_depth = self.stack.next_index().plus(additional_depth); + if let Some(last) = self.stack.raw.last_mut() { + last.reached_depth = last.reached_depth.max(reached_depth); + last.encountered_overflow |= encountered_overflow; + } + + Some(result) + } +} + +enum StepResult { + Done(StackEntry, QueryResult), + HasChanged, +} + +impl<'tcx> SearchGraph> { + /// When we encounter a coinductive cycle, we have to fetch the + /// result of that cycle while we are still computing it. Because + /// of this we continuously recompute the cycle until the result + /// of the previous iteration is equal to the final result, at which + /// point we are done. + fn fixpoint_step_in_task( + &mut self, + tcx: TyCtxt<'tcx>, + input: CanonicalInput>, + inspect: &mut ProofTreeBuilder>, + prove_goal: &mut F, + ) -> StepResult> + where + F: FnMut(&mut Self, &mut ProofTreeBuilder>) -> QueryResult>, + { + let result = prove_goal(self, inspect); + let stack_entry = self.pop_stack(); + debug_assert_eq!(stack_entry.input, input); + + // If the current goal is not the root of a cycle, we are done. + if stack_entry.has_been_used.is_empty() { + return StepResult::Done(stack_entry, result); + } + + // If it is a cycle head, we have to keep trying to prove it until + // we reach a fixpoint. We need to do so for all cycle heads, + // not only for the root. + // + // See tests/ui/traits/next-solver/cycles/fixpoint-rerun-all-cycle-heads.rs + // for an example. + + // Start by clearing all provisional cache entries which depend on this + // the current goal. + Self::clear_dependent_provisional_results( + &mut self.provisional_cache, + self.stack.next_index(), + ); + + // Check whether we reached a fixpoint, either because the final result + // is equal to the provisional result of the previous iteration, or because + // this was only the root of either coinductive or inductive cycles, and the + // final result is equal to the initial response for that case. + let reached_fixpoint = if let Some(r) = stack_entry.provisional_result { + r == result + } else if stack_entry.has_been_used == HasBeenUsed::COINDUCTIVE_CYCLE { + Self::response_no_constraints(tcx, input, Certainty::Yes) == result + } else if stack_entry.has_been_used == HasBeenUsed::INDUCTIVE_CYCLE { + Self::response_no_constraints(tcx, input, Certainty::overflow(false)) == result + } else { + false + }; + + // If we did not reach a fixpoint, update the provisional result and reevaluate. + if reached_fixpoint { + StepResult::Done(stack_entry, result) + } else { + let depth = self.stack.push(StackEntry { + has_been_used: HasBeenUsed::empty(), + provisional_result: Some(result), + ..stack_entry + }); + debug_assert_eq!(self.provisional_cache[&input].stack_depth, Some(depth)); + StepResult::HasChanged + } + } + fn response_no_constraints( tcx: TyCtxt<'tcx>, - goal: CanonicalInput<'tcx>, + goal: CanonicalInput>, certainty: Certainty, - ) -> QueryResult<'tcx> { + ) -> QueryResult> { Ok(super::response_no_constraints_raw(tcx, goal.max_universe, goal.variables, certainty)) } } + +impl SearchGraph { + #[allow(rustc::potential_query_instability)] + fn check_invariants(&self) { + if !cfg!(debug_assertions) { + return; + } + + let SearchGraph { mode: _, stack, provisional_cache } = self; + if stack.is_empty() { + assert!(provisional_cache.is_empty()); + } + + for (depth, entry) in stack.iter_enumerated() { + let StackEntry { + input, + available_depth: _, + reached_depth: _, + non_root_cycle_participant, + encountered_overflow: _, + has_been_used, + ref cycle_participants, + provisional_result, + } = *entry; + let cache_entry = provisional_cache.get(&entry.input).unwrap(); + assert_eq!(cache_entry.stack_depth, Some(depth)); + if let Some(head) = non_root_cycle_participant { + assert!(head < depth); + assert!(cycle_participants.is_empty()); + assert_ne!(stack[head].has_been_used, HasBeenUsed::empty()); + + let mut current_root = head; + while let Some(parent) = stack[current_root].non_root_cycle_participant { + current_root = parent; + } + assert!(stack[current_root].cycle_participants.contains(&input)); + } + + if !cycle_participants.is_empty() { + assert!(provisional_result.is_some() || !has_been_used.is_empty()); + for entry in stack.iter().take(depth.as_usize()) { + assert_eq!(cycle_participants.get(&entry.input), None); + } + } + } + + for (&input, entry) in &self.provisional_cache { + let ProvisionalCacheEntry { stack_depth, with_coinductive_stack, with_inductive_stack } = + entry; + assert!( + stack_depth.is_some() + || with_coinductive_stack.is_some() + || with_inductive_stack.is_some() + ); + + if let &Some(stack_depth) = stack_depth { + assert_eq!(stack[stack_depth].input, input); + } + + let check_detached = |detached_entry: &DetachedEntry| { + let DetachedEntry { head, result: _ } = *detached_entry; + assert_ne!(stack[head].has_been_used, HasBeenUsed::empty()); + }; + + if let Some(with_coinductive_stack) = with_coinductive_stack { + check_detached(with_coinductive_stack); + } + + if let Some(with_inductive_stack) = with_inductive_stack { + check_detached(with_inductive_stack); + } + } + } +} diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs index 9139c75d3999a..e59eef22f4111 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -8,6 +8,7 @@ use super::{EvalCtxt, GoalSource, SolverMode}; use rustc_data_structures::fx::FxIndexSet; use rustc_hir::def_id::DefId; use rustc_hir::{LangItem, Movability}; +use rustc_infer::infer::InferCtxt; use rustc_infer::traits::query::NoSolution; use rustc_infer::traits::solve::MaybeCause; use rustc_middle::bug; @@ -37,7 +38,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { } fn consider_impl_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, + ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>, goal: Goal<'tcx, TraitPredicate<'tcx>>, impl_def_id: DefId, ) -> Result, NoSolution> { @@ -93,7 +94,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { } fn consider_error_guaranteed_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, + ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>, _guar: ErrorGuaranteed, ) -> Result, NoSolution> { // FIXME: don't need to enter a probe here. @@ -102,11 +103,11 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { } fn probe_and_match_goal_against_assumption( - ecx: &mut EvalCtxt<'_, 'tcx>, + ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>, source: CandidateSource<'tcx>, goal: Goal<'tcx, Self>, assumption: ty::Clause<'tcx>, - then: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> QueryResult<'tcx>, + then: impl FnOnce(&mut EvalCtxt<'_, InferCtxt<'tcx>>) -> QueryResult<'tcx>, ) -> Result, NoSolution> { if let Some(trait_clause) = assumption.as_trait_clause() { if trait_clause.def_id() == goal.predicate.def_id() @@ -130,7 +131,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { } fn consider_auto_trait_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, + ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>, goal: Goal<'tcx, Self>, ) -> Result, NoSolution> { if goal.predicate.polarity != ty::PredicatePolarity::Positive { @@ -173,7 +174,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { } fn consider_trait_alias_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, + ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>, goal: Goal<'tcx, Self>, ) -> Result, NoSolution> { if goal.predicate.polarity != ty::PredicatePolarity::Positive { @@ -196,7 +197,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { } fn consider_builtin_sized_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, + ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>, goal: Goal<'tcx, Self>, ) -> Result, NoSolution> { if goal.predicate.polarity != ty::PredicatePolarity::Positive { @@ -211,7 +212,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { } fn consider_builtin_copy_clone_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, + ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>, goal: Goal<'tcx, Self>, ) -> Result, NoSolution> { if goal.predicate.polarity != ty::PredicatePolarity::Positive { @@ -226,7 +227,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { } fn consider_builtin_pointer_like_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, + ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>, goal: Goal<'tcx, Self>, ) -> Result, NoSolution> { if goal.predicate.polarity != ty::PredicatePolarity::Positive { @@ -256,7 +257,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { } fn consider_builtin_fn_ptr_trait_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, + ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>, goal: Goal<'tcx, Self>, ) -> Result, NoSolution> { let self_ty = goal.predicate.self_ty(); @@ -287,7 +288,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { } fn consider_builtin_fn_trait_candidates( - ecx: &mut EvalCtxt<'_, 'tcx>, + ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>, goal: Goal<'tcx, Self>, goal_kind: ty::ClosureKind, ) -> Result, NoSolution> { @@ -328,7 +329,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { } fn consider_builtin_async_fn_trait_candidates( - ecx: &mut EvalCtxt<'_, 'tcx>, + ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>, goal: Goal<'tcx, Self>, goal_kind: ty::ClosureKind, ) -> Result, NoSolution> { @@ -379,7 +380,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { } fn consider_builtin_async_fn_kind_helper_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, + ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>, goal: Goal<'tcx, Self>, ) -> Result, NoSolution> { let [closure_fn_kind_ty, goal_kind_ty] = **goal.predicate.trait_ref.args else { @@ -406,7 +407,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { /// impl Tuple for (T1, .., Tn) {} /// ``` fn consider_builtin_tuple_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, + ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>, goal: Goal<'tcx, Self>, ) -> Result, NoSolution> { if goal.predicate.polarity != ty::PredicatePolarity::Positive { @@ -422,7 +423,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { } fn consider_builtin_pointee_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, + ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>, goal: Goal<'tcx, Self>, ) -> Result, NoSolution> { if goal.predicate.polarity != ty::PredicatePolarity::Positive { @@ -434,7 +435,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { } fn consider_builtin_future_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, + ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>, goal: Goal<'tcx, Self>, ) -> Result, NoSolution> { if goal.predicate.polarity != ty::PredicatePolarity::Positive { @@ -460,7 +461,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { } fn consider_builtin_iterator_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, + ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>, goal: Goal<'tcx, Self>, ) -> Result, NoSolution> { if goal.predicate.polarity != ty::PredicatePolarity::Positive { @@ -486,7 +487,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { } fn consider_builtin_fused_iterator_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, + ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>, goal: Goal<'tcx, Self>, ) -> Result, NoSolution> { if goal.predicate.polarity != ty::PredicatePolarity::Positive { @@ -510,7 +511,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { } fn consider_builtin_async_iterator_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, + ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>, goal: Goal<'tcx, Self>, ) -> Result, NoSolution> { if goal.predicate.polarity != ty::PredicatePolarity::Positive { @@ -536,7 +537,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { } fn consider_builtin_coroutine_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, + ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>, goal: Goal<'tcx, Self>, ) -> Result, NoSolution> { if goal.predicate.polarity != ty::PredicatePolarity::Positive { @@ -568,7 +569,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { } fn consider_builtin_discriminant_kind_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, + ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>, goal: Goal<'tcx, Self>, ) -> Result, NoSolution> { if goal.predicate.polarity != ty::PredicatePolarity::Positive { @@ -581,7 +582,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { } fn consider_builtin_async_destruct_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, + ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>, goal: Goal<'tcx, Self>, ) -> Result, NoSolution> { if goal.predicate.polarity != ty::PredicatePolarity::Positive { @@ -594,7 +595,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { } fn consider_builtin_destruct_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, + ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>, goal: Goal<'tcx, Self>, ) -> Result, NoSolution> { if goal.predicate.polarity != ty::PredicatePolarity::Positive { @@ -610,7 +611,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { } fn consider_builtin_transmute_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, + ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>, goal: Goal<'tcx, Self>, ) -> Result, NoSolution> { if goal.predicate.polarity != ty::PredicatePolarity::Positive { @@ -651,7 +652,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { /// impl<'a, T: Trait + 'a> Unsize for T {} /// ``` fn consider_structural_builtin_unsize_candidates( - ecx: &mut EvalCtxt<'_, 'tcx>, + ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>, goal: Goal<'tcx, Self>, ) -> Vec> { if goal.predicate.polarity != ty::PredicatePolarity::Positive { @@ -722,7 +723,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { } } -impl<'tcx> EvalCtxt<'_, 'tcx> { +impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> { /// Trait upcasting allows for coercions between trait objects: /// ```ignore (builtin impl example) /// trait Super {} @@ -846,7 +847,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { // having any inference side-effects. We process obligations because // unification may initially succeed due to deferred projection equality. let projection_may_match = - |ecx: &mut EvalCtxt<'_, 'tcx>, + |ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>, source_projection: ty::PolyExistentialProjection<'tcx>, target_projection: ty::PolyExistentialProjection<'tcx>| { source_projection.item_def_id() == target_projection.item_def_id() @@ -1152,7 +1153,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { source: CandidateSource<'tcx>, goal: Goal<'tcx, TraitPredicate<'tcx>>, constituent_tys: impl Fn( - &EvalCtxt<'_, 'tcx>, + &EvalCtxt<'_, InferCtxt<'tcx>>, Ty<'tcx>, ) -> Result>>, NoSolution>, ) -> Result, NoSolution> { diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs index d693bac90dc47..494fca0336cc0 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs @@ -422,6 +422,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_predicate)) => { let trait_predicate = bound_predicate.rebind(trait_predicate); let trait_predicate = self.resolve_vars_if_possible(trait_predicate); + let trait_predicate = self.apply_do_not_recommend(trait_predicate, &mut obligation); // Let's use the root obligation as the main message, when we care about the // most general case ("X doesn't implement Pattern<'_>") over the case that @@ -1003,6 +1004,34 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { err.emit() } + fn apply_do_not_recommend( + &self, + mut trait_predicate: ty::Binder<'tcx, ty::TraitPredicate<'tcx>>, + obligation: &'_ mut PredicateObligation<'tcx>, + ) -> ty::Binder<'tcx, ty::TraitPredicate<'tcx>> { + let mut base_cause = obligation.cause.code().clone(); + loop { + if let ObligationCauseCode::ImplDerived(ref c) = base_cause { + if self.tcx.has_attrs_with_path( + c.impl_or_alias_def_id, + &[sym::diagnostic, sym::do_not_recommend], + ) { + let code = (*c.derived.parent_code).clone(); + obligation.cause.map_code(|_| code); + obligation.predicate = c.derived.parent_trait_pred.upcast(self.tcx); + trait_predicate = c.derived.parent_trait_pred.clone(); + } + } + if let Some((parent_cause, _parent_pred)) = base_cause.parent() { + base_cause = parent_cause.clone(); + } else { + break; + } + } + + trait_predicate + } + fn emit_specialized_closure_kind_error( &self, obligation: &PredicateObligation<'tcx>, diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs index abb19c7efd861..8ce1271fc17a8 100644 --- a/compiler/rustc_trait_selection/src/traits/object_safety.rs +++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs @@ -398,7 +398,7 @@ pub fn object_safety_violations_for_assoc_item( // Associated types can only be object safe if they have `Self: Sized` bounds. ty::AssocKind::Type => { if !tcx.features().generic_associated_types_extended - && !tcx.generics_of(item.def_id).own_params.is_empty() + && !tcx.generics_of(item.def_id).is_own_empty() && !item.is_impl_trait_in_trait() { vec![ObjectSafetyViolation::GAT(item.name, item.ident(tcx).span)] diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 65048ffdfba53..c684f087d3210 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -619,7 +619,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // higher-ranked things. // Prevent, e.g., `dyn Iterator`. for bound in self.tcx().item_bounds(assoc_type).transpose_iter() { - let arg_bound = if defs.count() == 0 { + let arg_bound = if defs.is_empty() { bound.instantiate(tcx, trait_predicate.trait_ref.args) } else { let mut args = smallvec::SmallVec::with_capacity(defs.count()); diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 19affac7970b9..4a94643d908bf 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -1781,7 +1781,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // FIXME(generic-associated-types): This only detects one layer of inference, // which is probably not what we actually want, but fixing it causes some ambiguity: // . - if !generics.own_params.is_empty() + if !generics.is_own_empty() && obligation.predicate.args[generics.parent_count..].iter().any(|&p| { p.has_non_region_infer() && match p.unpack() { diff --git a/compiler/rustc_ty_utils/src/consts.rs b/compiler/rustc_ty_utils/src/consts.rs index fec02f515caf3..b40a0d0a58e4a 100644 --- a/compiler/rustc_ty_utils/src/consts.rs +++ b/compiler/rustc_ty_utils/src/consts.rs @@ -81,9 +81,9 @@ fn destructure_const<'tcx>( fn check_binop(op: mir::BinOp) -> bool { use mir::BinOp::*; match op { - Add | AddUnchecked | Sub | SubUnchecked | Mul | MulUnchecked | Div | Rem | BitXor - | BitAnd | BitOr | Shl | ShlUnchecked | Shr | ShrUnchecked | Eq | Lt | Le | Ne | Ge - | Gt | Cmp => true, + Add | AddUnchecked | AddWithOverflow | Sub | SubUnchecked | SubWithOverflow | Mul + | MulUnchecked | MulWithOverflow | Div | Rem | BitXor | BitAnd | BitOr | Shl + | ShlUnchecked | Shr | ShrUnchecked | Eq | Lt | Le | Ne | Ge | Gt | Cmp => true, Offset => false, } } diff --git a/compiler/rustc_type_ir/src/debug.rs b/compiler/rustc_type_ir/src/debug.rs index 9298360f749ac..4e8be1ee4c285 100644 --- a/compiler/rustc_type_ir/src/debug.rs +++ b/compiler/rustc_type_ir/src/debug.rs @@ -1,4 +1,6 @@ -use crate::{ConstVid, InferCtxtLike, Interner, TyVid, UniverseIndex}; +use crate::{ + ConstVid, EffectVid, FloatVid, InferCtxtLike, IntVid, Interner, RegionVid, TyVid, UniverseIndex, +}; use core::fmt; use std::marker::PhantomData; @@ -16,7 +18,7 @@ impl InferCtxtLike for NoInfcx { None } - fn universe_of_lt(&self, _lt: I::InferRegion) -> Option { + fn universe_of_lt(&self, _lt: RegionVid) -> Option { None } @@ -24,27 +26,31 @@ impl InferCtxtLike for NoInfcx { None } - fn root_ty_var(&self, vid: TyVid) -> TyVid { - vid + fn opportunistic_resolve_ty_var(&self, vid: TyVid) -> I::Ty { + panic!("cannot resolve {vid:?}") } - fn probe_ty_var(&self, _vid: TyVid) -> Option { - None + fn opportunistic_resolve_int_var(&self, vid: IntVid) -> I::Ty { + panic!("cannot resolve {vid:?}") } - fn opportunistic_resolve_lt_var(&self, _vid: I::InferRegion) -> Option { - None + fn opportunistic_resolve_float_var(&self, vid: FloatVid) -> I::Ty { + panic!("cannot resolve {vid:?}") } - fn root_ct_var(&self, vid: ConstVid) -> ConstVid { - vid + fn opportunistic_resolve_ct_var(&self, vid: ConstVid, _: I::Ty) -> I::Const { + panic!("cannot resolve {vid:?}") } - fn probe_ct_var(&self, _vid: ConstVid) -> Option { - None + fn opportunistic_resolve_effect_var(&self, vid: EffectVid, _: I::Ty) -> I::Const { + panic!("cannot resolve {vid:?}") + } + + fn opportunistic_resolve_lt_var(&self, vid: crate::RegionVid) -> I::Region { + panic!("cannot resolve {vid:?}") } - fn defining_opaque_types(&self) -> ::DefiningOpaqueTypes { + fn defining_opaque_types(&self) -> I::DefiningOpaqueTypes { Default::default() } } diff --git a/compiler/rustc_type_ir/src/generic_arg.rs b/compiler/rustc_type_ir/src/generic_arg.rs index 622a40806083b..cc8c44446579f 100644 --- a/compiler/rustc_type_ir/src/generic_arg.rs +++ b/compiler/rustc_type_ir/src/generic_arg.rs @@ -16,3 +16,17 @@ pub enum GenericArgKind { Type(I::Ty), Const(I::Const), } + +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = ""), + Copy(bound = ""), + Debug(bound = ""), + Eq(bound = ""), + PartialEq(bound = "") +)] +#[cfg_attr(feature = "nightly", derive(TyDecodable, TyEncodable, HashStable_NoContext))] +pub enum TermKind { + Ty(I::Ty), + Const(I::Const), +} diff --git a/compiler/rustc_type_ir/src/infcx.rs b/compiler/rustc_type_ir/src/infcx.rs index a53287c19878b..bb5081fb33576 100644 --- a/compiler/rustc_type_ir/src/infcx.rs +++ b/compiler/rustc_type_ir/src/infcx.rs @@ -1,4 +1,4 @@ -use crate::{ConstVid, Interner, TyVid, UniverseIndex}; +use crate::{ConstVid, EffectVid, FloatVid, IntVid, Interner, RegionVid, TyVid, UniverseIndex}; pub trait InferCtxtLike { type Interner: Interner; @@ -6,37 +6,23 @@ pub trait InferCtxtLike { fn interner(&self) -> Self::Interner; fn universe_of_ty(&self, ty: TyVid) -> Option; + fn universe_of_lt(&self, lt: RegionVid) -> Option; + fn universe_of_ct(&self, ct: ConstVid) -> Option; - /// Resolve `TyVid` to its root `TyVid`. - fn root_ty_var(&self, vid: TyVid) -> TyVid; - - /// Resolve `TyVid` to its inferred type, if it has been equated with a non-infer type. - fn probe_ty_var(&self, vid: TyVid) -> Option<::Ty>; - - fn universe_of_lt( + fn opportunistic_resolve_ty_var(&self, vid: TyVid) -> ::Ty; + fn opportunistic_resolve_int_var(&self, vid: IntVid) -> ::Ty; + fn opportunistic_resolve_float_var(&self, vid: FloatVid) -> ::Ty; + fn opportunistic_resolve_ct_var( &self, - lt: ::InferRegion, - ) -> Option; - - /// Resolve `InferRegion` to its inferred region, if it has been equated with - /// a non-infer region. - /// - /// FIXME: This has slightly different semantics than `{probe,resolve}_{ty,ct}_var`, - /// that has to do with the fact unlike `Ty` or `Const` vars, in rustc, we may - /// not always be able to *name* the root region var from the universe of the - /// var we're trying to resolve. That's why it's called *opportunistic*. - fn opportunistic_resolve_lt_var( + vid: ConstVid, + ty: ::Ty, + ) -> ::Const; + fn opportunistic_resolve_effect_var( &self, - vid: ::InferRegion, - ) -> Option<::Region>; - - fn universe_of_ct(&self, ct: ConstVid) -> Option; - - /// Resolve `ConstVid` to its root `ConstVid`. - fn root_ct_var(&self, vid: ConstVid) -> ConstVid; - - /// Resolve `ConstVid` to its inferred type, if it has been equated with a non-infer type. - fn probe_ct_var(&self, vid: ConstVid) -> Option<::Const>; + vid: EffectVid, + ty: ::Ty, + ) -> ::Const; + fn opportunistic_resolve_lt_var(&self, vid: RegionVid) -> ::Region; fn defining_opaque_types(&self) -> ::DefiningOpaqueTypes; } diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs index 5289dfd932f90..f66c6e722f7ca 100644 --- a/compiler/rustc_type_ir/src/inherent.rs +++ b/compiler/rustc_type_ir/src/inherent.rs @@ -7,11 +7,11 @@ use std::fmt::Debug; use std::hash::Hash; use std::ops::Deref; -use crate::fold::TypeSuperFoldable; +use crate::fold::{TypeFoldable, TypeSuperFoldable}; use crate::visit::{Flags, TypeSuperVisitable}; use crate::{ - AliasTy, AliasTyKind, BoundVar, ConstKind, DebruijnIndex, DebugWithInfcx, Interner, RegionKind, - TyKind, UnevaluatedConst, UniverseIndex, + AliasTy, AliasTyKind, BoundVar, ConstKind, ConstVid, DebruijnIndex, DebugWithInfcx, InferConst, + InferTy, Interner, RegionKind, TyKind, TyVid, UnevaluatedConst, UniverseIndex, }; pub trait Ty>: @@ -28,6 +28,10 @@ pub trait Ty>: { fn new_bool(interner: I) -> Self; + fn new_infer(interner: I, var: InferTy) -> Self; + + fn new_var(interner: I, var: TyVid) -> Self; + fn new_anon_bound(interner: I, debruijn: DebruijnIndex, var: BoundVar) -> Self; fn new_alias(interner: I, kind: AliasTyKind, alias_ty: AliasTy) -> Self; @@ -68,6 +72,10 @@ pub trait Const>: + TypeSuperFoldable + Flags { + fn new_infer(interner: I, var: InferConst, ty: I::Ty) -> Self; + + fn new_var(interner: I, var: ConstVid, ty: I::Ty) -> Self; + fn new_anon_bound(interner: I, debruijn: DebruijnIndex, var: BoundVar, ty: I::Ty) -> Self; fn new_unevaluated(interner: I, uv: UnevaluatedConst, ty: I::Ty) -> Self; @@ -87,6 +95,7 @@ pub trait GenericArgs>: + IntoIterator + Deref> + Default + + TypeFoldable { fn type_at(self, i: usize) -> I::Ty; @@ -96,6 +105,7 @@ pub trait GenericArgs>: pub trait Predicate>: Copy + Debug + Hash + Eq + TypeSuperVisitable + TypeSuperFoldable + Flags { + fn is_coinductive(self, interner: I) -> bool; } /// Common capabilities of placeholder kinds diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index 9acf7c04dd61c..6516d5b164540 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -10,7 +10,7 @@ use crate::visit::{Flags, TypeSuperVisitable, TypeVisitable}; use crate::{ AliasTerm, AliasTermKind, AliasTy, AliasTyKind, CanonicalVarInfo, CoercePredicate, DebugWithInfcx, ExistentialProjection, ExistentialTraitRef, FnSig, GenericArgKind, - NormalizesTo, ProjectionPredicate, SubtypePredicate, TraitPredicate, TraitRef, + NormalizesTo, ProjectionPredicate, SubtypePredicate, TermKind, TraitPredicate, TraitRef, }; pub trait Interner: @@ -36,7 +36,7 @@ pub trait Interner: /// not including the args from the parent item (trait or impl). type OwnItemArgs: Copy + Debug + Hash + Eq; type GenericArg: Copy + DebugWithInfcx + Hash + Eq + IntoKind>; - type Term: Copy + Debug + Hash + Eq; + type Term: Copy + Debug + Hash + Eq + IntoKind>; type Binder>: BoundVars + TypeSuperVisitable; type BoundVars: IntoIterator; @@ -79,7 +79,6 @@ pub trait Interner: type EarlyParamRegion: Copy + Debug + Hash + Eq; type LateParamRegion: Copy + Debug + Hash + Eq; type BoundRegion: Copy + Debug + Hash + Eq + BoundVarLike; - type InferRegion: Copy + DebugWithInfcx + Hash + Eq; type PlaceholderRegion: PlaceholderLike; // Predicates @@ -124,6 +123,8 @@ pub trait Interner: ) -> Self::GenericArgs; fn parent(self, def_id: Self::DefId) -> Self::DefId; + + fn recursion_limit(self) -> usize; } /// Imagine you have a function `F: FnOnce(&[T]) -> R`, plus an iterator `iter` diff --git a/compiler/rustc_type_ir/src/region_kind.rs b/compiler/rustc_type_ir/src/region_kind.rs index eaae4ee0130bf..48ade27328943 100644 --- a/compiler/rustc_type_ir/src/region_kind.rs +++ b/compiler/rustc_type_ir/src/region_kind.rs @@ -1,13 +1,35 @@ #[cfg(feature = "nightly")] use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; #[cfg(feature = "nightly")] -use rustc_macros::{TyDecodable, TyEncodable}; +use rustc_macros::{HashStable_NoContext, TyDecodable, TyEncodable}; use std::fmt; use crate::{DebruijnIndex, DebugWithInfcx, InferCtxtLike, Interner, WithInfcx}; use self::RegionKind::*; +rustc_index::newtype_index! { + /// A **region** **v**ariable **ID**. + #[encodable] + #[orderable] + #[debug_format = "'?{}"] + #[gate_rustc_only] + #[cfg_attr(feature = "nightly", derive(HashStable_NoContext))] + pub struct RegionVid {} +} + +impl DebugWithInfcx for RegionVid { + fn fmt>( + this: WithInfcx<'_, Infcx, &Self>, + f: &mut core::fmt::Formatter<'_>, + ) -> core::fmt::Result { + match this.infcx.universe_of_lt(*this.data) { + Some(universe) => write!(f, "'?{}_{}", this.data.index(), universe.index()), + None => write!(f, "{:?}", this.data), + } + } +} + /// Representation of regions. Note that the NLL checker uses a distinct /// representation of regions. For this reason, it internally replaces all the /// regions with inference variables -- the index of the variable is then used @@ -152,7 +174,7 @@ pub enum RegionKind { ReStatic, /// A region variable. Should not exist outside of type inference. - ReVar(I::InferRegion), + ReVar(RegionVid), /// A placeholder region -- the higher-ranked version of `ReLateParam`. /// Should not exist outside of type inference. @@ -251,7 +273,6 @@ where I::EarlyParamRegion: HashStable, I::BoundRegion: HashStable, I::LateParamRegion: HashStable, - I::InferRegion: HashStable, I::PlaceholderRegion: HashStable, { #[inline] diff --git a/compiler/stable_mir/src/ty.rs b/compiler/stable_mir/src/ty.rs index 759e3f166bda2..321c56b623a25 100644 --- a/compiler/stable_mir/src/ty.rs +++ b/compiler/stable_mir/src/ty.rs @@ -1205,6 +1205,7 @@ pub struct TraitDecl { pub is_marker: bool, pub is_coinductive: bool, pub skip_array_during_method_dispatch: bool, + pub skip_boxed_slice_during_method_dispatch: bool, pub specialization_kind: TraitSpecializationKind, pub must_implement_one_of: Option>, pub implement_via_object: bool, diff --git a/library/alloc/Cargo.toml b/library/alloc/Cargo.toml index e8afed6b35a83..2e7fcb9dbd35f 100644 --- a/library/alloc/Cargo.toml +++ b/library/alloc/Cargo.toml @@ -37,4 +37,6 @@ compiler-builtins-no-asm = ["compiler_builtins/no-asm"] compiler-builtins-mangled-names = ["compiler_builtins/mangled-names"] compiler-builtins-weak-intrinsics = ["compiler_builtins/weak-intrinsics"] # Make panics and failed asserts immediately abort without formatting any message -panic_immediate_abort = [] +panic_immediate_abort = ["core/panic_immediate_abort"] +# Choose algorithms that are optimized for binary size instead of runtime performance +optimize_for_size = ["core/optimize_for_size"] diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index f1a6df94e1179..21d0050300170 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -135,6 +135,45 @@ //! is not allowed. For more guidance on working with box from unsafe code, see //! [rust-lang/unsafe-code-guidelines#326][ucg#326]. //! +//! # Editions +//! +//! A special case exists for the implementation of `IntoIterator` for arrays on the Rust 2021 +//! edition, as documented [here][array]. Unfortunately, it was later found that a similar +//! workaround should be added for boxed slices, and this was applied in the 2024 edition. +//! +//! Specifically, `IntoIterator` is implemented for `Box<[T]>` on all editions, but specific calls +//! to `into_iter()` for boxed slices will defer to the slice implementation on editions before +//! 2024: +//! +#![cfg_attr(bootstrap, doc = "```rust,edition2021,ignore")] +#![cfg_attr(not(bootstrap), doc = "```rust,edition2021")] +//! // Rust 2015, 2018, and 2021: +//! +//! # #![allow(boxed_slice_into_iter)] // override our `deny(warnings)` +//! let boxed_slice: Box<[i32]> = vec![0; 3].into_boxed_slice(); +//! +//! // This creates a slice iterator, producing references to each value. +//! for item in boxed_slice.into_iter().enumerate() { +//! let (i, x): (usize, &i32) = item; +//! println!("boxed_slice[{i}] = {x}"); +//! } +//! +//! // The `boxed_slice_into_iter` lint suggests this change for future compatibility: +//! for item in boxed_slice.iter().enumerate() { +//! let (i, x): (usize, &i32) = item; +//! println!("boxed_slice[{i}] = {x}"); +//! } +//! +//! // You can explicitly iterate a boxed slice by value using `IntoIterator::into_iter` +//! for item in IntoIterator::into_iter(boxed_slice).enumerate() { +//! let (i, x): (usize, i32) = item; +//! println!("boxed_slice[{i}] = {x}"); +//! } +//! ``` +//! +//! Similar to the array implementation, this may be modified in the future to remove this override, +//! and it's best to avoid relying on this edition-dependent behavior if you wish to preserve +//! compatibility with future versions of the compiler. //! //! [ucg#198]: https://github.com/rust-lang/unsafe-code-guidelines/issues/198 //! [ucg#326]: https://github.com/rust-lang/unsafe-code-guidelines/issues/326 @@ -165,6 +204,7 @@ use core::ops::{ }; use core::pin::Pin; use core::ptr::{self, addr_of_mut, NonNull, Unique}; +use core::slice; use core::task::{Context, Poll}; #[cfg(not(no_global_oom_handling))] @@ -177,6 +217,7 @@ use crate::raw_vec::RawVec; use crate::str::from_boxed_utf8_unchecked; #[cfg(not(no_global_oom_handling))] use crate::string::String; +use crate::vec; #[cfg(not(no_global_oom_handling))] use crate::vec::Vec; @@ -2080,6 +2121,51 @@ impl FromIterator for Box<[I]> { } } +/// This implementation is required to make sure that the `Box<[I]>: IntoIterator` +/// implementation doesn't overlap with `IntoIterator for T where T: Iterator` blanket. +#[stable(feature = "boxed_slice_into_iter", since = "CURRENT_RUSTC_VERSION")] +impl !Iterator for Box<[I], A> {} + +/// This implementation is required to make sure that the `&Box<[I]>: IntoIterator` +/// implementation doesn't overlap with `IntoIterator for T where T: Iterator` blanket. +#[stable(feature = "boxed_slice_into_iter", since = "CURRENT_RUSTC_VERSION")] +impl<'a, I, A: Allocator> !Iterator for &'a Box<[I], A> {} + +/// This implementation is required to make sure that the `&mut Box<[I]>: IntoIterator` +/// implementation doesn't overlap with `IntoIterator for T where T: Iterator` blanket. +#[stable(feature = "boxed_slice_into_iter", since = "CURRENT_RUSTC_VERSION")] +impl<'a, I, A: Allocator> !Iterator for &'a mut Box<[I], A> {} + +// Note: the `#[rustc_skip_during_method_dispatch(boxed_slice)]` on `trait IntoIterator` +// hides this implementation from explicit `.into_iter()` calls on editions < 2024, +// so those calls will still resolve to the slice implementation, by reference. +#[stable(feature = "boxed_slice_into_iter", since = "CURRENT_RUSTC_VERSION")] +impl IntoIterator for Box<[I], A> { + type IntoIter = vec::IntoIter; + type Item = I; + fn into_iter(self) -> vec::IntoIter { + self.into_vec().into_iter() + } +} + +#[stable(feature = "boxed_slice_into_iter", since = "CURRENT_RUSTC_VERSION")] +impl<'a, I, A: Allocator> IntoIterator for &'a Box<[I], A> { + type IntoIter = slice::Iter<'a, I>; + type Item = &'a I; + fn into_iter(self) -> slice::Iter<'a, I> { + self.iter() + } +} + +#[stable(feature = "boxed_slice_into_iter", since = "CURRENT_RUSTC_VERSION")] +impl<'a, I, A: Allocator> IntoIterator for &'a mut Box<[I], A> { + type IntoIter = slice::IterMut<'a, I>; + type Item = &'a mut I; + fn into_iter(self) -> slice::IterMut<'a, I> { + self.iter_mut() + } +} + #[cfg(not(no_global_oom_handling))] #[stable(feature = "boxed_str_from_iter", since = "CURRENT_RUSTC_VERSION")] impl FromIterator for Box { diff --git a/library/alloc/src/ffi/c_str.rs b/library/alloc/src/ffi/c_str.rs index f143e5578717f..b13af93d06c57 100644 --- a/library/alloc/src/ffi/c_str.rs +++ b/library/alloc/src/ffi/c_str.rs @@ -910,6 +910,19 @@ impl From<&CStr> for Rc { } } +#[cfg(not(no_global_oom_handling))] +#[stable(feature = "more_rc_default_impls", since = "CURRENT_RUSTC_VERSION")] +impl Default for Rc { + /// Creates an empty CStr inside an Rc + /// + /// This may or may not share an allocation with other Rcs on the same thread. + #[inline] + fn default() -> Self { + let c_str: &CStr = Default::default(); + Rc::from(c_str) + } +} + #[cfg(not(test))] #[stable(feature = "default_box_extra", since = "1.17.0")] impl Default for Box { diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 91b83cfe011f2..4ac0c9b15be7a 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -160,6 +160,7 @@ #![feature(tuple_trait)] #![feature(unicode_internals)] #![feature(unsize)] +#![feature(unwrap_infallible)] #![feature(vec_pop_if)] // tidy-alphabetical-end // diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index 45b205356758f..875c24c28e4a9 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -1356,6 +1356,33 @@ impl Rc { ptr } + /// Consumes the `Rc`, returning the wrapped pointer and allocator. + /// + /// To avoid a memory leak the pointer must be converted back to an `Rc` using + /// [`Rc::from_raw_in`]. + /// + /// # Examples + /// + /// ``` + /// #![feature(allocator_api)] + /// use std::rc::Rc; + /// use std::alloc::System; + /// + /// let x = Rc::new_in("hello".to_owned(), System); + /// let (ptr, alloc) = Rc::into_raw_with_allocator(x); + /// assert_eq!(unsafe { &*ptr }, "hello"); + /// let x = unsafe { Rc::from_raw_in(ptr, alloc) }; + /// assert_eq!(&*x, "hello"); + /// ``` + #[unstable(feature = "allocator_api", issue = "32838")] + pub fn into_raw_with_allocator(this: Self) -> (*const T, A) { + let this = mem::ManuallyDrop::new(this); + let ptr = Self::as_ptr(&this); + // Safety: `this` is ManuallyDrop so the allocator will not be double-dropped + let alloc = unsafe { ptr::read(&this.alloc) }; + (ptr, alloc) + } + /// Provides a raw pointer to the data. /// /// The counts are not affected in any way and the `Rc` is not consumed. The pointer is valid @@ -2224,6 +2251,31 @@ impl Default for Rc { } } +#[cfg(not(no_global_oom_handling))] +#[stable(feature = "more_rc_default_impls", since = "CURRENT_RUSTC_VERSION")] +impl Default for Rc { + /// Creates an empty str inside an Rc + /// + /// This may or may not share an allocation with other Rcs on the same thread. + #[inline] + fn default() -> Self { + Rc::from("") + } +} + +#[cfg(not(no_global_oom_handling))] +#[stable(feature = "more_rc_default_impls", since = "CURRENT_RUSTC_VERSION")] +impl Default for Rc<[T]> { + /// Creates an empty `[T]` inside an Rc + /// + /// This may or may not share an allocation with other Rcs on the same thread. + #[inline] + fn default() -> Self { + let arr: [T; 0] = []; + Rc::from(arr) + } +} + #[stable(feature = "rust1", since = "1.0.0")] trait RcEqIdent { fn eq(&self, other: &Rc) -> bool; @@ -2999,11 +3051,11 @@ impl Weak { result } - /// Consumes the `Weak` and turns it into a raw pointer. + /// Consumes the `Weak`, returning the wrapped pointer and allocator. /// /// This converts the weak pointer into a raw pointer, while still preserving the ownership of /// one weak reference (the weak count is not modified by this operation). It can be turned - /// back into the `Weak` with [`from_raw`]. + /// back into the `Weak` with [`from_raw_in`]. /// /// The same restrictions of accessing the target of the pointer as with /// [`as_ptr`] apply. @@ -3011,27 +3063,30 @@ impl Weak { /// # Examples /// /// ``` + /// #![feature(allocator_api)] /// use std::rc::{Rc, Weak}; + /// use std::alloc::System; /// - /// let strong = Rc::new("hello".to_owned()); + /// let strong = Rc::new_in("hello".to_owned(), System); /// let weak = Rc::downgrade(&strong); - /// let raw = weak.into_raw(); + /// let (raw, alloc) = weak.into_raw_with_allocator(); /// /// assert_eq!(1, Rc::weak_count(&strong)); /// assert_eq!("hello", unsafe { &*raw }); /// - /// drop(unsafe { Weak::from_raw(raw) }); + /// drop(unsafe { Weak::from_raw_in(raw, alloc) }); /// assert_eq!(0, Rc::weak_count(&strong)); /// ``` /// - /// [`from_raw`]: Weak::from_raw + /// [`from_raw_in`]: Weak::from_raw_in /// [`as_ptr`]: Weak::as_ptr #[inline] #[unstable(feature = "allocator_api", issue = "32838")] - pub fn into_raw_and_alloc(self) -> (*const T, A) { - let rc = mem::ManuallyDrop::new(self); - let result = rc.as_ptr(); - let alloc = unsafe { ptr::read(&rc.alloc) }; + pub fn into_raw_with_allocator(self) -> (*const T, A) { + let this = mem::ManuallyDrop::new(self); + let result = this.as_ptr(); + // Safety: `this` is ManuallyDrop so the allocator will not be double-dropped + let alloc = unsafe { ptr::read(&this.alloc) }; (result, alloc) } diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index a35c99849b343..7dcaa59dcd1c7 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -1496,6 +1496,34 @@ impl Arc { ptr } + /// Consumes the `Arc`, returning the wrapped pointer and allocator. + /// + /// To avoid a memory leak the pointer must be converted back to an `Arc` using + /// [`Arc::from_raw_in`]. + /// + /// # Examples + /// + /// ``` + /// #![feature(allocator_api)] + /// use std::sync::Arc; + /// use std::alloc::System; + /// + /// let x = Arc::new_in("hello".to_owned(), System); + /// let (ptr, alloc) = Arc::into_raw_with_allocator(x); + /// assert_eq!(unsafe { &*ptr }, "hello"); + /// let x = unsafe { Arc::from_raw_in(ptr, alloc) }; + /// assert_eq!(&*x, "hello"); + /// ``` + #[must_use = "losing the pointer will leak memory"] + #[unstable(feature = "allocator_api", issue = "32838")] + pub fn into_raw_with_allocator(this: Self) -> (*const T, A) { + let this = mem::ManuallyDrop::new(this); + let ptr = Self::as_ptr(&this); + // Safety: `this` is ManuallyDrop so the allocator will not be double-dropped + let alloc = unsafe { ptr::read(&this.alloc) }; + (ptr, alloc) + } + /// Provides a raw pointer to the data. /// /// The counts are not affected in any way and the `Arc` is not consumed. The pointer is valid for @@ -2468,6 +2496,14 @@ unsafe impl<#[may_dangle] T: ?Sized, A: Allocator> Drop for Arc { // [2]: (https://github.com/rust-lang/rust/pull/41714) acquire!(self.inner().strong); + // Make sure we aren't trying to "drop" the shared static for empty slices + // used by Default::default. + debug_assert!( + !ptr::addr_eq(self.ptr.as_ptr(), &STATIC_INNER_SLICE.inner), + "Arcs backed by a static should never reach a strong count of 0. \ + Likely decrement_strong_count or from_raw were called too many times.", + ); + unsafe { self.drop_slow(); } @@ -2740,6 +2776,45 @@ impl Weak { result } + /// Consumes the `Weak`, returning the wrapped pointer and allocator. + /// + /// This converts the weak pointer into a raw pointer, while still preserving the ownership of + /// one weak reference (the weak count is not modified by this operation). It can be turned + /// back into the `Weak` with [`from_raw_in`]. + /// + /// The same restrictions of accessing the target of the pointer as with + /// [`as_ptr`] apply. + /// + /// # Examples + /// + /// ``` + /// #![feature(allocator_api)] + /// use std::sync::{Arc, Weak}; + /// use std::alloc::System; + /// + /// let strong = Arc::new_in("hello".to_owned(), System); + /// let weak = Arc::downgrade(&strong); + /// let (raw, alloc) = weak.into_raw_with_allocator(); + /// + /// assert_eq!(1, Arc::weak_count(&strong)); + /// assert_eq!("hello", unsafe { &*raw }); + /// + /// drop(unsafe { Weak::from_raw_in(raw, alloc) }); + /// assert_eq!(0, Arc::weak_count(&strong)); + /// ``` + /// + /// [`from_raw_in`]: Weak::from_raw_in + /// [`as_ptr`]: Weak::as_ptr + #[must_use = "losing the pointer will leak memory"] + #[unstable(feature = "allocator_api", issue = "32838")] + pub fn into_raw_with_allocator(self) -> (*const T, A) { + let this = mem::ManuallyDrop::new(self); + let result = this.as_ptr(); + // Safety: `this` is ManuallyDrop so the allocator will not be double-dropped + let alloc = unsafe { ptr::read(&this.alloc) }; + (result, alloc) + } + /// Converts a raw pointer previously created by [`into_raw`] back into `Weak` in the provided /// allocator. /// @@ -3059,6 +3134,15 @@ unsafe impl<#[may_dangle] T: ?Sized, A: Allocator> Drop for Weak { if inner.weak.fetch_sub(1, Release) == 1 { acquire!(inner.weak); + + // Make sure we aren't trying to "deallocate" the shared static for empty slices + // used by Default::default. + debug_assert!( + !ptr::addr_eq(self.ptr.as_ptr(), &STATIC_INNER_SLICE.inner), + "Arc/Weaks backed by a static should never be deallocated. \ + Likely decrement_strong_count or from_raw were called too many times.", + ); + unsafe { self.alloc.deallocate(self.ptr.cast(), Layout::for_value_raw(self.ptr.as_ptr())) } @@ -3300,6 +3384,89 @@ impl Default for Arc { } } +/// Struct to hold the static `ArcInner` used for empty `Arc` as +/// returned by `Default::default`. +/// +/// Layout notes: +/// * `repr(align(16))` so we can use it for `[T]` with `align_of::() <= 16`. +/// * `repr(C)` so `inner` is at offset 0 (and thus guaranteed to actually be aligned to 16). +/// * `[u8; 1]` (to be initialized with 0) so it can be used for `Arc`. +#[repr(C, align(16))] +struct SliceArcInnerForStatic { + inner: ArcInner<[u8; 1]>, +} +#[cfg(not(no_global_oom_handling))] +const MAX_STATIC_INNER_SLICE_ALIGNMENT: usize = 16; + +static STATIC_INNER_SLICE: SliceArcInnerForStatic = SliceArcInnerForStatic { + inner: ArcInner { + strong: atomic::AtomicUsize::new(1), + weak: atomic::AtomicUsize::new(1), + data: [0], + }, +}; + +#[cfg(not(no_global_oom_handling))] +#[stable(feature = "more_rc_default_impls", since = "CURRENT_RUSTC_VERSION")] +impl Default for Arc { + /// Creates an empty str inside an Arc + /// + /// This may or may not share an allocation with other Arcs. + #[inline] + fn default() -> Self { + let arc: Arc<[u8]> = Default::default(); + debug_assert!(core::str::from_utf8(&*arc).is_ok()); + let (ptr, alloc) = Arc::into_inner_with_allocator(arc); + unsafe { Arc::from_ptr_in(ptr.as_ptr() as *mut ArcInner, alloc) } + } +} + +#[cfg(not(no_global_oom_handling))] +#[stable(feature = "more_rc_default_impls", since = "CURRENT_RUSTC_VERSION")] +impl Default for Arc { + /// Creates an empty CStr inside an Arc + /// + /// This may or may not share an allocation with other Arcs. + #[inline] + fn default() -> Self { + use core::ffi::CStr; + let inner: NonNull> = NonNull::from(&STATIC_INNER_SLICE.inner); + let inner: NonNull> = + NonNull::new(inner.as_ptr() as *mut ArcInner).unwrap(); + // `this` semantically is the Arc "owned" by the static, so make sure not to drop it. + let this: mem::ManuallyDrop> = + unsafe { mem::ManuallyDrop::new(Arc::from_inner(inner)) }; + (*this).clone() + } +} + +#[cfg(not(no_global_oom_handling))] +#[stable(feature = "more_rc_default_impls", since = "CURRENT_RUSTC_VERSION")] +impl Default for Arc<[T]> { + /// Creates an empty `[T]` inside an Arc + /// + /// This may or may not share an allocation with other Arcs. + #[inline] + fn default() -> Self { + if mem::align_of::() <= MAX_STATIC_INNER_SLICE_ALIGNMENT { + // We take a reference to the whole struct instead of the ArcInner<[u8; 1]> inside it so + // we don't shrink the range of bytes the ptr is allowed to access under Stacked Borrows. + // (Miri complains on 32-bit targets with Arc<[Align16]> otherwise.) + // (Note that NonNull::from(&STATIC_INNER_SLICE.inner) is fine under Tree Borrows.) + let inner: NonNull = NonNull::from(&STATIC_INNER_SLICE); + let inner: NonNull> = inner.cast(); + // `this` semantically is the Arc "owned" by the static, so make sure not to drop it. + let this: mem::ManuallyDrop> = + unsafe { mem::ManuallyDrop::new(Arc::from_inner(inner)) }; + return (*this).clone(); + } + + // If T's alignment is too large for the static, make a new unique allocation. + let arr: [T; 0] = []; + Arc::from(arr) + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl Hash for Arc { fn hash(&self, state: &mut H) { diff --git a/library/alloc/src/vec/in_place_collect.rs b/library/alloc/src/vec/in_place_collect.rs index 88aa1b1b0e081..22541a2b9d82f 100644 --- a/library/alloc/src/vec/in_place_collect.rs +++ b/library/alloc/src/vec/in_place_collect.rs @@ -259,7 +259,8 @@ where inner.cap, inner.buf.cast::(), inner.end as *const T, - inner.cap * mem::size_of::() / mem::size_of::(), + // SAFETY: the multiplication can not overflow, since `inner.cap * size_of::()` is the size of the allocation. + inner.cap.unchecked_mul(mem::size_of::()) / mem::size_of::(), ) }; @@ -374,7 +375,7 @@ where // - it lets us thread the write pointer through its innards and get it back in the end let sink = InPlaceDrop { inner: dst_buf, dst: dst_buf }; let sink = - self.try_fold::<_, _, Result<_, !>>(sink, write_in_place_with_drop(end)).unwrap(); + self.try_fold::<_, _, Result<_, !>>(sink, write_in_place_with_drop(end)).into_ok(); // iteration succeeded, don't drop head unsafe { ManuallyDrop::new(sink).dst.sub_ptr(dst_buf) } } diff --git a/library/alloc/src/vec/into_iter.rs b/library/alloc/src/vec/into_iter.rs index b0226c848332c..c47989337708f 100644 --- a/library/alloc/src/vec/into_iter.rs +++ b/library/alloc/src/vec/into_iter.rs @@ -289,6 +289,60 @@ impl Iterator for IntoIter { }; } + fn fold(mut self, mut accum: B, mut f: F) -> B + where + F: FnMut(B, Self::Item) -> B, + { + if T::IS_ZST { + while self.ptr.as_ptr() != self.end.cast_mut() { + // SAFETY: we just checked that `self.ptr` is in bounds. + let tmp = unsafe { self.ptr.read() }; + // See `next` for why we subtract from `end` here. + self.end = self.end.wrapping_byte_sub(1); + accum = f(accum, tmp); + } + } else { + // SAFETY: `self.end` can only be null if `T` is a ZST. + while self.ptr != non_null!(self.end, T) { + // SAFETY: we just checked that `self.ptr` is in bounds. + let tmp = unsafe { self.ptr.read() }; + // SAFETY: the maximum this can be is `self.end`. + // Increment `self.ptr` first to avoid double dropping in the event of a panic. + self.ptr = unsafe { self.ptr.add(1) }; + accum = f(accum, tmp); + } + } + accum + } + + fn try_fold(&mut self, mut accum: B, mut f: F) -> R + where + Self: Sized, + F: FnMut(B, Self::Item) -> R, + R: core::ops::Try, + { + if T::IS_ZST { + while self.ptr.as_ptr() != self.end.cast_mut() { + // SAFETY: we just checked that `self.ptr` is in bounds. + let tmp = unsafe { self.ptr.read() }; + // See `next` for why we subtract from `end` here. + self.end = self.end.wrapping_byte_sub(1); + accum = f(accum, tmp)?; + } + } else { + // SAFETY: `self.end` can only be null if `T` is a ZST. + while self.ptr != non_null!(self.end, T) { + // SAFETY: we just checked that `self.ptr` is in bounds. + let tmp = unsafe { self.ptr.read() }; + // SAFETY: the maximum this can be is `self.end`. + // Increment `self.ptr` first to avoid double dropping in the event of a panic. + self.ptr = unsafe { self.ptr.add(1) }; + accum = f(accum, tmp)?; + } + } + R::from_output(accum) + } + unsafe fn __iterator_get_unchecked(&mut self, i: usize) -> Self::Item where Self: TrustedRandomAccessNoCoerce, diff --git a/library/core/Cargo.toml b/library/core/Cargo.toml index a02fcf504168a..11d33971f2563 100644 --- a/library/core/Cargo.toml +++ b/library/core/Cargo.toml @@ -31,6 +31,8 @@ rand_xorshift = { version = "0.3.0", default-features = false } [features] # Make panics and failed asserts immediately abort without formatting any message panic_immediate_abort = [] +# Choose algorithms that are optimized for binary size instead of runtime performance +optimize_for_size = [] # Make `RefCell` store additional debugging information, which is printed out when # a borrow error occurs debug_refcell = [] diff --git a/library/core/src/array/iter.rs b/library/core/src/array/iter.rs index e3d2cd2a31fbc..b314d0536a35a 100644 --- a/library/core/src/array/iter.rs +++ b/library/core/src/array/iter.rs @@ -38,7 +38,7 @@ pub struct IntoIter { alive: IndexRange, } -// Note: the `#[rustc_skip_array_during_method_dispatch]` on `trait IntoIterator` +// Note: the `#[rustc_skip_during_method_dispatch(array)]` on `trait IntoIterator` // hides this implementation from explicit `.into_iter()` calls on editions < 2021, // so those calls will still resolve to the slice implementation, by reference. #[stable(feature = "array_into_iter_impl", since = "1.53.0")] diff --git a/library/core/src/fmt/nofloat.rs b/library/core/src/fmt/nofloat.rs index a36e7efcd95c7..6b07236f1da12 100644 --- a/library/core/src/fmt/nofloat.rs +++ b/library/core/src/fmt/nofloat.rs @@ -4,6 +4,7 @@ macro_rules! floating { ($ty:ident) => { #[stable(feature = "rust1", since = "1.0.0")] impl Debug for $ty { + #[inline] fn fmt(&self, _fmt: &mut Formatter<'_>) -> Result { panic!("floating point support is turned off"); } diff --git a/library/core/src/intrinsics/simd.rs b/library/core/src/intrinsics/simd.rs index ceea67901294a..d1be534eaf083 100644 --- a/library/core/src/intrinsics/simd.rs +++ b/library/core/src/intrinsics/simd.rs @@ -569,6 +569,13 @@ extern "rust-intrinsic" { #[rustc_nounwind] pub fn simd_ctlz(x: T) -> T; + /// Count the number of ones in each element. + /// + /// `T` must be a vector of integers. + #[rustc_nounwind] + #[cfg(not(bootstrap))] + pub fn simd_ctpop(x: T) -> T; + /// Count the trailing zeros of each element. /// /// `T` must be a vector of integers. diff --git a/library/core/src/io/borrowed_buf.rs b/library/core/src/io/borrowed_buf.rs index 81371708b51e9..d497da33dd923 100644 --- a/library/core/src/io/borrowed_buf.rs +++ b/library/core/src/io/borrowed_buf.rs @@ -92,14 +92,20 @@ impl<'data> BorrowedBuf<'data> { #[inline] pub fn filled(&self) -> &[u8] { // SAFETY: We only slice the filled part of the buffer, which is always valid - unsafe { MaybeUninit::slice_assume_init_ref(&self.buf[0..self.filled]) } + unsafe { + let buf = self.buf.get_unchecked(..self.filled); + MaybeUninit::slice_assume_init_ref(buf) + } } /// Returns a mutable reference to the filled portion of the buffer. #[inline] pub fn filled_mut(&mut self) -> &mut [u8] { // SAFETY: We only slice the filled part of the buffer, which is always valid - unsafe { MaybeUninit::slice_assume_init_mut(&mut self.buf[0..self.filled]) } + unsafe { + let buf = self.buf.get_unchecked_mut(..self.filled); + MaybeUninit::slice_assume_init_mut(buf) + } } /// Returns a cursor over the unfilled part of the buffer. @@ -205,7 +211,10 @@ impl<'a> BorrowedCursor<'a> { #[inline] pub fn init_ref(&self) -> &[u8] { // SAFETY: We only slice the initialized part of the buffer, which is always valid - unsafe { MaybeUninit::slice_assume_init_ref(&self.buf.buf[self.buf.filled..self.buf.init]) } + unsafe { + let buf = self.buf.buf.get_unchecked(self.buf.filled..self.buf.init); + MaybeUninit::slice_assume_init_ref(buf) + } } /// Returns a mutable reference to the initialized portion of the cursor. @@ -213,7 +222,8 @@ impl<'a> BorrowedCursor<'a> { pub fn init_mut(&mut self) -> &mut [u8] { // SAFETY: We only slice the initialized part of the buffer, which is always valid unsafe { - MaybeUninit::slice_assume_init_mut(&mut self.buf.buf[self.buf.filled..self.buf.init]) + let buf = self.buf.buf.get_unchecked_mut(self.buf.filled..self.buf.init); + MaybeUninit::slice_assume_init_mut(buf) } } @@ -222,7 +232,8 @@ impl<'a> BorrowedCursor<'a> { /// It is safe to uninitialize any of these bytes. #[inline] pub fn uninit_mut(&mut self) -> &mut [MaybeUninit] { - &mut self.buf.buf[self.buf.init..] + // SAFETY: always in bounds + unsafe { self.buf.buf.get_unchecked_mut(self.buf.init..) } } /// Returns a mutable reference to the whole cursor. @@ -232,7 +243,8 @@ impl<'a> BorrowedCursor<'a> { /// The caller must not uninitialize any bytes in the initialized portion of the cursor. #[inline] pub unsafe fn as_mut(&mut self) -> &mut [MaybeUninit] { - &mut self.buf.buf[self.buf.filled..] + // SAFETY: always in bounds + unsafe { self.buf.buf.get_unchecked_mut(self.buf.filled..) } } /// Advance the cursor by asserting that `n` bytes have been filled. diff --git a/library/core/src/iter/traits/collect.rs b/library/core/src/iter/traits/collect.rs index 563781230c023..d9d860c7b6cba 100644 --- a/library/core/src/iter/traits/collect.rs +++ b/library/core/src/iter/traits/collect.rs @@ -268,7 +268,6 @@ where /// } /// ``` #[rustc_diagnostic_item = "IntoIterator"] -#[rustc_skip_array_during_method_dispatch] #[rustc_on_unimplemented( on( _Self = "core::ops::range::RangeTo", @@ -312,6 +311,8 @@ where label = "`{Self}` is not an iterator", message = "`{Self}` is not an iterator" )] +#[cfg_attr(bootstrap, rustc_skip_array_during_method_dispatch)] +#[cfg_attr(not(bootstrap), rustc_skip_during_method_dispatch(array, boxed_slice))] #[stable(feature = "rust1", since = "1.0.0")] pub trait IntoIterator { /// The type of the elements being iterated over. diff --git a/library/core/src/slice/iter.rs b/library/core/src/slice/iter.rs index d7d4f90c1a538..96fc87ab2e9ec 100644 --- a/library/core/src/slice/iter.rs +++ b/library/core/src/slice/iter.rs @@ -16,6 +16,9 @@ use crate::ptr::{self, without_provenance, without_provenance_mut, NonNull}; use super::{from_raw_parts, from_raw_parts_mut}; +#[stable(feature = "boxed_slice_into_iter", since = "CURRENT_RUSTC_VERSION")] +impl !Iterator for [T] {} + #[stable(feature = "rust1", since = "1.0.0")] impl<'a, T> IntoIterator for &'a [T] { type Item = &'a T; diff --git a/library/portable-simd/crates/core_simd/src/ops.rs b/library/portable-simd/crates/core_simd/src/ops.rs index d8e10eeaa1a2a..dd7303a97b197 100644 --- a/library/portable-simd/crates/core_simd/src/ops.rs +++ b/library/portable-simd/crates/core_simd/src/ops.rs @@ -122,7 +122,7 @@ macro_rules! for_base_types { #[inline] #[must_use = "operator returns a new vector without mutating the inputs"] // TODO: only useful for int Div::div, but we hope that this - // will essentially always always get inlined anyway. + // will essentially always get inlined anyway. #[track_caller] fn $call(self, rhs: Self) -> Self::Output { $macro_impl!(self, rhs, $inner, $scalar) diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index 1720fe84fa7c1..5b36867fe2436 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -31,11 +31,7 @@ miniz_oxide = { version = "0.7.0", optional = true, default-features = false } addr2line = { version = "0.21.0", optional = true, default-features = false } [target.'cfg(not(all(windows, target_env = "msvc")))'.dependencies] -libc = { version = "=0.2.153", default-features = false, features = ['rustc-dep-of-std'], public = true } - -# Pin libc (pending https://github.com/rust-lang/rust/pull/124560) -[target.'cfg(all(windows, target_env = "msvc"))'.dependencies] -libc = { version = "=0.2.153", default-features = false } +libc = { version = "0.2.153", default-features = false, features = ['rustc-dep-of-std'], public = true } [target.'cfg(all(not(target_os = "aix"), not(all(windows, target_env = "msvc", not(target_vendor = "uwp")))))'.dependencies] object = { version = "0.32.0", default-features = false, optional = true, features = ['read_core', 'elf', 'macho', 'pe', 'unaligned', 'archive'] } @@ -82,6 +78,8 @@ system-llvm-libunwind = ["unwind/system-llvm-libunwind"] # Make panics and failed asserts immediately abort without formatting any message panic_immediate_abort = ["core/panic_immediate_abort", "alloc/panic_immediate_abort"] +# Choose algorithms that are optimized for binary size instead of runtime performance +optimize_for_size = ["core/optimize_for_size", "alloc/optimize_for_size"] # Enable std_detect default features for stdarch/crates/std_detect: # https://github.com/rust-lang/stdarch/blob/master/crates/std_detect/Cargo.toml diff --git a/library/std/src/io/cursor.rs b/library/std/src/io/cursor.rs index 37492e9efab74..a1a8b2a3505c7 100644 --- a/library/std/src/io/cursor.rs +++ b/library/std/src/io/cursor.rs @@ -328,7 +328,7 @@ where fn read_buf(&mut self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> { let prev_written = cursor.written(); - Read::read_buf(&mut self.fill_buf()?, cursor.reborrow())?; + Read::read_buf(&mut self.remaining_slice(), cursor.reborrow())?; self.pos += (cursor.written() - prev_written) as u64; @@ -352,17 +352,24 @@ where } fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { - let n = buf.len(); - Read::read_exact(&mut self.remaining_slice(), buf)?; - self.pos += n as u64; - Ok(()) + let result = Read::read_exact(&mut self.remaining_slice(), buf); + + match result { + Ok(_) => self.pos += buf.len() as u64, + // The only possible error condition is EOF, so place the cursor at "EOF" + Err(_) => self.pos = self.inner.as_ref().len() as u64, + } + + result } - fn read_buf_exact(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> { - let n = cursor.capacity(); - Read::read_buf_exact(&mut self.remaining_slice(), cursor)?; - self.pos += n as u64; - Ok(()) + fn read_buf_exact(&mut self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> { + let prev_written = cursor.written(); + + let result = Read::read_buf_exact(&mut self.remaining_slice(), cursor.reborrow()); + self.pos += (cursor.written() - prev_written) as u64; + + result } fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { diff --git a/library/std/src/io/impls.rs b/library/std/src/io/impls.rs index 46f04c7cd3957..a8a2e9413e11c 100644 --- a/library/std/src/io/impls.rs +++ b/library/std/src/io/impls.rs @@ -287,6 +287,9 @@ impl Read for &[u8] { #[inline] fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { if buf.len() > self.len() { + // `read_exact` makes no promise about the content of `buf` if it + // fails so don't bother about that. + *self = &self[self.len()..]; return Err(io::Error::READ_EXACT_EOF); } let (a, b) = self.split_at(buf.len()); @@ -307,6 +310,9 @@ impl Read for &[u8] { #[inline] fn read_buf_exact(&mut self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> { if cursor.capacity() > self.len() { + // Append everything we can to the cursor. + cursor.append(*self); + *self = &self[self.len()..]; return Err(io::Error::READ_EXACT_EOF); } let (a, b) = self.split_at(cursor.capacity()); diff --git a/library/std/src/io/stdio.rs b/library/std/src/io/stdio.rs index 07fa9259e0b7f..c8968b74b12d1 100644 --- a/library/std/src/io/stdio.rs +++ b/library/std/src/io/stdio.rs @@ -1161,7 +1161,41 @@ pub trait IsTerminal: crate::sealed::Sealed { /// starting with `msys-` or `cygwin-` and ending in `-pty` will be considered terminals. /// Note that this [may change in the future][changes]. /// + /// # Examples + /// + /// An example of a type for which `IsTerminal` is implemented is [`Stdin`]: + /// + /// ```no_run + /// use std::io::{self, IsTerminal, Write}; + /// + /// fn main() -> io::Result<()> { + /// let stdin = io::stdin(); + /// + /// // Indicate that the user is prompted for input, if this is a terminal. + /// if stdin.is_terminal() { + /// print!("> "); + /// io::stdout().flush()?; + /// } + /// + /// let mut name = String::new(); + /// let _ = stdin.read_line(&mut name)?; + /// + /// println!("Hello {}", name.trim_end()); + /// + /// Ok(()) + /// } + /// ``` + /// + /// The example can be run in two ways: + /// + /// - If you run this example by piping some text to it, e.g. `echo "foo" | path/to/executable` + /// it will print: `Hello foo`. + /// - If you instead run the example interactively by running the executable directly, it will + /// panic with the message "Expected input to be piped to the process". + /// + /// /// [changes]: io#platform-specific-behavior + /// [`Stdin`]: crate::io::Stdin #[stable(feature = "is_terminal", since = "1.70.0")] fn is_terminal(&self) -> bool; } diff --git a/library/std/src/io/tests.rs b/library/std/src/io/tests.rs index 090a091b09a13..a2c1c430863ab 100644 --- a/library/std/src/io/tests.rs +++ b/library/std/src/io/tests.rs @@ -653,6 +653,38 @@ fn test_take_wrong_length() { let _ = reader.read(&mut buffer[..]); } +#[test] +fn slice_read_exact_eof() { + let slice = &b"123456"[..]; + + let mut r = slice; + assert!(r.read_exact(&mut [0; 10]).is_err()); + assert!(r.is_empty()); + + let mut r = slice; + let buf = &mut [0; 10]; + let mut buf = BorrowedBuf::from(buf.as_mut_slice()); + assert!(r.read_buf_exact(buf.unfilled()).is_err()); + assert!(r.is_empty()); + assert_eq!(buf.filled(), b"123456"); +} + +#[test] +fn cursor_read_exact_eof() { + let slice = Cursor::new(b"123456"); + + let mut r = slice.clone(); + assert!(r.read_exact(&mut [0; 10]).is_err()); + assert!(r.is_empty()); + + let mut r = slice; + let buf = &mut [0; 10]; + let mut buf = BorrowedBuf::from(buf.as_mut_slice()); + assert!(r.read_buf_exact(buf.unfilled()).is_err()); + assert!(r.is_empty()); + assert_eq!(buf.filled(), b"123456"); +} + #[bench] fn bench_take_read(b: &mut test::Bencher) { b.iter(|| { diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 27b46b462044a..949c543a26479 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -435,6 +435,7 @@ extern crate alloc as alloc_crate; // so include it here even if it's unused. #[doc(masked)] #[allow(unused_extern_crates)] +#[cfg(not(all(windows, target_env = "msvc")))] extern crate libc; // We always need an unwinder currently for backtraces diff --git a/library/std/src/os/hermit/mod.rs b/library/std/src/os/hermit/mod.rs index 89b1b831912df..02a4b2c3ab5e7 100644 --- a/library/std/src/os/hermit/mod.rs +++ b/library/std/src/os/hermit/mod.rs @@ -2,7 +2,7 @@ #[allow(unused_extern_crates)] #[stable(feature = "rust1", since = "1.0.0")] -pub extern crate hermit_abi as abi; +pub extern crate hermit_abi; pub mod ffi; pub mod io; diff --git a/library/std/src/os/raw/tests.rs b/library/std/src/os/raw/tests.rs index e7bb7d7e73e80..f41a22e1bcce4 100644 --- a/library/std/src/os/raw/tests.rs +++ b/library/std/src/os/raw/tests.rs @@ -1,3 +1,5 @@ +#![cfg(not(all(windows, target_env = "msvc")))] + use crate::any::TypeId; macro_rules! ok { diff --git a/library/std/src/os/windows/process.rs b/library/std/src/os/windows/process.rs index 15ab225012238..9cca27fa5dd5b 100644 --- a/library/std/src/os/windows/process.rs +++ b/library/std/src/os/windows/process.rs @@ -199,14 +199,14 @@ pub trait CommandExt: Sealed { /// Append literal text to the command line without any quoting or escaping. /// - /// This is useful for passing arguments to applications which doesn't follow + /// This is useful for passing arguments to applications that don't follow /// the standard C run-time escaping rules, such as `cmd.exe /c`. /// - /// # Bat files + /// # Batch files /// - /// Note the `cmd /c` command line has slightly different escaping rules then bat files + /// Note the `cmd /c` command line has slightly different escaping rules than batch files /// themselves. If possible, it may be better to write complex arguments to a temporary - /// .bat file, with appropriate escaping, and simply run that using: + /// `.bat` file, with appropriate escaping, and simply run that using: /// /// ```no_run /// # use std::process::Command; @@ -217,7 +217,7 @@ pub trait CommandExt: Sealed { /// /// # Example /// - /// Run a bat script using both trusted and untrusted arguments. + /// Run a batch script using both trusted and untrusted arguments. /// /// ```no_run /// #[cfg(windows)] @@ -241,9 +241,10 @@ pub trait CommandExt: Sealed { /// if !user_name.chars().all(|c| c.is_alphanumeric()) { /// return Err(Error::new(ErrorKind::InvalidInput, "invalid user name")); /// } - /// // now we've checked the user name, let's add that too. - /// cmd_args.push(' '); - /// cmd_args.push_str(&format!("--user {user_name}")); + /// + /// // now we have validated the user name, let's add that too. + /// cmd_args.push_str(" --user "); + /// cmd_args.push_str(user_name); /// /// // call cmd.exe and return the output /// Command::new("cmd.exe") @@ -287,25 +288,37 @@ pub trait CommandExt: Sealed { #[unstable(feature = "windows_process_extensions_async_pipes", issue = "98289")] fn async_pipes(&mut self, always_async: bool) -> &mut process::Command; - /// Sets a raw attribute on the command, providing extended configuration options for Windows processes. + /// Set a raw attribute on the command, providing extended configuration options for Windows + /// processes. + /// + /// This method allows you to specify custom attributes for a child process on Windows systems + /// using raw attribute values. Raw attributes provide extended configurability for process + /// creation, but their usage can be complex and potentially unsafe. /// - /// This method allows you to specify custom attributes for a child process on Windows systems using raw attribute values. - /// Raw attributes provide extended configurability for process creation, but their usage can be complex and potentially unsafe. + /// The `attribute` parameter specifies the raw attribute to be set, while the `value` + /// parameter holds the value associated with that attribute. Please refer to the + /// [`windows-rs` documentation] or the [Win32 API documentation] for detailed information + /// about available attributes and their meanings. /// - /// The `attribute` parameter specifies the raw attribute to be set, while the `value` parameter holds the value associated with that attribute. - /// Please refer to the [`windows-rs`](https://microsoft.github.io/windows-docs-rs/doc/windows/) documentation or the [`Win32 API documentation`](https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-updateprocthreadattribute) for detailed information about available attributes and their meanings. + /// [`windows-rs` documentation]: https://microsoft.github.io/windows-docs-rs/doc/windows/ + /// [Win32 API documentation]: https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-updateprocthreadattribute /// /// # Note /// /// The maximum number of raw attributes is the value of [`u32::MAX`]. - /// If this limit is exceeded, the call to [`process::Command::spawn`] will return an `Error` indicating that the maximum number of attributes has been exceeded. + /// If this limit is exceeded, the call to [`process::Command::spawn`] will return an `Error` + /// indicating that the maximum number of attributes has been exceeded. + /// /// # Safety /// - /// The usage of raw attributes is potentially unsafe and should be done with caution. Incorrect attribute values or improper configuration can lead to unexpected behavior or errors. + /// The usage of raw attributes is potentially unsafe and should be done with caution. + /// Incorrect attribute values or improper configuration can lead to unexpected behavior or + /// errors. /// /// # Example /// - /// The following example demonstrates how to create a child process with a specific parent process ID using a raw attribute. + /// The following example demonstrates how to create a child process with a specific parent + /// process ID using a raw attribute. /// /// ```rust /// #![feature(windows_process_extensions_raw_attribute)] @@ -339,7 +352,9 @@ pub trait CommandExt: Sealed { /// /// # Safety Note /// - /// Remember that improper use of raw attributes can lead to undefined behavior or security vulnerabilities. Always consult the documentation and ensure proper attribute values are used. + /// Remember that improper use of raw attributes can lead to undefined behavior or security + /// vulnerabilities. Always consult the documentation and ensure proper attribute values are + /// used. #[unstable(feature = "windows_process_extensions_raw_attribute", issue = "114854")] unsafe fn raw_attribute( &mut self, diff --git a/library/std/src/path.rs b/library/std/src/path.rs index 79d800ff0729b..f835b69f0cfb5 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -3323,7 +3323,7 @@ impl Error for StripPrefixError { /// /// # Examples /// -/// ## Posix paths +/// ## POSIX paths /// /// ``` /// # #[cfg(unix)] @@ -3369,9 +3369,12 @@ impl Error for StripPrefixError { /// ``` /// /// For verbatim paths this will simply return the path as given. For other -/// paths this is currently equivalent to calling [`GetFullPathNameW`][windows-path] -/// This may change in the future. +/// paths this is currently equivalent to calling +/// [`GetFullPathNameW`][windows-path]. /// +/// Note that this [may change in the future][changes]. +/// +/// [changes]: io#platform-specific-behavior /// [posix-semantics]: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_13 /// [windows-path]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getfullpathnamew #[stable(feature = "absolute_path", since = "1.79.0")] diff --git a/library/std/src/process.rs b/library/std/src/process.rs index d1848224251af..c926c89f7a97f 100644 --- a/library/std/src/process.rs +++ b/library/std/src/process.rs @@ -90,8 +90,8 @@ //! //! # Windows argument splitting //! -//! On Unix systems arguments are passed to a new process as an array of strings -//! but on Windows arguments are passed as a single commandline string and it's +//! On Unix systems arguments are passed to a new process as an array of strings, +//! but on Windows arguments are passed as a single commandline string and it is //! up to the child process to parse it into an array. Therefore the parent and //! child processes must agree on how the commandline string is encoded. //! @@ -107,26 +107,26 @@ //! * Use [`raw_arg`] to build a custom commandline. This bypasses the escaping //! rules used by [`arg`] so should be used with due caution. //! -//! `cmd.exe` and `.bat` use non-standard argument parsing and are especially +//! `cmd.exe` and `.bat` files use non-standard argument parsing and are especially //! vulnerable to malicious input as they may be used to run arbitrary shell //! commands. Untrusted arguments should be restricted as much as possible. //! For examples on handling this see [`raw_arg`]. //! -//! ### Bat file special handling +//! ### Batch file special handling //! //! On Windows, `Command` uses the Windows API function [`CreateProcessW`] to -//! spawn new processes. An undocumented feature of this function is that, +//! spawn new processes. An undocumented feature of this function is that //! when given a `.bat` file as the application to run, it will automatically -//! convert that into running `cmd.exe /c` with the bat file as the next argument. +//! convert that into running `cmd.exe /c` with the batch file as the next argument. //! //! For historical reasons Rust currently preserves this behaviour when using //! [`Command::new`], and escapes the arguments according to `cmd.exe` rules. //! Due to the complexity of `cmd.exe` argument handling, it might not be -//! possible to safely escape some special chars, and using them will result +//! possible to safely escape some special characters, and using them will result //! in an error being returned at process spawn. The set of unescapeable -//! special chars might change between releases. +//! special characters might change between releases. //! -//! Also note that running `.bat` scripts in this way may be removed in the +//! Also note that running batch scripts in this way may be removed in the //! future and so should not be relied upon. //! //! [`spawn`]: Command::spawn @@ -659,16 +659,19 @@ impl Command { /// /// Note that the argument is not passed through a shell, but given /// literally to the program. This means that shell syntax like quotes, - /// escaped characters, word splitting, glob patterns, variable substitution, etc. - /// have no effect. + /// escaped characters, word splitting, glob patterns, variable substitution, + /// etc. have no effect. /// ///
/// - /// On Windows use caution with untrusted inputs. Most applications use the - /// standard convention for decoding arguments passed to them. These are safe to use with `arg`. - /// However some applications, such as `cmd.exe` and `.bat` files, use a non-standard way of decoding arguments - /// and are therefore vulnerable to malicious input. - /// In the case of `cmd.exe` this is especially important because a malicious argument can potentially run arbitrary shell commands. + /// On Windows, use caution with untrusted inputs. Most applications use the + /// standard convention for decoding arguments passed to them. These are safe to + /// use with `arg`. However, some applications such as `cmd.exe` and `.bat` files + /// use a non-standard way of decoding arguments. They are therefore vulnerable + /// to malicious input. + /// + /// In the case of `cmd.exe` this is especially important because a malicious + /// argument can potentially run arbitrary shell commands. /// /// See [Windows argument splitting][windows-args] for more details /// or [`raw_arg`] for manually implementing non-standard argument encoding. @@ -710,11 +713,14 @@ impl Command { /// ///
/// - /// On Windows use caution with untrusted inputs. Most applications use the - /// standard convention for decoding arguments passed to them. These are safe to use with `args`. - /// However some applications, such as `cmd.exe` and `.bat` files, use a non-standard way of decoding arguments - /// and are therefore vulnerable to malicious input. - /// In the case of `cmd.exe` this is especially important because a malicious argument can potentially run arbitrary shell commands. + /// On Windows, use caution with untrusted inputs. Most applications use the + /// standard convention for decoding arguments passed to them. These are safe to + /// use with `arg`. However, some applications such as `cmd.exe` and `.bat` files + /// use a non-standard way of decoding arguments. They are therefore vulnerable + /// to malicious input. + /// + /// In the case of `cmd.exe` this is especially important because a malicious + /// argument can potentially run arbitrary shell commands. /// /// See [Windows argument splitting][windows-args] for more details /// or [`raw_arg`] for manually implementing non-standard argument encoding. diff --git a/library/std/src/sys/pal/hermit/alloc.rs b/library/std/src/sys/pal/hermit/alloc.rs index de550987a4357..2cd0db909403b 100644 --- a/library/std/src/sys/pal/hermit/alloc.rs +++ b/library/std/src/sys/pal/hermit/alloc.rs @@ -1,4 +1,4 @@ -use super::abi; +use super::hermit_abi; use crate::alloc::{GlobalAlloc, Layout, System}; use crate::ptr; @@ -6,11 +6,11 @@ use crate::ptr; unsafe impl GlobalAlloc for System { #[inline] unsafe fn alloc(&self, layout: Layout) -> *mut u8 { - abi::malloc(layout.size(), layout.align()) + hermit_abi::malloc(layout.size(), layout.align()) } unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { - let addr = abi::malloc(layout.size(), layout.align()); + let addr = hermit_abi::malloc(layout.size(), layout.align()); if !addr.is_null() { ptr::write_bytes(addr, 0x00, layout.size()); @@ -21,11 +21,11 @@ unsafe impl GlobalAlloc for System { #[inline] unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { - abi::free(ptr, layout.size(), layout.align()) + hermit_abi::free(ptr, layout.size(), layout.align()) } #[inline] unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { - abi::realloc(ptr, layout.size(), layout.align(), new_size) + hermit_abi::realloc(ptr, layout.size(), layout.align(), new_size) } } diff --git a/library/std/src/sys/pal/hermit/fd.rs b/library/std/src/sys/pal/hermit/fd.rs index 962577bb1ed83..d7dab08cfbd57 100644 --- a/library/std/src/sys/pal/hermit/fd.rs +++ b/library/std/src/sys/pal/hermit/fd.rs @@ -1,6 +1,6 @@ #![unstable(reason = "not public", issue = "none", feature = "fd")] -use super::abi; +use super::hermit_abi; use crate::io::{self, Read}; use crate::os::hermit::io::{FromRawFd, OwnedFd, RawFd}; use crate::sys::cvt; @@ -16,7 +16,8 @@ pub struct FileDesc { impl FileDesc { pub fn read(&self, buf: &mut [u8]) -> io::Result { - let result = cvt(unsafe { abi::read(self.fd.as_raw_fd(), buf.as_mut_ptr(), buf.len()) })?; + let result = + cvt(unsafe { hermit_abi::read(self.fd.as_raw_fd(), buf.as_mut_ptr(), buf.len()) })?; Ok(result as usize) } @@ -26,7 +27,8 @@ impl FileDesc { } pub fn write(&self, buf: &[u8]) -> io::Result { - let result = cvt(unsafe { abi::write(self.fd.as_raw_fd(), buf.as_ptr(), buf.len()) })?; + let result = + cvt(unsafe { hermit_abi::write(self.fd.as_raw_fd(), buf.as_ptr(), buf.len()) })?; Ok(result as usize) } @@ -49,8 +51,8 @@ impl FileDesc { unsupported() } - pub fn fstat(&self, stat: *mut abi::stat) -> io::Result<()> { - cvt(unsafe { abi::fstat(self.fd.as_raw_fd(), stat) })?; + pub fn fstat(&self, stat: *mut hermit_abi::stat) -> io::Result<()> { + cvt(unsafe { hermit_abi::fstat(self.fd.as_raw_fd(), stat) })?; Ok(()) } } diff --git a/library/std/src/sys/pal/hermit/fs.rs b/library/std/src/sys/pal/hermit/fs.rs index 6519cc22f1f6f..a4a16e6e86b0c 100644 --- a/library/std/src/sys/pal/hermit/fs.rs +++ b/library/std/src/sys/pal/hermit/fs.rs @@ -1,8 +1,8 @@ -use super::abi::{ +use super::fd::FileDesc; +use super::hermit_abi::{ self, dirent64, stat as stat_struct, DT_DIR, DT_LNK, DT_REG, DT_UNKNOWN, O_APPEND, O_CREAT, O_EXCL, O_RDONLY, O_RDWR, O_TRUNC, O_WRONLY, S_IFDIR, S_IFLNK, S_IFMT, S_IFREG, }; -use super::fd::FileDesc; use crate::ffi::{CStr, OsStr, OsString}; use crate::fmt; use crate::io::{self, Error, ErrorKind}; @@ -47,7 +47,7 @@ impl InnerReadDir { pub struct ReadDir { inner: Arc, - pos: i64, + pos: usize, } impl ReadDir { @@ -197,38 +197,31 @@ impl Iterator for ReadDir { fn next(&mut self) -> Option> { let mut counter: usize = 0; - let mut offset: i64 = 0; + let mut offset: usize = 0; // loop over all directory entries and search the entry for the current position loop { // leave function, if the loop reaches the of the buffer (with all entries) - if offset >= self.inner.dir.len().try_into().unwrap() { + if offset >= self.inner.dir.len() { return None; } - let dir = unsafe { - &*(self.inner.dir.as_ptr().offset(offset.try_into().unwrap()) as *const dirent64) - }; + let dir = unsafe { &*(self.inner.dir.as_ptr().add(offset) as *const dirent64) }; - if counter == self.pos.try_into().unwrap() { + if counter == self.pos { self.pos += 1; // After dirent64, the file name is stored. d_reclen represents the length of the dirent64 // plus the length of the file name. Consequently, file name has a size of d_reclen minus // the size of dirent64. The file name is always a C string and terminated by `\0`. // Consequently, we are able to ignore the last byte. - let name_bytes = unsafe { - core::slice::from_raw_parts( - &dir.d_name as *const _ as *const u8, - dir.d_reclen as usize - core::mem::size_of::() - 1, - ) - .to_vec() - }; + let name_bytes = + unsafe { CStr::from_ptr(&dir.d_name as *const _ as *const i8).to_bytes() }; let entry = DirEntry { root: self.inner.root.clone(), ino: dir.d_ino, type_: dir.d_type as u32, - name: OsString::from_vec(name_bytes), + name: OsString::from_vec(name_bytes.to_vec()), }; return Some(Ok(entry)); @@ -237,7 +230,7 @@ impl Iterator for ReadDir { counter += 1; // move to the next dirent64, which is directly stored after the previous one - offset = offset + dir.d_off; + offset = offset + usize::from(dir.d_reclen); } } } @@ -365,7 +358,7 @@ impl File { mode = 0; } - let fd = unsafe { cvt(abi::open(path.as_ptr(), flags, mode))? }; + let fd = unsafe { cvt(hermit_abi::open(path.as_ptr(), flags, mode))? }; Ok(File(unsafe { FileDesc::from_raw_fd(fd as i32) })) } @@ -446,7 +439,7 @@ impl DirBuilder { pub fn mkdir(&self, path: &Path) -> io::Result<()> { run_path_with_cstr(path, &|path| { - cvt(unsafe { abi::mkdir(path.as_ptr(), self.mode) }).map(|_| ()) + cvt(unsafe { hermit_abi::mkdir(path.as_ptr(), self.mode) }).map(|_| ()) }) } @@ -508,7 +501,8 @@ impl FromRawFd for File { } pub fn readdir(path: &Path) -> io::Result { - let fd_raw = run_path_with_cstr(path, &|path| cvt(unsafe { abi::opendir(path.as_ptr()) }))?; + let fd_raw = + run_path_with_cstr(path, &|path| cvt(unsafe { hermit_abi::opendir(path.as_ptr()) }))?; let fd = unsafe { FileDesc::from_raw_fd(fd_raw as i32) }; let root = path.to_path_buf(); @@ -519,8 +513,9 @@ pub fn readdir(path: &Path) -> io::Result { // reserve memory to receive all directory entries vec.resize(sz, 0); - let readlen = - unsafe { abi::getdents64(fd.as_raw_fd(), vec.as_mut_ptr() as *mut dirent64, sz) }; + let readlen = unsafe { + hermit_abi::getdents64(fd.as_raw_fd(), vec.as_mut_ptr() as *mut dirent64, sz) + }; if readlen > 0 { // shrink down to the minimal size vec.resize(readlen.try_into().unwrap(), 0); @@ -529,7 +524,7 @@ pub fn readdir(path: &Path) -> io::Result { // if the buffer is too small, getdents64 returns EINVAL // otherwise, getdents64 returns an error number - if readlen != (-abi::errno::EINVAL).into() { + if readlen != (-hermit_abi::errno::EINVAL).into() { return Err(Error::from_raw_os_error(readlen.try_into().unwrap())); } @@ -547,7 +542,7 @@ pub fn readdir(path: &Path) -> io::Result { } pub fn unlink(path: &Path) -> io::Result<()> { - run_path_with_cstr(path, &|path| cvt(unsafe { abi::unlink(path.as_ptr()) }).map(|_| ())) + run_path_with_cstr(path, &|path| cvt(unsafe { hermit_abi::unlink(path.as_ptr()) }).map(|_| ())) } pub fn rename(_old: &Path, _new: &Path) -> io::Result<()> { @@ -559,7 +554,7 @@ pub fn set_perm(_p: &Path, _perm: FilePermissions) -> io::Result<()> { } pub fn rmdir(path: &Path) -> io::Result<()> { - run_path_with_cstr(path, &|path| cvt(unsafe { abi::rmdir(path.as_ptr()) }).map(|_| ())) + run_path_with_cstr(path, &|path| cvt(unsafe { hermit_abi::rmdir(path.as_ptr()) }).map(|_| ())) } pub fn remove_dir_all(_path: &Path) -> io::Result<()> { @@ -581,7 +576,7 @@ pub fn link(_original: &Path, _link: &Path) -> io::Result<()> { pub fn stat(path: &Path) -> io::Result { run_path_with_cstr(path, &|path| { let mut stat_val: stat_struct = unsafe { mem::zeroed() }; - cvt(unsafe { abi::stat(path.as_ptr(), &mut stat_val) })?; + cvt(unsafe { hermit_abi::stat(path.as_ptr(), &mut stat_val) })?; Ok(FileAttr::from_stat(stat_val)) }) } @@ -589,7 +584,7 @@ pub fn stat(path: &Path) -> io::Result { pub fn lstat(path: &Path) -> io::Result { run_path_with_cstr(path, &|path| { let mut stat_val: stat_struct = unsafe { mem::zeroed() }; - cvt(unsafe { abi::lstat(path.as_ptr(), &mut stat_val) })?; + cvt(unsafe { hermit_abi::lstat(path.as_ptr(), &mut stat_val) })?; Ok(FileAttr::from_stat(stat_val)) }) } diff --git a/library/std/src/sys/pal/hermit/futex.rs b/library/std/src/sys/pal/hermit/futex.rs index 427d8ff6f2e4d..571b288565871 100644 --- a/library/std/src/sys/pal/hermit/futex.rs +++ b/library/std/src/sys/pal/hermit/futex.rs @@ -1,4 +1,4 @@ -use super::abi; +use super::hermit_abi; use crate::ptr::null; use crate::sync::atomic::AtomicU32; use crate::time::Duration; @@ -8,32 +8,32 @@ pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option) - // // Overflows are rounded up to an infinite timeout (None). let timespec = timeout.and_then(|dur| { - Some(abi::timespec { + Some(hermit_abi::timespec { tv_sec: dur.as_secs().try_into().ok()?, tv_nsec: dur.subsec_nanos().into(), }) }); let r = unsafe { - abi::futex_wait( + hermit_abi::futex_wait( futex.as_ptr(), expected, - timespec.as_ref().map_or(null(), |t| t as *const abi::timespec), - abi::FUTEX_RELATIVE_TIMEOUT, + timespec.as_ref().map_or(null(), |t| t as *const hermit_abi::timespec), + hermit_abi::FUTEX_RELATIVE_TIMEOUT, ) }; - r != -abi::errno::ETIMEDOUT + r != -hermit_abi::errno::ETIMEDOUT } #[inline] pub fn futex_wake(futex: &AtomicU32) -> bool { - unsafe { abi::futex_wake(futex.as_ptr(), 1) > 0 } + unsafe { hermit_abi::futex_wake(futex.as_ptr(), 1) > 0 } } #[inline] pub fn futex_wake_all(futex: &AtomicU32) { unsafe { - abi::futex_wake(futex.as_ptr(), i32::MAX); + hermit_abi::futex_wake(futex.as_ptr(), i32::MAX); } } diff --git a/library/std/src/sys/pal/hermit/mod.rs b/library/std/src/sys/pal/hermit/mod.rs index 910935541bd33..a64323a3a296e 100644 --- a/library/std/src/sys/pal/hermit/mod.rs +++ b/library/std/src/sys/pal/hermit/mod.rs @@ -39,7 +39,7 @@ pub mod thread_local_key; pub mod time; use crate::io::ErrorKind; -use crate::os::hermit::abi; +use crate::os::hermit::hermit_abi; pub fn unsupported() -> crate::io::Result { Err(unsupported_err()) @@ -54,7 +54,7 @@ pub fn unsupported_err() -> crate::io::Error { pub fn abort_internal() -> ! { unsafe { - abi::abort(); + hermit_abi::abort(); } } @@ -62,7 +62,7 @@ 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 { abi::read_entropy(slice.as_mut_ptr(), slice.len(), 0) }) + 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..]; } @@ -109,31 +109,31 @@ pub unsafe extern "C" fn runtime_entry( let result = main(argc as isize, argv); run_dtors(); - abi::exit(result); + hermit_abi::exit(result); } #[inline] pub(crate) fn is_interrupted(errno: i32) -> bool { - errno == abi::errno::EINTR + errno == hermit_abi::errno::EINTR } pub fn decode_error_kind(errno: i32) -> ErrorKind { match errno { - abi::errno::EACCES => ErrorKind::PermissionDenied, - abi::errno::EADDRINUSE => ErrorKind::AddrInUse, - abi::errno::EADDRNOTAVAIL => ErrorKind::AddrNotAvailable, - abi::errno::EAGAIN => ErrorKind::WouldBlock, - abi::errno::ECONNABORTED => ErrorKind::ConnectionAborted, - abi::errno::ECONNREFUSED => ErrorKind::ConnectionRefused, - abi::errno::ECONNRESET => ErrorKind::ConnectionReset, - abi::errno::EEXIST => ErrorKind::AlreadyExists, - abi::errno::EINTR => ErrorKind::Interrupted, - abi::errno::EINVAL => ErrorKind::InvalidInput, - abi::errno::ENOENT => ErrorKind::NotFound, - abi::errno::ENOTCONN => ErrorKind::NotConnected, - abi::errno::EPERM => ErrorKind::PermissionDenied, - abi::errno::EPIPE => ErrorKind::BrokenPipe, - abi::errno::ETIMEDOUT => ErrorKind::TimedOut, + hermit_abi::errno::EACCES => ErrorKind::PermissionDenied, + hermit_abi::errno::EADDRINUSE => ErrorKind::AddrInUse, + hermit_abi::errno::EADDRNOTAVAIL => ErrorKind::AddrNotAvailable, + hermit_abi::errno::EAGAIN => ErrorKind::WouldBlock, + hermit_abi::errno::ECONNABORTED => ErrorKind::ConnectionAborted, + hermit_abi::errno::ECONNREFUSED => ErrorKind::ConnectionRefused, + hermit_abi::errno::ECONNRESET => ErrorKind::ConnectionReset, + hermit_abi::errno::EEXIST => ErrorKind::AlreadyExists, + hermit_abi::errno::EINTR => ErrorKind::Interrupted, + hermit_abi::errno::EINVAL => ErrorKind::InvalidInput, + hermit_abi::errno::ENOENT => ErrorKind::NotFound, + hermit_abi::errno::ENOTCONN => ErrorKind::NotConnected, + hermit_abi::errno::EPERM => ErrorKind::PermissionDenied, + hermit_abi::errno::EPIPE => ErrorKind::BrokenPipe, + hermit_abi::errno::ETIMEDOUT => ErrorKind::TimedOut, _ => ErrorKind::Uncategorized, } } diff --git a/library/std/src/sys/pal/hermit/net.rs b/library/std/src/sys/pal/hermit/net.rs index 23ac71cb9f29e..00dbca86a4bae 100644 --- a/library/std/src/sys/pal/hermit/net.rs +++ b/library/std/src/sys/pal/hermit/net.rs @@ -175,23 +175,12 @@ impl Socket { } pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - let mut size: isize = 0; - - for i in bufs.iter_mut() { - let ret: isize = - cvt(unsafe { netc::read(self.0.as_raw_fd(), i.as_mut_ptr(), i.len()) })?; - - if ret != 0 { - size += ret; - } - } - - Ok(size.try_into().unwrap()) + crate::io::default_read_vectored(|b| self.read(b), bufs) } #[inline] pub fn is_read_vectored(&self) -> bool { - true + false } fn recv_from_with_flags(&self, buf: &mut [u8], flags: i32) -> io::Result<(usize, SocketAddr)> { @@ -225,17 +214,11 @@ impl Socket { } pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { - let mut size: isize = 0; - - for i in bufs.iter() { - size += cvt(unsafe { netc::write(self.0.as_raw_fd(), i.as_ptr(), i.len()) })?; - } - - Ok(size.try_into().unwrap()) + crate::io::default_write_vectored(|b| self.write(b), bufs) } pub fn is_write_vectored(&self) -> bool { - true + false } pub fn set_timeout(&self, dur: Option, kind: i32) -> io::Result<()> { diff --git a/library/std/src/sys/pal/hermit/os.rs b/library/std/src/sys/pal/hermit/os.rs index 1f9affbf7735d..cc6781238319b 100644 --- a/library/std/src/sys/pal/hermit/os.rs +++ b/library/std/src/sys/pal/hermit/os.rs @@ -1,4 +1,4 @@ -use super::abi; +use super::hermit_abi; use crate::collections::HashMap; use crate::error::Error as StdError; use crate::ffi::{CStr, OsStr, OsString}; @@ -14,11 +14,11 @@ use crate::vec; use core::slice::memchr; pub fn errno() -> i32 { - unsafe { abi::get_errno() } + unsafe { hermit_abi::get_errno() } } pub fn error_string(errno: i32) -> String { - abi::error_string(errno).to_string() + hermit_abi::error_string(errno).to_string() } pub fn getcwd() -> io::Result { @@ -197,10 +197,10 @@ pub fn home_dir() -> Option { pub fn exit(code: i32) -> ! { unsafe { - abi::exit(code); + hermit_abi::exit(code); } } pub fn getpid() -> u32 { - unsafe { abi::getpid() } + unsafe { hermit_abi::getpid() } } diff --git a/library/std/src/sys/pal/hermit/stdio.rs b/library/std/src/sys/pal/hermit/stdio.rs index ac54385e8cec5..777c57b391c89 100644 --- a/library/std/src/sys/pal/hermit/stdio.rs +++ b/library/std/src/sys/pal/hermit/stdio.rs @@ -1,4 +1,4 @@ -use super::abi; +use super::hermit_abi; use crate::io; use crate::io::{IoSlice, IoSliceMut}; @@ -37,7 +37,7 @@ impl io::Write for Stdout { fn write(&mut self, data: &[u8]) -> io::Result { let len; - unsafe { len = abi::write(1, data.as_ptr() as *const u8, data.len()) } + unsafe { len = hermit_abi::write(1, data.as_ptr() as *const u8, data.len()) } if len < 0 { Err(io::const_io_error!(io::ErrorKind::Uncategorized, "Stdout is not able to print")) @@ -49,7 +49,7 @@ impl io::Write for Stdout { fn write_vectored(&mut self, data: &[IoSlice<'_>]) -> io::Result { let len; - unsafe { len = abi::write(1, data.as_ptr() as *const u8, data.len()) } + unsafe { len = hermit_abi::write(1, data.as_ptr() as *const u8, data.len()) } if len < 0 { Err(io::const_io_error!(io::ErrorKind::Uncategorized, "Stdout is not able to print")) @@ -78,7 +78,7 @@ impl io::Write for Stderr { fn write(&mut self, data: &[u8]) -> io::Result { let len; - unsafe { len = abi::write(2, data.as_ptr() as *const u8, data.len()) } + unsafe { len = hermit_abi::write(2, data.as_ptr() as *const u8, data.len()) } if len < 0 { Err(io::const_io_error!(io::ErrorKind::Uncategorized, "Stderr is not able to print")) @@ -90,7 +90,7 @@ impl io::Write for Stderr { fn write_vectored(&mut self, data: &[IoSlice<'_>]) -> io::Result { let len; - unsafe { len = abi::write(2, data.as_ptr() as *const u8, data.len()) } + unsafe { len = hermit_abi::write(2, data.as_ptr() as *const u8, data.len()) } if len < 0 { Err(io::const_io_error!(io::ErrorKind::Uncategorized, "Stderr is not able to print")) diff --git a/library/std/src/sys/pal/hermit/thread.rs b/library/std/src/sys/pal/hermit/thread.rs index 4fe6b12a95b09..b336dcd6860e4 100644 --- a/library/std/src/sys/pal/hermit/thread.rs +++ b/library/std/src/sys/pal/hermit/thread.rs @@ -1,6 +1,6 @@ #![allow(dead_code)] -use super::abi; +use super::hermit_abi; use super::thread_local_dtor::run_dtors; use crate::ffi::CStr; use crate::io; @@ -9,7 +9,7 @@ use crate::num::NonZero; use crate::ptr; use crate::time::Duration; -pub type Tid = abi::Tid; +pub type Tid = hermit_abi::Tid; pub struct Thread { tid: Tid, @@ -27,10 +27,10 @@ impl Thread { core_id: isize, ) -> io::Result { let p = Box::into_raw(Box::new(p)); - let tid = abi::spawn2( + let tid = hermit_abi::spawn2( thread_start, p.expose_provenance(), - abi::Priority::into(abi::NORMAL_PRIO), + hermit_abi::Priority::into(hermit_abi::NORMAL_PRIO), stack, core_id, ); @@ -62,7 +62,7 @@ impl Thread { #[inline] pub fn yield_now() { unsafe { - abi::yield_now(); + hermit_abi::yield_now(); } } @@ -74,13 +74,13 @@ impl Thread { #[inline] pub fn sleep(dur: Duration) { unsafe { - abi::usleep(dur.as_micros() as u64); + hermit_abi::usleep(dur.as_micros() as u64); } } pub fn join(self) { unsafe { - let _ = abi::join(self.tid); + let _ = hermit_abi::join(self.tid); } } @@ -98,5 +98,5 @@ impl Thread { } pub fn available_parallelism() -> io::Result> { - unsafe { Ok(NonZero::new_unchecked(abi::get_processor_count())) } + unsafe { Ok(NonZero::new_unchecked(hermit_abi::get_processor_count())) } } diff --git a/library/std/src/sys/pal/hermit/time.rs b/library/std/src/sys/pal/hermit/time.rs index d6f9e4c1476b0..2bf24462fa825 100644 --- a/library/std/src/sys/pal/hermit/time.rs +++ b/library/std/src/sys/pal/hermit/time.rs @@ -1,8 +1,6 @@ #![allow(dead_code)] -use super::abi; -use super::abi::timespec; -use super::abi::{CLOCK_MONOTONIC, CLOCK_REALTIME, NSEC_PER_SEC}; +use super::hermit_abi::{self, timespec, CLOCK_MONOTONIC, CLOCK_REALTIME, NSEC_PER_SEC}; use crate::cmp::Ordering; use crate::ops::{Add, AddAssign, Sub, SubAssign}; use crate::time::Duration; @@ -106,7 +104,8 @@ pub struct Instant(Timespec); impl Instant { pub fn now() -> Instant { let mut time: Timespec = Timespec::zero(); - let _ = unsafe { abi::clock_gettime(CLOCK_MONOTONIC, core::ptr::addr_of_mut!(time.t)) }; + let _ = + unsafe { hermit_abi::clock_gettime(CLOCK_MONOTONIC, core::ptr::addr_of_mut!(time.t)) }; Instant(time) } @@ -207,7 +206,8 @@ impl SystemTime { pub fn now() -> SystemTime { let mut time: Timespec = Timespec::zero(); - let _ = unsafe { abi::clock_gettime(CLOCK_REALTIME, core::ptr::addr_of_mut!(time.t)) }; + let _ = + unsafe { hermit_abi::clock_gettime(CLOCK_REALTIME, core::ptr::addr_of_mut!(time.t)) }; SystemTime(time) } diff --git a/library/std/src/sys/pal/unix/args.rs b/library/std/src/sys/pal/unix/args.rs index 2a3298e8b4c9d..db2ec73148e38 100644 --- a/library/std/src/sys/pal/unix/args.rs +++ b/library/std/src/sys/pal/unix/args.rs @@ -5,8 +5,9 @@ #![allow(dead_code)] // runtime init functions not used during testing -use crate::ffi::OsString; +use crate::ffi::{CStr, OsString}; use crate::fmt; +use crate::os::unix::ffi::OsStringExt; use crate::vec; /// One-time global initialization. @@ -16,7 +17,46 @@ pub unsafe fn init(argc: isize, argv: *const *const u8) { /// Returns the command line arguments pub fn args() -> Args { - imp::args() + let (argc, argv) = imp::argc_argv(); + + let mut vec = Vec::with_capacity(argc as usize); + + for i in 0..argc { + // SAFETY: `argv` is non-null if `argc` is positive, and it is + // guaranteed to be at least as long as `argc`, so reading from it + // should be safe. + let ptr = unsafe { argv.offset(i).read() }; + + // Some C commandline parsers (e.g. GLib and Qt) are replacing already + // handled arguments in `argv` with `NULL` and move them to the end. + // + // Since they can't directly ensure updates to `argc` as well, this + // means that `argc` might be bigger than the actual number of + // non-`NULL` pointers in `argv` at this point. + // + // To handle this we simply stop iterating at the first `NULL` + // argument. `argv` is also guaranteed to be `NULL`-terminated so any + // non-`NULL` arguments after the first `NULL` can safely be ignored. + if ptr.is_null() { + // NOTE: On Apple platforms, `-[NSProcessInfo arguments]` does not + // stop iterating here, but instead `continue`, always iterating + // up until it reached `argc`. + // + // This difference will only matter in very specific circumstances + // where `argc`/`argv` have been modified, but in unexpected ways, + // so it likely doesn't really matter which option we choose. + // See the following PR for further discussion: + // + break; + } + + // SAFETY: Just checked that the pointer is not NULL, and arguments + // are otherwise guaranteed to be valid C strings. + let cstr = unsafe { CStr::from_ptr(ptr) }; + vec.push(OsStringExt::from_vec(cstr.to_bytes().to_vec())); + } + + Args { iter: vec.into_iter() } } pub struct Args { @@ -75,9 +115,7 @@ impl DoubleEndedIterator for Args { target_os = "hurd", ))] mod imp { - use super::Args; - use crate::ffi::{CStr, OsString}; - use crate::os::unix::prelude::*; + use crate::ffi::c_char; use crate::ptr; use crate::sync::atomic::{AtomicIsize, AtomicPtr, Ordering}; @@ -126,162 +164,78 @@ mod imp { init_wrapper }; - pub fn args() -> Args { - Args { iter: clone().into_iter() } - } - - fn clone() -> Vec { - unsafe { - // Load ARGC and ARGV, which hold the unmodified system-provided - // argc/argv, so we can read the pointed-to memory without atomics - // or synchronization. - // - // If either ARGC or ARGV is still zero or null, then either there - // really are no arguments, or someone is asking for `args()` - // before initialization has completed, and we return an empty - // list. - let argv = ARGV.load(Ordering::Relaxed); - let argc = if argv.is_null() { 0 } else { ARGC.load(Ordering::Relaxed) }; - let mut args = Vec::with_capacity(argc as usize); - for i in 0..argc { - let ptr = *argv.offset(i) as *const libc::c_char; - - // Some C commandline parsers (e.g. GLib and Qt) are replacing already - // handled arguments in `argv` with `NULL` and move them to the end. That - // means that `argc` might be bigger than the actual number of non-`NULL` - // pointers in `argv` at this point. - // - // To handle this we simply stop iterating at the first `NULL` argument. - // - // `argv` is also guaranteed to be `NULL`-terminated so any non-`NULL` arguments - // after the first `NULL` can safely be ignored. - if ptr.is_null() { - break; - } - - let cstr = CStr::from_ptr(ptr); - args.push(OsStringExt::from_vec(cstr.to_bytes().to_vec())); - } - - args - } + pub fn argc_argv() -> (isize, *const *const c_char) { + // Load ARGC and ARGV, which hold the unmodified system-provided + // argc/argv, so we can read the pointed-to memory without atomics or + // synchronization. + // + // If either ARGC or ARGV is still zero or null, then either there + // really are no arguments, or someone is asking for `args()` before + // initialization has completed, and we return an empty list. + let argv = ARGV.load(Ordering::Relaxed); + let argc = if argv.is_null() { 0 } else { ARGC.load(Ordering::Relaxed) }; + + // Cast from `*mut *const u8` to `*const *const c_char` + (argc, argv.cast()) } } +// Use `_NSGetArgc` and `_NSGetArgv` on Apple platforms. +// +// Even though these have underscores in their names, they've been available +// since since the first versions of both macOS and iOS, and are declared in +// the header `crt_externs.h`. +// +// NOTE: This header was added to the iOS 13.0 SDK, which has been the source +// of a great deal of confusion in the past about the availability of these +// APIs. +// +// NOTE(madsmtm): This has not strictly been verified to not cause App Store +// rejections; if this is found to be the case, the previous implementation +// of this used `[[NSProcessInfo processInfo] arguments]`. #[cfg(target_vendor = "apple")] mod imp { - use super::Args; - use crate::ffi::CStr; + use crate::ffi::{c_char, c_int}; - pub unsafe fn init(_argc: isize, _argv: *const *const u8) {} - - #[cfg(target_os = "macos")] - pub fn args() -> Args { - use crate::os::unix::prelude::*; - extern "C" { - // These functions are in crt_externs.h. - fn _NSGetArgc() -> *mut libc::c_int; - fn _NSGetArgv() -> *mut *mut *mut libc::c_char; - } - - let vec = unsafe { - let (argc, argv) = - (*_NSGetArgc() as isize, *_NSGetArgv() as *const *const libc::c_char); - (0..argc as isize) - .map(|i| { - let bytes = CStr::from_ptr(*argv.offset(i)).to_bytes().to_vec(); - OsStringExt::from_vec(bytes) - }) - .collect::>() - }; - Args { iter: vec.into_iter() } + pub unsafe fn init(_argc: isize, _argv: *const *const u8) { + // No need to initialize anything in here, `libdyld.dylib` has already + // done the work for us. } - // As _NSGetArgc and _NSGetArgv aren't mentioned in iOS docs - // and use underscores in their names - they're most probably - // are considered private and therefore should be avoided. - // Here is another way to get arguments using the Objective-C - // runtime. - // - // In general it looks like: - // res = Vec::new() - // let args = [[NSProcessInfo processInfo] arguments] - // for i in (0..[args count]) - // res.push([args objectAtIndex:i]) - // res - #[cfg(not(target_os = "macos"))] - pub fn args() -> Args { - use crate::ffi::{c_char, c_void, OsString}; - use crate::mem; - use crate::str; - - type Sel = *const c_void; - type NsId = *const c_void; - type NSUInteger = usize; - + pub fn argc_argv() -> (isize, *const *const c_char) { extern "C" { - fn sel_registerName(name: *const c_char) -> Sel; - fn objc_getClass(class_name: *const c_char) -> NsId; - - // This must be transmuted to an appropriate function pointer type before being called. - fn objc_msgSend(); - } - - const MSG_SEND_PTR: unsafe extern "C" fn() = objc_msgSend; - const MSG_SEND_NO_ARGUMENTS_RETURN_PTR: unsafe extern "C" fn(NsId, Sel) -> *const c_void = - unsafe { mem::transmute(MSG_SEND_PTR) }; - const MSG_SEND_NO_ARGUMENTS_RETURN_NSUINTEGER: unsafe extern "C" fn( - NsId, - Sel, - ) -> NSUInteger = unsafe { mem::transmute(MSG_SEND_PTR) }; - const MSG_SEND_NSINTEGER_ARGUMENT_RETURN_PTR: unsafe extern "C" fn( - NsId, - Sel, - NSUInteger, - ) - -> *const c_void = unsafe { mem::transmute(MSG_SEND_PTR) }; - - let mut res = Vec::new(); - - unsafe { - let process_info_sel = sel_registerName(c"processInfo".as_ptr()); - let arguments_sel = sel_registerName(c"arguments".as_ptr()); - let count_sel = sel_registerName(c"count".as_ptr()); - let object_at_index_sel = sel_registerName(c"objectAtIndex:".as_ptr()); - let utf8string_sel = sel_registerName(c"UTF8String".as_ptr()); - - let klass = objc_getClass(c"NSProcessInfo".as_ptr()); - // `+[NSProcessInfo processInfo]` returns an object with +0 retain count, so no need to manually `retain/release`. - let info = MSG_SEND_NO_ARGUMENTS_RETURN_PTR(klass, process_info_sel); - - // `-[NSProcessInfo arguments]` returns an object with +0 retain count, so no need to manually `retain/release`. - let args = MSG_SEND_NO_ARGUMENTS_RETURN_PTR(info, arguments_sel); - - let cnt = MSG_SEND_NO_ARGUMENTS_RETURN_NSUINTEGER(args, count_sel); - for i in 0..cnt { - // `-[NSArray objectAtIndex:]` returns an object whose lifetime is tied to the array, so no need to manually `retain/release`. - let ns_string = - MSG_SEND_NSINTEGER_ARGUMENT_RETURN_PTR(args, object_at_index_sel, i); - // The lifetime of this pointer is tied to the NSString, as well as the current autorelease pool, which is why we heap-allocate the string below. - let utf_c_str: *const c_char = - MSG_SEND_NO_ARGUMENTS_RETURN_PTR(ns_string, utf8string_sel).cast(); - let bytes = CStr::from_ptr(utf_c_str).to_bytes(); - res.push(OsString::from(str::from_utf8(bytes).unwrap())) - } + // These functions are in crt_externs.h. + fn _NSGetArgc() -> *mut c_int; + fn _NSGetArgv() -> *mut *mut *mut c_char; } - Args { iter: res.into_iter() } + // SAFETY: The returned pointer points to a static initialized early + // in the program lifetime by `libdyld.dylib`, and as such is always + // valid. + // + // NOTE: Similar to `_NSGetEnviron`, there technically isn't anything + // protecting us against concurrent modifications to this, and there + // doesn't exist a lock that we can take. Instead, it is generally + // expected that it's only modified in `main` / before other code + // runs, so reading this here should be fine. + let argc = unsafe { _NSGetArgc().read() }; + // SAFETY: Same as above. + let argv = unsafe { _NSGetArgv().read() }; + + // Cast from `*mut *mut c_char` to `*const *const c_char` + (argc as isize, argv.cast()) } } #[cfg(any(target_os = "espidf", target_os = "vita"))] mod imp { - use super::Args; + use crate::ffi::c_char; + use crate::ptr; #[inline(always)] pub unsafe fn init(_argc: isize, _argv: *const *const u8) {} - pub fn args() -> Args { - Args { iter: Vec::new().into_iter() } + pub fn argc_argv() -> (isize, *const *const c_char) { + (0, ptr::null()) } } diff --git a/library/std/src/sys/pal/unix/mod.rs b/library/std/src/sys/pal/unix/mod.rs index 21f233e22620c..735ed96bc7b16 100644 --- a/library/std/src/sys/pal/unix/mod.rs +++ b/library/std/src/sys/pal/unix/mod.rs @@ -399,14 +399,13 @@ cfg_if::cfg_if! { // Use libumem for the (malloc-compatible) allocator #[link(name = "umem")] extern "C" {} - } else if #[cfg(target_os = "macos")] { + } else if #[cfg(target_vendor = "apple")] { + // Link to `libSystem.dylib`. + // + // Don't get confused by the presence of `System.framework`, + // it is a deprecated wrapper over the dynamic library. #[link(name = "System")] extern "C" {} - } else if #[cfg(all(target_vendor = "apple", not(target_os = "macos")))] { - #[link(name = "System")] - #[link(name = "objc")] - #[link(name = "Foundation", kind = "framework")] - extern "C" {} } else if #[cfg(target_os = "fuchsia")] { #[link(name = "zircon")] #[link(name = "fdio")] diff --git a/library/std/src/sys/pal/unix/os.rs b/library/std/src/sys/pal/unix/os.rs index 3a281525f8df3..8afc49f52274c 100644 --- a/library/std/src/sys/pal/unix/os.rs +++ b/library/std/src/sys/pal/unix/os.rs @@ -576,12 +576,36 @@ impl Iterator for Env { } } -#[cfg(target_os = "macos")] +// Use `_NSGetEnviron` on Apple platforms. +// +// `_NSGetEnviron` is the documented alternative (see `man environ`), and has +// been available since the first versions of both macOS and iOS. +// +// Nowadays, specifically since macOS 10.8, `environ` has been exposed through +// `libdyld.dylib`, which is linked via. `libSystem.dylib`: +// +// +// So in the end, it likely doesn't really matter which option we use, but the +// performance cost of using `_NSGetEnviron` is extremely miniscule, and it +// might be ever so slightly more supported, so let's just use that. +// +// NOTE: The header where this is defined (`crt_externs.h`) was added to the +// iOS 13.0 SDK, which has been the source of a great deal of confusion in the +// past about the availability of this API. +// +// NOTE(madsmtm): Neither this nor using `environ` has been verified to not +// cause App Store rejections; if this is found to be the case, an alternative +// implementation of this is possible using `[NSProcessInfo environment]` +// - which internally uses `_NSGetEnviron` and a system-wide lock on the +// environment variables to protect against `setenv`, so using that might be +// desirable anyhow? Though it also means that we have to link to Foundation. +#[cfg(target_vendor = "apple")] pub unsafe fn environ() -> *mut *const *const c_char { libc::_NSGetEnviron() as *mut *const *const c_char } -#[cfg(not(target_os = "macos"))] +// Use the `environ` static which is part of POSIX. +#[cfg(not(target_vendor = "apple"))] pub unsafe fn environ() -> *mut *const *const c_char { extern "C" { static mut environ: *const *const c_char; diff --git a/library/sysroot/Cargo.toml b/library/sysroot/Cargo.toml index 6ff24a8db59c3..1ddacd92e6b94 100644 --- a/library/sysroot/Cargo.toml +++ b/library/sysroot/Cargo.toml @@ -22,6 +22,7 @@ llvm-libunwind = ["std/llvm-libunwind"] system-llvm-libunwind = ["std/system-llvm-libunwind"] panic-unwind = ["std/panic_unwind"] panic_immediate_abort = ["std/panic_immediate_abort"] +optimize_for_size = ["std/optimize_for_size"] profiler = ["std/profiler"] std_detect_file_io = ["std/std_detect_file_io"] std_detect_dlsym_getauxval = ["std/std_detect_dlsym_getauxval"] diff --git a/rustfmt.toml b/rustfmt.toml index 850d01ea7cb81..ef56059feb1c7 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -40,6 +40,7 @@ ignore = [ "src/tools/clippy", "src/tools/miri", "src/tools/rust-analyzer", + "src/tools/rustc-perf", "src/tools/rustfmt", # these are ignored by a standard cargo fmt run diff --git a/src/bootstrap/Cargo.lock b/src/bootstrap/Cargo.lock index 4a5d768961ff9..127699b86411f 100644 --- a/src/bootstrap/Cargo.lock +++ b/src/bootstrap/Cargo.lock @@ -341,9 +341,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.151" +version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] name = "linux-raw-sys" diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index 1f006e1453f4b..91039d0c8dcc1 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -1010,6 +1010,9 @@ impl Step for PlainSourceTarball { if builder.rust_info().is_managed_git_subrepository() || builder.rust_info().is_from_tarball() { + // FIXME: This code looks _very_ similar to what we have in `src/core/build_steps/vendor.rs` + // perhaps it should be removed in favor of making `dist` perform the `vendor` step? + // Ensure we have all submodules from src and other directories checked out. for submodule in builder.get_all_submodules() { builder.update_submodule(Path::new(submodule)); @@ -1029,6 +1032,10 @@ impl Step for PlainSourceTarball { .arg(builder.src.join("./compiler/rustc_codegen_gcc/Cargo.toml")) .arg("--sync") .arg(builder.src.join("./src/bootstrap/Cargo.toml")) + .arg("--sync") + .arg(builder.src.join("./src/tools/opt-dist/Cargo.toml")) + .arg("--sync") + .arg(builder.src.join("./src/tools/rustc-perf/Cargo.toml")) // Will read the libstd Cargo.toml // which uses the unstable `public-dependency` feature. .env("RUSTC_BOOTSTRAP", "1") diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs index 3af1a05caa829..8ca7af2febee4 100644 --- a/src/bootstrap/src/core/build_steps/llvm.rs +++ b/src/bootstrap/src/core/build_steps/llvm.rs @@ -508,7 +508,7 @@ impl Step for Llvm { cfg.define("LLVM_VERSION_SUFFIX", suffix); } - configure_cmake(builder, target, &mut cfg, true, ldflags, &[], &[]); + configure_cmake(builder, target, &mut cfg, true, ldflags, &[]); configure_llvm(builder, target, &mut cfg); for (key, val) in &builder.config.llvm_build_config { @@ -597,7 +597,6 @@ fn configure_cmake( cfg: &mut cmake::Config, use_compiler_launcher: bool, mut ldflags: LdFlags, - extra_compiler_flags: &[&str], suppressed_compiler_flag_prefixes: &[&str], ) { // Do not print installation messages for up-to-date files. @@ -751,9 +750,6 @@ fn configure_cmake( if builder.config.llvm_clang_cl.is_some() { cflags.push(&format!(" --target={target}")); } - for flag in extra_compiler_flags { - cflags.push(&format!(" {flag}")); - } cfg.define("CMAKE_C_FLAGS", cflags); let mut cxxflags: OsString = builder .cflags(target, GitRepo::Llvm, CLang::Cxx) @@ -773,9 +769,6 @@ fn configure_cmake( if builder.config.llvm_clang_cl.is_some() { cxxflags.push(&format!(" --target={target}")); } - for flag in extra_compiler_flags { - cxxflags.push(&format!(" {flag}")); - } cfg.define("CMAKE_CXX_FLAGS", cxxflags); if let Some(ar) = builder.ar(target) { if ar.is_absolute() { @@ -944,7 +937,7 @@ impl Step for Lld { ldflags.push_all("-Wl,-rpath,'$ORIGIN/../../../'"); } - configure_cmake(builder, target, &mut cfg, true, ldflags, &[], &[]); + configure_cmake(builder, target, &mut cfg, true, ldflags, &[]); configure_llvm(builder, target, &mut cfg); // Re-use the same flags as llvm to control the level of debug information @@ -1043,8 +1036,6 @@ impl Step for Sanitizers { // Unfortunately sccache currently lacks support to build them successfully. // Disable compiler launcher on Darwin targets to avoid potential issues. let use_compiler_launcher = !self.target.contains("apple-darwin"); - let extra_compiler_flags: &[&str] = - if self.target.contains("apple") { &["-fembed-bitcode=off"] } else { &[] }; // Since v1.0.86, the cc crate adds -mmacosx-version-min to the default // flags on MacOS. A long-standing bug in the CMake rules for compiler-rt // causes architecture detection to be skipped when this flag is present, @@ -1057,7 +1048,6 @@ impl Step for Sanitizers { &mut cfg, use_compiler_launcher, LdFlags::default(), - extra_compiler_flags, suppressed_compiler_flag_prefixes, ); diff --git a/src/bootstrap/src/core/build_steps/synthetic_targets.rs b/src/bootstrap/src/core/build_steps/synthetic_targets.rs index 89d50b5ffffa4..281a9b093b90b 100644 --- a/src/bootstrap/src/core/build_steps/synthetic_targets.rs +++ b/src/bootstrap/src/core/build_steps/synthetic_targets.rs @@ -80,8 +80,5 @@ fn create_synthetic_target( customize(spec_map); std::fs::write(&path, serde_json::to_vec_pretty(&spec).unwrap()).unwrap(); - let target = TargetSelection::create_synthetic(&name, path.to_str().unwrap()); - crate::utils::cc_detect::find_target(builder, target); - - target + TargetSelection::create_synthetic(&name, path.to_str().unwrap()) } diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index 21344a4224e40..2db3f8f79364e 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -1,6 +1,6 @@ use std::env; use std::fs; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use std::process::Command; use crate::core::build_steps::compile; @@ -313,10 +313,47 @@ bootstrap_tool!( SuggestTests, "src/tools/suggest-tests", "suggest-tests"; GenerateWindowsSys, "src/tools/generate-windows-sys", "generate-windows-sys"; RustdocGUITest, "src/tools/rustdoc-gui-test", "rustdoc-gui-test", is_unstable_tool = true, allow_features = "test"; - OptimizedDist, "src/tools/opt-dist", "opt-dist"; CoverageDump, "src/tools/coverage-dump", "coverage-dump"; ); +#[derive(Debug, Clone, Hash, PartialEq, Eq)] +pub struct OptimizedDist { + pub compiler: Compiler, + pub target: TargetSelection, +} + +impl Step for OptimizedDist { + type Output = PathBuf; + + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + run.path("src/tools/opt-dist") + } + + fn make_run(run: RunConfig<'_>) { + run.builder.ensure(OptimizedDist { + compiler: run.builder.compiler(0, run.builder.config.build), + target: run.target, + }); + } + + fn run(self, builder: &Builder<'_>) -> PathBuf { + // We need to ensure the rustc-perf submodule is initialized when building opt-dist since + // the tool requires it to be in place to run. + builder.update_submodule(Path::new("src/tools/rustc-perf")); + + builder.ensure(ToolBuild { + compiler: self.compiler, + target: self.target, + tool: "opt-dist", + mode: Mode::ToolBootstrap, + path: "src/tools/opt-dist", + source_type: SourceType::InTree, + extra_features: Vec::new(), + allow_features: "", + }) + } +} + #[derive(Debug, Clone, Hash, PartialEq, Eq, Ord, PartialOrd)] pub struct ErrorIndex { pub compiler: Compiler, diff --git a/src/bootstrap/src/core/sanity.rs b/src/bootstrap/src/core/sanity.rs index 493ad99cc705b..9c3df6fa9e39b 100644 --- a/src/bootstrap/src/core/sanity.rs +++ b/src/bootstrap/src/core/sanity.rs @@ -8,7 +8,7 @@ //! In theory if we get past this phase it's a bug if a build fails, but in //! practice that's likely not true! -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use std::env; use std::ffi::{OsStr, OsString}; use std::fs; @@ -33,8 +33,6 @@ pub struct Finder { // Targets can be removed from this list once they are present in the stage0 compiler (usually by updating the beta compiler of the bootstrap). const STAGE0_MISSING_TARGETS: &[&str] = &[ // just a dummy comment so the list doesn't get onelined - "aarch64-apple-visionos", - "aarch64-apple-visionos-sim", ]; impl Finder { @@ -169,6 +167,12 @@ than building it. .map(|p| cmd_finder.must_have(p)) .or_else(|| cmd_finder.maybe_have("reuse")); + let stage0_supported_target_list: HashSet = + output(Command::new(&build.config.initial_rustc).args(["--print", "target-list"])) + .lines() + .map(|s| s.to_string()) + .collect(); + // We're gonna build some custom C code here and there, host triples // also build some C++ shims for LLVM so we need a C++ compiler. for target in &build.targets { @@ -195,11 +199,19 @@ than building it. if !["A-A", "B-B", "C-C"].contains(&target_str.as_str()) { let mut has_target = false; - let supported_target_list = - output(Command::new(&build.config.initial_rustc).args(["--print", "target-list"])); + let missing_targets_hashset: HashSet<_> = STAGE0_MISSING_TARGETS.iter().map(|t| t.to_string()).collect(); + let duplicated_targets: Vec<_> = stage0_supported_target_list.intersection(&missing_targets_hashset).collect(); + + if !duplicated_targets.is_empty() { + println!("Following targets supported from the stage0 compiler, please remove them from STAGE0_MISSING_TARGETS list."); + for duplicated_target in duplicated_targets { + println!(" {duplicated_target}"); + } + std::process::exit(1); + } // Check if it's a built-in target. - has_target |= supported_target_list.contains(&target_str); + has_target |= stage0_supported_target_list.contains(&target_str); has_target |= STAGE0_MISSING_TARGETS.contains(&target_str.as_str()); if !has_target { diff --git a/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile index fe84c23a11c14..4e1aae9822135 100644 --- a/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile @@ -58,14 +58,6 @@ COPY host-x86_64/dist-x86_64-linux/build-clang.sh /tmp/ RUN ./build-clang.sh ENV CC=clang CXX=clang++ -# rustc-perf version from 2023-10-22 -# Should also be changed in the opt-dist tool for other environments. -ENV PERF_COMMIT 4f313add609f43e928e98132358e8426ed3969ae -RUN curl -LS -o perf.zip https://ci-mirrors.rust-lang.org/rustc/rustc-perf-$PERF_COMMIT.zip && \ - unzip perf.zip && \ - mv rustc-perf-$PERF_COMMIT rustc-perf && \ - rm perf.zip - COPY scripts/sccache.sh /scripts/ RUN sh /scripts/sccache.sh diff --git a/src/ci/docker/scripts/x86_64-gnu-llvm.sh b/src/ci/docker/scripts/x86_64-gnu-llvm.sh index 2eb751ca3766d..876b300d35c6b 100755 --- a/src/ci/docker/scripts/x86_64-gnu-llvm.sh +++ b/src/ci/docker/scripts/x86_64-gnu-llvm.sh @@ -23,6 +23,10 @@ if [[ -z "${PR_CI_JOB}" ]]; then # Run `ui-fulldeps` in `--stage=1`, which actually uses the stage0 # compiler, and is sensitive to the addition of new flags. ../x.py --stage 1 test tests/ui-fulldeps + + # The tests are run a second time with the size optimizations enabled. + ../x.py --stage 1 test library/std library/alloc library/core \ + --rustc-args "--cfg feature=\"optimize_for_size\"" fi # When running gcc backend tests, we need to install `libgccjit` and to not run llvm codegen diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml index 04888dc09b50d..05470eebf014b 100644 --- a/src/ci/github-actions/jobs.yml +++ b/src/ci/github-actions/jobs.yml @@ -50,8 +50,6 @@ envs: production: &production DEPLOY_BUCKET: rust-lang-ci2 - TOOLSTATE_ISSUES_API_URL: https://api.github.com/repos/rust-lang/rust/issues - TOOLSTATE_PUBLISH: 1 # AWS_SECRET_ACCESS_KEYs are stored in GitHub's secrets storage, named # AWS_SECRET_ACCESS_KEY_. Including the key id in the name allows to # rotate them in a single branch while keeping the old key in another @@ -108,66 +106,66 @@ auto: <<: *job-aarch64-linux - image: arm-android - <<: *job-linux-8c + <<: *job-linux-4c - image: armhf-gnu - <<: *job-linux-8c + <<: *job-linux-4c - image: dist-aarch64-linux env: CODEGEN_BACKENDS: llvm,cranelift - <<: *job-linux-8c + <<: *job-linux-4c - image: dist-android - <<: *job-linux-8c + <<: *job-linux-4c - image: dist-arm-linux - <<: *job-linux-16c + <<: *job-linux-8c - image: dist-armhf-linux - <<: *job-linux-8c + <<: *job-linux-4c - image: dist-armv7-linux - <<: *job-linux-8c + <<: *job-linux-4c - image: dist-i586-gnu-i586-i686-musl - <<: *job-linux-8c + <<: *job-linux-4c - image: dist-i686-linux - <<: *job-linux-8c + <<: *job-linux-4c - image: dist-loongarch64-linux - <<: *job-linux-8c + <<: *job-linux-4c - image: dist-ohos - <<: *job-linux-8c + <<: *job-linux-4c - image: dist-powerpc-linux - <<: *job-linux-8c + <<: *job-linux-4c - image: dist-powerpc64-linux - <<: *job-linux-8c + <<: *job-linux-4c - image: dist-powerpc64le-linux - <<: *job-linux-8c + <<: *job-linux-4c - image: dist-riscv64-linux - <<: *job-linux-8c + <<: *job-linux-4c - image: dist-s390x-linux - <<: *job-linux-8c + <<: *job-linux-4c - image: dist-various-1 - <<: *job-linux-8c + <<: *job-linux-4c - image: dist-various-2 - <<: *job-linux-8c + <<: *job-linux-4c - image: dist-x86_64-freebsd - <<: *job-linux-8c + <<: *job-linux-4c - image: dist-x86_64-illumos - <<: *job-linux-8c + <<: *job-linux-4c - image: dist-x86_64-linux env: @@ -186,7 +184,7 @@ auto: <<: *job-linux-8c - image: dist-x86_64-netbsd - <<: *job-linux-8c + <<: *job-linux-4c - image: i686-gnu <<: *job-linux-8c @@ -198,7 +196,7 @@ auto: <<: *job-linux-4c - image: test-various - <<: *job-linux-8c + <<: *job-linux-4c - image: x86_64-gnu <<: *job-linux-4c @@ -229,7 +227,7 @@ auto: <<: *job-linux-8c - image: x86_64-gnu-debug - <<: *job-linux-8c + <<: *job-linux-4c - image: x86_64-gnu-distcheck <<: *job-linux-8c diff --git a/src/doc/book b/src/doc/book index bebcf527e6775..5e9051f71638a 160000 --- a/src/doc/book +++ b/src/doc/book @@ -1 +1 @@ -Subproject commit bebcf527e67755a989a1739b7cfaa8f0e6b30040 +Subproject commit 5e9051f71638aa941cd5dda465e25c61cde9594f diff --git a/src/doc/embedded-book b/src/doc/embedded-book index 17842ebb050f6..dd962bb82865a 160000 --- a/src/doc/embedded-book +++ b/src/doc/embedded-book @@ -1 +1 @@ -Subproject commit 17842ebb050f62e40a4618edeb8e8ee86e758707 +Subproject commit dd962bb82865a5284f2404e5234f1e3222b9c022 diff --git a/src/doc/reference b/src/doc/reference index 51817951d0d21..e356977fceaa8 160000 --- a/src/doc/reference +++ b/src/doc/reference @@ -1 +1 @@ -Subproject commit 51817951d0d213a0011f82b62aae02c3b3f2472e +Subproject commit e356977fceaa8591c762312d8d446769166d4b3e diff --git a/src/doc/rust-by-example b/src/doc/rust-by-example index 229ad13b64d91..20482893d1a50 160000 --- a/src/doc/rust-by-example +++ b/src/doc/rust-by-example @@ -1 +1 @@ -Subproject commit 229ad13b64d919b12e548d560f06d88963b25cd3 +Subproject commit 20482893d1a502df72f76762c97aed88854cdf81 diff --git a/src/doc/rustc-dev-guide b/src/doc/rustc-dev-guide index 2d1947ff34d50..b6d4a4940bab8 160000 --- a/src/doc/rustc-dev-guide +++ b/src/doc/rustc-dev-guide @@ -1 +1 @@ -Subproject commit 2d1947ff34d50ca46dfe242ad75531a4c429bb52 +Subproject commit b6d4a4940bab85cc91eec70cc2e3096dd48da62d diff --git a/src/doc/rustc/src/SUMMARY.md b/src/doc/rustc/src/SUMMARY.md index c7e3293e35ade..6f81495585707 100644 --- a/src/doc/rustc/src/SUMMARY.md +++ b/src/doc/rustc/src/SUMMARY.md @@ -16,13 +16,17 @@ - [Platform Support](platform-support.md) - [Target Tier Policy](target-tier-policy.md) - [Template for Target-specific Documentation](platform-support/TEMPLATE.md) - - [arm64e-apple-ios.md](platform-support/arm64e-apple-ios.md) - - [arm64e-apple-darwin.md](platform-support/arm64e-apple-darwin.md) - - [aarch64-apple-ios-sim](platform-support/aarch64-apple-ios-sim.md) - [arm64ec-pc-windows-msvc](platform-support/arm64ec-pc-windows-msvc.md) + - [\*-apple-darwin](platform-support/apple-darwin.md) + - [i686-apple-darwin](platform-support/i686-apple-darwin.md) + - [x86_64h-apple-darwin](platform-support/x86_64h-apple-darwin.md) + - [arm64e-apple-darwin.md](platform-support/arm64e-apple-darwin.md) + - [\*-apple-ios](platform-support/apple-ios.md) + - [\*-apple-ios-macabi](platform-support/apple-ios-macabi.md) + - [arm64e-apple-ios.md](platform-support/arm64e-apple-ios.md) - [\*-apple-tvos](platform-support/apple-tvos.md) - - [\*-apple-watchos\*](platform-support/apple-watchos.md) - - [aarch64-apple-visionos\*](platform-support/apple-visionos.md) + - [\*-apple-watchos](platform-support/apple-watchos.md) + - [\*-apple-visionos](platform-support/apple-visionos.md) - [aarch64-nintendo-switch-freestanding](platform-support/aarch64-nintendo-switch-freestanding.md) - [armeb-unknown-linux-gnueabi](platform-support/armeb-unknown-linux-gnueabi.md) - [arm-none-eabi](platform-support/arm-none-eabi.md) @@ -76,7 +80,6 @@ - [x86_64-fortanix-unknown-sgx](platform-support/x86_64-fortanix-unknown-sgx.md) - [x86_64-unknown-linux-none.md](platform-support/x86_64-unknown-linux-none.md) - [x86_64-unknown-none](platform-support/x86_64-unknown-none.md) - - [x86_64h-apple-darwin](platform-support/x86_64h-apple-darwin.md) - [Targets](targets/index.md) - [Built-in Targets](targets/built-in.md) - [Custom Targets](targets/custom.md) @@ -84,7 +87,8 @@ - [Profile-guided Optimization](profile-guided-optimization.md) - [Instrumentation-based Code Coverage](instrument-coverage.md) - [Linker-plugin-based LTO](linker-plugin-lto.md) -- [Checking conditional configurations](check-cfg.md) +- [Checking Conditional Configurations](check-cfg.md) + - [Cargo Specifics](check-cfg/cargo-specifics.md) - [Exploit Mitigations](exploit-mitigations.md) - [Symbol Mangling](symbol-mangling/index.md) - [v0 Symbol Format](symbol-mangling/v0.md) diff --git a/src/doc/rustc/src/check-cfg/cargo-specifics.md b/src/doc/rustc/src/check-cfg/cargo-specifics.md new file mode 100644 index 0000000000000..bfa6016192614 --- /dev/null +++ b/src/doc/rustc/src/check-cfg/cargo-specifics.md @@ -0,0 +1,73 @@ +# Cargo Specifics - Checking Conditional Configurations + + + +This document is intented to summarize the principal ways Cargo interacts with +the `unexpected_cfgs` lint and `--check-cfg` flag. It is not intended to provide +individual details, for that refer to the [`--check-cfg` documentation](../check-cfg.md) and +to the [Cargo book](../../cargo/index.html). + +## Cargo feature + +*See the [`[features]` section in the Cargo book][cargo-features] for more details.* + +With the `[features]` table Cargo provides a mechanism to express conditional compilation and +optional dependencies. Cargo *automatically* declares corresponding cfgs for every feature as +expected. + +`Cargo.toml`: +```toml +[features] +serde = ["dep:serde"] +my_feature = [] +``` + +[cargo-features]: ../../cargo/reference/features.html + +## `check-cfg` in `[lints.rust]` table + + + +*See the [`[lints]` section in the Cargo book][cargo-lints-table] for more details.* + +When using a staticlly known custom config (ie. not dependant on a build-script), Cargo provides +the custom lint config `check-cfg` under `[lints.rust.unexpected_cfgs]`. + +It can be used to set custom static [`--check-cfg`](../check-cfg.md) args, it is mainly useful when +the list of expected cfgs is known is advance. + +`Cargo.toml`: +```toml +[lints.rust] +unexpected_cfgs = { level = "warn", check-cfg = ['cfg(has_foo)'] } +``` + +[cargo-lints-table]: ../../cargo/reference/manifest.html#the-lints-section + +## `cargo::rustc-check-cfg` for `build.rs`/build-script + +*See the [`cargo::rustc-check-cfg` section in the Cargo book][cargo-rustc-check-cfg] for more details.* + +When setting a custom config with [`cargo::rustc-cfg`][cargo-rustc-cfg], Cargo provides the +corollary instruction: [`cargo::rustc-check-cfg`][cargo-rustc-check-cfg] to expect custom configs. + +`build.rs`: +```rust,ignore (cannot-test-this-because-has_foo-isnt-declared) +fn main() { + println!("cargo::rustc-check-cfg=cfg(has_foo)"); + // ^^^^^^^^^^^^^^^^^^^^^^ new with Cargo 1.80 + if has_foo() { + println!("cargo::rustc-cfg=has_foo"); + } +} +``` + +[cargo-rustc-cfg]: ../../cargo/reference/build-scripts.html#rustc-cfg +[cargo-rustc-check-cfg]: ../../cargo/reference/build-scripts.html#rustc-check-cfg diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index 8adc410455eda..77859956c95fa 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -36,7 +36,7 @@ target | notes `i686-pc-windows-gnu` | 32-bit MinGW (Windows 10+) [^x86_32-floats-return-ABI] `i686-pc-windows-msvc` | 32-bit MSVC (Windows 10+) [^x86_32-floats-return-ABI] `i686-unknown-linux-gnu` | 32-bit Linux (kernel 3.2+, glibc 2.17+) [^x86_32-floats-return-ABI] -`x86_64-apple-darwin` | 64-bit macOS (10.12+, Sierra+) +[`x86_64-apple-darwin`](platform-support/apple-darwin.md) | 64-bit macOS (10.12+, Sierra+) `x86_64-pc-windows-gnu` | 64-bit MinGW (Windows 10+) `x86_64-pc-windows-msvc` | 64-bit MSVC (Windows 10+) `x86_64-unknown-linux-gnu` | 64-bit Linux (kernel 3.2+, glibc 2.17+) @@ -86,7 +86,7 @@ so Rustup may install the documentation for a similar tier 1 target instead. target | notes -------|------- -`aarch64-apple-darwin` | ARM64 macOS (11.0+, Big Sur+) +[`aarch64-apple-darwin`](platform-support/apple-darwin.md) | ARM64 macOS (11.0+, Big Sur+) `aarch64-pc-windows-msvc` | ARM64 Windows MSVC `aarch64-unknown-linux-musl` | ARM64 Linux with musl 1.2.3 `arm-unknown-linux-gnueabi` | Armv6 Linux (kernel 3.2, glibc 2.17) @@ -133,8 +133,8 @@ so Rustup may install the documentation for a similar tier 1 target instead. target | std | notes -------|:---:|------- -`aarch64-apple-ios` | ✓ | ARM64 iOS -[`aarch64-apple-ios-sim`](platform-support/aarch64-apple-ios-sim.md) | ✓ | Apple iOS Simulator on ARM64 +[`aarch64-apple-ios`](platform-support/apple-ios.md) | ✓ | ARM64 iOS +[`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 @@ -192,7 +192,7 @@ target | std | notes `wasm32-wasi` | ✓ | WebAssembly with WASI (undergoing a [rename to `wasm32-wasip1`][wasi-rename]) [`wasm32-wasip1`](platform-support/wasm32-wasip1.md) | ✓ | WebAssembly with WASI [`wasm32-wasip1-threads`](platform-support/wasm32-wasip1-threads.md) | ✓ | | WebAssembly with WASI Preview 1 and threads -`x86_64-apple-ios` | ✓ | 64-bit x86 iOS +[`x86_64-apple-ios`](platform-support/apple-ios.md) | ✓ | 64-bit x86 iOS [`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 @@ -241,9 +241,9 @@ target | std | host | notes [`arm64e-apple-ios`](platform-support/arm64e-apple-ios.md) | ✓ | | ARM64e Apple iOS [`arm64e-apple-darwin`](platform-support/arm64e-apple-darwin.md) | ✓ | ✓ | ARM64e Apple Darwin [`arm64ec-pc-windows-msvc`](platform-support/arm64ec-pc-windows-msvc.md) | ? | | Arm64EC Windows MSVC -`aarch64-apple-ios-macabi` | ? | | Apple Catalyst on ARM64 -[`aarch64-apple-tvos`](platform-support/apple-tvos.md) | ? | | ARM64 tvOS -[`aarch64-apple-tvos-sim`](platform-support/apple-tvos.md) | ? | | ARM64 tvOS Simulator +[`aarch64-apple-ios-macabi`](platform-support/apple-ios-macabi.md) | ✓ | | Apple Catalyst on ARM64 +[`aarch64-apple-tvos`](platform-support/apple-tvos.md) | ✓ | | ARM64 tvOS +[`aarch64-apple-tvos-sim`](platform-support/apple-tvos.md) | ✓ | | ARM64 tvOS Simulator [`aarch64-apple-watchos`](platform-support/apple-watchos.md) | ✓ | | ARM64 Apple WatchOS [`aarch64-apple-watchos-sim`](platform-support/apple-watchos.md) | ✓ | | ARM64 Apple WatchOS Simulator [`aarch64-apple-visionos`](platform-support/apple-visionos.md) | ✓ | | ARM64 Apple visionOS @@ -283,7 +283,7 @@ target | std | host | notes [`armv7a-kmc-solid_asp3-eabihf`](platform-support/kmc-solid.md) | ✓ | | ARM SOLID with TOPPERS/ASP3, hardfloat [`armv7a-none-eabihf`](platform-support/arm-none-eabi.md) | * | | Bare Armv7-A, hardfloat [`armv7k-apple-watchos`](platform-support/apple-watchos.md) | ✓ | | Armv7-A Apple WatchOS -`armv7s-apple-ios` | ✓ | | Armv7-A Apple-A6 Apple iOS +[`armv7s-apple-ios`](platform-support/apple-ios.md) | ✓ | | Armv7-A Apple-A6 Apple iOS [`armv8r-none-eabihf`](platform-support/armv8r-none-eabihf.md) | * | | Bare Armv8-R, hardfloat `avr-unknown-gnu-atmega328` | * | | AVR. Requires `-Z build-std=core` `bpfeb-unknown-none` | * | | BPF (big endian) @@ -292,10 +292,10 @@ target | std | host | notes `csky-unknown-linux-gnuabiv2hf` | ✓ | | C-SKY abiv2 Linux, hardfloat (little endian) [`hexagon-unknown-none-elf`](platform-support/hexagon-unknown-none-elf.md)| * | | Bare Hexagon (v60+, HVX) [`hexagon-unknown-linux-musl`](platform-support/hexagon-unknown-linux-musl.md) | ✓ | | Hexagon Linux with musl 1.2.3 -`i386-apple-ios` | ✓ | | 32-bit x86 iOS [^x86_32-floats-return-ABI] +[`i386-apple-ios`](platform-support/apple-ios.md) | ✓ | | 32-bit x86 iOS [^x86_32-floats-return-ABI] [`i586-pc-nto-qnx700`](platform-support/nto-qnx.md) | * | | 32-bit x86 QNX Neutrino 7.0 RTOS [^x86_32-floats-return-ABI] [`i586-unknown-netbsd`](platform-support/netbsd.md) | ✓ | | 32-bit x86, restricted to Pentium -`i686-apple-darwin` | ✓ | ✓ | 32-bit macOS (10.12+, Sierra+) [^x86_32-floats-return-ABI] +[`i686-apple-darwin`](platform-support/apple-darwin.md) | ✓ | ✓ | 32-bit macOS (10.12+, Sierra+) [^x86_32-floats-return-ABI] `i686-unknown-haiku` | ✓ | ✓ | 32-bit Haiku [^x86_32-floats-return-ABI] [`i686-unknown-hurd-gnu`](platform-support/hurd.md) | ✓ | ✓ | 32-bit GNU/Hurd [^x86_32-floats-return-ABI] [`i686-unknown-netbsd`](platform-support/netbsd.md) | ✓ | ✓ | NetBSD/i386 with SSE2 [^x86_32-floats-return-ABI] @@ -367,8 +367,8 @@ target | std | host | notes `thumbv7neon-unknown-linux-musleabihf` | ? | | Thumb2-mode Armv7-A Linux with NEON, musl 1.2.3 [`wasm32-wasip2`](platform-support/wasm32-wasip2.md) | ✓ | | WebAssembly [`wasm64-unknown-unknown`](platform-support/wasm64-unknown-unknown.md) | ? | | WebAssembly -`x86_64-apple-ios-macabi` | ✓ | | Apple Catalyst on x86_64 -[`x86_64-apple-tvos`](platform-support/apple-tvos.md) | ? | | x86 64-bit tvOS +[`x86_64-apple-ios-macabi`](platform-support/apple-ios-macabi.md) | ✓ | | Apple Catalyst on x86_64 +[`x86_64-apple-tvos`](platform-support/apple-tvos.md) | ✓ | | x86 64-bit tvOS [`x86_64-apple-watchos-sim`](platform-support/apple-watchos.md) | ✓ | | x86 64-bit Apple WatchOS simulator [`x86_64-pc-nto-qnx710`](platform-support/nto-qnx.md) | ✓ | | x86 64-bit QNX Neutrino 7.1 RTOS | [`x86_64-unikraft-linux-musl`](platform-support/unikraft-linux-musl.md) | ✓ | | 64-bit Unikraft with musl 1.2.3 diff --git a/src/doc/rustc/src/platform-support/aarch64-apple-ios-sim.md b/src/doc/rustc/src/platform-support/aarch64-apple-ios-sim.md deleted file mode 100644 index 3f29e2c5e1f7a..0000000000000 --- a/src/doc/rustc/src/platform-support/aarch64-apple-ios-sim.md +++ /dev/null @@ -1,55 +0,0 @@ -# aarch64-apple-ios-sim - -**Tier: 2** - -Apple iOS Simulator on ARM64. - -## Designated Developers - -* [@badboy](https://github.com/badboy) -* [@deg4uss3r](https://github.com/deg4uss3r) - -## Requirements - -This target is cross-compiled. -To build this target Xcode 12 or higher on macOS is required. - -## Building - -The target can be built by enabling it for a `rustc` build: - -```toml -[build] -build-stage = 1 -target = ["aarch64-apple-ios-sim"] -``` - -## Cross-compilation - -This target can be cross-compiled from `x86_64` or `aarch64` macOS hosts. - -Other hosts are not supported for cross-compilation, but might work when also providing the required Xcode SDK. - -## Testing - -Currently there is no support to run the rustc test suite for this target. - - -## Building Rust programs - -*Note: Building for this target requires the corresponding iOS SDK, as provided by Xcode 12+.* - -From Rust Nightly 1.56.0 (2021-08-03) on the artifacts are shipped pre-compiled: - -```text -rustup target add aarch64-apple-ios-sim --toolchain nightly -``` - -Rust programs can be built for that target: - -```text -rustc --target aarch64-apple-ios-sim your-code.rs -``` - -There is no easy way to run simple programs in the iOS simulator. -Static library builds can be embedded into iOS applications. diff --git a/src/doc/rustc/src/platform-support/apple-darwin.md b/src/doc/rustc/src/platform-support/apple-darwin.md new file mode 100644 index 0000000000000..0fb86949a4b79 --- /dev/null +++ b/src/doc/rustc/src/platform-support/apple-darwin.md @@ -0,0 +1,59 @@ +# `*-apple-darwin` + +Apple macOS targets. + +**Tier: 1** + +- `x86_64-apple-darwin`: macOS on 64-bit x86. + +**Tier: 2 (with Host Tools)** + +- `aarch64-apple-darwin`: macOS on ARM64 (M1-family or later Apple Silicon CPUs). + +## Target maintainers + +- [@thomcc](https://github.com/thomcc) +- [@madsmtm](https://github.com/madsmtm) + +## Requirements + +### OS version + +The minimum supported version is macOS 10.12 Sierra on x86, and macOS 11.0 Big +Sur on ARM64. + +This version can be raised per-binary by changing the [deployment target], +which might yield more performance optimizations. `rustc` respects the common +environment variables used by Xcode to do so, in this case +`MACOSX_DEPLOYMENT_TARGET`. + +The current default deployment target for `rustc` can be retrieved with +[`rustc --print=deployment-target`][rustc-print]. + +[deployment target]: https://developer.apple.com/library/archive/documentation/DeveloperTools/Conceptual/cross_development/Configuring/configuring.html +[rustc-print]: ../command-line-arguments.md#option-print + +### Binary format + +The default binary format is Mach-O, the executable format used on Apple's +platforms. + +## Building + +These targets are distributed through `rustup`, and otherwise require no +special configuration. + +## Testing + +There are no special requirements for testing and running this target. + +x86 binaries can be run on Apple Silicon by using Rosetta. + +## Cross-compilation toolchains and C code + +Cross-compilation of these targets are supported using Clang, but may require +Xcode or the macOS SDK (`MacOSX.sdk`) to be available to compile C code and +to link. + +The path to the SDK can be passed to `rustc` using the common `SDKROOT` +environment variable. diff --git a/src/doc/rustc/src/platform-support/apple-ios-macabi.md b/src/doc/rustc/src/platform-support/apple-ios-macabi.md new file mode 100644 index 0000000000000..278ee94b6d4e0 --- /dev/null +++ b/src/doc/rustc/src/platform-support/apple-ios-macabi.md @@ -0,0 +1,58 @@ +# `*-apple-ios-macabi` + +Apple Mac Catalyst targets. + +**Tier: 3** + +- `aarch64-apple-ios-macabi`: Mac Catalyst on ARM64. +- `x86_64-apple-ios-macabi`: Mac Catalyst on 64-bit x86. + +## Target maintainers + +- [@madsmtm](https://github.com/madsmtm) + +## Requirements + +These targets are cross-compiled, and require the corresponding macOS SDK +(`MacOSX.sdk`) which contain `./System/iOSSupport` headers to allow linking to +iOS-specific headers, as provided by Xcode 11 or higher. + +The path to the SDK can be passed to `rustc` using the common `SDKROOT` +environment variable. + +### OS version + +The minimum supported version is iOS 13.1. + +This can be raised per-binary by changing the deployment target. `rustc` +respects the common environment variables used by Xcode to do so, in this +case `IPHONEOS_DEPLOYMENT_TARGET`. + +## Building the target + +The targets can be built by enabling them for a `rustc` build in +`config.toml`, by adding, for example: + +```toml +[build] +target = ["aarch64-apple-ios-macabi", "x86_64-apple-ios-macabi"] +``` + +Using the unstable `-Zbuild-std` with a nightly Cargo may also work. + +## Building Rust programs + +Rust programs can be built for these targets by specifying `--target`, if +`rustc` has been built with support for them. For example: + +```console +$ rustc --target aarch64-apple-ios-macabi your-code.rs +``` + +## Testing + +Mac Catalyst binaries can be run directly on macOS 10.15 Catalina or newer. + +x86 binaries can be run on Apple Silicon by using Rosetta. + +Note that using certain UIKit functionality requires the binary to be bundled. diff --git a/src/doc/rustc/src/platform-support/apple-ios.md b/src/doc/rustc/src/platform-support/apple-ios.md new file mode 100644 index 0000000000000..5045f810400f5 --- /dev/null +++ b/src/doc/rustc/src/platform-support/apple-ios.md @@ -0,0 +1,74 @@ +# `*-apple-ios` + +Apple iOS / iPadOS targets. + +**Tier: 2 (without Host Tools)** + +- `aarch64-apple-ios`: Apple iOS on ARM64. +- `aarch64-apple-ios-sim`: Apple iOS Simulator on ARM64. +- `x86_64-apple-ios`: Apple iOS Simulator on 64-bit x86. + +**Tier: 3** + +- `armv7s-apple-ios`: Apple iOS on Armv7-A. +- `i386-apple-ios`: Apple iOS Simulator on 32-bit x86. + +## Target maintainers + +- [@badboy](https://github.com/badboy) +- [@deg4uss3r](https://github.com/deg4uss3r) +- [@madsmtm](https://github.com/madsmtm) + +## Requirements + +These targets are cross-compiled, and require the corresponding iOS SDK +(`iPhoneOS.sdk` or `iPhoneSimulator.sdk`), as provided by Xcode. To build the +ARM64 targets, Xcode 12 or higher is required. + +The path to the SDK can be passed to `rustc` using the common `SDKROOT` +environment variable. + +### OS version + +The minimum supported version is iOS 10.0. + +This can be raised per-binary by changing the deployment target. `rustc` +respects the common environment variables used by Xcode to do so, in this +case `IPHONEOS_DEPLOYMENT_TARGET`. + +## Building the target + +The tier 2 targets are distributed through `rustup`, and can be installed +using one of: +```console +$ rustup target add aarch64-apple-ios +$ rustup target add aarch64-apple-ios-sim +$ rustup target add x86_64-apple-ios +``` + +The tier 3 targets can be built by enabling them for a `rustc` build in +`config.toml`, by adding, for example: + +```toml +[build] +target = ["armv7s-apple-ios", "i386-apple-ios"] +``` + +Using the unstable `-Zbuild-std` with a nightly Cargo may also work. + +## Building Rust programs + +Rust programs can be built for these targets by specifying `--target`, if +`rustc` has been built with support for them. For example: + +```console +$ rustc --target aarch64-apple-ios your-code.rs +``` + +## Testing + +There is no support for running the Rust or standard library testsuite at the +moment. Testing has mostly been done manually with builds of static libraries +embedded into applications called from Xcode or a simulator. + +It hopefully will be possible to improve this in the future. diff --git a/src/doc/rustc/src/platform-support/apple-tvos.md b/src/doc/rustc/src/platform-support/apple-tvos.md index e7ea109df1ba1..7a3b601579a01 100644 --- a/src/doc/rustc/src/platform-support/apple-tvos.md +++ b/src/doc/rustc/src/platform-support/apple-tvos.md @@ -1,40 +1,44 @@ # `*-apple-tvos` -- aarch64-apple-tvos -- x86_64-apple-tvos + +Apple tvOS targets. **Tier: 3** -Apple tvOS targets: -- Apple tvOS on aarch64 -- Apple tvOS Simulator on x86_64 +- `aarch64-apple-tvos`: Apple tvOS on ARM64. +- `aarch64-apple-tvos-sim`: Apple tvOS Simulator on ARM64. +- `x86_64-apple-tvos`: Apple tvOS Simulator on x86_64. ## Target maintainers -* [@thomcc](https://github.com/thomcc) +- [@thomcc](https://github.com/thomcc) +- [@madsmtm](https://github.com/madsmtm) ## Requirements -These targets are cross-compiled. You will need appropriate versions of Xcode -and the SDKs for tvOS (`AppleTVOS.sdk`) and/or the tvOS Simulator -(`AppleTVSimulator.sdk`) to build a toolchain and target these platforms. +These targets are cross-compiled, and require the corresponding tvOS SDK +(`AppleTVOS.sdk` or `AppleTVSimulator.sdk`), as provided by Xcode. To build the +ARM64 targets, Xcode 12 or higher is required. + +The path to the SDK can be passed to `rustc` using the common `SDKROOT` +environment variable. -The targets support most (see below) of the standard library including the -allocator to the best of my knowledge, however they are very new, not yet -well-tested, and it is possible that there are various bugs. +### OS version -In theory we support back to tvOS version 7.0, although the actual minimum -version you can target may be newer than this, for example due to the versions -of Xcode and your SDKs. +The minimum supported version is tvOS 10.0, although the actual minimum version +you can target may be newer than this, for example due to the versions of Xcode +and your SDKs. -As with the other Apple targets, `rustc` respects the common environment -variables used by Xcode to configure this, in this case -`TVOS_DEPLOYMENT_TARGET`. +The version can be raised per-binary by changing the deployment target. `rustc` +respects the common environment variables used by Xcode to do so, in this +case `TVOS_DEPLOYMENT_TARGET`. -#### Incompletely supported library functionality +### Incompletely supported library functionality -As mentioned, "most" of the standard library is supported, which means that some portions -are known to be unsupported. The following APIs are currently known to have -missing or incomplete support: +The targets support most of the standard library including the allocator to the +best of my knowledge, however they are very new, not yet well-tested, and it is +possible that there are various bugs. + +The following APIs are currently known to have missing or incomplete support: - `std::process::Command`'s API will return an error if it is configured in a manner which cannot be performed using `posix_spawn` -- this is because the @@ -47,41 +51,30 @@ missing or incomplete support: ## Building the target -The targets can be built by enabling them for a `rustc` build in `config.toml`, by adding, for example: +The targets can be built by enabling them for a `rustc` build in +`config.toml`, by adding, for example: ```toml [build] build-stage = 1 -target = ["aarch64-apple-tvos", "x86_64-apple-tvos", "aarch64-apple-tvos-sim"] +target = ["aarch64-apple-tvos", "aarch64-apple-tvos-sim"] ``` -It's possible that cargo under `-Zbuild-std` may also be used to target them. +Using the unstable `-Zbuild-std` with a nightly Cargo may also work. ## Building Rust programs -*Note: Building for this target requires the corresponding TVOS SDK, as provided by Xcode.* - -Rust programs can be built for these targets +Rust programs can be built for these targets by specifying `--target`, if +`rustc` has been built with support for them. For example: -```text +```console $ rustc --target aarch64-apple-tvos your-code.rs -... -$ rustc --target x86_64-apple-tvos your-code.rs -... -$ rustc --target aarch64-apple-tvos-sim your-code.rs ``` ## Testing -There is no support for running the Rust or standard library testsuite on tvOS -or the simulators at the moment. Testing has mostly been done manually with -builds of static libraries called from Xcode or a simulator. +There is no support for running the Rust or standard library testsuite at the +moment. Testing has mostly been done manually with builds of static libraries +embedded into applications called from Xcode or a simulator. It hopefully will be possible to improve this in the future. - -## Cross-compilation toolchains and C code - -This target can be cross-compiled from x86_64 or aarch64 macOS hosts. - -Other hosts are not supported for cross-compilation, but might work when also -providing the required Xcode SDK. diff --git a/src/doc/rustc/src/platform-support/apple-visionos.md b/src/doc/rustc/src/platform-support/apple-visionos.md index 9874126e42fe1..56224d7e20d4e 100644 --- a/src/doc/rustc/src/platform-support/apple-visionos.md +++ b/src/doc/rustc/src/platform-support/apple-visionos.md @@ -1,53 +1,67 @@ -# aarch64-apple-visionos\* +# `*-apple-visionos` -- aarch64-apple-visionos -- aarch64-apple-visionos-sim +Apple visionOS / xrOS targets. **Tier: 3** -Apple visionOS targets: - -- Apple visionOS on arm64 -- Apple visionOS Simulator on arm64 +- `aarch64-apple-visionos`: Apple visionOS on arm64. +- `aarch64-apple-visionos-sim`: Apple visionOS Simulator on arm64. ## Target maintainers -- [@agg23](https://github.com/agg23) -- [@madsmtm](https://github.com/madsmtm) +- [@agg23](https://github.com/agg23) +- [@madsmtm](https://github.com/madsmtm) ## Requirements -These targets are cross-compiled. -To build these targets Xcode 15 or higher on macOS is required, along with LLVM 18. +These targets are cross-compiled, and require the corresponding visionOS SDK +(`XROS.sdk` or `XRSimulator.sdk`), as provided by Xcode 15 or newer. + +The path to the SDK can be passed to `rustc` using the common `SDKROOT` +environment variable. + +### OS version + +The minimum supported version is visionOS 1.0. + +This can be raised per-binary by changing the deployment target. `rustc` +respects the common environment variables used by Xcode to do so, in this +case `XROS_DEPLOYMENT_TARGET`. ## Building the target -The targets can be built by enabling them for a `rustc` build, for example: +The targets can be built by enabling them for a `rustc` build in +`config.toml`, by adding, for example: ```toml [build] -build-stage = 1 -target = ["aarch64-apple-visionos-sim"] +target = ["aarch64-apple-visionos", "aarch64-apple-visionos-sim"] ``` -## Building Rust programs +Using the unstable `-Zbuild-std` with a nightly Cargo may also work. -_Note: Building for this target requires the corresponding visionOS SDK, as provided by Xcode 15+._ +Note: Currently, a newer version of `libc` and `cc` may be required, this will +be fixed in [#124560](https://github.com/rust-lang/rust/pull/124560). + +## Building Rust programs -Rust programs can be built for these targets, if `rustc` has been built with support for them, for example: +Rust programs can be built for these targets by specifying `--target`, if +`rustc` has been built with support for them. For example: -```text -rustc --target aarch64-apple-visionos-sim your-code.rs +```console +$ rustc --target aarch64-apple-visionos-sim your-code.rs ``` ## Testing -There is no support for running the Rust testsuite on visionOS or the simulators. +There is no support for running the Rust or standard library testsuite at the +moment. Testing has mostly been done manually with builds of static libraries +embedded into applications called from Xcode or a simulator. -There is no easy way to run simple programs on visionOS or the visionOS simulators. Static library builds can be embedded into visionOS applications. +It hopefully will be possible to improve this in the future. ## Cross-compilation toolchains and C code -This target can be cross-compiled from x86_64 or aarch64 macOS hosts. +The Clang target is suffixed with `-xros` for historical reasons. -Other hosts are not supported for cross-compilation, but might work when also providing the required Xcode SDK. +LLVM 18 or newer is required to build this target. diff --git a/src/doc/rustc/src/platform-support/apple-watchos.md b/src/doc/rustc/src/platform-support/apple-watchos.md index 7be2467352c21..8ba35f70b8578 100644 --- a/src/doc/rustc/src/platform-support/apple-watchos.md +++ b/src/doc/rustc/src/platform-support/apple-watchos.md @@ -1,58 +1,65 @@ -# *-apple-watchos -- arm64_32-apple-watchos -- armv7k-apple-watchos -- aarch64-apple-watchos -- aarch64-apple-watchos-sim -- x86_64-apple-watchos-sim +# `*-apple-watchos` + +Apple watchOS targets. **Tier: 3** -Apple WatchOS targets: -- Apple WatchOS on Arm 64_32 -- Apple WatchOS on Arm v7k -- Apple WatchOS on Arm 64 -- Apple WatchOS Simulator on arm64 -- Apple WatchOS Simulator on x86_64 +- `aarch64-apple-watchos`: Apple WatchOS on ARM64. +- `aarch64-apple-watchos-sim`: Apple WatchOS Simulator on ARM64. +- `x86_64-apple-watchos-sim`: Apple WatchOS Simulator on 64-bit x86. +- `arm64_32-apple-watchos`: Apple WatchOS on Arm 64_32. +- `armv7k-apple-watchos`: Apple WatchOS on Armv7k. ## Target maintainers -* [@deg4uss3r](https://github.com/deg4uss3r) -* [@vladimir-ea](https://github.com/vladimir-ea) -* [@leohowell](https://github.com/leohowell) +- [@deg4uss3r](https://github.com/deg4uss3r) +- [@vladimir-ea](https://github.com/vladimir-ea) +- [@leohowell](https://github.com/leohowell) +- [@madsmtm](https://github.com/madsmtm) ## Requirements -These targets are cross-compiled. -To build these targets Xcode 12 or higher on macOS is required. +These targets are cross-compiled, and require the corresponding watchOS SDK +(`WatchOS.sdk` or `WatchSimulator.sdk`), as provided by Xcode. To build the +ARM64 targets, Xcode 12 or higher is required. + +The path to the SDK can be passed to `rustc` using the common `SDKROOT` +environment variable. + +### OS version + +The minimum supported version is watchOS 5.0. + +This can be raised per-binary by changing the deployment target. `rustc` +respects the common environment variables used by Xcode to do so, in this +case `WATCHOS_DEPLOYMENT_TARGET`. ## Building the target -The targets can be built by enabling them for a `rustc` build, for example: +The targets can be built by enabling them for a `rustc` build in +`config.toml`, by adding, for example: ```toml [build] build-stage = 1 -target = ["aarch64-apple-watchos-sim"] +target = ["aarch64-apple-watchos", "aarch64-apple-watchos-sim"] ``` -## Building Rust programs +Using the unstable `-Zbuild-std` with a nightly Cargo may also work. -*Note: Building for this target requires the corresponding WatchOS SDK, as provided by Xcode 12+.* +## Building Rust programs -Rust programs can be built for these targets, if `rustc` has been built with support for them, for example: +Rust programs can be built for these targets by specifying `--target`, if +`rustc` has been built with support for them. For example: -```text -rustc --target aarch64-apple-watchos-sim your-code.rs +```console +$ rustc --target aarch64-apple-watchos-sim your-code.rs ``` ## Testing -There is no support for running the Rust testsuite on WatchOS or the simulators. - -There is no easy way to run simple programs on WatchOS or the WatchOS simulators. Static library builds can be embedded into WatchOS applications. - -## Cross-compilation toolchains and C code - -This target can be cross-compiled from x86_64 or aarch64 macOS hosts. +There is no support for running the Rust or standard library testsuite at the +moment. Testing has mostly been done manually with builds of static libraries +embedded into applications called from Xcode or a simulator. -Other hosts are not supported for cross-compilation, but might work when also providing the required Xcode SDK. +It hopefully will be possible to improve this in the future. diff --git a/src/doc/rustc/src/platform-support/arm64e-apple-darwin.md b/src/doc/rustc/src/platform-support/arm64e-apple-darwin.md index d9b9aeae1ae63..4d98b3a60986b 100644 --- a/src/doc/rustc/src/platform-support/arm64e-apple-darwin.md +++ b/src/doc/rustc/src/platform-support/arm64e-apple-darwin.md @@ -12,6 +12,8 @@ ARM64e macOS (11.0+, Big Sur+) Target for `macOS` on late-generation `M` series Apple chips. +See the docs on [`*-apple-darwin`](apple-darwin.md) for general macOS requirements. + ## Building the target You can build Rust with support for the targets by adding it to the `target` list in `config.toml`: diff --git a/src/doc/rustc/src/platform-support/arm64e-apple-ios.md b/src/doc/rustc/src/platform-support/arm64e-apple-ios.md index 0215621be3d02..3c878f7250e97 100644 --- a/src/doc/rustc/src/platform-support/arm64e-apple-ios.md +++ b/src/doc/rustc/src/platform-support/arm64e-apple-ios.md @@ -10,8 +10,7 @@ ARM64e iOS (12.0+) ## Requirements -These targets only support cross-compilation. -The targets do support `std`. +See the docs on [`*-apple-ios`](apple-ios.md) for general iOS requirements. ## Building the target diff --git a/src/doc/rustc/src/platform-support/i686-apple-darwin.md b/src/doc/rustc/src/platform-support/i686-apple-darwin.md new file mode 100644 index 0000000000000..d69fa97ce6373 --- /dev/null +++ b/src/doc/rustc/src/platform-support/i686-apple-darwin.md @@ -0,0 +1,41 @@ +# `i686-apple-darwin` + +Apple macOS on 32-bit x86. + +## Target maintainers + +- [@thomcc](https://github.com/thomcc) +- [@madsmtm](https://github.com/madsmtm) + +## Requirements + +See the docs on [`*-apple-darwin`](apple-darwin.md) for general macOS requirements. + +## Building the target + +You'll need the macOS 10.13 SDK shipped with Xcode 9. The location of the SDK +can be passed to `rustc` using the common `SDKROOT` environment variable. + +Once you have that, you can build Rust with support for the target by adding +it to the `target` list in `config.toml`: + +```toml +[build] +target = ["i686-apple-darwin"] +``` + +Using the unstable `-Zbuild-std` with a nightly Cargo may also work. + +## Building Rust programs + +Rust [no longer] ships pre-compiled artifacts for this target. To compile for +this target, you will either need to build Rust with the target enabled (see +"Building the target" above), or build your own copy using `build-std` or +similar. + +[no longer]: https://blog.rust-lang.org/2020/01/03/reducing-support-for-32-bit-apple-targets.html + +## Testing + +Running this target requires an Intel Macbook running macOS 10.14 or earlier, +as later versions removed support for running 32-bit binaries. diff --git a/src/doc/rustc/src/platform-support/x86_64h-apple-darwin.md b/src/doc/rustc/src/platform-support/x86_64h-apple-darwin.md index 0fe9d4edaca10..6c2a6a41101d1 100644 --- a/src/doc/rustc/src/platform-support/x86_64h-apple-darwin.md +++ b/src/doc/rustc/src/platform-support/x86_64h-apple-darwin.md @@ -23,9 +23,8 @@ default or user-defined allocators). This target is probably most useful when targeted via cross-compilation (including from `x86_64-apple-darwin`), but if built manually, the host tools work. -It is similar to `x86_64-apple-darwin` in nearly all respects, although the -minimum supported OS version is slightly higher (it requires 10.8 rather than -`x86_64-apple-darwin`'s 10.7). +It is similar to [`x86_64-apple-darwin`](apple-darwin.md) in nearly all +respects. ## Building the target diff --git a/src/librustdoc/passes/strip_aliased_non_local.rs b/src/librustdoc/passes/strip_aliased_non_local.rs index 848cbd5ed99fc..ac7d422ec800f 100644 --- a/src/librustdoc/passes/strip_aliased_non_local.rs +++ b/src/librustdoc/passes/strip_aliased_non_local.rs @@ -46,8 +46,13 @@ impl<'tcx> DocFolder for NonLocalStripper<'tcx> { // the field and not the one given by the user for the currrent crate. // // FIXME(#125009): Not-local should probably consider same Cargo workspace - if !i.def_id().map_or(true, |did| did.is_local()) { - if i.visibility(self.tcx) != Some(Visibility::Public) || i.is_doc_hidden() { + if let Some(def_id) = i.def_id() + && !def_id.is_local() + { + if i.is_doc_hidden() + // Default to *not* stripping items with inherited visibility. + || i.visibility(self.tcx).map_or(false, |viz| viz != Visibility::Public) + { return Some(strip_item(i)); } } diff --git a/src/llvm-project b/src/llvm-project index 5399a24c66cb6..b31c30a9bb4db 160000 --- a/src/llvm-project +++ b/src/llvm-project @@ -1 +1 @@ -Subproject commit 5399a24c66cb6164cf32280e7d300488c90d5765 +Subproject commit b31c30a9bb4dbbd13c359d0e2bea7f65d20adf3f diff --git a/src/tools/cargo b/src/tools/cargo index 0de7f2ec6c39d..84dc5dc11a900 160000 --- a/src/tools/cargo +++ b/src/tools/cargo @@ -1 +1 @@ -Subproject commit 0de7f2ec6c39d68022e6b97a39559d2f4dbf3930 +Subproject commit 84dc5dc11a9007a08f27170454da6097265e510e diff --git a/src/tools/clippy/.github/driver.sh b/src/tools/clippy/.github/driver.sh index 2eafdd0fbc87b..09202b1878b23 100755 --- a/src/tools/clippy/.github/driver.sh +++ b/src/tools/clippy/.github/driver.sh @@ -2,15 +2,18 @@ set -ex +sysroot="$(rustc --print sysroot)" +case $OS in + Linux) export LD_LIBRARY_PATH="$sysroot/lib" ;; + macOS) export DYLD_FALLBACK_LIBRARY_PATH="$sysroot/lib" ;; + Windows) export PATH="$(cygpath "$sysroot")/bin:$PATH" ;; + *) exit 1 +esac + # Check sysroot handling -sysroot=$(./target/debug/clippy-driver --print sysroot) -test "$sysroot" = "$(rustc --print sysroot)" - -if [[ ${OS} == "Windows" ]]; then - desired_sysroot=C:/tmp -else - desired_sysroot=/tmp -fi +test "$(./target/debug/clippy-driver --print sysroot)" = "$sysroot" + +desired_sysroot="target/sysroot" # Set --sysroot in command line sysroot=$(./target/debug/clippy-driver --sysroot $desired_sysroot --print sysroot) test "$sysroot" = $desired_sysroot diff --git a/src/tools/clippy/.github/workflows/clippy.yml b/src/tools/clippy/.github/workflows/clippy.yml index 8179e3e65b541..06bf3b6fdbfab 100644 --- a/src/tools/clippy/.github/workflows/clippy.yml +++ b/src/tools/clippy/.github/workflows/clippy.yml @@ -69,6 +69,6 @@ jobs: working-directory: clippy_dev - name: Test clippy-driver - run: | - TOOLCHAIN=$(rustup show active-toolchain | cut -f1 -d' ') - rustup run $TOOLCHAIN bash .github/driver.sh + run: .github/driver.sh + env: + OS: ${{ runner.os }} diff --git a/src/tools/clippy/.github/workflows/clippy_bors.yml b/src/tools/clippy/.github/workflows/clippy_bors.yml index 94515987eba4a..1f4bec9291823 100644 --- a/src/tools/clippy/.github/workflows/clippy_bors.yml +++ b/src/tools/clippy/.github/workflows/clippy_bors.yml @@ -116,9 +116,7 @@ jobs: working-directory: clippy_dev - name: Test clippy-driver - run: | - TOOLCHAIN=$(rustup show active-toolchain | cut -f1 -d' ') - rustup run $TOOLCHAIN bash .github/driver.sh + run: .github/driver.sh env: OS: ${{ runner.os }} diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md index 9c9ea11408143..d5115f70f6620 100644 --- a/src/tools/clippy/CHANGELOG.md +++ b/src/tools/clippy/CHANGELOG.md @@ -5249,6 +5249,7 @@ Released 2018-09-13 [`disallowed_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_type [`disallowed_types`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_types [`diverging_sub_expression`]: https://rust-lang.github.io/rust-clippy/master/index.html#diverging_sub_expression +[`doc_lazy_continuation`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_lazy_continuation [`doc_link_with_quotes`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_link_with_quotes [`doc_markdown`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown [`double_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_comparisons @@ -5447,6 +5448,7 @@ Released 2018-09-13 [`little_endian_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#little_endian_bytes [`logic_bug`]: https://rust-lang.github.io/rust-clippy/master/index.html#logic_bug [`lossy_float_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#lossy_float_literal +[`macro_metavars_in_unsafe`]: https://rust-lang.github.io/rust-clippy/master/index.html#macro_metavars_in_unsafe [`macro_use_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#macro_use_imports [`main_recursion`]: https://rust-lang.github.io/rust-clippy/master/index.html#main_recursion [`manual_assert`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_assert @@ -5702,6 +5704,7 @@ Released 2018-09-13 [`ref_option_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_option_ref [`ref_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_patterns [`regex_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#regex_macro +[`renamed_function_params`]: https://rust-lang.github.io/rust-clippy/master/index.html#renamed_function_params [`repeat_once`]: https://rust-lang.github.io/rust-clippy/master/index.html#repeat_once [`repeat_vec_with_capacity`]: https://rust-lang.github.io/rust-clippy/master/index.html#repeat_vec_with_capacity [`replace_consts`]: https://rust-lang.github.io/rust-clippy/master/index.html#replace_consts @@ -5908,6 +5911,7 @@ Released 2018-09-13 [`verbose_file_reads`]: https://rust-lang.github.io/rust-clippy/master/index.html#verbose_file_reads [`vtable_address_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#vtable_address_comparisons [`waker_clone_wake`]: https://rust-lang.github.io/rust-clippy/master/index.html#waker_clone_wake +[`while_float`]: https://rust-lang.github.io/rust-clippy/master/index.html#while_float [`while_immutable_condition`]: https://rust-lang.github.io/rust-clippy/master/index.html#while_immutable_condition [`while_let_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#while_let_loop [`while_let_on_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#while_let_on_iterator @@ -5939,8 +5943,10 @@ Released 2018-09-13 [`allow-expect-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-expect-in-tests [`allow-mixed-uninlined-format-args`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-mixed-uninlined-format-args [`allow-one-hash-in-raw-strings`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-one-hash-in-raw-strings +[`allow-panic-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-panic-in-tests [`allow-print-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-print-in-tests [`allow-private-module-inception`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-private-module-inception +[`allow-renamed-params-for`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-renamed-params-for [`allow-unwrap-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-unwrap-in-tests [`allow-useless-vec-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-useless-vec-in-tests [`allowed-dotfiles`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allowed-dotfiles @@ -6002,4 +6008,5 @@ Released 2018-09-13 [`vec-box-size-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#vec-box-size-threshold [`verbose-bit-mask-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#verbose-bit-mask-threshold [`warn-on-all-wildcard-imports`]: https://doc.rust-lang.org/clippy/lint_configuration.html#warn-on-all-wildcard-imports +[`warn-unsafe-macro-metavars-in-private-macros`]: https://doc.rust-lang.org/clippy/lint_configuration.html#warn-unsafe-macro-metavars-in-private-macros diff --git a/src/tools/clippy/book/src/lint_configuration.md b/src/tools/clippy/book/src/lint_configuration.md index f6af9810ca166..c8223007df7b3 100644 --- a/src/tools/clippy/book/src/lint_configuration.md +++ b/src/tools/clippy/book/src/lint_configuration.md @@ -101,6 +101,16 @@ Whether to allow `r#""#` when `r""` can be used * [`unnecessary_raw_string_hashes`](https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_raw_string_hashes) +## `allow-panic-in-tests` +Whether `panic` should be allowed in test functions or `#[cfg(test)]` + +**Default Value:** `false` + +--- +**Affected lints:** +* [`panic`](https://rust-lang.github.io/rust-clippy/master/index.html#panic) + + ## `allow-print-in-tests` Whether print macros (ex. `println!`) should be allowed in test functions or `#[cfg(test)]` @@ -122,6 +132,28 @@ Whether to allow module inception if it's not public. * [`module_inception`](https://rust-lang.github.io/rust-clippy/master/index.html#module_inception) +## `allow-renamed-params-for` +List of trait paths to ignore when checking renamed function parameters. + +#### Example + +```toml +allow-renamed-params-for = [ "std::convert::From" ] +``` + +#### Noteworthy + +- By default, the following traits are ignored: `From`, `TryFrom`, `FromStr` +- `".."` can be used as part of the list to indicate that the configured values should be appended to the +default configuration of Clippy. By default, any configuration will replace the default value. + +**Default Value:** `["core::convert::From", "core::convert::TryFrom", "core::str::FromStr"]` + +--- +**Affected lints:** +* [`renamed_function_params`](https://rust-lang.github.io/rust-clippy/master/index.html#renamed_function_params) + + ## `allow-unwrap-in-tests` Whether `unwrap` should be allowed in test functions or `#[cfg(test)]` @@ -900,3 +932,13 @@ Whether to allow certain wildcard imports (prelude, super in tests). * [`wildcard_imports`](https://rust-lang.github.io/rust-clippy/master/index.html#wildcard_imports) +## `warn-unsafe-macro-metavars-in-private-macros` +Whether to also emit warnings for unsafe blocks with metavariable expansions in **private** macros. + +**Default Value:** `false` + +--- +**Affected lints:** +* [`macro_metavars_in_unsafe`](https://rust-lang.github.io/rust-clippy/master/index.html#macro_metavars_in_unsafe) + + diff --git a/src/tools/clippy/clippy_config/src/conf.rs b/src/tools/clippy/clippy_config/src/conf.rs index 5cfcbdb57d735..cfdf620b7d074 100644 --- a/src/tools/clippy/clippy_config/src/conf.rs +++ b/src/tools/clippy/clippy_config/src/conf.rs @@ -40,6 +40,8 @@ const DEFAULT_DOC_VALID_IDENTS: &[&str] = &[ const DEFAULT_DISALLOWED_NAMES: &[&str] = &["foo", "baz", "quux"]; const DEFAULT_ALLOWED_IDENTS_BELOW_MIN_CHARS: &[&str] = &["i", "j", "x", "y", "z", "w", "n"]; const DEFAULT_ALLOWED_PREFIXES: &[&str] = &["to", "as", "into", "from", "try_into", "try_from"]; +const DEFAULT_ALLOWED_TRAITS_WITH_RENAMED_PARAMS: &[&str] = + &["core::convert::From", "core::convert::TryFrom", "core::str::FromStr"]; /// Conf with parse errors #[derive(Default)] @@ -455,6 +457,10 @@ define_Conf! { /// /// Whether `unwrap` should be allowed in test functions or `#[cfg(test)]` (allow_unwrap_in_tests: bool = false), + /// Lint: PANIC. + /// + /// Whether `panic` should be allowed in test functions or `#[cfg(test)]` + (allow_panic_in_tests: bool = false), /// Lint: DBG_MACRO. /// /// Whether `dbg!` should be allowed in test functions or `#[cfg(test)]` @@ -613,6 +619,27 @@ define_Conf! { /// - Use `".."` as part of the list to indicate that the configured values should be appended to the /// default configuration of Clippy. By default, any configuration will replace the default value (allowed_prefixes: Vec = DEFAULT_ALLOWED_PREFIXES.iter().map(ToString::to_string).collect()), + /// Lint: RENAMED_FUNCTION_PARAMS. + /// + /// List of trait paths to ignore when checking renamed function parameters. + /// + /// #### Example + /// + /// ```toml + /// allow-renamed-params-for = [ "std::convert::From" ] + /// ``` + /// + /// #### Noteworthy + /// + /// - By default, the following traits are ignored: `From`, `TryFrom`, `FromStr` + /// - `".."` can be used as part of the list to indicate that the configured values should be appended to the + /// default configuration of Clippy. By default, any configuration will replace the default value. + (allow_renamed_params_for: Vec = + DEFAULT_ALLOWED_TRAITS_WITH_RENAMED_PARAMS.iter().map(ToString::to_string).collect()), + /// Lint: MACRO_METAVARS_IN_UNSAFE. + /// + /// Whether to also emit warnings for unsafe blocks with metavariable expansions in **private** macros. + (warn_unsafe_macro_metavars_in_private_macros: bool = false), } /// Search for the configuration file. @@ -674,6 +701,10 @@ fn deserialize(file: &SourceFile) -> TryConf { extend_vec_if_indicator_present(&mut conf.conf.doc_valid_idents, DEFAULT_DOC_VALID_IDENTS); extend_vec_if_indicator_present(&mut conf.conf.disallowed_names, DEFAULT_DISALLOWED_NAMES); extend_vec_if_indicator_present(&mut conf.conf.allowed_prefixes, DEFAULT_ALLOWED_PREFIXES); + extend_vec_if_indicator_present( + &mut conf.conf.allow_renamed_params_for, + DEFAULT_ALLOWED_TRAITS_WITH_RENAMED_PARAMS, + ); // TODO: THIS SHOULD BE TESTED, this comment will be gone soon if conf.conf.allowed_idents_below_min_chars.contains("..") { conf.conf diff --git a/src/tools/clippy/clippy_config/src/msrvs.rs b/src/tools/clippy/clippy_config/src/msrvs.rs index 14808440d48d3..a3e7d0c3fa5fe 100644 --- a/src/tools/clippy/clippy_config/src/msrvs.rs +++ b/src/tools/clippy/clippy_config/src/msrvs.rs @@ -26,7 +26,8 @@ msrv_aliases! { 1,63,0 { CLONE_INTO } 1,62,0 { BOOL_THEN_SOME, DEFAULT_ENUM_ATTRIBUTE } 1,59,0 { THREAD_LOCAL_INITIALIZER_CAN_BE_MADE_CONST } - 1,58,0 { FORMAT_ARGS_CAPTURE, PATTERN_TRAIT_CHAR_ARRAY } + 1,58,0 { FORMAT_ARGS_CAPTURE, PATTERN_TRAIT_CHAR_ARRAY, CONST_RAW_PTR_DEREF } + 1,56,0 { CONST_FN_UNION } 1,55,0 { SEEK_REWIND } 1,54,0 { INTO_KEYS } 1,53,0 { OR_PATTERNS, MANUAL_BITS, BTREE_MAP_RETAIN, BTREE_SET_RETAIN, ARRAY_INTO_ITERATOR } diff --git a/src/tools/clippy/clippy_dev/Cargo.toml b/src/tools/clippy/clippy_dev/Cargo.toml index 42a953039b1c0..4104e7d94f146 100644 --- a/src/tools/clippy/clippy_dev/Cargo.toml +++ b/src/tools/clippy/clippy_dev/Cargo.toml @@ -1,11 +1,12 @@ [package] name = "clippy_dev" +description = "Clippy developer tooling" version = "0.0.1" edition = "2021" [dependencies] aho-corasick = "1.0" -clap = "4.1.4" +clap = { version = "4.4", features = ["derive"] } indoc = "1.0" itertools = "0.12" opener = "0.6" diff --git a/src/tools/clippy/clippy_dev/src/main.rs b/src/tools/clippy/clippy_dev/src/main.rs index 397a0e990829c..366b52b25dfcf 100644 --- a/src/tools/clippy/clippy_dev/src/main.rs +++ b/src/tools/clippy/clippy_dev/src/main.rs @@ -2,350 +2,292 @@ // warn on lints, that are included in `rust-lang/rust`s bootstrap #![warn(rust_2018_idioms, unused_lifetimes)] -use clap::{Arg, ArgAction, ArgMatches, Command}; +use clap::{Args, Parser, Subcommand}; use clippy_dev::{dogfood, fmt, lint, new_lint, serve, setup, update_lints}; -use indoc::indoc; use std::convert::Infallible; fn main() { - let matches = get_clap_config(); + let dev = Dev::parse(); - match matches.subcommand() { - Some(("bless", _)) => { + match dev.command { + DevCommand::Bless => { eprintln!("use `cargo bless` to automatically replace `.stderr` and `.fixed` files as tests are being run"); }, - Some(("dogfood", matches)) => { - dogfood::dogfood( - matches.get_flag("fix"), - matches.get_flag("allow-dirty"), - matches.get_flag("allow-staged"), - ); - }, - Some(("fmt", matches)) => { - fmt::run(matches.get_flag("check"), matches.get_flag("verbose")); - }, - Some(("update_lints", matches)) => { - if matches.get_flag("print-only") { + DevCommand::Dogfood { + fix, + allow_dirty, + allow_staged, + } => dogfood::dogfood(fix, allow_dirty, allow_staged), + DevCommand::Fmt { check, verbose } => fmt::run(check, verbose), + DevCommand::UpdateLints { print_only, check } => { + if print_only { update_lints::print_lints(); - } else if matches.get_flag("check") { + } else if check { update_lints::update(update_lints::UpdateMode::Check); } else { update_lints::update(update_lints::UpdateMode::Change); } }, - Some(("new_lint", matches)) => { - match new_lint::create( - matches.get_one::("pass").unwrap(), - matches.get_one::("name"), - matches.get_one::("category").map(String::as_str), - matches.get_one::("type").map(String::as_str), - matches.get_flag("msrv"), - ) { - Ok(()) => update_lints::update(update_lints::UpdateMode::Change), - Err(e) => eprintln!("Unable to create lint: {e}"), - } + DevCommand::NewLint { + pass, + name, + category, + r#type, + msrv, + } => match new_lint::create(&pass, &name, &category, r#type.as_deref(), msrv) { + Ok(()) => update_lints::update(update_lints::UpdateMode::Change), + Err(e) => eprintln!("Unable to create lint: {e}"), }, - Some(("setup", sub_command)) => match sub_command.subcommand() { - Some(("git-hook", matches)) => { - if matches.get_flag("remove") { - setup::git_hook::remove_hook(); + DevCommand::Setup(SetupCommand { subcommand }) => match subcommand { + SetupSubcommand::Intellij { remove, repo_path } => { + if remove { + setup::intellij::remove_rustc_src(); } else { - setup::git_hook::install_hook(matches.get_flag("force-override")); + setup::intellij::setup_rustc_src(&repo_path); } }, - Some(("intellij", matches)) => { - if matches.get_flag("remove") { - setup::intellij::remove_rustc_src(); + SetupSubcommand::GitHook { remove, force_override } => { + if remove { + setup::git_hook::remove_hook(); } else { - setup::intellij::setup_rustc_src( - matches - .get_one::("rustc-repo-path") - .expect("this field is mandatory and therefore always valid"), - ); + setup::git_hook::install_hook(force_override); } }, - Some(("toolchain", matches)) => { - setup::toolchain::create( - matches.get_flag("force"), - matches.get_flag("release"), - matches.get_one::("name").unwrap(), - ); - }, - Some(("vscode-tasks", matches)) => { - if matches.get_flag("remove") { + SetupSubcommand::Toolchain { force, release, name } => setup::toolchain::create(force, release, &name), + SetupSubcommand::VscodeTasks { remove, force_override } => { + if remove { setup::vscode::remove_tasks(); } else { - setup::vscode::install_tasks(matches.get_flag("force-override")); + setup::vscode::install_tasks(force_override); } }, - _ => {}, - }, - Some(("remove", sub_command)) => match sub_command.subcommand() { - Some(("git-hook", _)) => setup::git_hook::remove_hook(), - Some(("intellij", _)) => setup::intellij::remove_rustc_src(), - Some(("vscode-tasks", _)) => setup::vscode::remove_tasks(), - _ => {}, - }, - Some(("serve", matches)) => { - let port = *matches.get_one::("port").unwrap(); - let lint = matches.get_one::("lint"); - serve::run(port, lint); }, - Some(("lint", matches)) => { - let path = matches.get_one::("path").unwrap(); - let args = matches.get_many::("args").into_iter().flatten(); - lint::run(path, args); + DevCommand::Remove(RemoveCommand { subcommand }) => match subcommand { + RemoveSubcommand::Intellij => setup::intellij::remove_rustc_src(), + RemoveSubcommand::GitHook => setup::git_hook::remove_hook(), + RemoveSubcommand::VscodeTasks => setup::vscode::remove_tasks(), }, - Some(("rename_lint", matches)) => { - let old_name = matches.get_one::("old_name").unwrap(); - let new_name = matches.get_one::("new_name").unwrap_or(old_name); - let uplift = matches.get_flag("uplift"); - update_lints::rename(old_name, new_name, uplift); - }, - Some(("deprecate", matches)) => { - let name = matches.get_one::("name").unwrap(); - let reason = matches.get_one("reason"); - update_lints::deprecate(name, reason); - }, - _ => {}, + DevCommand::Serve { port, lint } => serve::run(port, lint), + DevCommand::Lint { path, args } => lint::run(&path, args.iter()), + DevCommand::RenameLint { + old_name, + new_name, + uplift, + } => update_lints::rename(&old_name, new_name.as_ref().unwrap_or(&old_name), uplift), + DevCommand::Deprecate { name, reason } => update_lints::deprecate(&name, reason.as_deref()), } } -fn get_clap_config() -> ArgMatches { - Command::new("Clippy developer tooling") - .arg_required_else_help(true) - .subcommands([ - Command::new("bless").about("bless the test output changes").arg( - Arg::new("ignore-timestamp") - .long("ignore-timestamp") - .action(ArgAction::SetTrue) - .help("Include files updated before clippy was built"), - ), - Command::new("dogfood").about("Runs the dogfood test").args([ - Arg::new("fix") - .long("fix") - .action(ArgAction::SetTrue) - .help("Apply the suggestions when possible"), - Arg::new("allow-dirty") - .long("allow-dirty") - .action(ArgAction::SetTrue) - .help("Fix code even if the working directory has changes") - .requires("fix"), - Arg::new("allow-staged") - .long("allow-staged") - .action(ArgAction::SetTrue) - .help("Fix code even if the working directory has staged changes") - .requires("fix"), - ]), - Command::new("fmt") - .about("Run rustfmt on all projects and tests") - .args([ - Arg::new("check") - .long("check") - .action(ArgAction::SetTrue) - .help("Use the rustfmt --check option"), - Arg::new("verbose") - .short('v') - .long("verbose") - .action(ArgAction::SetTrue) - .help("Echo commands run"), - ]), - Command::new("update_lints") - .about("Updates lint registration and information from the source code") - .long_about( - "Makes sure that:\n \ - * the lint count in README.md is correct\n \ - * the changelog contains markdown link references at the bottom\n \ - * all lint groups include the correct lints\n \ - * lint modules in `clippy_lints/*` are visible in `src/lib.rs` via `pub mod`\n \ - * all lints are registered in the lint store", - ) - .args([ - Arg::new("print-only") - .long("print-only") - .action(ArgAction::SetTrue) - .help( - "Print a table of lints to STDOUT. \ - This does not include deprecated and internal lints. \ - (Does not modify any files)", - ), - Arg::new("check") - .long("check") - .action(ArgAction::SetTrue) - .help("Checks that `cargo dev update_lints` has been run. Used on CI."), - ]), - Command::new("new_lint") - .about("Create new lint and run `cargo dev update_lints`") - .args([ - Arg::new("pass") - .short('p') - .long("pass") - .help("Specify whether the lint runs during the early or late pass") - .value_parser(["early", "late"]) - .conflicts_with("type") - .default_value("late"), - Arg::new("name") - .short('n') - .long("name") - .help("Name of the new lint in snake case, ex: fn_too_long") - .required(true) - .value_parser(|name: &str| Ok::<_, Infallible>(name.replace('-', "_"))), - Arg::new("category") - .short('c') - .long("category") - .help("What category the lint belongs to") - .default_value("nursery") - .value_parser([ - "style", - "correctness", - "suspicious", - "complexity", - "perf", - "pedantic", - "restriction", - "cargo", - "nursery", - "internal", - ]), - Arg::new("type").long("type").help("What directory the lint belongs in"), - Arg::new("msrv") - .long("msrv") - .action(ArgAction::SetTrue) - .help("Add MSRV config code to the lint"), - ]), - Command::new("setup") - .about("Support for setting up your personal development environment") - .arg_required_else_help(true) - .subcommands([ - Command::new("git-hook") - .about("Add a pre-commit git hook that formats your code to make it look pretty") - .args([ - Arg::new("remove") - .long("remove") - .action(ArgAction::SetTrue) - .help("Remove the pre-commit hook added with 'cargo dev setup git-hook'"), - Arg::new("force-override") - .long("force-override") - .short('f') - .action(ArgAction::SetTrue) - .help("Forces the override of an existing git pre-commit hook"), - ]), - Command::new("intellij") - .about("Alter dependencies so Intellij Rust can find rustc internals") - .args([ - Arg::new("remove") - .long("remove") - .action(ArgAction::SetTrue) - .help("Remove the dependencies added with 'cargo dev setup intellij'"), - Arg::new("rustc-repo-path") - .long("repo-path") - .short('r') - .help("The path to a rustc repo that will be used for setting the dependencies") - .value_name("path") - .conflicts_with("remove") - .required(true), - ]), - Command::new("toolchain") - .about("Install a rustup toolchain pointing to the local clippy build") - .args([ - Arg::new("force") - .long("force") - .short('f') - .action(ArgAction::SetTrue) - .help("Override an existing toolchain"), - Arg::new("release") - .long("release") - .short('r') - .action(ArgAction::SetTrue) - .help("Point to --release clippy binaries"), - Arg::new("name") - .long("name") - .default_value("clippy") - .help("The name of the created toolchain"), - ]), - Command::new("vscode-tasks") - .about("Add several tasks to vscode for formatting, validation and testing") - .args([ - Arg::new("remove") - .long("remove") - .action(ArgAction::SetTrue) - .help("Remove the tasks added with 'cargo dev setup vscode-tasks'"), - Arg::new("force-override") - .long("force-override") - .short('f') - .action(ArgAction::SetTrue) - .help("Forces the override of existing vscode tasks"), - ]), - ]), - Command::new("remove") - .about("Support for undoing changes done by the setup command") - .arg_required_else_help(true) - .subcommands([ - Command::new("git-hook").about("Remove any existing pre-commit git hook"), - Command::new("vscode-tasks").about("Remove any existing vscode tasks"), - Command::new("intellij").about("Removes rustc source paths added via `cargo dev setup intellij`"), - ]), - Command::new("serve") - .about("Launch a local 'ALL the Clippy Lints' website in a browser") - .args([ - Arg::new("port") - .long("port") - .short('p') - .help("Local port for the http server") - .default_value("8000") - .value_parser(clap::value_parser!(u16)), - Arg::new("lint").help("Which lint's page to load initially (optional)"), - ]), - Command::new("lint") - .about("Manually run clippy on a file or package") - .after_help(indoc! {" - EXAMPLES - Lint a single file: - cargo dev lint tests/ui/attrs.rs +#[derive(Parser)] +#[command(name = "dev", about)] +struct Dev { + #[command(subcommand)] + command: DevCommand, +} - Lint a package directory: - cargo dev lint tests/ui-cargo/wildcard_dependencies/fail - cargo dev lint ~/my-project +#[derive(Subcommand)] +enum DevCommand { + /// Bless the test output changes + Bless, + /// Runs the dogfood test + Dogfood { + #[arg(long)] + /// Apply the suggestions when possible + fix: bool, + #[arg(long, requires = "fix")] + /// Fix code even if the working directory has changes + allow_dirty: bool, + #[arg(long, requires = "fix")] + /// Fix code even if the working directory has staged changes + allow_staged: bool, + }, + /// Run rustfmt on all projects and tests + Fmt { + #[arg(long)] + /// Use the rustfmt --check option + check: bool, + #[arg(short, long)] + /// Echo commands run + verbose: bool, + }, + #[command(name = "update_lints")] + /// Updates lint registration and information from the source code + /// + /// Makes sure that: {n} + /// * the lint count in README.md is correct {n} + /// * the changelog contains markdown link references at the bottom {n} + /// * all lint groups include the correct lints {n} + /// * lint modules in `clippy_lints/*` are visible in `src/lib.rs` via `pub mod` {n} + /// * all lints are registered in the lint store + UpdateLints { + #[arg(long)] + /// Print a table of lints to STDOUT + /// + /// This does not include deprecated and internal lints. (Does not modify any files) + print_only: bool, + #[arg(long)] + /// Checks that `cargo dev update_lints` has been run. Used on CI. + check: bool, + }, + #[command(name = "new_lint")] + /// Create a new lint and run `cargo dev update_lints` + NewLint { + #[arg(short, long, value_parser = ["early", "late"], conflicts_with = "type", default_value = "late")] + /// Specify whether the lint runs during the early or late pass + pass: String, + #[arg( + short, + long, + value_parser = |name: &str| Ok::<_, Infallible>(name.replace('-', "_")), + )] + /// Name of the new lint in snake case, ex: `fn_too_long` + name: String, + #[arg( + short, + long, + value_parser = [ + "style", + "correctness", + "suspicious", + "complexity", + "perf", + "pedantic", + "restriction", + "cargo", + "nursery", + "internal", + ], + default_value = "nursery", + )] + /// What category the lint belongs to + category: String, + #[arg(long)] + /// What directory the lint belongs in + r#type: Option, + #[arg(long)] + /// Add MSRV config code to the lint + msrv: bool, + }, + /// Support for setting up your personal development environment + Setup(SetupCommand), + /// Support for removing changes done by the setup command + Remove(RemoveCommand), + /// Launch a local 'ALL the Clippy Lints' website in a browser + Serve { + #[arg(short, long, default_value = "8000")] + /// Local port for the http server + port: u16, + #[arg(long)] + /// Which lint's page to load initially (optional) + lint: Option, + }, + #[allow(clippy::doc_markdown)] + /// Manually run clippy on a file or package + /// + /// ## Examples + /// + /// Lint a single file: {n} + /// cargo dev lint tests/ui/attrs.rs + /// + /// Lint a package directory: {n} + /// cargo dev lint tests/ui-cargo/wildcard_dependencies/fail {n} + /// cargo dev lint ~/my-project + /// + /// Run rustfix: {n} + /// cargo dev lint ~/my-project -- --fix + /// + /// Set lint levels: {n} + /// cargo dev lint file.rs -- -W clippy::pedantic {n} + /// cargo dev lint ~/my-project -- -- -W clippy::pedantic + Lint { + /// The path to a file or package directory to lint + path: String, + /// Pass extra arguments to cargo/clippy-driver + args: Vec, + }, + #[command(name = "rename_lint")] + /// Rename a lint + RenameLint { + /// The name of the lint to rename + old_name: String, + #[arg(required_unless_present = "uplift")] + /// The new name of the lint + new_name: Option, + #[arg(long)] + /// This lint will be uplifted into rustc + uplift: bool, + }, + /// Deprecate the given lint + Deprecate { + /// The name of the lint to deprecate + name: String, + #[arg(long, short)] + /// The reason for deprecation + reason: Option, + }, +} - Run rustfix: - cargo dev lint ~/my-project -- --fix +#[derive(Args)] +struct SetupCommand { + #[command(subcommand)] + subcommand: SetupSubcommand, +} + +#[derive(Subcommand)] +enum SetupSubcommand { + /// Alter dependencies so Intellij Rust can find rustc internals + Intellij { + #[arg(long)] + /// Remove the dependencies added with 'cargo dev setup intellij' + remove: bool, + #[arg(long, short, conflicts_with = "remove")] + /// The path to a rustc repo that will be used for setting the dependencies + repo_path: String, + }, + /// Add a pre-commit git hook that formats your code to make it look pretty + GitHook { + #[arg(long)] + /// Remove the pre-commit hook added with 'cargo dev setup git-hook' + remove: bool, + #[arg(long, short)] + /// Forces the override of an existing git pre-commit hook + force_override: bool, + }, + /// Install a rustup toolchain pointing to the local clippy build + Toolchain { + #[arg(long, short)] + /// Override an existing toolchain + force: bool, + #[arg(long, short)] + /// Point to --release clippy binary + release: bool, + #[arg(long, default_value = "clippy")] + /// Name of the toolchain + name: String, + }, + /// Add several tasks to vscode for formatting, validation and testing + VscodeTasks { + #[arg(long)] + /// Remove the tasks added with 'cargo dev setup vscode-tasks' + remove: bool, + #[arg(long, short)] + /// Forces the override of existing vscode tasks + force_override: bool, + }, +} + +#[derive(Args)] +struct RemoveCommand { + #[command(subcommand)] + subcommand: RemoveSubcommand, +} - Set lint levels: - cargo dev lint file.rs -- -W clippy::pedantic - cargo dev lint ~/my-project -- -- -W clippy::pedantic - "}) - .args([ - Arg::new("path") - .required(true) - .help("The path to a file or package directory to lint"), - Arg::new("args") - .action(ArgAction::Append) - .help("Pass extra arguments to cargo/clippy-driver"), - ]), - Command::new("rename_lint").about("Renames the given lint").args([ - Arg::new("old_name") - .index(1) - .required(true) - .help("The name of the lint to rename"), - Arg::new("new_name") - .index(2) - .required_unless_present("uplift") - .help("The new name of the lint"), - Arg::new("uplift") - .long("uplift") - .action(ArgAction::SetTrue) - .help("This lint will be uplifted into rustc"), - ]), - Command::new("deprecate").about("Deprecates the given lint").args([ - Arg::new("name") - .index(1) - .required(true) - .help("The name of the lint to deprecate"), - Arg::new("reason") - .long("reason") - .short('r') - .help("The reason for deprecation"), - ]), - ]) - .get_matches() +#[derive(Subcommand)] +enum RemoveSubcommand { + /// Remove the dependencies added with 'cargo dev setup intellij' + Intellij, + /// Remove the pre-commit git hook + GitHook, + /// Remove the tasks added with 'cargo dev setup vscode-tasks' + VscodeTasks, } diff --git a/src/tools/clippy/clippy_dev/src/new_lint.rs b/src/tools/clippy/clippy_dev/src/new_lint.rs index 2940d56350f41..b6481dde4dde0 100644 --- a/src/tools/clippy/clippy_dev/src/new_lint.rs +++ b/src/tools/clippy/clippy_dev/src/new_lint.rs @@ -36,22 +36,16 @@ impl Context for io::Result { /// # Errors /// /// This function errors out if the files couldn't be created or written to. -pub fn create( - pass: &String, - lint_name: Option<&String>, - category: Option<&str>, - mut ty: Option<&str>, - msrv: bool, -) -> io::Result<()> { - if category == Some("cargo") && ty.is_none() { +pub fn create(pass: &str, name: &str, category: &str, mut ty: Option<&str>, msrv: bool) -> io::Result<()> { + if category == "cargo" && ty.is_none() { // `cargo` is a special category, these lints should always be in `clippy_lints/src/cargo` ty = Some("cargo"); } let lint = LintData { pass, - name: lint_name.expect("`name` argument is validated by clap"), - category: category.expect("`category` argument is validated by clap"), + name, + category, ty, project_root: clippy_project_root(), }; diff --git a/src/tools/clippy/clippy_dev/src/serve.rs b/src/tools/clippy/clippy_dev/src/serve.rs index ea925f6709f9b..4a4261d1a1e63 100644 --- a/src/tools/clippy/clippy_dev/src/serve.rs +++ b/src/tools/clippy/clippy_dev/src/serve.rs @@ -8,7 +8,7 @@ use std::{env, thread}; /// # Panics /// /// Panics if the python commands could not be spawned -pub fn run(port: u16, lint: Option<&String>) -> ! { +pub fn run(port: u16, lint: Option) -> ! { let mut url = Some(match lint { None => format!("http://localhost:{port}"), Some(lint) => format!("http://localhost:{port}/#{lint}"), diff --git a/src/tools/clippy/clippy_dev/src/update_lints.rs b/src/tools/clippy/clippy_dev/src/update_lints.rs index 625b133959133..45353901c98fd 100644 --- a/src/tools/clippy/clippy_dev/src/update_lints.rs +++ b/src/tools/clippy/clippy_dev/src/update_lints.rs @@ -314,7 +314,7 @@ const DEFAULT_DEPRECATION_REASON: &str = "default deprecation note"; /// # Panics /// /// If a file path could not read from or written to -pub fn deprecate(name: &str, reason: Option<&String>) { +pub fn deprecate(name: &str, reason: Option<&str>) { fn finish( (lints, mut deprecated_lints, renamed_lints): (Vec, Vec, Vec), name: &str, @@ -335,7 +335,7 @@ pub fn deprecate(name: &str, reason: Option<&String>) { println!("note: you must run `cargo uitest` to update the test results"); } - let reason = reason.map_or(DEFAULT_DEPRECATION_REASON, String::as_str); + let reason = reason.unwrap_or(DEFAULT_DEPRECATION_REASON); let name_lower = name.to_lowercase(); let name_upper = name.to_uppercase(); diff --git a/src/tools/clippy/clippy_lints/src/assigning_clones.rs b/src/tools/clippy/clippy_lints/src/assigning_clones.rs index f0dafb1ae0d52..e94a6f3e3fc5c 100644 --- a/src/tools/clippy/clippy_lints/src/assigning_clones.rs +++ b/src/tools/clippy/clippy_lints/src/assigning_clones.rs @@ -2,15 +2,15 @@ use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::HirNode; use clippy_utils::sugg::Sugg; -use clippy_utils::{is_trait_method, path_to_local}; +use clippy_utils::{is_trait_method, local_is_initialized, path_to_local}; use rustc_errors::Applicability; -use rustc_hir::{self as hir, Expr, ExprKind, Node}; +use rustc_hir::{self as hir, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, Instance, Mutability}; use rustc_session::impl_lint_pass; use rustc_span::def_id::DefId; use rustc_span::symbol::sym; -use rustc_span::ExpnKind; +use rustc_span::{ExpnKind, SyntaxContext}; declare_clippy_lint! { /// ### What it does @@ -36,6 +36,7 @@ declare_clippy_lint! { /// Use instead: /// ```rust /// struct Thing; + /// /// impl Clone for Thing { /// fn clone(&self) -> Self { todo!() } /// fn clone_from(&mut self, other: &Self) { todo!() } @@ -47,7 +48,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.78.0"] pub ASSIGNING_CLONES, - perf, + pedantic, "assigning the result of cloning may be inefficient" } @@ -67,7 +68,8 @@ impl_lint_pass!(AssigningClones => [ASSIGNING_CLONES]); impl<'tcx> LateLintPass<'tcx> for AssigningClones { fn check_expr(&mut self, cx: &LateContext<'tcx>, assign_expr: &'tcx Expr<'_>) { // Do not fire the lint in macros - let expn_data = assign_expr.span().ctxt().outer_expn_data(); + let ctxt = assign_expr.span().ctxt(); + let expn_data = ctxt.outer_expn_data(); match expn_data.kind { ExpnKind::AstPass(_) | ExpnKind::Desugaring(_) | ExpnKind::Macro(..) => return, ExpnKind::Root => {}, @@ -82,7 +84,7 @@ impl<'tcx> LateLintPass<'tcx> for AssigningClones { }; if is_ok_to_suggest(cx, lhs, &call, &self.msrv) { - suggest(cx, assign_expr, lhs, &call); + suggest(cx, ctxt, assign_expr, lhs, &call); } } @@ -163,9 +165,7 @@ fn is_ok_to_suggest<'tcx>(cx: &LateContext<'tcx>, lhs: &Expr<'tcx>, call: &CallC // TODO: This check currently bails if the local variable has no initializer. // That is overly conservative - the lint should fire even if there was no initializer, // but the variable has been initialized before `lhs` was evaluated. - if let Some(Node::LetStmt(local)) = cx.tcx.hir().parent_id_iter(local).next().map(|p| cx.tcx.hir_node(p)) - && local.init.is_none() - { + if !local_is_initialized(cx, local) { return false; } } @@ -222,14 +222,20 @@ fn is_ok_to_suggest<'tcx>(cx: &LateContext<'tcx>, lhs: &Expr<'tcx>, call: &CallC implemented_fns.contains_key(&provided_fn.def_id) } -fn suggest<'tcx>(cx: &LateContext<'tcx>, assign_expr: &Expr<'tcx>, lhs: &Expr<'tcx>, call: &CallCandidate<'tcx>) { +fn suggest<'tcx>( + cx: &LateContext<'tcx>, + ctxt: SyntaxContext, + assign_expr: &Expr<'tcx>, + lhs: &Expr<'tcx>, + call: &CallCandidate<'tcx>, +) { span_lint_and_then(cx, ASSIGNING_CLONES, assign_expr.span, call.message(), |diag| { let mut applicability = Applicability::Unspecified; diag.span_suggestion( assign_expr.span, call.suggestion_msg(), - call.suggested_replacement(cx, lhs, &mut applicability), + call.suggested_replacement(cx, ctxt, lhs, &mut applicability), applicability, ); }); @@ -275,6 +281,7 @@ impl<'tcx> CallCandidate<'tcx> { fn suggested_replacement( &self, cx: &LateContext<'tcx>, + ctxt: SyntaxContext, lhs: &Expr<'tcx>, applicability: &mut Applicability, ) -> String { @@ -294,7 +301,7 @@ impl<'tcx> CallCandidate<'tcx> { // Determine whether we need to reference the argument to clone_from(). let clone_receiver_type = cx.typeck_results().expr_ty(receiver); let clone_receiver_adj_type = cx.typeck_results().expr_ty_adjusted(receiver); - let mut arg_sugg = Sugg::hir_with_applicability(cx, receiver, "_", applicability); + let mut arg_sugg = Sugg::hir_with_context(cx, receiver, ctxt, "_", applicability); if clone_receiver_type != clone_receiver_adj_type { // The receiver may have been a value type, so we need to add an `&` to // be sure the argument to clone_from will be a reference. @@ -312,7 +319,7 @@ impl<'tcx> CallCandidate<'tcx> { Sugg::hir_with_applicability(cx, lhs, "_", applicability).mut_addr() }; // The RHS had to be exactly correct before the call, there is no auto-deref for function calls. - let rhs_sugg = Sugg::hir_with_applicability(cx, self_arg, "_", applicability); + let rhs_sugg = Sugg::hir_with_context(cx, self_arg, ctxt, "_", applicability); format!("Clone::clone_from({self_sugg}, {rhs_sugg})") }, @@ -341,11 +348,11 @@ impl<'tcx> CallCandidate<'tcx> { match self.kind { CallKind::MethodCall { receiver } => { - let receiver_sugg = Sugg::hir_with_applicability(cx, receiver, "_", applicability); + let receiver_sugg = Sugg::hir_with_context(cx, receiver, ctxt, "_", applicability); format!("{receiver_sugg}.clone_into({rhs_sugg})") }, CallKind::FunctionCall { self_arg, .. } => { - let self_sugg = Sugg::hir_with_applicability(cx, self_arg, "_", applicability); + let self_sugg = Sugg::hir_with_context(cx, self_arg, ctxt, "_", applicability); format!("ToOwned::clone_into({self_sugg}, {rhs_sugg})") }, } 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 736ee48641d8d..40a1c4e288422 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/duplicated_attributes.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/duplicated_attributes.rs @@ -36,9 +36,10 @@ fn check_duplicated_attr( } let Some(ident) = attr.ident() else { return }; let name = ident.name; - if name == sym::doc || name == sym::cfg_attr { + if name == sym::doc || name == sym::cfg_attr || name == sym::rustc_on_unimplemented { // 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. return; } if let Some(direct_parent) = parent.last() diff --git a/src/tools/clippy/clippy_lints/src/attrs/mod.rs b/src/tools/clippy/clippy_lints/src/attrs/mod.rs index 8f47bc7653b7c..39f4060779957 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/mod.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/mod.rs @@ -61,11 +61,21 @@ declare_clippy_lint! { /// /// This lint permits lint attributes for lints emitted on the items themself. /// For `use` items these lints are: + /// * ambiguous_glob_reexports + /// * dead_code /// * deprecated + /// * hidden_glob_reexports /// * unreachable_pub - /// * unused_imports + /// * unused + /// * unused_braces + /// * unused_import_braces + /// * clippy::disallowed_types /// * clippy::enum_glob_use /// * clippy::macro_use_imports + /// * clippy::module_name_repetitions + /// * clippy::redundant_pub_crate + /// * clippy::single_component_path_imports + /// * clippy::unsafe_removed_from_name /// * clippy::wildcard_imports /// /// For `extern crate` items these lints are: 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 7575f502a7c29..f0868231d01a3 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs @@ -2,6 +2,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, snippet_opt}; +use rustc_ast::NestedMetaItem; use rustc_errors::Applicability; use rustc_hir::{Item, ItemKind}; use rustc_lint::{LateContext, LintContext}; @@ -20,26 +21,40 @@ pub(super) fn check(cx: &LateContext<'_>, item: &Item<'_>, attrs: &[Attribute]) for lint in lint_list { match item.kind { ItemKind::Use(..) => { - if is_word(lint, sym::unused_imports) - || is_word(lint, sym::deprecated) - || is_word(lint, sym!(unreachable_pub)) - || is_word(lint, sym!(unused)) - || is_word(lint, sym!(unused_import_braces)) - || extract_clippy_lint(lint).map_or(false, |s| { - matches!( - s.as_str(), - "wildcard_imports" - | "enum_glob_use" - | "redundant_pub_crate" - | "macro_use_imports" - | "unsafe_removed_from_name" - | "module_name_repetitions" - | "single_component_path_imports" - ) - }) + if let NestedMetaItem::MetaItem(meta_item) = lint + && meta_item.is_word() + && let Some(ident) = meta_item.ident() + && matches!( + ident.name.as_str(), + "ambiguous_glob_reexports" + | "dead_code" + | "deprecated" + | "hidden_glob_reexports" + | "unreachable_pub" + | "unused" + | "unused_braces" + | "unused_import_braces" + | "unused_imports" + ) { return; } + + if extract_clippy_lint(lint).is_some_and(|symbol| { + matches!( + symbol.as_str(), + "wildcard_imports" + | "enum_glob_use" + | "redundant_pub_crate" + | "macro_use_imports" + | "unsafe_removed_from_name" + | "module_name_repetitions" + | "single_component_path_imports" + | "disallowed_types" + ) + }) { + return; + } }, ItemKind::ExternCrate(..) => { if is_word(lint, sym::unused_imports) && skip_unused_imports { 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 a3291c9da1096..0d9eaac882f79 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 @@ -49,7 +49,7 @@ impl LintConfig { type LintTable = BTreeMap, Spanned>; -#[derive(Deserialize, Debug)] +#[derive(Deserialize, Debug, Default)] struct Lints { #[serde(default)] rust: LintTable, @@ -57,9 +57,18 @@ struct Lints { clippy: LintTable, } +#[derive(Deserialize, Debug, Default)] +struct Workspace { + #[serde(default)] + lints: Lints, +} + #[derive(Deserialize, Debug)] struct CargoToml { + #[serde(default)] lints: Lints, + #[serde(default)] + workspace: Workspace, } #[derive(Default, Debug)] @@ -164,5 +173,7 @@ pub fn check(cx: &LateContext<'_>) { check_table(cx, cargo_toml.lints.rust, &rustc_groups, &file); check_table(cx, cargo_toml.lints.clippy, &clippy_groups, &file); + check_table(cx, cargo_toml.workspace.lints.rust, &rustc_groups, &file); + check_table(cx, cargo_toml.workspace.lints.clippy, &clippy_groups, &file); } } 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 2b6e17dc1030d..864489ee3fcd5 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 @@ -255,8 +255,10 @@ fn expr_add_sign(cx: &LateContext<'_>, expr: &Expr<'_>) -> Sign { /// Peels binary operators such as [`BinOpKind::Mul`], [`BinOpKind::Div`] or [`BinOpKind::Rem`], /// where the result depends on: +/// /// - the number of negative values in the entire expression, or /// - the number of negative values on the left hand side of the expression. +/// /// Ignores overflow. /// /// @@ -303,8 +305,10 @@ fn exprs_with_muldiv_binop_peeled<'e>(expr: &'e Expr<'_>) -> Vec<&'e Expr<'e>> { } /// Peels binary operators such as [`BinOpKind::Add`], where the result depends on: +/// /// - all the expressions being positive, or /// - all the expressions being negative. +/// /// Ignores overflow. /// /// Expressions using other operators are preserved, so we can try to evaluate them later. diff --git a/src/tools/clippy/clippy_lints/src/declared_lints.rs b/src/tools/clippy/clippy_lints/src/declared_lints.rs index 5ff7d8e513435..df2ef465700d4 100644 --- a/src/tools/clippy/clippy_lints/src/declared_lints.rs +++ b/src/tools/clippy/clippy_lints/src/declared_lints.rs @@ -140,6 +140,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::disallowed_names::DISALLOWED_NAMES_INFO, crate::disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS_INFO, crate::disallowed_types::DISALLOWED_TYPES_INFO, + crate::doc::DOC_LAZY_CONTINUATION_INFO, crate::doc::DOC_LINK_WITH_QUOTES_INFO, crate::doc::DOC_MARKDOWN_INFO, crate::doc::EMPTY_DOCS_INFO, @@ -205,6 +206,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::functions::MUST_USE_CANDIDATE_INFO, crate::functions::MUST_USE_UNIT_INFO, crate::functions::NOT_UNSAFE_PTR_ARG_DEREF_INFO, + crate::functions::RENAMED_FUNCTION_PARAMS_INFO, crate::functions::RESULT_LARGE_ERR_INFO, crate::functions::RESULT_UNIT_ERR_INFO, crate::functions::TOO_MANY_ARGUMENTS_INFO, @@ -291,9 +293,11 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::loops::SAME_ITEM_PUSH_INFO, crate::loops::SINGLE_ELEMENT_LOOP_INFO, crate::loops::UNUSED_ENUMERATE_INDEX_INFO, + crate::loops::WHILE_FLOAT_INFO, crate::loops::WHILE_IMMUTABLE_CONDITION_INFO, crate::loops::WHILE_LET_LOOP_INFO, crate::loops::WHILE_LET_ON_ITERATOR_INFO, + crate::macro_metavars_in_unsafe::MACRO_METAVARS_IN_UNSAFE_INFO, crate::macro_use::MACRO_USE_IMPORTS_INFO, crate::main_recursion::MAIN_RECURSION_INFO, crate::manual_assert::MANUAL_ASSERT_INFO, diff --git a/src/tools/clippy/clippy_lints/src/doc/lazy_continuation.rs b/src/tools/clippy/clippy_lints/src/doc/lazy_continuation.rs new file mode 100644 index 0000000000000..38bc58a550196 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/doc/lazy_continuation.rs @@ -0,0 +1,95 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use itertools::Itertools; +use rustc_errors::{Applicability, SuggestionStyle}; +use rustc_lint::LateContext; +use rustc_span::{BytePos, Span}; +use std::ops::Range; + +use super::DOC_LAZY_CONTINUATION; + +fn map_container_to_text(c: &super::Container) -> &'static str { + match c { + super::Container::Blockquote => "> ", + // numbered list can have up to nine digits, plus the dot, plus four spaces on either side + super::Container::List(indent) => &" "[0..*indent], + } +} + +// TODO: Adjust the parameters as necessary +pub(super) fn check( + cx: &LateContext<'_>, + doc: &str, + range: Range, + mut span: Span, + containers: &[super::Container], +) { + if doc[range.clone()].contains('\t') { + // We don't do tab stops correctly. + return; + } + + let ccount = doc[range.clone()].chars().filter(|c| *c == '>').count(); + let blockquote_level = containers + .iter() + .filter(|c| matches!(c, super::Container::Blockquote)) + .count(); + let lcount = doc[range.clone()].chars().filter(|c| *c == ' ').count(); + let list_indentation = containers + .iter() + .map(|c| { + if let super::Container::List(indent) = c { + *indent + } else { + 0 + } + }) + .sum(); + if ccount < blockquote_level || lcount < list_indentation { + let msg = if ccount < blockquote_level { + "doc quote missing `>` marker" + } else { + "doc list item missing indentation" + }; + span_lint_and_then(cx, DOC_LAZY_CONTINUATION, span, msg, |diag| { + if ccount == 0 && blockquote_level == 0 { + // simpler suggestion style for indentation + let indent = list_indentation - lcount; + diag.span_suggestion_with_style( + span.shrink_to_hi(), + "indent this line", + std::iter::repeat(" ").take(indent).join(""), + Applicability::MaybeIncorrect, + SuggestionStyle::ShowAlways, + ); + diag.help("if this is supposed to be its own paragraph, add a blank line"); + return; + } + let mut doc_start_range = &doc[range]; + let mut suggested = String::new(); + for c in containers { + let text = map_container_to_text(c); + if doc_start_range.starts_with(text) { + doc_start_range = &doc_start_range[text.len()..]; + span = span + .with_lo(span.lo() + BytePos(u32::try_from(text.len()).expect("text is not 2**32 or bigger"))); + } else if matches!(c, super::Container::Blockquote) + && let Some(i) = doc_start_range.find('>') + { + doc_start_range = &doc_start_range[i + 1..]; + span = + span.with_lo(span.lo() + BytePos(u32::try_from(i).expect("text is not 2**32 or bigger") + 1)); + } else { + suggested.push_str(text); + } + } + diag.span_suggestion_with_style( + span, + "add markers to start of line", + suggested, + Applicability::MachineApplicable, + SuggestionStyle::ShowAlways, + ); + diag.help("if this not intended to be a quote at all, escape it 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 36ba19698c72f..010fab803d99c 100644 --- a/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs +++ b/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs @@ -1,3 +1,4 @@ +use super::{DocHeaders, MISSING_ERRORS_DOC, MISSING_PANICS_DOC, MISSING_SAFETY_DOC, UNNECESSARY_SAFETY_DOC}; use clippy_utils::diagnostics::{span_lint, span_lint_and_note}; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; use clippy_utils::{is_doc_hidden, return_ty}; @@ -6,15 +7,13 @@ use rustc_lint::LateContext; use rustc_middle::ty; use rustc_span::{sym, Span}; -use super::{DocHeaders, MISSING_ERRORS_DOC, MISSING_PANICS_DOC, MISSING_SAFETY_DOC, UNNECESSARY_SAFETY_DOC}; - pub fn check( cx: &LateContext<'_>, owner_id: OwnerId, sig: FnSig<'_>, headers: DocHeaders, body_id: Option, - panic_span: Option, + panic_info: Option<(Span, bool)>, check_private_items: bool, ) { if !check_private_items && !cx.effective_visibilities.is_exported(owner_id.def_id) { @@ -48,13 +47,13 @@ pub fn check( ), _ => (), } - if !headers.panics && panic_span.is_some() { + if !headers.panics && panic_info.map_or(false, |el| !el.1) { span_lint_and_note( cx, MISSING_PANICS_DOC, span, "docs for function which may panic missing `# Panics` section", - panic_span, + panic_info.map(|el| el.0), "first possible panic found here", ); } diff --git a/src/tools/clippy/clippy_lints/src/doc/mod.rs b/src/tools/clippy/clippy_lints/src/doc/mod.rs index 7fdb582e64033..9f08973948a32 100644 --- a/src/tools/clippy/clippy_lints/src/doc/mod.rs +++ b/src/tools/clippy/clippy_lints/src/doc/mod.rs @@ -1,13 +1,14 @@ +mod lazy_continuation; use clippy_utils::attrs::is_doc_hidden; use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use clippy_utils::macros::{is_panic, root_macro_call_first_node}; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::Visitable; -use clippy_utils::{is_entrypoint_fn, is_trait_impl_item, method_chain_args}; +use clippy_utils::{in_constant, is_entrypoint_fn, is_trait_impl_item, method_chain_args}; use pulldown_cmark::Event::{ Code, End, FootnoteReference, HardBreak, Html, Rule, SoftBreak, Start, TaskListMarker, Text, }; -use pulldown_cmark::Tag::{BlockQuote, CodeBlock, Heading, Item, Link, Paragraph}; +use pulldown_cmark::Tag::{BlockQuote, CodeBlock, FootnoteDefinition, Heading, Item, Link, Paragraph}; use pulldown_cmark::{BrokenLink, CodeBlockKind, CowStr, Options}; use rustc_ast::ast::Attribute; use rustc_data_structures::fx::FxHashSet; @@ -362,6 +363,63 @@ declare_clippy_lint! { "docstrings exist but documentation is empty" } +declare_clippy_lint! { + /// ### What it does + /// + /// In CommonMark Markdown, the language used to write doc comments, a + /// paragraph nested within a list or block quote does not need any line + /// after the first one to be indented or marked. The specification calls + /// this a "lazy paragraph continuation." + /// + /// ### Why is this bad? + /// + /// This is easy to write but hard to read. Lazy continuations makes + /// unintended markers hard to see, and make it harder to deduce the + /// document's intended structure. + /// + /// ### Example + /// + /// This table is probably intended to have two rows, + /// but it does not. It has zero rows, and is followed by + /// a block quote. + /// ```no_run + /// /// Range | Description + /// /// ----- | ----------- + /// /// >= 1 | fully opaque + /// /// < 1 | partially see-through + /// fn set_opacity(opacity: f32) {} + /// ``` + /// + /// Fix it by escaping the marker: + /// ```no_run + /// /// Range | Description + /// /// ----- | ----------- + /// /// \>= 1 | fully opaque + /// /// < 1 | partially see-through + /// fn set_opacity(opacity: f32) {} + /// ``` + /// + /// This example is actually intended to be a list: + /// ```no_run + /// /// * Do nothing. + /// /// * Then do something. Whatever it is needs done, + /// /// it should be done right now. + /// # fn do_stuff() {} + /// ``` + /// + /// Fix it by indenting the list contents: + /// ```no_run + /// /// * Do nothing. + /// /// * Then do something. Whatever it is needs done, + /// /// it should be done right now. + /// # fn do_stuff() {} + /// ``` + #[clippy::version = "1.80.0"] + pub DOC_LAZY_CONTINUATION, + style, + "require every line of a paragraph to be indented and marked" +} + #[derive(Clone)] pub struct Documentation { valid_idents: FxHashSet, @@ -388,6 +446,7 @@ impl_lint_pass!(Documentation => [ UNNECESSARY_SAFETY_DOC, SUSPICIOUS_DOC_COMMENTS, EMPTY_DOCS, + DOC_LAZY_CONTINUATION, ]); impl<'tcx> LateLintPass<'tcx> for Documentation { @@ -402,14 +461,14 @@ impl<'tcx> LateLintPass<'tcx> for Documentation { if !(is_entrypoint_fn(cx, item.owner_id.to_def_id()) || in_external_macro(cx.tcx.sess, item.span)) { let body = cx.tcx.hir().body(body_id); - let panic_span = FindPanicUnwrap::find_span(cx, cx.tcx.typeck(item.owner_id), body.value); + let panic_info = FindPanicUnwrap::find_span(cx, cx.tcx.typeck(item.owner_id), body.value); missing_headers::check( cx, item.owner_id, sig, headers, Some(body_id), - panic_span, + panic_info, self.check_private_items, ); } @@ -551,6 +610,7 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet, attrs: &[ cx, valid_idents, parser.into_offset_iter(), + &doc, Fragments { fragments: &fragments, doc: &doc, @@ -560,6 +620,11 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet, attrs: &[ const RUST_CODE: &[&str] = &["rust", "no_run", "should_panic", "compile_fail"]; +enum Container { + Blockquote, + List(usize), +} + /// Checks parsed documentation. /// This walks the "events" (think sections of markdown) produced by `pulldown_cmark`, /// so lints here will generally access that information. @@ -569,6 +634,7 @@ fn check_doc<'a, Events: Iterator, Range, valid_idents: &FxHashSet, events: Events, + doc: &str, fragments: Fragments<'_>, ) -> DocHeaders { // true if a safety header was found @@ -576,6 +642,7 @@ fn check_doc<'a, Events: Iterator, Range, Range { if tag.starts_with(", Range blockquote_level += 1, - End(BlockQuote) => blockquote_level -= 1, + Start(BlockQuote) => { + blockquote_level += 1; + containers.push(Container::Blockquote); + }, + End(BlockQuote) => { + blockquote_level -= 1; + containers.pop(); + }, Start(CodeBlock(ref kind)) => { in_code = true; if let CodeBlockKind::Fenced(lang) = kind { @@ -633,6 +710,13 @@ fn check_doc<'a, Events: Iterator, Range, Range, Range in_footnote_definition = true, + End(FootnoteDefinition(..)) => in_footnote_definition = false, Start(_tag) | End(_tag) => (), // We don't care about other tags - SoftBreak | HardBreak | TaskListMarker(_) | Code(_) | Rule => (), + SoftBreak | HardBreak => { + if !containers.is_empty() + && let Some((next_event, next_range)) = events.peek() + && let Some(next_span) = fragments.span(cx, next_range.clone()) + && let Some(span) = fragments.span(cx, range.clone()) + && !in_footnote_definition + && !matches!(next_event, End(_)) + { + lazy_continuation::check( + cx, + doc, + range.end..next_range.start, + Span::new(span.hi(), next_span.lo(), span.ctxt(), span.parent()), + &containers[..], + ); + } + }, + TaskListMarker(_) | Code(_) | Rule => (), FootnoteReference(text) | Text(text) => { paragraph_range.end = range.end; ticks_unbalanced |= text.contains('`') && !in_code; @@ -701,6 +807,7 @@ fn check_doc<'a, Events: Iterator, Range { cx: &'a LateContext<'tcx>, + is_const: bool, panic_span: Option, typeck_results: &'tcx ty::TypeckResults<'tcx>, } @@ -710,14 +817,15 @@ impl<'a, 'tcx> FindPanicUnwrap<'a, 'tcx> { cx: &'a LateContext<'tcx>, typeck_results: &'tcx ty::TypeckResults<'tcx>, body: impl Visitable<'tcx>, - ) -> Option { + ) -> Option<(Span, bool)> { let mut vis = Self { cx, + is_const: false, panic_span: None, typeck_results, }; body.visit(&mut vis); - vis.panic_span + vis.panic_span.map(|el| (el, vis.is_const)) } } @@ -736,6 +844,7 @@ impl<'a, 'tcx> Visitor<'tcx> for FindPanicUnwrap<'a, 'tcx> { "assert" | "assert_eq" | "assert_ne" ) { + self.is_const = in_constant(self.cx, expr.hir_id); self.panic_span = Some(macro_call.span); } } diff --git a/src/tools/clippy/clippy_lints/src/escape.rs b/src/tools/clippy/clippy_lints/src/escape.rs index 6715de52649d4..8d6e27700d8d4 100644 --- a/src/tools/clippy/clippy_lints/src/escape.rs +++ b/src/tools/clippy/clippy_lints/src/escape.rs @@ -104,7 +104,9 @@ impl<'tcx> LateLintPass<'tcx> for BoxedLocal { too_large_for_stack: self.too_large_for_stack, }; - ExprUseVisitor::for_clippy(cx, fn_def_id, &mut v).consume_body(body).into_ok(); + ExprUseVisitor::for_clippy(cx, fn_def_id, &mut v) + .consume_body(body) + .into_ok(); for node in v.set { span_lint_hir( diff --git a/src/tools/clippy/clippy_lints/src/explicit_write.rs b/src/tools/clippy/clippy_lints/src/explicit_write.rs index 724e1843359b2..3c4a043a732b7 100644 --- a/src/tools/clippy/clippy_lints/src/explicit_write.rs +++ b/src/tools/clippy/clippy_lints/src/explicit_write.rs @@ -1,12 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::macros::{find_format_args, format_args_inputs_span}; +use clippy_utils::macros::{format_args_inputs_span, FormatArgsStorage}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::{is_expn_of, path_def_id}; use rustc_errors::Applicability; 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::declare_lint_pass; +use rustc_session::impl_lint_pass; use rustc_span::{sym, ExpnId}; declare_clippy_lint! { @@ -38,7 +38,17 @@ declare_clippy_lint! { "using the `write!()` family of functions instead of the `print!()` family of functions, when using the latter would work" } -declare_lint_pass!(ExplicitWrite => [EXPLICIT_WRITE]); +pub struct ExplicitWrite { + format_args: FormatArgsStorage, +} + +impl ExplicitWrite { + pub fn new(format_args: FormatArgsStorage) -> Self { + Self { format_args } + } +} + +impl_lint_pass!(ExplicitWrite => [EXPLICIT_WRITE]); impl<'tcx> LateLintPass<'tcx> for ExplicitWrite { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { @@ -57,7 +67,7 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite { Some(sym::io_stderr) => ("stderr", "e"), _ => return, }; - let Some(format_args) = find_format_args(cx, write_arg, ExpnId::root()) else { + let Some(format_args) = self.format_args.get(cx, write_arg, ExpnId::root()) else { return; }; @@ -83,7 +93,7 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite { }; let mut applicability = Applicability::MachineApplicable; let inputs_snippet = - snippet_with_applicability(cx, format_args_inputs_span(&format_args), "..", &mut applicability); + snippet_with_applicability(cx, format_args_inputs_span(format_args), "..", &mut applicability); span_lint_and_sugg( cx, EXPLICIT_WRITE, diff --git a/src/tools/clippy/clippy_lints/src/format.rs b/src/tools/clippy/clippy_lints/src/format.rs index 8a0cd155d211c..0b248f784b762 100644 --- a/src/tools/clippy/clippy_lints/src/format.rs +++ b/src/tools/clippy/clippy_lints/src/format.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::macros::{find_format_arg_expr, find_format_args, root_macro_call_first_node}; +use clippy_utils::macros::{find_format_arg_expr, root_macro_call_first_node, FormatArgsStorage}; use clippy_utils::source::{snippet_opt, snippet_with_context}; use clippy_utils::sugg::Sugg; use rustc_ast::{FormatArgsPiece, FormatOptions, FormatTrait}; @@ -7,7 +7,7 @@ use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; -use rustc_session::declare_lint_pass; +use rustc_session::impl_lint_pass; use rustc_span::{sym, Span}; declare_clippy_lint! { @@ -39,13 +39,24 @@ declare_clippy_lint! { "useless use of `format!`" } -declare_lint_pass!(UselessFormat => [USELESS_FORMAT]); +#[allow(clippy::module_name_repetitions)] +pub struct UselessFormat { + format_args: FormatArgsStorage, +} + +impl UselessFormat { + pub fn new(format_args: FormatArgsStorage) -> Self { + Self { format_args } + } +} + +impl_lint_pass!(UselessFormat => [USELESS_FORMAT]); impl<'tcx> LateLintPass<'tcx> for UselessFormat { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if let Some(macro_call) = root_macro_call_first_node(cx, expr) && cx.tcx.is_diagnostic_item(sym::format_macro, macro_call.def_id) - && let Some(format_args) = find_format_args(cx, expr, macro_call.expn) + && let Some(format_args) = self.format_args.get(cx, expr, macro_call.expn) { let mut applicability = Applicability::MachineApplicable; let call_site = macro_call.span; diff --git a/src/tools/clippy/clippy_lints/src/format_args.rs b/src/tools/clippy/clippy_lints/src/format_args.rs index 003a9995c15f3..86115807aa4d1 100644 --- a/src/tools/clippy/clippy_lints/src/format_args.rs +++ b/src/tools/clippy/clippy_lints/src/format_args.rs @@ -3,8 +3,8 @@ 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, find_format_args, 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, 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, FormatArgsStorage, FormatParamUsage, MacroCall, }; use clippy_utils::source::snippet_opt; use clippy_utils::ty::{implements_trait, is_type_lang_item}; @@ -167,15 +167,18 @@ impl_lint_pass!(FormatArgs => [ UNUSED_FORMAT_SPECS, ]); +#[allow(clippy::struct_field_names)] pub struct FormatArgs { + format_args: FormatArgsStorage, msrv: Msrv, ignore_mixed: bool, } impl FormatArgs { #[must_use] - pub fn new(msrv: Msrv, allow_mixed_uninlined_format_args: bool) -> Self { + pub fn new(format_args: FormatArgsStorage, msrv: Msrv, allow_mixed_uninlined_format_args: bool) -> Self { Self { + format_args, msrv, ignore_mixed: allow_mixed_uninlined_format_args, } @@ -186,13 +189,13 @@ impl<'tcx> LateLintPass<'tcx> for FormatArgs { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { if let Some(macro_call) = root_macro_call_first_node(cx, expr) && is_format_macro(cx, macro_call.def_id) - && let Some(format_args) = find_format_args(cx, expr, macro_call.expn) + && let Some(format_args) = self.format_args.get(cx, expr, macro_call.expn) { let linter = FormatArgsExpr { cx, expr, macro_call: ¯o_call, - format_args: &format_args, + format_args, ignore_mixed: self.ignore_mixed, }; diff --git a/src/tools/clippy/clippy_lints/src/format_impl.rs b/src/tools/clippy/clippy_lints/src/format_impl.rs index 0a52347940abb..09be7237b5ca3 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, find_format_args, is_format_macro, root_macro_call_first_node}; +use clippy_utils::macros::{find_format_arg_expr, is_format_macro, root_macro_call_first_node, FormatArgsStorage}; 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; @@ -99,13 +99,15 @@ struct FormatTraitNames { #[derive(Default)] pub struct FormatImpl { + format_args: FormatArgsStorage, // Whether we are inside Display or Debug trait impl - None for neither format_trait_impl: Option, } impl FormatImpl { - pub fn new() -> Self { + pub fn new(format_args: FormatArgsStorage) -> Self { Self { + format_args, format_trait_impl: None, } } @@ -129,6 +131,7 @@ impl<'tcx> LateLintPass<'tcx> for FormatImpl { if let Some(format_trait_impl) = self.format_trait_impl { let linter = FormatImplExpr { cx, + format_args: &self.format_args, expr, format_trait_impl, }; @@ -141,6 +144,7 @@ impl<'tcx> LateLintPass<'tcx> for FormatImpl { struct FormatImplExpr<'a, 'tcx> { cx: &'a LateContext<'tcx>, + format_args: &'a FormatArgsStorage, expr: &'tcx Expr<'tcx>, format_trait_impl: FormatTraitNames, } @@ -175,7 +179,7 @@ impl<'a, 'tcx> FormatImplExpr<'a, 'tcx> { if let Some(outer_macro) = root_macro_call_first_node(self.cx, self.expr) && let macro_def_id = outer_macro.def_id && is_format_macro(self.cx, macro_def_id) - && let Some(format_args) = find_format_args(self.cx, self.expr, outer_macro.expn) + && let Some(format_args) = self.format_args.get(self.cx, self.expr, outer_macro.expn) { for piece in &format_args.template { if let FormatArgsPiece::Placeholder(placeholder) = piece 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 633ed96d6a6d5..82ce501bac59b 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 @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::is_integer_literal; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; +use clippy_utils::{in_constant, is_integer_literal}; use rustc_errors::Applicability; use rustc_hir::{def, Expr, ExprKind, LangItem, PrimTy, QPath, TyKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -47,6 +47,9 @@ impl<'tcx> LateLintPass<'tcx> for FromStrRadix10 { fn check_expr(&mut self, cx: &LateContext<'tcx>, exp: &Expr<'tcx>) { if let ExprKind::Call(maybe_path, [src, radix]) = &exp.kind && let ExprKind::Path(QPath::TypeRelative(ty, pathseg)) = &maybe_path.kind + // do not lint in constant context, because the suggestion won't work. + // NB: keep this check until a new `const_trait_impl` is available and stablized. + && !in_constant(cx, exp.hir_id) // check if the first part of the path is some integer primitive && let TyKind::Path(ty_qpath) = &ty.kind diff --git a/src/tools/clippy/clippy_lints/src/functions/mod.rs b/src/tools/clippy/clippy_lints/src/functions/mod.rs index 9cc51fa8cd5dd..dfcaac9abefc7 100644 --- a/src/tools/clippy/clippy_lints/src/functions/mod.rs +++ b/src/tools/clippy/clippy_lints/src/functions/mod.rs @@ -2,15 +2,17 @@ mod impl_trait_in_params; mod misnamed_getters; mod must_use; mod not_unsafe_ptr_arg_deref; +mod renamed_function_params; mod result; mod too_many_arguments; mod too_many_lines; +use clippy_utils::def_path_def_ids; use rustc_hir as hir; use rustc_hir::intravisit; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; -use rustc_span::def_id::LocalDefId; +use rustc_span::def_id::{DefIdSet, LocalDefId}; use rustc_span::Span; declare_clippy_lint! { @@ -359,13 +361,51 @@ declare_clippy_lint! { "`impl Trait` is used in the function's parameters" } -#[derive(Copy, Clone)] -#[allow(clippy::struct_field_names)] +declare_clippy_lint! { + /// ### What it does + /// Lints when the name of function parameters from trait impl is + /// different than its default implementation. + /// + /// ### Why is this bad? + /// Using the default name for parameters of a trait method is often + /// more desirable for consistency's sake. + /// + /// ### Example + /// ```rust + /// struct A(u32); + /// + /// impl PartialEq for A { + /// fn eq(&self, b: &Self) -> bool { + /// self.0 == b.0 + /// } + /// } + /// ``` + /// Use instead: + /// ```rust + /// struct A(u32); + /// + /// impl PartialEq for A { + /// fn eq(&self, other: &Self) -> bool { + /// self.0 == other.0 + /// } + /// } + /// ``` + #[clippy::version = "1.74.0"] + pub RENAMED_FUNCTION_PARAMS, + restriction, + "renamed function parameters in trait implementation" +} + +#[derive(Clone)] pub struct Functions { too_many_arguments_threshold: u64, too_many_lines_threshold: u64, large_error_threshold: u64, avoid_breaking_exported_api: bool, + allow_renamed_params_for: Vec, + /// A set of resolved `def_id` of traits that are configured to allow + /// function params renaming. + trait_ids: DefIdSet, } impl Functions { @@ -374,12 +414,15 @@ impl Functions { too_many_lines_threshold: u64, large_error_threshold: u64, avoid_breaking_exported_api: bool, + allow_renamed_params_for: Vec, ) -> Self { Self { too_many_arguments_threshold, too_many_lines_threshold, large_error_threshold, avoid_breaking_exported_api, + allow_renamed_params_for, + trait_ids: DefIdSet::default(), } } } @@ -395,6 +438,7 @@ impl_lint_pass!(Functions => [ RESULT_LARGE_ERR, MISNAMED_GETTERS, IMPL_TRAIT_IN_PARAMS, + RENAMED_FUNCTION_PARAMS, ]); impl<'tcx> LateLintPass<'tcx> for Functions { @@ -424,6 +468,7 @@ impl<'tcx> LateLintPass<'tcx> for Functions { must_use::check_impl_item(cx, item); result::check_impl_item(cx, item, self.large_error_threshold); impl_trait_in_params::check_impl_item(cx, item); + renamed_function_params::check_impl_item(cx, item, &self.trait_ids); } fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) { @@ -433,4 +478,12 @@ impl<'tcx> LateLintPass<'tcx> for Functions { result::check_trait_item(cx, item, self.large_error_threshold); impl_trait_in_params::check_trait_item(cx, item, self.avoid_breaking_exported_api); } + + fn check_crate(&mut self, cx: &LateContext<'tcx>) { + for path in &self.allow_renamed_params_for { + let path_segments: Vec<&str> = path.split("::").collect(); + let ids = def_path_def_ids(cx, &path_segments); + self.trait_ids.extend(ids); + } + } } 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 new file mode 100644 index 0000000000000..c7de0385c021b --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/functions/renamed_function_params.rs @@ -0,0 +1,110 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use rustc_errors::{Applicability, MultiSpan}; +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 super::RENAMED_FUNCTION_PARAMS; + +pub(super) fn check_impl_item(cx: &LateContext<'_>, item: &ImplItem<'_>, ignored_traits: &DefIdSet) { + if !item.span.from_expansion() + && let ImplItemKind::Fn(_, body_id) = item.kind + && let parent_node = cx.tcx.parent_hir_node(item.hir_id()) + && let Node::Item(parent_item) = parent_node + && let ItemKind::Impl(Impl { + items, + of_trait: Some(trait_ref), + .. + }) = &parent_item.kind + && let Some(did) = trait_item_def_id_of_impl(items, item.owner_id) + && !is_from_ignored_trait(trait_ref, ignored_traits) + { + let mut param_idents_iter = cx.tcx.hir().body_param_names(body_id); + let mut default_param_idents_iter = cx.tcx.fn_arg_names(did).iter().copied(); + + let renames = RenamedFnArgs::new(&mut default_param_idents_iter, &mut param_idents_iter); + if !renames.0.is_empty() { + let multi_span = renames.multi_span(); + let plural = if renames.0.len() == 1 { "" } else { "s" }; + span_lint_and_then( + cx, + RENAMED_FUNCTION_PARAMS, + multi_span, + format!("renamed function parameter{plural} of trait impl"), + |diag| { + diag.multipart_suggestion( + format!("consider using the default name{plural}"), + renames.0, + Applicability::Unspecified, + ); + }, + ); + } + } +} + +struct RenamedFnArgs(Vec<(Span, String)>); + +impl RenamedFnArgs { + /// Comparing between an iterator of default names and one with current names, + /// then collect the ones that got renamed. + fn new(default_names: &mut I, current_names: &mut T) -> Self + where + I: Iterator, + T: Iterator, + { + let mut renamed: Vec<(Span, String)> = vec![]; + + debug_assert!(default_names.size_hint() == current_names.size_hint()); + while let (Some(def_name), Some(cur_name)) = (default_names.next(), current_names.next()) { + let current_name = cur_name.name; + let default_name = def_name.name; + if is_unused_or_empty_symbol(current_name) || is_unused_or_empty_symbol(default_name) { + continue; + } + if current_name != default_name { + renamed.push((cur_name.span, default_name.to_string())); + } + } + + Self(renamed) + } + + fn multi_span(&self) -> MultiSpan { + self.0 + .iter() + .map(|(span, _)| span) + .copied() + .collect::>() + .into() + } +} + +fn is_unused_or_empty_symbol(symbol: Symbol) -> bool { + // FIXME: `body_param_names` currently returning empty symbols for `wild` as well, + // so we need to check if the symbol is empty first. + // Therefore the check of whether it's equal to [`kw::Underscore`] has no use for now, + // but it would be nice to keep it here just to be future-proof. + symbol.is_empty() || symbol == kw::Underscore || symbol.as_str().starts_with('_') +} + +/// Get the [`trait_item_def_id`](ImplItemRef::trait_item_def_id) of a relevant impl item. +fn trait_item_def_id_of_impl(items: &[ImplItemRef], target: OwnerId) -> Option { + items.iter().find_map(|item| { + if item.id.owner_id == target { + item.trait_item_def_id + } else { + None + } + }) +} + +fn is_from_ignored_trait(of_trait: &TraitRef<'_>, ignored_traits: &DefIdSet) -> bool { + let Some(trait_did) = of_trait.trait_def_id() else { + return false; + }; + ignored_traits.contains(&trait_did) +} 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 2c2daac023492..192fb611c2d4b 100644 --- a/src/tools/clippy/clippy_lints/src/future_not_send.rs +++ b/src/tools/clippy/clippy_lints/src/future_not_send.rs @@ -4,8 +4,8 @@ use rustc_hir::intravisit::FnKind; use rustc_hir::{Body, FnDecl}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::{self, AliasTy, ClauseKind, PredicateKind}; 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}; diff --git a/src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs b/src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs index a75dfaf286fdf..601d0e151aae8 100644 --- a/src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs +++ b/src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs @@ -225,7 +225,7 @@ impl {self_ty_without_ref} {{ && let ImplItemKind::Fn(sig, _) = item.kind && let FnRetTy::Return(ret) = sig.decl.output && is_nameable_in_impl_trait(ret) - && cx.tcx.generics_of(item_did).own_params.is_empty() + && cx.tcx.generics_of(item_did).is_own_empty() && sig.decl.implicit_self == expected_implicit_self && sig.decl.inputs.len() == 1 && let Some(imp) = get_parent_as_impl(cx.tcx, item.hir_id()) diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index a8bfbbdd9ecab..3328d642bd855 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -61,11 +61,6 @@ extern crate clippy_utils; #[macro_use] extern crate declare_clippy_lint; -use std::collections::BTreeMap; - -use rustc_data_structures::fx::FxHashSet; -use rustc_lint::{Lint, LintId}; - #[cfg(feature = "internal")] pub mod deprecated_lints; #[cfg_attr(feature = "internal", allow(clippy::missing_clippy_version_attribute))] @@ -199,6 +194,7 @@ mod lifetimes; mod lines_filter_map_ok; mod literal_representation; mod loops; +mod macro_metavars_in_unsafe; mod macro_use; mod main_recursion; mod manual_assert; @@ -385,6 +381,10 @@ mod zero_sized_map_values; // end lints modules, do not remove this comment, it’s used in `update_lints` use clippy_config::{get_configuration_metadata, Conf}; +use clippy_utils::macros::FormatArgsStorage; +use rustc_data_structures::fx::FxHashSet; +use rustc_lint::{Lint, LintId}; +use std::collections::BTreeMap; /// Register all pre expansion lints /// @@ -533,6 +533,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { allow_expect_in_tests, allow_mixed_uninlined_format_args, allow_one_hash_in_raw_strings, + allow_panic_in_tests, allow_print_in_tests, allow_private_module_inception, allow_unwrap_in_tests, @@ -597,9 +598,11 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { ref allowed_duplicate_crates, allow_comparison_to_zero, ref allowed_prefixes, + ref allow_renamed_params_for, blacklisted_names: _, cyclomatic_complexity_threshold: _, + warn_unsafe_macro_metavars_in_private_macros, } = *conf; let msrv = || msrv.clone(); @@ -616,6 +619,14 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { } } + let format_args_storage = FormatArgsStorage::default(); + let format_args = format_args_storage.clone(); + store.register_early_pass(move || { + Box::new(utils::format_args_collector::FormatArgsCollector::new( + format_args.clone(), + )) + }); + // all the internal lints #[cfg(feature = "internal")] { @@ -656,7 +667,6 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { .collect(), )) }); - store.register_early_pass(|| Box::::default()); store.register_late_pass(|_| Box::new(utils::dump_hir::DumpHir)); store.register_late_pass(|_| Box::new(utils::author::Author)); store.register_late_pass(move |_| { @@ -698,6 +708,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::new(non_octal_unix_permissions::NonOctalUnixPermissions)); store.register_early_pass(|| Box::new(unnecessary_self_imports::UnnecessarySelfImports)); store.register_late_pass(move |_| Box::new(approx_const::ApproxConstant::new(msrv()))); + let format_args = format_args_storage.clone(); store.register_late_pass(move |_| { Box::new(methods::Methods::new( avoid_breaking_exported_api, @@ -705,6 +716,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { allow_expect_in_tests, allow_unwrap_in_tests, allowed_dotfiles.clone(), + format_args.clone(), )) }); store.register_late_pass(move |_| Box::new(matches::Matches::new(msrv()))); @@ -759,7 +771,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { allow_in_test: allow_useless_vec_in_tests, }) }); - store.register_late_pass(|_| Box::new(panic_unimplemented::PanicUnimplemented)); + store.register_late_pass(move |_| Box::new(panic_unimplemented::PanicUnimplemented { allow_panic_in_tests })); store.register_late_pass(|_| Box::new(strings::StringLitAsBytes)); store.register_late_pass(|_| Box::new(derive::Derive)); store.register_late_pass(move |_| Box::new(derivable_impls::DerivableImpls::new(msrv()))); @@ -769,7 +781,8 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::::default()); store.register_late_pass(move |_| Box::new(copies::CopyAndPaste::new(ignore_interior_mutability.clone()))); store.register_late_pass(|_| Box::new(copy_iterator::CopyIterator)); - store.register_late_pass(|_| Box::new(format::UselessFormat)); + let format_args = format_args_storage.clone(); + store.register_late_pass(move |_| Box::new(format::UselessFormat::new(format_args.clone()))); store.register_late_pass(|_| Box::new(swap::Swap)); store.register_late_pass(|_| Box::new(overflow_check_conditional::OverflowCheckConditional)); store.register_late_pass(|_| Box::::default()); @@ -780,6 +793,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { too_many_lines_threshold, large_error_threshold, avoid_breaking_exported_api, + allow_renamed_params_for.clone(), )) }); store.register_late_pass(move |_| Box::new(doc::Documentation::new(doc_valid_idents, check_private_items))); @@ -793,7 +807,8 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::new(partialeq_ne_impl::PartialEqNeImpl)); store.register_late_pass(|_| Box::new(unused_io_amount::UnusedIoAmount)); store.register_late_pass(move |_| Box::new(large_enum_variant::LargeEnumVariant::new(enum_variant_size_threshold))); - store.register_late_pass(|_| Box::new(explicit_write::ExplicitWrite)); + let format_args = format_args_storage.clone(); + store.register_late_pass(move |_| Box::new(explicit_write::ExplicitWrite::new(format_args.clone()))); store.register_late_pass(|_| Box::new(needless_pass_by_value::NeedlessPassByValue)); store.register_late_pass(move |tcx| { Box::new(pass_by_ref_or_value::PassByRefOrValue::new( @@ -835,7 +850,8 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(move |_| Box::new(mut_key::MutableKeyType::new(ignore_interior_mutability.clone()))); store.register_early_pass(|| Box::new(reference::DerefAddrOf)); store.register_early_pass(|| Box::new(double_parens::DoubleParens)); - store.register_late_pass(|_| Box::new(format_impl::FormatImpl::new())); + let format_args = format_args_storage.clone(); + store.register_late_pass(move |_| Box::new(format_impl::FormatImpl::new(format_args.clone()))); store.register_early_pass(|| Box::new(unsafe_removed_from_name::UnsafeNameRemoval)); store.register_early_pass(|| Box::new(else_if_without_else::ElseIfWithoutElse)); store.register_early_pass(|| Box::new(int_plus_one::IntPlusOne)); @@ -961,8 +977,14 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { accept_comment_above_attributes, )) }); - store - .register_late_pass(move |_| Box::new(format_args::FormatArgs::new(msrv(), allow_mixed_uninlined_format_args))); + let format_args = format_args_storage.clone(); + store.register_late_pass(move |_| { + Box::new(format_args::FormatArgs::new( + format_args.clone(), + msrv(), + allow_mixed_uninlined_format_args, + )) + }); store.register_late_pass(|_| Box::new(trailing_empty_array::TrailingEmptyArray)); store.register_early_pass(|| Box::new(octal_escapes::OctalEscapes)); store.register_late_pass(|_| Box::new(needless_late_init::NeedlessLateInit)); @@ -973,7 +995,8 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::new(default_union_representation::DefaultUnionRepresentation)); store.register_late_pass(|_| Box::::default()); store.register_late_pass(move |_| Box::new(dbg_macro::DbgMacro::new(allow_dbg_in_tests))); - store.register_late_pass(move |_| Box::new(write::Write::new(allow_print_in_tests))); + let format_args = format_args_storage.clone(); + store.register_late_pass(move |_| Box::new(write::Write::new(format_args.clone(), allow_print_in_tests))); store.register_late_pass(move |_| { Box::new(cargo::Cargo { ignore_publish: cargo_ignore_publish, @@ -1136,6 +1159,12 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::new(zero_repeat_side_effects::ZeroRepeatSideEffects)); store.register_late_pass(|_| Box::new(manual_unwrap_or_default::ManualUnwrapOrDefault)); store.register_late_pass(|_| Box::new(integer_division_remainder_used::IntegerDivisionRemainderUsed)); + store.register_late_pass(move |_| { + Box::new(macro_metavars_in_unsafe::ExprMetavarsInUnsafe { + warn_unsafe_macro_metavars_in_private_macros, + ..Default::default() + }) + }); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/src/tools/clippy/clippy_lints/src/loops/mod.rs b/src/tools/clippy/clippy_lints/src/loops/mod.rs index b5e39b33c6a15..3dcb050d77e68 100644 --- a/src/tools/clippy/clippy_lints/src/loops/mod.rs +++ b/src/tools/clippy/clippy_lints/src/loops/mod.rs @@ -17,6 +17,7 @@ mod same_item_push; mod single_element_loop; mod unused_enumerate_index; mod utils; +mod while_float; mod while_immutable_condition; mod while_let_loop; mod while_let_on_iterator; @@ -416,6 +417,39 @@ declare_clippy_lint! { "variables used within while expression are not mutated in the body" } +declare_clippy_lint! { + /// ### What it does + /// Checks for while loops comparing floating point values. + /// + /// ### Why is this bad? + /// If you increment floating point values, errors can compound, + /// so, use integers instead if possible. + /// + /// ### Known problems + /// The lint will catch all while loops comparing floating point + /// values without regarding the increment. + /// + /// ### Example + /// ```no_run + /// let mut x = 0.0; + /// while x < 42.0 { + /// x += 1.0; + /// } + /// ``` + /// + /// Use instead: + /// ```no_run + /// let mut x = 0; + /// while x < 42 { + /// x += 1; + /// } + /// ``` + #[clippy::version = "1.80.0"] + pub WHILE_FLOAT, + nursery, + "while loops comaparing floating point values" +} + declare_clippy_lint! { /// ### What it does /// Checks whether a for loop is being used to push a constant @@ -706,6 +740,7 @@ impl_lint_pass!(Loops => [ NEVER_LOOP, MUT_RANGE_BOUND, WHILE_IMMUTABLE_CONDITION, + WHILE_FLOAT, SAME_ITEM_PUSH, SINGLE_ELEMENT_LOOP, MISSING_SPIN_LOOP, @@ -762,6 +797,7 @@ impl<'tcx> LateLintPass<'tcx> for Loops { if let Some(higher::While { condition, body, span }) = higher::While::hir(expr) { while_immutable_condition::check(cx, condition, body); + while_float::check(cx, condition); missing_spin_loop::check(cx, condition, body); manual_while_let_some::check(cx, condition, body, span); } diff --git a/src/tools/clippy/clippy_lints/src/loops/mut_range_bound.rs b/src/tools/clippy/clippy_lints/src/loops/mut_range_bound.rs index 6b9ecf5f14130..6c6a9a1a2e000 100644 --- a/src/tools/clippy/clippy_lints/src/loops/mut_range_bound.rs +++ b/src/tools/clippy/clippy_lints/src/loops/mut_range_bound.rs @@ -60,12 +60,9 @@ fn check_for_mutation( span_low: None, span_high: None, }; - ExprUseVisitor::for_clippy( - cx, - body.hir_id.owner.def_id, - &mut delegate, - ) - .walk_expr(body).into_ok(); + ExprUseVisitor::for_clippy(cx, body.hir_id.owner.def_id, &mut delegate) + .walk_expr(body) + .into_ok(); delegate.mutation_span() } diff --git a/src/tools/clippy/clippy_lints/src/loops/while_float.rs b/src/tools/clippy/clippy_lints/src/loops/while_float.rs new file mode 100644 index 0000000000000..cf62ce29f0c7a --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/loops/while_float.rs @@ -0,0 +1,20 @@ +use clippy_utils::diagnostics::span_lint; +use rustc_hir::ExprKind; + +pub(super) fn check(cx: &rustc_lint::LateContext<'_>, condition: &rustc_hir::Expr<'_>) { + if let ExprKind::Binary(_op, left, right) = condition.kind + && is_float_type(cx, left) + && is_float_type(cx, right) + { + span_lint( + cx, + super::WHILE_FLOAT, + condition.span, + "while condition comparing floats", + ); + } +} + +fn is_float_type(cx: &rustc_lint::LateContext<'_>, expr: &rustc_hir::Expr<'_>) -> bool { + cx.typeck_results().expr_ty(expr).is_floating_point() +} 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 new file mode 100644 index 0000000000000..aea3d26e18788 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/macro_metavars_in_unsafe.rs @@ -0,0 +1,256 @@ +use std::collections::btree_map::Entry; +use std::collections::BTreeMap; + +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::{BlockCheckMode, Expr, ExprKind, HirId, Stmt, UnsafeSource}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::impl_lint_pass; +use rustc_span::{sym, Span, SyntaxContext}; + +declare_clippy_lint! { + /// ### What it does + /// Looks for macros that expand metavariables in an unsafe block. + /// + /// ### Why is this bad? + /// This hides an unsafe block and allows the user of the macro to write unsafe code without an explicit + /// unsafe block at callsite, making it possible to perform unsafe operations in seemingly safe code. + /// + /// The macro should be restructured so that these metavariables are referenced outside of unsafe blocks + /// and that the usual unsafety checks apply to the macro argument. + /// + /// This is usually done by binding it to a variable outside of the unsafe block + /// and then using that variable inside of the block as shown in the example, or by referencing it a second time + /// in a safe context, e.g. `if false { $expr }`. + /// + /// ### Known limitations + /// Due to how macros are represented in the compiler at the time Clippy runs its lints, + /// it's not possible to look for metavariables in macro definitions directly. + /// + /// Instead, this lint looks at expansions of macros. + /// This leads to false negatives for macros that are never actually invoked. + /// + /// By default, this lint is rather conservative and will only emit warnings on publicly-exported + /// macros from the same crate, because oftentimes private internal macros are one-off macros where + /// this lint would just be noise (e.g. macros that generate `impl` blocks). + /// The default behavior should help with preventing a high number of such false positives, + /// however it can be configured to also emit warnings in private macros if desired. + /// + /// ### Example + /// ```no_run + /// /// Gets the first element of a slice + /// macro_rules! first { + /// ($slice:expr) => { + /// unsafe { + /// let slice = $slice; // ⚠️ expansion inside of `unsafe {}` + /// + /// assert!(!slice.is_empty()); + /// // SAFETY: slice is checked to have at least one element + /// slice.first().unwrap_unchecked() + /// } + /// } + /// } + /// + /// assert_eq!(*first!(&[1i32]), 1); + /// + /// // This will compile as a consequence (note the lack of `unsafe {}`) + /// assert_eq!(*first!(std::hint::unreachable_unchecked() as &[i32]), 1); + /// ``` + /// Use instead: + /// ```compile_fail + /// macro_rules! first { + /// ($slice:expr) => {{ + /// let slice = $slice; // ✅ outside of `unsafe {}` + /// unsafe { + /// assert!(!slice.is_empty()); + /// // SAFETY: slice is checked to have at least one element + /// slice.first().unwrap_unchecked() + /// } + /// }} + /// } + /// + /// assert_eq!(*first!(&[1]), 1); + /// + /// // This won't compile: + /// assert_eq!(*first!(std::hint::unreachable_unchecked() as &[i32]), 1); + /// ``` + #[clippy::version = "1.80.0"] + pub MACRO_METAVARS_IN_UNSAFE, + suspicious, + "expanding macro metavariables in an unsafe block" +} +impl_lint_pass!(ExprMetavarsInUnsafe => [MACRO_METAVARS_IN_UNSAFE]); + +#[derive(Clone, Debug)] +pub enum MetavarState { + ReferencedInUnsafe { unsafe_blocks: Vec }, + ReferencedInSafe, +} + +#[derive(Default)] +pub struct ExprMetavarsInUnsafe { + pub warn_unsafe_macro_metavars_in_private_macros: bool, + /// A metavariable can be expanded more than once, potentially across multiple bodies, so it + /// requires some state kept across HIR nodes to make it possible to delay a warning + /// and later undo: + /// + /// ```ignore + /// macro_rules! x { + /// ($v:expr) => { + /// unsafe { $v; } // unsafe context, it might be possible to emit a warning here, so add it to the map + /// + /// $v; // `$v` expanded another time but in a safe context, set to ReferencedInSafe to suppress + /// } + /// } + /// ``` + pub metavar_expns: BTreeMap, +} + +struct BodyVisitor<'a, 'tcx> { + /// Stack of unsafe blocks -- the top item always represents the last seen unsafe block from + /// within a relevant macro. + macro_unsafe_blocks: Vec, + /// When this is >0, it means that the node currently being visited is "within" a + /// macro definition. This is not necessary for correctness, it merely helps reduce the number + /// of spans we need to insert into the map, since only spans from macros are relevant. + expn_depth: u32, + cx: &'a LateContext<'tcx>, + lint: &'a mut ExprMetavarsInUnsafe, +} + +fn is_public_macro(cx: &LateContext<'_>, def_id: LocalDefId) -> bool { + (cx.effective_visibilities.is_exported(def_id) || cx.tcx.has_attr(def_id, sym::macro_export)) + && !cx.tcx.is_doc_hidden(def_id) +} + +impl<'a, 'tcx> Visitor<'tcx> for BodyVisitor<'a, 'tcx> { + fn visit_stmt(&mut self, s: &'tcx Stmt<'tcx>) { + let from_expn = s.span.from_expansion(); + if from_expn { + self.expn_depth += 1; + } + walk_stmt(self, s); + if from_expn { + self.expn_depth -= 1; + } + } + + fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) { + let ctxt = e.span.ctxt(); + + if let ExprKind::Block(block, _) = e.kind + && let BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) = block.rules + && !ctxt.is_root() + && let Some(macro_def_id) = ctxt.outer_expn_data().macro_def_id + && let Some(macro_def_id) = macro_def_id.as_local() + && (self.lint.warn_unsafe_macro_metavars_in_private_macros || is_public_macro(self.cx, macro_def_id)) + { + self.macro_unsafe_blocks.push(block.hir_id); + walk_block(self, block); + self.macro_unsafe_blocks.pop(); + } else if ctxt.is_root() && self.expn_depth > 0 { + let unsafe_block = self.macro_unsafe_blocks.last().copied(); + + match (self.lint.metavar_expns.entry(e.span), unsafe_block) { + (Entry::Vacant(e), None) => { + e.insert(MetavarState::ReferencedInSafe); + }, + (Entry::Vacant(e), Some(unsafe_block)) => { + e.insert(MetavarState::ReferencedInUnsafe { + unsafe_blocks: vec![unsafe_block], + }); + }, + (Entry::Occupied(mut e), None) => { + if let MetavarState::ReferencedInUnsafe { .. } = *e.get() { + e.insert(MetavarState::ReferencedInSafe); + } + }, + (Entry::Occupied(mut e), Some(unsafe_block)) => { + if let MetavarState::ReferencedInUnsafe { unsafe_blocks } = e.get_mut() + && !unsafe_blocks.contains(&unsafe_block) + { + unsafe_blocks.push(unsafe_block); + } + }, + } + + // NB: No need to visit descendant nodes. They're guaranteed to represent the same + // metavariable + } else { + walk_expr(self, e); + } + } +} + +impl<'tcx> LateLintPass<'tcx> for ExprMetavarsInUnsafe { + fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx rustc_hir::Body<'tcx>) { + if is_lint_allowed(cx, MACRO_METAVARS_IN_UNSAFE, body.value.hir_id) { + return; + } + + // This BodyVisitor is separate and not part of the lint pass because there is no + // `check_stmt_post` on `(Late)LintPass`, which we'd need to detect when we're leaving a macro span + + let mut vis = BodyVisitor { + #[expect(clippy::bool_to_int_with_if)] // obfuscates the meaning + expn_depth: if body.value.span.from_expansion() { 1 } else { 0 }, + macro_unsafe_blocks: Vec::new(), + lint: self, + cx + }; + vis.visit_body(body); + } + + fn check_crate_post(&mut self, cx: &LateContext<'tcx>) { + // Aggregate all unsafe blocks from all spans: + // ``` + // macro_rules! x { + // ($w:expr, $x:expr, $y:expr) => { $w; unsafe { $w; $x; }; unsafe { $x; $y; }; } + // } + // $w: [] (unsafe#0 is never added because it was referenced in a safe context) + // $x: [unsafe#0, unsafe#1] + // $y: [unsafe#1] + // ``` + // We want to lint unsafe blocks #0 and #1 + let bad_unsafe_blocks = self + .metavar_expns + .iter() + .filter_map(|(_, state)| match state { + MetavarState::ReferencedInUnsafe { unsafe_blocks } => Some(unsafe_blocks.as_slice()), + MetavarState::ReferencedInSafe => None, + }) + .flatten() + .copied() + .map(|id| { + // Remove the syntax context to hide "in this macro invocation" in the diagnostic. + // The invocation doesn't matter. Also we want to dedupe by the unsafe block and not by anything + // related to the callsite. + let span = cx.tcx.hir().span(id); + + (id, Span::new(span.lo(), span.hi(), SyntaxContext::root(), None)) + }) + .dedup_by(|&(_, a), &(_, b)| a == b); + + for (id, span) in bad_unsafe_blocks { + span_lint_hir_and_then( + cx, + MACRO_METAVARS_IN_UNSAFE, + id, + span, + "this macro expands metavariables in an unsafe block", + |diag| { + diag.note("this allows the user of the macro to write unsafe code outside of an unsafe block"); + diag.help( + "consider expanding any metavariables outside of this block, e.g. by storing them in a variable", + ); + diag.help( + "... or also expand referenced metavariables in a safe context to require an unsafe block at callsite", + ); + }, + ); + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/manual_clamp.rs b/src/tools/clippy/clippy_lints/src/manual_clamp.rs index 1eadc200bedc8..e2ab441551824 100644 --- a/src/tools/clippy/clippy_lints/src/manual_clamp.rs +++ b/src/tools/clippy/clippy_lints/src/manual_clamp.rs @@ -611,15 +611,22 @@ impl<'tcx> BinaryOp<'tcx> { /// The clamp meta pattern is a pattern shared between many (but not all) patterns. /// In summary, this pattern consists of two if statements that meet many criteria, +/// /// - binary operators that are one of [`>`, `<`, `>=`, `<=`]. +/// /// - Both binary statements must have a shared argument +/// /// - Which can appear on the left or right side of either statement +/// /// - The binary operators must define a finite range for the shared argument. To put this in /// the terms of Rust `std` library, the following ranges are acceptable +/// /// - `Range` /// - `RangeInclusive` +/// /// And all other range types are not accepted. For the purposes of `clamp` it's irrelevant /// whether the range is inclusive or not, the output is the same. +/// /// - The result of each if statement must be equal to the argument unique to that if statement. The /// result can not be the shared argument in either case. fn is_clamp_meta_pattern<'tcx>( 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 cd61e733694bb..da8c918a62bb6 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,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; -use clippy_utils::source::snippet; +use clippy_utils::source::snippet_with_applicability; use clippy_utils::{is_lint_allowed, path_to_local, search_same, SpanlessEq, SpanlessHash}; use core::cmp::Ordering; use core::{iter, slice}; @@ -9,9 +9,9 @@ use rustc_errors::Applicability; use rustc_hir::def_id::DefId; use rustc_hir::{Arm, Expr, ExprKind, HirId, HirIdMap, HirIdMapEntry, HirIdSet, Pat, PatKind, RangeEnd}; use rustc_lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS; -use rustc_lint::LateContext; +use rustc_lint::{LateContext, LintContext}; use rustc_middle::ty; -use rustc_span::{ErrorGuaranteed, Symbol}; +use rustc_span::{ErrorGuaranteed, Span, Symbol}; use super::MATCH_SAME_ARMS; @@ -110,20 +110,22 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) { && check_same_body() }; + let mut appl = Applicability::MaybeIncorrect; let indexed_arms: Vec<(usize, &Arm<'_>)> = arms.iter().enumerate().collect(); for (&(i, arm1), &(j, arm2)) in search_same(&indexed_arms, hash, eq) { if matches!(arm2.pat.kind, PatKind::Wild) { if !cx.tcx.features().non_exhaustive_omitted_patterns_lint || is_lint_allowed(cx, NON_EXHAUSTIVE_OMITTED_PATTERNS, arm2.hir_id) { + let arm_span = adjusted_arm_span(cx, arm1.span); span_lint_hir_and_then( cx, MATCH_SAME_ARMS, arm1.hir_id, - arm1.span, + arm_span, "this match arm has an identical body to the `_` wildcard arm", |diag| { - diag.span_suggestion(arm1.span, "try removing the arm", "", Applicability::MaybeIncorrect) + diag.span_suggestion(arm_span, "try removing the arm", "", appl) .help("or try changing either arm body") .span_note(arm2.span, "`_` wildcard arm here"); }, @@ -144,23 +146,36 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) { keep_arm.span, "this match arm has an identical body to another arm", |diag| { - let move_pat_snip = snippet(cx, move_arm.pat.span, ""); - let keep_pat_snip = snippet(cx, keep_arm.pat.span, ""); + let move_pat_snip = snippet_with_applicability(cx, move_arm.pat.span, "", &mut appl); + let keep_pat_snip = snippet_with_applicability(cx, keep_arm.pat.span, "", &mut appl); diag.span_suggestion( keep_arm.pat.span, - "try merging the arm patterns", + "or try merging the arm patterns", format!("{keep_pat_snip} | {move_pat_snip}"), - Applicability::MaybeIncorrect, + appl, ) - .help("or try changing either arm body") - .span_note(move_arm.span, "other arm here"); + .span_suggestion( + adjusted_arm_span(cx, move_arm.span), + "and remove this obsolete arm", + "", + appl, + ) + .help("try changing either arm body"); }, ); } } } +/// Extend arm's span to include the comma and whitespaces after it. +fn adjusted_arm_span(cx: &LateContext<'_>, span: Span) -> Span { + let source_map = cx.sess().source_map(); + source_map + .span_extend_while(span, |c| c == ',' || c.is_ascii_whitespace()) + .unwrap_or(span) +} + #[derive(Clone, Copy)] enum NormalizedPat<'a> { Wild, 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 f775ea072e1f0..6ac0705abb212 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 @@ -115,45 +115,60 @@ impl<'a, 'tcx> SigDropChecker<'a, 'tcx> { } } - fn get_type(&self, ex: &'tcx Expr<'_>) -> Ty<'tcx> { - self.cx.typeck_results().expr_ty(ex) + fn is_sig_drop_expr(&mut self, ex: &'tcx Expr<'_>) -> bool { + !ex.is_syntactic_place_expr() && self.has_sig_drop_attr(self.cx.typeck_results().expr_ty(ex)) } - fn has_seen_type(&mut self, ty: Ty<'tcx>) -> bool { - !self.seen_types.insert(ty) + fn has_sig_drop_attr(&mut self, ty: Ty<'tcx>) -> bool { + self.seen_types.clear(); + self.has_sig_drop_attr_impl(ty) } - fn has_sig_drop_attr(&mut self, cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + fn has_sig_drop_attr_impl(&mut self, ty: Ty<'tcx>) -> bool { if let Some(adt) = ty.ty_adt_def() { - if get_attr(cx.sess(), cx.tcx.get_attrs_unchecked(adt.did()), "has_significant_drop").count() > 0 { + if get_attr( + self.cx.sess(), + self.cx.tcx.get_attrs_unchecked(adt.did()), + "has_significant_drop", + ) + .count() + > 0 + { return true; } } - match ty.kind() { - rustc_middle::ty::Adt(a, b) => { - for f in a.all_fields() { - let ty = f.ty(cx.tcx, b); - if !self.has_seen_type(ty) && self.has_sig_drop_attr(cx, ty) { - return true; - } - } + if !self.seen_types.insert(ty) { + return false; + } - for generic_arg in *b { - if let GenericArgKind::Type(ty) = generic_arg.unpack() { - if self.has_sig_drop_attr(cx, ty) { - return true; - } - } - } - false + let result = match ty.kind() { + rustc_middle::ty::Adt(adt, args) => { + // if some field has significant drop, + adt.all_fields() + .map(|field| field.ty(self.cx.tcx, args)) + .any(|ty| self.has_sig_drop_attr_impl(ty)) + // or if there is no generic lifetime and.. + // (to avoid false positive on `Ref<'a, MutexGuard>`) + || (args + .iter() + .all(|arg| !matches!(arg.unpack(), GenericArgKind::Lifetime(_))) + // some generic parameter has significant drop + // (to avoid false negative on `Box>`) + && args + .iter() + .filter_map(|arg| match arg.unpack() { + GenericArgKind::Type(ty) => Some(ty), + _ => None, + }) + .any(|ty| self.has_sig_drop_attr_impl(ty))) }, - rustc_middle::ty::Array(ty, _) - | rustc_middle::ty::RawPtr(ty, _) - | rustc_middle::ty::Ref(_, ty, _) - | rustc_middle::ty::Slice(ty) => self.has_sig_drop_attr(cx, *ty), + rustc_middle::ty::Tuple(tys) => tys.iter().any(|ty| self.has_sig_drop_attr_impl(ty)), + rustc_middle::ty::Array(ty, _) | rustc_middle::ty::Slice(ty) => self.has_sig_drop_attr_impl(*ty), _ => false, - } + }; + + result } } @@ -232,7 +247,7 @@ impl<'a, 'tcx> SigDropHelper<'a, 'tcx> { if self.current_sig_drop.is_some() { return; } - let ty = self.sig_drop_checker.get_type(expr); + let ty = self.cx.typeck_results().expr_ty(expr); if ty.is_ref() { // We checked that the type was ref, so builtin_deref will return Some, // but let's avoid any chance of an ICE. @@ -279,11 +294,7 @@ impl<'a, 'tcx> SigDropHelper<'a, 'tcx> { impl<'a, 'tcx> Visitor<'tcx> for SigDropHelper<'a, 'tcx> { fn visit_expr(&mut self, ex: &'tcx Expr<'_>) { - if !self.is_chain_end - && self - .sig_drop_checker - .has_sig_drop_attr(self.cx, self.sig_drop_checker.get_type(ex)) - { + if !self.is_chain_end && self.sig_drop_checker.is_sig_drop_expr(ex) { self.has_significant_drop = true; return; } @@ -387,10 +398,7 @@ fn has_significant_drop_in_arms<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<' impl<'a, 'tcx> Visitor<'tcx> for ArmSigDropHelper<'a, 'tcx> { fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) { - if self - .sig_drop_checker - .has_sig_drop_attr(self.sig_drop_checker.cx, self.sig_drop_checker.get_type(ex)) - { + if self.sig_drop_checker.is_sig_drop_expr(ex) { self.found_sig_drop_spans.insert(ex.span); return; } 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 fba76852344f5..c9f56e1d98097 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,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::macros::{find_format_args, format_args_inputs_span, root_macro_call_first_node}; +use clippy_utils::macros::{format_args_inputs_span, root_macro_call_first_node, FormatArgsStorage}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; use rustc_errors::Applicability; @@ -16,6 +16,7 @@ use super::EXPECT_FUN_CALL; #[allow(clippy::too_many_lines)] pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, + format_args_storage: &FormatArgsStorage, expr: &hir::Expr<'_>, method_span: Span, name: &str, @@ -134,9 +135,9 @@ pub(super) fn check<'tcx>( // Special handling for `format!` as arg_root if let Some(macro_call) = root_macro_call_first_node(cx, arg_root) { if cx.tcx.is_diagnostic_item(sym::format_macro, macro_call.def_id) - && let Some(format_args) = find_format_args(cx, arg_root, macro_call.expn) + && let Some(format_args) = format_args_storage.get(cx, arg_root, macro_call.expn) { - let span = format_args_inputs_span(&format_args); + let span = format_args_inputs_span(format_args); let sugg = snippet_with_applicability(cx, span, "..", &mut applicability); span_lint_and_sugg( cx, 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 12647ea1ffcb3..b93d51eac647b 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iter_filter.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iter_filter.rs @@ -126,15 +126,15 @@ enum FilterType { /// /// How this is done: /// 1. we know that this is invoked in a method call with `filter` as the method name via `mod.rs` -/// 2. we check that we are in a trait method. Therefore we are in an -/// `(x as Iterator).filter({filter_arg})` method call. +/// 2. we check that we are in a trait method. Therefore we are in an `(x as +/// Iterator).filter({filter_arg})` method call. /// 3. we check that the parent expression is not a map. This is because we don't want to lint /// twice, and we already have a specialized lint for that. /// 4. we check that the span of the filter does not contain a comment. /// 5. we get the type of the `Item` in the `Iterator`, and compare against the type of Option and -/// Result. +/// Result. /// 6. we finally check the contents of the filter argument to see if it is a call to `is_some` or -/// `is_ok`. +/// `is_ok`. /// 7. if all of the above are true, then we return the `FilterType` fn expression_type( cx: &LateContext<'_>, 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 7c852a3768d16..05e77386128f9 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 @@ -12,8 +12,10 @@ use rustc_middle::ty; use rustc_span::{sym, Span}; /// lint use of: +/// /// - `hashmap.iter().map(|(_, v)| v)` /// - `hashmap.into_iter().map(|(_, v)| v)` +/// /// on `HashMaps` and `BTreeMaps` in std pub(super) fn check<'tcx>( diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_overeager_cloned.rs b/src/tools/clippy/clippy_lints/src/methods/iter_overeager_cloned.rs index a52d38790a2bd..5ccb5243e903c 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iter_overeager_cloned.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iter_overeager_cloned.rs @@ -69,12 +69,9 @@ pub(super) fn check<'tcx>( used_move: HirIdSet::default(), }; - ExprUseVisitor::for_clippy( - cx, - closure.def_id, - &mut delegate, - ) - .consume_body(body).into_ok(); + ExprUseVisitor::for_clippy(cx, closure.def_id, &mut delegate) + .consume_body(body) + .into_ok(); let mut to_be_discarded = false; diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs index 2b92bff016db3..9d67aa2337979 100644 --- a/src/tools/clippy/clippy_lints/src/methods/mod.rs +++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs @@ -133,6 +133,7 @@ use bind_instead_of_map::BindInsteadOfMap; use clippy_config::msrvs::{self, Msrv}; use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; +use clippy_utils::macros::FormatArgsStorage; use clippy_utils::ty::{contains_ty_adt_constructor_opaque, implements_trait, is_copy, is_type_diagnostic_item}; use clippy_utils::{contains_return, is_bool, is_trait_method, iter_input_pats, peel_blocks, return_ty}; pub use path_ends_with_ext::DEFAULT_ALLOWED_DOTFILES; @@ -4087,12 +4088,14 @@ declare_clippy_lint! { suspicious, "is_empty() called on strings known at compile time" } + pub struct Methods { avoid_breaking_exported_api: bool, msrv: Msrv, allow_expect_in_tests: bool, allow_unwrap_in_tests: bool, allowed_dotfiles: FxHashSet, + format_args: FormatArgsStorage, } impl Methods { @@ -4103,6 +4106,7 @@ impl Methods { allow_expect_in_tests: bool, allow_unwrap_in_tests: bool, mut allowed_dotfiles: FxHashSet, + format_args: FormatArgsStorage, ) -> Self { allowed_dotfiles.extend(DEFAULT_ALLOWED_DOTFILES.iter().map(ToString::to_string)); @@ -4112,6 +4116,7 @@ impl Methods { allow_expect_in_tests, allow_unwrap_in_tests, allowed_dotfiles, + format_args, } } } @@ -4281,7 +4286,15 @@ impl<'tcx> LateLintPass<'tcx> for Methods { ExprKind::MethodCall(method_call, receiver, args, _) => { let method_span = method_call.ident.span; or_fun_call::check(cx, expr, method_span, method_call.ident.as_str(), receiver, args); - expect_fun_call::check(cx, expr, method_span, method_call.ident.as_str(), receiver, args); + expect_fun_call::check( + cx, + &self.format_args, + expr, + method_span, + method_call.ident.as_str(), + receiver, + args, + ); clone_on_copy::check(cx, expr, method_call.ident.name, receiver, args); clone_on_ref_ptr::check(cx, expr, method_call.ident.name, receiver, args); inefficient_to_string::check(cx, expr, method_call.ident.name, receiver, args); 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 1c695655536c4..f26f164fa54a4 100644 --- a/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs +++ b/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs @@ -127,7 +127,7 @@ pub(super) fn check<'tcx>( } } -/// checks for for collecting into a (generic) method or function argument +/// checks for collecting into a (generic) method or function argument /// taking an `IntoIterator` fn check_collect_into_intoiterator<'tcx>( cx: &LateContext<'tcx>, 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 520dcb2d52dcb..7431dc1cf0b16 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 @@ -3,10 +3,12 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::ForLoop; use clippy_utils::source::snippet_opt; use clippy_utils::ty::{get_iterator_item_ty, implements_trait}; -use clippy_utils::{fn_def_id, get_parent_expr}; +use clippy_utils::visitors::for_each_expr; +use clippy_utils::{can_mut_borrow_both, fn_def_id, get_parent_expr, path_to_local}; +use core::ops::ControlFlow; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; -use rustc_hir::{Expr, ExprKind}; +use rustc_hir::{BindingMode, Expr, ExprKind, Node, PatKind}; use rustc_lint::LateContext; use rustc_span::{sym, Symbol}; @@ -40,6 +42,53 @@ pub fn check_for_loop_iter( && !clone_or_copy_needed && let Some(receiver_snippet) = snippet_opt(cx, receiver.span) { + // Issue 12098 + // https://github.com/rust-lang/rust-clippy/issues/12098 + // if the assignee have `mut borrow` conflict with the iteratee + // the lint should not execute, former didn't consider the mut case + + // check whether `expr` is mutable + fn is_mutable(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + if let Some(hir_id) = path_to_local(expr) + && let Node::Pat(pat) = cx.tcx.hir_node(hir_id) + { + matches!(pat.kind, PatKind::Binding(BindingMode::MUT, ..)) + } else { + true + } + } + + fn is_caller_or_fields_change(cx: &LateContext<'_>, body: &Expr<'_>, caller: &Expr<'_>) -> bool { + let mut change = false; + if let ExprKind::Block(block, ..) = body.kind { + for_each_expr(block, |e| { + match e.kind { + ExprKind::Assign(assignee, _, _) | ExprKind::AssignOp(_, assignee, _) => { + change |= !can_mut_borrow_both(cx, caller, assignee); + }, + _ => {}, + } + // the return value has no effect but the function need one return value + ControlFlow::<()>::Continue(()) + }); + } + change + } + + if let ExprKind::Call(_, [child, ..]) = expr.kind { + // filter first layer of iterator + let mut child = child; + // get inner real caller requests for clone + while let ExprKind::MethodCall(_, caller, _, _) = child.kind { + child = caller; + } + if is_mutable(cx, child) && is_caller_or_fields_change(cx, body, child) { + // skip lint + return true; + } + }; + + // the lint should not be executed if no violation happens let snippet = if let ExprKind::MethodCall(maybe_iter_method_name, collection, [], _) = receiver.kind && maybe_iter_method_name.ident.name == sym::iter && let Some(iterator_trait_id) = cx.tcx.get_diagnostic_item(sym::Iterator) diff --git a/src/tools/clippy/clippy_lints/src/methods/utils.rs b/src/tools/clippy/clippy_lints/src/methods/utils.rs index c50f24f824ab0..34d7b9acbe4b0 100644 --- a/src/tools/clippy/clippy_lints/src/methods/utils.rs +++ b/src/tools/clippy/clippy_lints/src/methods/utils.rs @@ -120,6 +120,7 @@ fn pat_bindings(pat: &Pat<'_>) -> Vec { /// operations performed on `binding_hir_ids` are: /// * to take non-mutable references to them /// * to use them as non-mutable `&self` in method calls +/// /// If any of `binding_hir_ids` is used in any other way, then `clone_or_copy_needed` will be true /// when `CloneOrCopyVisitor` is done visiting. struct CloneOrCopyVisitor<'cx, 'tcx> { 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 ae6cf992ef7b4..daf166bad90d8 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,6 +1,6 @@ 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; 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}; @@ -11,7 +11,6 @@ use rustc_hir::{Body, Expr, ExprKind, Mutability, Path, QPath}; use rustc_index::bit_set::BitSet; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::mir::{Rvalue, StatementKind}; use rustc_middle::ty::{ self, ClauseKind, EarlyBinder, FnSig, GenericArg, GenericArgKind, ParamTy, ProjectionPredicate, Ty, }; @@ -106,7 +105,6 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrowsForGenericArgs<'tcx> { } && let count = needless_borrow_count( cx, - &mut self.possible_borrowers, fn_id, cx.typeck_results().node_args(hir_id), i, @@ -155,11 +153,9 @@ fn path_has_args(p: &QPath<'_>) -> bool { /// The following constraints will be checked: /// * The borrowed expression meets all the generic type's constraints. /// * The generic type appears only once in the functions signature. -/// * The borrowed value will not be moved if it is used later in the function. -#[expect(clippy::too_many_arguments)] +/// * The borrowed value is Copy itself OR not a variable (created by a function call) fn needless_borrow_count<'tcx>( cx: &LateContext<'tcx>, - possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>, fn_id: DefId, callee_args: ty::GenericArgsRef<'tcx>, arg_index: usize, @@ -234,9 +230,9 @@ fn needless_borrow_count<'tcx>( let referent_ty = cx.typeck_results().expr_ty(referent); - if !is_copy(cx, referent_ty) - && (referent_ty.has_significant_drop(cx.tcx, cx.param_env) - || !referent_used_exactly_once(cx, possible_borrowers, reference)) + if (!is_copy(cx, referent_ty) && !referent_ty.is_ref()) + && let ExprKind::AddrOf(_, _, inner) = reference.kind + && !matches!(inner.kind, ExprKind::Call(..) | ExprKind::MethodCall(..)) { return false; } @@ -339,37 +335,6 @@ fn is_mixed_projection_predicate<'tcx>( } } -fn referent_used_exactly_once<'tcx>( - cx: &LateContext<'tcx>, - possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>, - reference: &Expr<'tcx>, -) -> bool { - if let Some(mir) = enclosing_mir(cx.tcx, reference.hir_id) - && let Some(local) = expr_local(cx.tcx, reference) - && let [location] = *local_assignments(mir, local).as_slice() - && let block_data = &mir.basic_blocks[location.block] - && let Some(statement) = block_data.statements.get(location.statement_index) - && let StatementKind::Assign(box (_, Rvalue::Ref(_, _, place))) = statement.kind - && !place.is_indirect_first_projection() - { - let body_owner_local_def_id = cx.tcx.hir().enclosing_body_owner(reference.hir_id); - if possible_borrowers - .last() - .map_or(true, |&(local_def_id, _)| local_def_id != body_owner_local_def_id) - { - possible_borrowers.push((body_owner_local_def_id, PossibleBorrowerMap::new(cx, mir))); - } - let possible_borrower = &mut possible_borrowers.last_mut().unwrap().1; - // If `only_borrowers` were used here, the `copyable_iterator::warn` test would fail. The reason is - // that `PossibleBorrowerVisitor::visit_terminator` considers `place.local` a possible borrower of - // itself. See the comment in that method for an explanation as to why. - possible_borrower.bounded_borrowers(&[local], &[local, place.local], place.local, location) - && used_exactly_once(mir, place.local).unwrap_or(false) - } else { - false - } -} - // Iteratively replaces `param_ty` with `new_ty` in `args`, and similarly for each resulting // projected type that is a type parameter. Returns `false` if replacing the types would have an // effect on the function signature beyond substituting `new_ty` for `param_ty`. @@ -408,7 +373,11 @@ fn replace_types<'tcx>( && let Some(term_ty) = projection_predicate.term.ty() && let ty::Param(term_param_ty) = term_ty.kind() { - let projection = projection_predicate.projection_term.with_self_ty(cx.tcx, new_ty).expect_ty(cx.tcx).to_ty(cx.tcx); + let projection = projection_predicate + .projection_term + .with_self_ty(cx.tcx, new_ty) + .expect_ty(cx.tcx) + .to_ty(cx.tcx); if let Ok(projected_ty) = cx.tcx.try_normalize_erasing_regions(cx.param_env, projection) && args[term_param_ty.index as usize] != GenericArg::from(projected_ty) diff --git a/src/tools/clippy/clippy_lints/src/needless_continue.rs b/src/tools/clippy/clippy_lints/src/needless_continue.rs index 8b4a12bb76648..b97cb4579ca7b 100644 --- a/src/tools/clippy/clippy_lints/src/needless_continue.rs +++ b/src/tools/clippy/clippy_lints/src/needless_continue.rs @@ -178,8 +178,7 @@ impl EarlyLintPass for NeedlessContinue { /// Given an expression, returns true if either of the following is true /// /// - The expression is a `continue` node. -/// - The expression node is a block with the first statement being a -/// `continue`. +/// - The expression node is a block with the first statement being a `continue`. fn needless_continue_in_else(else_expr: &ast::Expr, label: Option<&ast::Label>) -> bool { match else_expr.kind { ast::ExprKind::Block(ref else_block, _) => is_first_block_stmt_continue(else_block, label), diff --git a/src/tools/clippy/clippy_lints/src/needless_late_init.rs b/src/tools/clippy/clippy_lints/src/needless_late_init.rs index 6605d1fa51a36..5a0ae1a4d6d29 100644 --- a/src/tools/clippy/clippy_lints/src/needless_late_init.rs +++ b/src/tools/clippy/clippy_lints/src/needless_late_init.rs @@ -273,24 +273,16 @@ fn check<'tcx>( msg_span, "unneeded late initialization", |diag| { - diag.tool_only_span_suggestion( - local_stmt.span, - "remove the local", - "", - Applicability::MachineApplicable, - ); - - diag.span_suggestion( - assign.lhs_span, - format!("declare `{binding_name}` here"), - let_snippet, + diag.multipart_suggestion( + format!("move the declaration `{binding_name}` here"), + vec![(local_stmt.span, String::new()), (assign.lhs_span, let_snippet)], Applicability::MachineApplicable, ); }, ); }, ExprKind::If(cond, then_expr, Some(else_expr)) if !contains_let(cond) => { - let (applicability, suggestions) = assignment_suggestions(cx, binding_id, [then_expr, else_expr])?; + let (applicability, mut suggestions) = assignment_suggestions(cx, binding_id, [then_expr, else_expr])?; span_lint_and_then( cx, @@ -298,30 +290,26 @@ fn check<'tcx>( local_stmt.span, "unneeded late initialization", |diag| { - diag.tool_only_span_suggestion(local_stmt.span, "remove the local", String::new(), applicability); - - diag.span_suggestion_verbose( - usage.stmt.span.shrink_to_lo(), - format!("declare `{binding_name}` here"), - format!("{let_snippet} = "), - applicability, - ); - - diag.multipart_suggestion("remove the assignments from the branches", suggestions, applicability); + suggestions.push((local_stmt.span, String::new())); + suggestions.push((usage.stmt.span.shrink_to_lo(), format!("{let_snippet} = "))); if usage.needs_semi { - diag.span_suggestion( - usage.stmt.span.shrink_to_hi(), - "add a semicolon after the `if` expression", - ";", - applicability, - ); + suggestions.push((usage.stmt.span.shrink_to_hi(), ";".to_owned())); } + + diag.multipart_suggestion( + format!( + "move the declaration `{binding_name}` here and remove the assignments from the branches" + ), + suggestions, + applicability, + ); }, ); }, ExprKind::Match(_, arms, MatchSource::Normal) => { - let (applicability, suggestions) = assignment_suggestions(cx, binding_id, arms.iter().map(|arm| arm.body))?; + let (applicability, mut suggestions) = + assignment_suggestions(cx, binding_id, arms.iter().map(|arm| arm.body))?; span_lint_and_then( cx, @@ -329,29 +317,18 @@ fn check<'tcx>( local_stmt.span, "unneeded late initialization", |diag| { - diag.tool_only_span_suggestion(local_stmt.span, "remove the local", String::new(), applicability); + suggestions.push((local_stmt.span, String::new())); + suggestions.push((usage.stmt.span.shrink_to_lo(), format!("{let_snippet} = "))); - diag.span_suggestion_verbose( - usage.stmt.span.shrink_to_lo(), - format!("declare `{binding_name}` here"), - format!("{let_snippet} = "), - applicability, - ); + if usage.needs_semi { + suggestions.push((usage.stmt.span.shrink_to_hi(), ";".to_owned())); + } diag.multipart_suggestion( - "remove the assignments from the `match` arms", + format!("move the declaration `{binding_name}` here and remove the assignments from the `match` arms"), suggestions, applicability, ); - - if usage.needs_semi { - diag.span_suggestion( - usage.stmt.span.shrink_to_hi(), - "add a semicolon after the `match` expression", - ";", - applicability, - ); - } }, ); }, 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 9b852f52ea1eb..da6ed5fb96f12 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 @@ -117,7 +117,9 @@ fn check_closures<'tcx>( .associated_body() .map(|(_, body_id)| hir.body(body_id)) { - euv::ExprUseVisitor::for_clippy(cx, closure, &mut *ctx).consume_body(body).into_ok(); + euv::ExprUseVisitor::for_clippy(cx, closure, &mut *ctx) + .consume_body(body) + .into_ok(); } } } @@ -194,7 +196,9 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> { async_closures: FxHashSet::default(), tcx: cx.tcx, }; - euv::ExprUseVisitor::for_clippy(cx, fn_def_id, &mut ctx).consume_body(body).into_ok(); + euv::ExprUseVisitor::for_clippy(cx, fn_def_id, &mut ctx) + .consume_body(body) + .into_ok(); let mut checked_closures = FxHashSet::default(); 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 0986571d0f281..f2e00cef7e9ff 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 @@ -133,7 +133,9 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { // function body. let MovedVariablesCtxt { moved_vars } = { let mut ctx = MovedVariablesCtxt::default(); - euv::ExprUseVisitor::for_clippy(cx, fn_def_id, &mut ctx).consume_body(body).into_ok(); + euv::ExprUseVisitor::for_clippy(cx, fn_def_id, &mut ctx) + .consume_body(body) + .into_ok(); ctx }; diff --git a/src/tools/clippy/clippy_lints/src/no_effect.rs b/src/tools/clippy/clippy_lints/src/no_effect.rs index f915145e794bc..87f886b1128d8 100644 --- a/src/tools/clippy/clippy_lints/src/no_effect.rs +++ b/src/tools/clippy/clippy_lints/src/no_effect.rs @@ -94,7 +94,6 @@ impl<'tcx> LateLintPass<'tcx> for NoEffect { fn check_block_post(&mut self, cx: &LateContext<'tcx>, _: &'tcx rustc_hir::Block<'tcx>) { for hir_id in self.local_bindings.pop().unwrap() { - // FIXME(rust/#120456) - is `swap_remove` correct? if let Some(span) = self.underscore_bindings.swap_remove(&hir_id) { span_lint_hir( cx, @@ -109,7 +108,6 @@ impl<'tcx> LateLintPass<'tcx> for NoEffect { fn check_expr(&mut self, _: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { if let Some(def_id) = path_to_local(expr) { - // FIXME(rust/#120456) - is `swap_remove` correct? self.underscore_bindings.swap_remove(&def_id); } } @@ -118,7 +116,11 @@ impl<'tcx> LateLintPass<'tcx> for NoEffect { impl NoEffect { fn check_no_effect(&mut self, cx: &LateContext<'_>, stmt: &Stmt<'_>) -> bool { if let StmtKind::Semi(expr) = stmt.kind { - // move `expr.span.from_expansion()` ahead + // Covered by rustc `path_statements` lint + if matches!(expr.kind, ExprKind::Path(_)) { + return true; + } + if expr.span.from_expansion() { return false; } diff --git a/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs b/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs index ef51a9a9a1c8a..75066c1f0d2e2 100644 --- a/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs +++ b/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs @@ -1,8 +1,14 @@ 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 rustc_lint::{LateContext, LateLintPass}; -use rustc_session::declare_lint_pass; +use rustc_session::impl_lint_pass; + +#[derive(Clone)] +pub struct PanicUnimplemented { + pub allow_panic_in_tests: bool, +} declare_clippy_lint! { /// ### What it does @@ -77,7 +83,7 @@ declare_clippy_lint! { "usage of the `unreachable!` macro" } -declare_lint_pass!(PanicUnimplemented => [UNIMPLEMENTED, UNREACHABLE, TODO, PANIC]); +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<'_>) { @@ -85,7 +91,9 @@ impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented { return; }; if is_panic(cx, macro_call.def_id) { - if cx.tcx.hir().is_inside_const_context(expr.hir_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; } diff --git a/src/tools/clippy/clippy_lints/src/strings.rs b/src/tools/clippy/clippy_lints/src/strings.rs index 87a3c3874d77b..292124196ff64 100644 --- a/src/tools/clippy/clippy_lints/src/strings.rs +++ b/src/tools/clippy/clippy_lints/src/strings.rs @@ -389,6 +389,10 @@ declare_lint_pass!(StrToString => [STR_TO_STRING]); impl<'tcx> LateLintPass<'tcx> for StrToString { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) { + if expr.span.from_expansion() { + return; + } + if let ExprKind::MethodCall(path, self_arg, ..) = &expr.kind && path.ident.name == sym::to_string && let ty = cx.typeck_results().expr_ty(self_arg) @@ -437,6 +441,10 @@ declare_lint_pass!(StringToString => [STRING_TO_STRING]); impl<'tcx> LateLintPass<'tcx> for StringToString { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) { + if expr.span.from_expansion() { + return; + } + if let ExprKind::MethodCall(path, self_arg, ..) = &expr.kind && path.ident.name == sym::to_string && let ty = cx.typeck_results().expr_ty(self_arg) diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_non_zero.rs b/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_non_zero.rs index 7d824ef213900..3729dfd3e86f2 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_non_zero.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_non_zero.rs @@ -28,7 +28,7 @@ pub(super) fn check<'tcx>( let int_ty = substs.type_at(0); if from_ty != int_ty { - return false; + return false; } span_lint_and_then( diff --git a/src/tools/clippy/clippy_lints/src/unwrap.rs b/src/tools/clippy/clippy_lints/src/unwrap.rs index 5b2841dcd8330..c0d9bcdd259c7 100644 --- a/src/tools/clippy/clippy_lints/src/unwrap.rs +++ b/src/tools/clippy/clippy_lints/src/unwrap.rs @@ -251,11 +251,7 @@ impl<'a, 'tcx> UnwrappableVariablesVisitor<'a, 'tcx> { local_id: unwrap_info.local_id, }; - let vis = ExprUseVisitor::for_clippy( - self.cx, - cond.hir_id.owner.def_id, - &mut delegate, - ); + let vis = ExprUseVisitor::for_clippy(self.cx, cond.hir_id.owner.def_id, &mut delegate); vis.walk_expr(cond).into_ok(); vis.walk_expr(branch).into_ok(); 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 58e66c9f9b951..5acfd35fd6ae6 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 @@ -1,4 +1,4 @@ -use clippy_utils::macros::AST_FORMAT_ARGS; +use clippy_utils::macros::FormatArgsStorage; use clippy_utils::source::snippet_opt; use itertools::Itertools; use rustc_ast::{Crate, Expr, ExprKind, FormatArgs}; @@ -9,13 +9,20 @@ use rustc_session::impl_lint_pass; use rustc_span::{hygiene, Span}; use std::iter::once; use std::mem; -use std::rc::Rc; -/// Collects [`rustc_ast::FormatArgs`] so that future late passes can call -/// [`clippy_utils::macros::find_format_args`] -#[derive(Default)] +/// Populates [`FormatArgsStorage`] with AST [`FormatArgs`] nodes pub struct FormatArgsCollector { - format_args: FxHashMap>, + format_args: FxHashMap, + storage: FormatArgsStorage, +} + +impl FormatArgsCollector { + pub fn new(storage: FormatArgsStorage) -> Self { + Self { + format_args: FxHashMap::default(), + storage, + } + } } impl_lint_pass!(FormatArgsCollector => []); @@ -27,16 +34,12 @@ impl EarlyLintPass for FormatArgsCollector { return; } - self.format_args - .insert(expr.span.with_parent(None), Rc::new((**args).clone())); + self.format_args.insert(expr.span.with_parent(None), (**args).clone()); } } fn check_crate_post(&mut self, _: &EarlyContext<'_>, _: &Crate) { - AST_FORMAT_ARGS.with(|ast_format_args| { - let result = ast_format_args.set(mem::take(&mut self.format_args)); - debug_assert!(result.is_ok()); - }); + self.storage.set(mem::take(&mut self.format_args)); } } diff --git a/src/tools/clippy/clippy_lints/src/write.rs b/src/tools/clippy/clippy_lints/src/write.rs index 26c6859233d53..ff6ee0d10ad56 100644 --- a/src/tools/clippy/clippy_lints/src/write.rs +++ b/src/tools/clippy/clippy_lints/src/write.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; -use clippy_utils::macros::{find_format_args, format_arg_removal_span, root_macro_call_first_node, MacroCall}; +use clippy_utils::macros::{format_arg_removal_span, root_macro_call_first_node, FormatArgsStorage, MacroCall}; use clippy_utils::source::{expand_past_previous_comma, snippet_opt}; use clippy_utils::{is_in_cfg_test, is_in_test_function}; use rustc_ast::token::LitKind; @@ -236,13 +236,15 @@ declare_clippy_lint! { #[derive(Default)] pub struct Write { + format_args: FormatArgsStorage, in_debug_impl: bool, allow_print_in_tests: bool, } impl Write { - pub fn new(allow_print_in_tests: bool) -> Self { + pub fn new(format_args: FormatArgsStorage, allow_print_in_tests: bool) -> Self { Self { + format_args, allow_print_in_tests, ..Default::default() } @@ -307,7 +309,7 @@ impl<'tcx> LateLintPass<'tcx> for Write { _ => return, } - if let Some(format_args) = find_format_args(cx, expr, macro_call.expn) { + if let Some(format_args) = self.format_args.get(cx, expr, macro_call.expn) { // ignore `writeln!(w)` and `write!(v, some_macro!())` if format_args.span.from_expansion() { return; @@ -315,15 +317,15 @@ impl<'tcx> LateLintPass<'tcx> for Write { match diag_name { sym::print_macro | sym::eprint_macro | sym::write_macro => { - check_newline(cx, &format_args, ¯o_call, name); + check_newline(cx, format_args, ¯o_call, name); }, sym::println_macro | sym::eprintln_macro | sym::writeln_macro => { - check_empty_string(cx, &format_args, ¯o_call, name); + check_empty_string(cx, format_args, ¯o_call, name); }, _ => {}, } - check_literal(cx, &format_args, name); + check_literal(cx, format_args, name); if !self.in_debug_impl { for piece in &format_args.template { diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index 99d7aba2f7a15..4c603bda770a9 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -193,6 +193,21 @@ pub fn find_binding_init<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option< None } +/// Checks if the given local has an initializer or is from something other than a `let` statement +/// +/// e.g. returns true for `x` in `fn f(x: usize) { .. }` and `let x = 1;` but false for `let x;` +pub fn local_is_initialized(cx: &LateContext<'_>, local: HirId) -> bool { + for (_, node) in cx.tcx.hir().parent_iter(local) { + match node { + Node::Pat(..) | Node::PatField(..) => {}, + Node::LetStmt(let_stmt) => return let_stmt.init.is_some(), + _ => return true, + } + } + + false +} + /// Returns `true` if the given `NodeId` is inside a constant context /// /// # Example @@ -1499,15 +1514,18 @@ pub fn is_else_clause_in_let_else(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool { } /// Checks whether the given `Expr` is a range equivalent to a `RangeFull`. +/// /// For the lower bound, this means that: /// - either there is none /// - or it is the smallest value that can be represented by the range's integer type +/// /// For the upper bound, this means that: /// - either there is none /// - or it is the largest value that can be represented by the range's integer type and is /// inclusive /// - or it is a call to some container's `len` method and is exclusive, and the range is passed to /// a method call on that same container (e.g. `v.drain(..v.len())`) +/// /// If the given `Expr` is not some kind of range, the function returns `false`. pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Option<&Path<'_>>) -> bool { let ty = cx.typeck_results().expr_ty(expr); diff --git a/src/tools/clippy/clippy_utils/src/macros.rs b/src/tools/clippy/clippy_utils/src/macros.rs index 257dd76ab15cf..8daab9b0d92cf 100644 --- a/src/tools/clippy/clippy_utils/src/macros.rs +++ b/src/tools/clippy/clippy_utils/src/macros.rs @@ -5,15 +5,13 @@ use crate::visitors::{for_each_expr, Descend}; use arrayvec::ArrayVec; use rustc_ast::{FormatArgs, FormatArgument, FormatPlaceholder}; use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::sync::{Lrc, OnceLock}; 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 std::cell::OnceCell; use std::ops::ControlFlow; -use std::rc::Rc; -use std::sync::atomic::{AtomicBool, Ordering}; const FORMAT_MACRO_DIAG_ITEMS: &[Symbol] = &[ sym::assert_eq_macro, @@ -388,50 +386,44 @@ fn is_assert_arg(cx: &LateContext<'_>, expr: &Expr<'_>, assert_expn: ExpnId) -> } } -thread_local! { - /// We preserve the [`FormatArgs`] structs from the early pass for use in the late pass to be - /// able to access the many features of a [`LateContext`]. - /// - /// A thread local is used because [`FormatArgs`] is `!Send` and `!Sync`, we are making an - /// assumption that the early pass that populates the map and the later late passes will all be - /// running on the same thread. - #[doc(hidden)] - pub static AST_FORMAT_ARGS: OnceCell>> = { - static CALLED: AtomicBool = AtomicBool::new(false); - debug_assert!( - !CALLED.swap(true, Ordering::SeqCst), - "incorrect assumption: `AST_FORMAT_ARGS` should only be accessed by a single thread", - ); - - OnceCell::new() - }; -} +/// Stores AST [`FormatArgs`] nodes for use in late lint passes, as they are in a desugared form in +/// the HIR +#[derive(Default, Clone)] +pub struct FormatArgsStorage(Lrc>>); -/// Returns an AST [`FormatArgs`] node if a `format_args` expansion is found as a descendant of -/// `expn_id` -pub fn find_format_args(cx: &LateContext<'_>, start: &Expr<'_>, expn_id: ExpnId) -> Option> { - let format_args_expr = for_each_expr(start, |expr| { - let ctxt = expr.span.ctxt(); - if ctxt.outer_expn().is_descendant_of(expn_id) { - if macro_backtrace(expr.span) - .map(|macro_call| cx.tcx.item_name(macro_call.def_id)) - .any(|name| matches!(name, sym::const_format_args | sym::format_args | sym::format_args_nl)) - { - ControlFlow::Break(expr) +impl FormatArgsStorage { + /// Returns an AST [`FormatArgs`] node if a `format_args` expansion is found as a descendant of + /// `expn_id` + /// + /// See also [`find_format_arg_expr`] + pub fn get(&self, cx: &LateContext<'_>, start: &Expr<'_>, expn_id: ExpnId) -> Option<&FormatArgs> { + let format_args_expr = for_each_expr(start, |expr| { + let ctxt = expr.span.ctxt(); + if ctxt.outer_expn().is_descendant_of(expn_id) { + if macro_backtrace(expr.span) + .map(|macro_call| cx.tcx.item_name(macro_call.def_id)) + .any(|name| matches!(name, sym::const_format_args | sym::format_args | sym::format_args_nl)) + { + ControlFlow::Break(expr) + } else { + ControlFlow::Continue(Descend::Yes) + } } else { - ControlFlow::Continue(Descend::Yes) + ControlFlow::Continue(Descend::No) } - } else { - ControlFlow::Continue(Descend::No) - } - })?; + })?; - AST_FORMAT_ARGS.with(|ast_format_args| { - ast_format_args - .get()? - .get(&format_args_expr.span.with_parent(None)) - .cloned() - }) + debug_assert!(self.0.get().is_some(), "`FormatArgsStorage` not yet populated"); + + self.0.get()?.get(&format_args_expr.span.with_parent(None)) + } + + /// Should only be called by `FormatArgsCollector` + pub fn set(&self, format_args: FxHashMap) { + self.0 + .set(format_args) + .expect("`FormatArgsStorage::set` should only be called once"); + } } /// Attempt to find the [`rustc_hir::Expr`] that corresponds to the [`FormatArgument`]'s value, if diff --git a/src/tools/clippy/clippy_utils/src/mir/possible_borrower.rs b/src/tools/clippy/clippy_utils/src/mir/possible_borrower.rs index 06229ac938f9a..7b4fd8a210edf 100644 --- a/src/tools/clippy/clippy_utils/src/mir/possible_borrower.rs +++ b/src/tools/clippy/clippy_utils/src/mir/possible_borrower.rs @@ -149,7 +149,7 @@ impl TypeVisitor> for ContainsRegion { } fn rvalue_locals(rvalue: &mir::Rvalue<'_>, mut visit: impl FnMut(mir::Local)) { - use rustc_middle::mir::Rvalue::{Aggregate, BinaryOp, Cast, CheckedBinaryOp, Repeat, UnaryOp, Use}; + use rustc_middle::mir::Rvalue::{Aggregate, BinaryOp, Cast, Repeat, UnaryOp, Use}; let mut visit_op = |op: &mir::Operand<'_>| match op { mir::Operand::Copy(p) | mir::Operand::Move(p) => visit(p.local), @@ -159,7 +159,7 @@ fn rvalue_locals(rvalue: &mir::Rvalue<'_>, mut visit: impl FnMut(mir::Local)) { match rvalue { Use(op) | Repeat(op, _) | Cast(_, op, _) | UnaryOp(_, op) => visit_op(op), Aggregate(_, ops) => ops.iter().for_each(visit_op), - BinaryOp(_, box (lhs, rhs)) | CheckedBinaryOp(_, box (lhs, rhs)) => { + BinaryOp(_, box (lhs, rhs)) => { visit_op(lhs); visit_op(rhs); }, 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 95851a2eed814..8ee7d87acb3e0 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 @@ -3,7 +3,7 @@ // of terminologies might not be relevant in the context of Clippy. Note that its behavior might // differ from the time of `rustc` even if the name stays the same. -use clippy_config::msrvs::Msrv; +use clippy_config::msrvs::{self, Msrv}; use hir::LangItem; use rustc_attr::StableSince; use rustc_const_eval::transform::check_consts::ConstCx; @@ -42,7 +42,7 @@ pub fn is_min_const_fn<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, msrv: &Msrv) for bb in &*body.basic_blocks { check_terminator(tcx, body, bb.terminator(), msrv)?; for stmt in &bb.statements { - check_statement(tcx, body, def_id, stmt)?; + check_statement(tcx, body, def_id, stmt, msrv)?; } } Ok(()) @@ -102,13 +102,14 @@ fn check_rvalue<'tcx>( def_id: DefId, rvalue: &Rvalue<'tcx>, span: Span, + msrv: &Msrv, ) -> McfResult { match rvalue { Rvalue::ThreadLocalRef(_) => Err((span, "cannot access thread local storage in const fn".into())), Rvalue::Len(place) | Rvalue::Discriminant(place) | Rvalue::Ref(_, _, place) | Rvalue::AddressOf(_, place) => { - check_place(tcx, *place, span, body) + check_place(tcx, *place, span, body, msrv) }, - Rvalue::CopyForDeref(place) => check_place(tcx, *place, span, body), + Rvalue::CopyForDeref(place) => check_place(tcx, *place, span, body, msrv), Rvalue::Repeat(operand, _) | Rvalue::Use(operand) | Rvalue::Cast( @@ -122,7 +123,7 @@ fn check_rvalue<'tcx>( | CastKind::PointerCoercion(PointerCoercion::MutToConstPointer | PointerCoercion::ArrayToPointer), operand, _, - ) => check_operand(tcx, operand, span, body), + ) => check_operand(tcx, operand, span, body, msrv), Rvalue::Cast( CastKind::PointerCoercion( PointerCoercion::UnsafeFnPointer @@ -133,15 +134,13 @@ fn check_rvalue<'tcx>( _, ) => Err((span, "function pointer casts are not allowed in const fn".into())), Rvalue::Cast(CastKind::PointerCoercion(PointerCoercion::Unsize), op, cast_ty) => { - let pointee_ty = if let Some(deref_ty) = cast_ty.builtin_deref(true) { - deref_ty - } else { + 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())); }; let unsized_ty = tcx.struct_tail_erasing_lifetimes(pointee_ty, tcx.param_env(def_id)); if let ty::Slice(_) | ty::Str = unsized_ty.kind() { - check_operand(tcx, op, span, body)?; + check_operand(tcx, op, span, body, msrv)?; // Casting/coercing things to slices is fine. Ok(()) } else { @@ -161,9 +160,9 @@ fn check_rvalue<'tcx>( "transmute can attempt to turn pointers into integers, so is unstable in const fn".into(), )), // binops are fine on integers - Rvalue::BinaryOp(_, box (lhs, rhs)) | Rvalue::CheckedBinaryOp(_, box (lhs, rhs)) => { - check_operand(tcx, lhs, span, body)?; - check_operand(tcx, rhs, span, body)?; + Rvalue::BinaryOp(_, box (lhs, rhs)) => { + check_operand(tcx, lhs, span, body, msrv)?; + check_operand(tcx, rhs, span, body, msrv)?; let ty = lhs.ty(body, tcx); if ty.is_integral() || ty.is_bool() || ty.is_char() { Ok(()) @@ -179,14 +178,14 @@ fn check_rvalue<'tcx>( Rvalue::UnaryOp(_, operand) => { let ty = operand.ty(body, tcx); if ty.is_integral() || ty.is_bool() { - check_operand(tcx, operand, span, body) + check_operand(tcx, operand, span, body, msrv) } else { Err((span, "only int and `bool` operations are stable in const fn".into())) } }, Rvalue::Aggregate(_, operands) => { for operand in operands { - check_operand(tcx, operand, span, body)?; + check_operand(tcx, operand, span, body, msrv)?; } Ok(()) }, @@ -198,28 +197,29 @@ fn check_statement<'tcx>( body: &Body<'tcx>, def_id: DefId, statement: &Statement<'tcx>, + msrv: &Msrv, ) -> McfResult { let span = statement.source_info.span; match &statement.kind { StatementKind::Assign(box (place, rval)) => { - check_place(tcx, *place, span, body)?; - check_rvalue(tcx, body, def_id, rval, span) + check_place(tcx, *place, span, body, msrv)?; + check_rvalue(tcx, body, def_id, rval, span, msrv) }, - StatementKind::FakeRead(box (_, place)) => check_place(tcx, *place, span, body), + StatementKind::FakeRead(box (_, place)) => check_place(tcx, *place, span, body, msrv), // just an assignment StatementKind::SetDiscriminant { place, .. } | StatementKind::Deinit(place) => { - check_place(tcx, **place, span, body) + check_place(tcx, **place, span, body, msrv) }, - StatementKind::Intrinsic(box NonDivergingIntrinsic::Assume(op)) => check_operand(tcx, op, span, body), + StatementKind::Intrinsic(box NonDivergingIntrinsic::Assume(op)) => check_operand(tcx, op, span, body, msrv), StatementKind::Intrinsic(box NonDivergingIntrinsic::CopyNonOverlapping( rustc_middle::mir::CopyNonOverlapping { dst, src, count }, )) => { - check_operand(tcx, dst, span, body)?; - check_operand(tcx, src, span, body)?; - check_operand(tcx, count, span, body) + check_operand(tcx, dst, span, body, msrv)?; + check_operand(tcx, src, span, body, msrv)?; + check_operand(tcx, count, span, body, msrv) }, // These are all NOPs StatementKind::StorageLive(_) @@ -233,7 +233,13 @@ fn check_statement<'tcx>( } } -fn check_operand<'tcx>(tcx: TyCtxt<'tcx>, operand: &Operand<'tcx>, span: Span, body: &Body<'tcx>) -> McfResult { +fn check_operand<'tcx>( + tcx: TyCtxt<'tcx>, + operand: &Operand<'tcx>, + span: Span, + body: &Body<'tcx>, + msrv: &Msrv, +) -> McfResult { match operand { Operand::Move(place) => { if !place.projection.as_ref().is_empty() @@ -245,9 +251,9 @@ fn check_operand<'tcx>(tcx: TyCtxt<'tcx>, operand: &Operand<'tcx>, span: Span, b )); } - check_place(tcx, *place, span, body) + check_place(tcx, *place, span, body, msrv) }, - Operand::Copy(place) => check_place(tcx, *place, span, body), + Operand::Copy(place) => check_place(tcx, *place, span, body, msrv), Operand::Constant(c) => match c.check_static_ptr(tcx) { Some(_) => Err((span, "cannot access `static` items in const fn".into())), None => Ok(()), @@ -255,23 +261,27 @@ fn check_operand<'tcx>(tcx: TyCtxt<'tcx>, operand: &Operand<'tcx>, span: Span, b } } -fn check_place<'tcx>(tcx: TyCtxt<'tcx>, place: Place<'tcx>, span: Span, body: &Body<'tcx>) -> McfResult { +fn check_place<'tcx>(tcx: TyCtxt<'tcx>, place: Place<'tcx>, span: Span, body: &Body<'tcx>, msrv: &Msrv) -> McfResult { for (base, elem) in place.as_ref().iter_projections() { match elem { ProjectionElem::Field(..) => { - let base_ty = base.ty(body, tcx).ty; - if let Some(def) = base_ty.ty_adt_def() { - // No union field accesses in `const fn` - if def.is_union() { - return Err((span, "accessing union fields is unstable".into())); - } + if base.ty(body, tcx).ty.is_union() && !msrv.meets(msrvs::CONST_FN_UNION) { + return Err((span, "accessing union fields is unstable".into())); } }, + ProjectionElem::Deref => match base.ty(body, tcx).ty.kind() { + ty::RawPtr(_, hir::Mutability::Mut) => { + return Err((span, "dereferencing raw mut pointer in const fn is unstable".into())); + }, + ty::RawPtr(_, hir::Mutability::Not) if !msrv.meets(msrvs::CONST_RAW_PTR_DEREF) => { + return Err((span, "dereferencing raw const pointer in const fn is unstable".into())); + }, + _ => (), + }, ProjectionElem::ConstantIndex { .. } | ProjectionElem::OpaqueCast(..) | ProjectionElem::Downcast(..) | ProjectionElem::Subslice { .. } - | ProjectionElem::Deref | ProjectionElem::Subtype(_) | ProjectionElem::Index(_) => {}, } @@ -304,7 +314,7 @@ fn check_terminator<'tcx>( } Ok(()) }, - TerminatorKind::SwitchInt { discr, targets: _ } => check_operand(tcx, discr, span, body), + TerminatorKind::SwitchInt { discr, targets: _ } => check_operand(tcx, discr, span, body, msrv), TerminatorKind::CoroutineDrop | TerminatorKind::Yield { .. } => { Err((span, "const fn coroutines are unstable".into())) }, @@ -341,10 +351,10 @@ fn check_terminator<'tcx>( )); } - check_operand(tcx, func, span, body)?; + check_operand(tcx, func, span, body, msrv)?; for arg in args { - check_operand(tcx, &arg.node, span, body)?; + check_operand(tcx, &arg.node, span, body, msrv)?; } Ok(()) } else { @@ -357,7 +367,7 @@ fn check_terminator<'tcx>( msg: _, target: _, unwind: _, - } => check_operand(tcx, cond, span, body), + } => check_operand(tcx, cond, span, body, msrv), TerminatorKind::InlineAsm { .. } => Err((span, "cannot use inline assembly in const fn".into())), } } diff --git a/src/tools/clippy/clippy_utils/src/source.rs b/src/tools/clippy/clippy_utils/src/source.rs index e72467edeeb0d..fd67e039c29a9 100644 --- a/src/tools/clippy/clippy_utils/src/source.rs +++ b/src/tools/clippy/clippy_utils/src/source.rs @@ -250,7 +250,7 @@ pub fn snippet<'a, T: LintContext>(cx: &T, span: Span, default: &'a str) -> Cow< /// - Applicability level `Unspecified` will never be changed. /// - If the span is inside a macro, change the applicability level to `MaybeIncorrect`. /// - If the default value is used and the applicability level is `MachineApplicable`, change it to -/// `HasPlaceholders` +/// `HasPlaceholders` pub fn snippet_with_applicability<'a, T: LintContext>( cx: &T, span: Span, diff --git a/src/tools/clippy/clippy_utils/src/sugg.rs b/src/tools/clippy/clippy_utils/src/sugg.rs index bf03c6c16015c..6319c7bfa6b81 100644 --- a/src/tools/clippy/clippy_utils/src/sugg.rs +++ b/src/tools/clippy/clippy_utils/src/sugg.rs @@ -67,8 +67,7 @@ impl<'a> Sugg<'a> { /// - Applicability level `Unspecified` will never be changed. /// - If the span is inside a macro, change the applicability level to `MaybeIncorrect`. /// - If the default value is used and the applicability level is `MachineApplicable`, change it - /// to - /// `HasPlaceholders` + /// to `HasPlaceholders` pub fn hir_with_applicability( cx: &LateContext<'_>, expr: &hir::Expr<'_>, diff --git a/src/tools/clippy/clippy_utils/src/ty.rs b/src/tools/clippy/clippy_utils/src/ty.rs index c29e3feac9ad5..2dacc34867f07 100644 --- a/src/tools/clippy/clippy_utils/src/ty.rs +++ b/src/tools/clippy/clippy_utils/src/ty.rs @@ -273,11 +273,7 @@ pub fn implements_trait_with_env_from_iter<'tcx>( let infcx = tcx.infer_ctxt().build(); let args = args .into_iter() - .map(|arg| { - arg.into().unwrap_or_else(|| { - infcx.next_ty_var(DUMMY_SP).into() - }) - }) + .map(|arg| arg.into().unwrap_or_else(|| infcx.next_ty_var(DUMMY_SP).into())) .collect::>(); // If an effect arg was not specified, we need to specify it. @@ -795,7 +791,8 @@ fn sig_from_bounds<'tcx>( inputs = Some(i); }, ty::ClauseKind::Projection(p) - if Some(p.projection_term.def_id) == lang_items.fn_once_output() && p.projection_term.self_ty() == ty => + if Some(p.projection_term.def_id) == lang_items.fn_once_output() + && p.projection_term.self_ty() == ty => { if output.is_some() { // Multiple different fn trait impls. Is this even allowed? @@ -956,11 +953,7 @@ pub struct AdtVariantInfo { impl AdtVariantInfo { /// Returns ADT variants ordered by size - pub fn new<'tcx>( - cx: &LateContext<'tcx>, - adt: AdtDef<'tcx>, - subst: GenericArgsRef<'tcx> - ) -> Vec { + pub fn new<'tcx>(cx: &LateContext<'tcx>, adt: AdtDef<'tcx>, subst: GenericArgsRef<'tcx>) -> Vec { let mut variants_size = adt .variants() .iter() 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 c2ff19931d5c3..8021930345026 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 @@ -176,7 +176,7 @@ fn qpath_certainty(cx: &LateContext<'_>, qpath: &QPath<'_>, resolves_to_type: bo .get(*lang_item) .map_or(Certainty::Uncertain, |def_id| { let generics = cx.tcx.generics_of(def_id); - if generics.parent_count == 0 && generics.own_params.is_empty() { + if generics.is_empty() { Certainty::Certain(if resolves_to_type { Some(def_id) } else { None }) } else { Certainty::Uncertain diff --git a/src/tools/clippy/clippy_utils/src/usage.rs b/src/tools/clippy/clippy_utils/src/usage.rs index 9abb4ef9b8d3a..2a25d51d8e509 100644 --- a/src/tools/clippy/clippy_utils/src/usage.rs +++ b/src/tools/clippy/clippy_utils/src/usage.rs @@ -16,13 +16,9 @@ pub fn mutated_variables<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) -> used_mutably: HirIdSet::default(), skip: false, }; - ExprUseVisitor::for_clippy( - cx, - expr.hir_id.owner.def_id, - &mut delegate, - ) - .walk_expr(expr) - .into_ok(); + ExprUseVisitor::for_clippy(cx, expr.hir_id.owner.def_id, &mut delegate) + .walk_expr(expr) + .into_ok(); if delegate.skip { return None; diff --git a/src/tools/clippy/lintcheck/Cargo.toml b/src/tools/clippy/lintcheck/Cargo.toml index a828d12370467..8c5a409e25b15 100644 --- a/src/tools/clippy/lintcheck/Cargo.toml +++ b/src/tools/clippy/lintcheck/Cargo.toml @@ -13,7 +13,7 @@ default-run = "lintcheck" [dependencies] anyhow = "1.0.69" cargo_metadata = "0.15.3" -clap = { version = "4.1.8", features = ["derive", "env"] } +clap = { version = "4.4", features = ["derive", "env"] } crates_io_api = "0.8.1" crossbeam-channel = "0.5.6" flate2 = "1.0" diff --git a/src/tools/clippy/rust-toolchain b/src/tools/clippy/rust-toolchain index 055f305eb8e18..a0585ffdb45b0 100644 --- a/src/tools/clippy/rust-toolchain +++ b/src/tools/clippy/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2024-05-02" +channel = "nightly-2024-05-16" components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] diff --git a/src/tools/clippy/tests/ui-cargo/lint_groups_priority/fail/Cargo.stderr b/src/tools/clippy/tests/ui-cargo/lint_groups_priority/fail/Cargo.stderr index 103e60d84844c..9177e99f8e6e6 100644 --- a/src/tools/clippy/tests/ui-cargo/lint_groups_priority/fail/Cargo.stderr +++ b/src/tools/clippy/tests/ui-cargo/lint_groups_priority/fail/Cargo.stderr @@ -42,4 +42,32 @@ help: to have lints override the group set `pedantic` to a lower priority 19 | pedantic = { level = "warn", priority = -2 } | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -error: could not compile `fail` (lib) due to 3 previous errors +error: lint group `rust_2018_idioms` has the same priority (0) as a lint + --> Cargo.toml:23:1 + | +23 | rust_2018_idioms = "warn" + | ^^^^^^^^^^^^^^^^ ------ has an implicit priority of 0 +24 | bare_trait_objects = "allow" + | ------------------ has the same priority as this lint + | + = note: the order of the lints in the table is ignored by Cargo +help: to have lints override the group set `rust_2018_idioms` to a lower priority + | +23 | rust_2018_idioms = { level = "warn", priority = -1 } + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: lint group `pedantic` has the same priority (0) as a lint + --> Cargo.toml:27:1 + | +27 | pedantic = "warn" + | ^^^^^^^^ ------ has an implicit priority of 0 +28 | similar_names = "allow" + | ------------- has the same priority as this lint + | + = note: the order of the lints in the table is ignored by Cargo +help: to have lints override the group set `pedantic` to a lower priority + | +27 | pedantic = { level = "warn", priority = -1 } + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: could not compile `fail` (lib) due to 5 previous errors diff --git a/src/tools/clippy/tests/ui-cargo/lint_groups_priority/fail/Cargo.toml b/src/tools/clippy/tests/ui-cargo/lint_groups_priority/fail/Cargo.toml index 4ce41f7817118..e4d4af9cd234a 100644 --- a/src/tools/clippy/tests/ui-cargo/lint_groups_priority/fail/Cargo.toml +++ b/src/tools/clippy/tests/ui-cargo/lint_groups_priority/fail/Cargo.toml @@ -18,3 +18,11 @@ deprecated = "allow" [lints.clippy] pedantic = { level = "warn", priority = -1 } similar_names = { level = "allow", priority = -1 } + +[workspace.lints.rust] +rust_2018_idioms = "warn" +bare_trait_objects = "allow" + +[workspace.lints.clippy] +pedantic = "warn" +similar_names = "allow" diff --git a/src/tools/clippy/tests/ui-toml/macro_metavars_in_unsafe/default/test.rs b/src/tools/clippy/tests/ui-toml/macro_metavars_in_unsafe/default/test.rs new file mode 100644 index 0000000000000..f5e01b431ad98 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/macro_metavars_in_unsafe/default/test.rs @@ -0,0 +1,260 @@ +//! Tests macro_metavars_in_unsafe with default configuration +#![feature(decl_macro, lint_reasons)] +#![warn(clippy::macro_metavars_in_unsafe)] +#![allow(clippy::no_effect)] + +#[macro_export] +macro_rules! allow_works { + ($v:expr) => { + #[expect(clippy::macro_metavars_in_unsafe)] + unsafe { + $v; + }; + }; +} + +#[macro_export] +macro_rules! simple { + ($v:expr) => { + unsafe { + //~^ ERROR: this macro expands metavariables in an unsafe block + dbg!($v); + } + }; +} + +#[macro_export] +#[rustfmt::skip] // for some reason rustfmt rewrites $r#unsafe to r#u$nsafe, bug? +macro_rules! raw_symbol { + ($r#mod:expr, $r#unsafe:expr) => { + unsafe { + //~^ ERROR: this macro expands metavariables in an unsafe block + $r#mod; + } + $r#unsafe; + }; +} + +#[macro_export] +macro_rules! multilevel_unsafe { + ($v:expr) => { + unsafe { + unsafe { + //~^ ERROR: this macro expands metavariables in an unsafe block + $v; + } + } + }; +} + +#[macro_export] +macro_rules! in_function { + ($v:expr) => { + unsafe { + fn f() { + // function introduces a new body, so don't lint. + $v; + } + } + }; +} + +#[macro_export] +macro_rules! in_function_with_unsafe { + ($v:expr) => { + unsafe { + fn f() { + unsafe { + //~^ ERROR: this macro expands metavariables in an unsafe block + $v; + } + } + } + }; +} + +#[macro_export] +macro_rules! const_static { + ($c:expr, $s:expr) => { + unsafe { + // const and static introduces new body, don't lint + const _X: i32 = $c; + static _Y: i32 = $s; + } + }; +} + +#[macro_export] +macro_rules! const_generic_in_struct { + ($inside_unsafe:expr, $outside_unsafe:expr) => { + unsafe { + struct Ty< + const L: i32 = 1, + const M: i32 = { + 1; + unsafe { $inside_unsafe } + //~^ ERROR: this macro expands metavariables in an unsafe block + }, + const N: i32 = { $outside_unsafe }, + >; + } + }; +} + +#[macro_export] +macro_rules! fn_with_const_generic { + ($inside_unsafe:expr, $outside_unsafe:expr) => { + unsafe { + fn f() { + $outside_unsafe; + unsafe { + //~^ ERROR: this macro expands metavariables in an unsafe block + $inside_unsafe; + } + } + } + }; +} + +#[macro_export] +macro_rules! variables { + ($inside_unsafe:expr, $outside_unsafe:expr) => { + unsafe { + //~^ ERROR: this macro expands metavariables in an unsafe block + $inside_unsafe; + let inside_unsafe = 1; + inside_unsafe; + } + $outside_unsafe; + let outside_unsafe = 1; + outside_unsafe; + }; +} + +#[macro_export] +macro_rules! multiple_matchers { + ($inside_unsafe:expr, $outside_unsafe:expr) => { + unsafe { + //~^ ERROR: this macro expands metavariables in an unsafe block + $inside_unsafe; + } + $outside_unsafe; + }; + ($($v:expr, $x:expr),+) => { + $( + $v; + unsafe { + //~^ ERROR: this macro expands metavariables in an unsafe block + $x; + } + );+ + }; +} + +#[macro_export] +macro_rules! multiple_unsafe_blocks { + ($w:expr, $x:expr, $y:expr) => { + $w; + unsafe { + //~^ ERROR: this macro expands metavariables in an unsafe block + $x; + } + unsafe { + //~^ ERROR: this macro expands metavariables in an unsafe block + $x; + $y; + } + }; +} + +pub macro macro2_0($v:expr) { + unsafe { + //~^ ERROR: this macro expands metavariables in an unsafe block + $v; + } +} + +// don't lint private macros with the default configuration +macro_rules! private_mac { + ($v:expr) => { + unsafe { + $v; + } + }; +} + +// don't lint exported macros that are doc(hidden) because they also aren't part of the public API +#[macro_export] +#[doc(hidden)] +macro_rules! exported_but_hidden { + ($v:expr) => { + unsafe { + $v; + } + }; +} + +// don't lint if the same metavariable is expanded in an unsafe block and then outside of one: +// unsafe {} is still needed at callsite so not problematic +#[macro_export] +macro_rules! does_require_unsafe { + ($v:expr) => { + unsafe { + $v; + } + $v; + }; +} + +#[macro_export] +macro_rules! unsafe_from_root_ctxt { + ($v:expr) => { + // Expands to unsafe { 1 }, but the unsafe block is from the root ctxt and not this macro, + // so no warning. + $v; + }; +} + +// invoked from another macro, should still generate a warning +#[macro_export] +macro_rules! nested_macro_helper { + ($v:expr) => {{ + unsafe { + //~^ ERROR: this macro expands metavariables in an unsafe block + $v; + } + }}; +} + +#[macro_export] +macro_rules! nested_macros { + ($v:expr, $v2:expr) => {{ + unsafe { + //~^ ERROR: this macro expands metavariables in an unsafe block + nested_macro_helper!($v); + $v; + } + }}; +} + +fn main() { + allow_works!(1); + simple!(1); + raw_symbol!(1, 1); + multilevel_unsafe!(1); + in_function!(1); + in_function_with_unsafe!(1); + const_static!(1, 1); + const_generic_in_struct!(1, 1); + fn_with_const_generic!(1, 1); + variables!(1, 1); + multiple_matchers!(1, 1); + multiple_matchers!(1, 1, 1, 1); + macro2_0!(1); + private_mac!(1); + exported_but_hidden!(1); + does_require_unsafe!(1); + multiple_unsafe_blocks!(1, 1, 1); + unsafe_from_root_ctxt!(unsafe { 1 }); + nested_macros!(1, 1); +} diff --git a/src/tools/clippy/tests/ui-toml/macro_metavars_in_unsafe/default/test.stderr b/src/tools/clippy/tests/ui-toml/macro_metavars_in_unsafe/default/test.stderr new file mode 100644 index 0000000000000..d6b97f6fde1e1 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/macro_metavars_in_unsafe/default/test.stderr @@ -0,0 +1,187 @@ +error: this macro expands metavariables in an unsafe block + --> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:19:9 + | +LL | / unsafe { +LL | | +LL | | dbg!($v); +LL | | } + | |_________^ + | + = note: this allows the user of the macro to write unsafe code outside of an unsafe block + = help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable + = help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite + = note: `-D clippy::macro-metavars-in-unsafe` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::macro_metavars_in_unsafe)]` + +error: this macro expands metavariables in an unsafe block + --> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:30:9 + | +LL | / unsafe { +LL | | +LL | | $r#mod; +LL | | } + | |_________^ + | + = note: this allows the user of the macro to write unsafe code outside of an unsafe block + = help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable + = help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite + +error: this macro expands metavariables in an unsafe block + --> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:42:13 + | +LL | / unsafe { +LL | | +LL | | $v; +LL | | } + | |_____________^ + | + = note: this allows the user of the macro to write unsafe code outside of an unsafe block + = help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable + = help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite + +error: this macro expands metavariables in an unsafe block + --> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:67:17 + | +LL | / unsafe { +LL | | +LL | | $v; +LL | | } + | |_________________^ + | + = note: this allows the user of the macro to write unsafe code outside of an unsafe block + = help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable + = help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite + +error: this macro expands metavariables in an unsafe block + --> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:95:21 + | +LL | unsafe { $inside_unsafe } + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this allows the user of the macro to write unsafe code outside of an unsafe block + = help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable + = help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite + +error: this macro expands metavariables in an unsafe block + --> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:110:17 + | +LL | / unsafe { +LL | | +LL | | $inside_unsafe; +LL | | } + | |_________________^ + | + = note: this allows the user of the macro to write unsafe code outside of an unsafe block + = help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable + = help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite + +error: this macro expands metavariables in an unsafe block + --> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:122:9 + | +LL | / unsafe { +LL | | +LL | | $inside_unsafe; +LL | | let inside_unsafe = 1; +LL | | inside_unsafe; +LL | | } + | |_________^ + | + = note: this allows the user of the macro to write unsafe code outside of an unsafe block + = help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable + = help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite + +error: this macro expands metavariables in an unsafe block + --> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:137:9 + | +LL | / unsafe { +LL | | +LL | | $inside_unsafe; +LL | | } + | |_________^ + | + = note: this allows the user of the macro to write unsafe code outside of an unsafe block + = help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable + = help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite + +error: this macro expands metavariables in an unsafe block + --> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:146:13 + | +LL | / unsafe { +LL | | +LL | | $x; +LL | | } + | |_____________^ + | + = note: this allows the user of the macro to write unsafe code outside of an unsafe block + = help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable + = help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite + +error: this macro expands metavariables in an unsafe block + --> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:171:5 + | +LL | / unsafe { +LL | | +LL | | $v; +LL | | } + | |_____^ + | + = note: this allows the user of the macro to write unsafe code outside of an unsafe block + = help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable + = help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite + +error: this macro expands metavariables in an unsafe block + --> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:158:9 + | +LL | / unsafe { +LL | | +LL | | $x; +LL | | } + | |_________^ + | + = note: this allows the user of the macro to write unsafe code outside of an unsafe block + = help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable + = help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite + +error: this macro expands metavariables in an unsafe block + --> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:162:9 + | +LL | / unsafe { +LL | | +LL | | $x; +LL | | $y; +LL | | } + | |_________^ + | + = note: this allows the user of the macro to write unsafe code outside of an unsafe block + = help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable + = help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite + +error: this macro expands metavariables in an unsafe block + --> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:222:9 + | +LL | / unsafe { +LL | | +LL | | $v; +LL | | } + | |_________^ + | + = note: this allows the user of the macro to write unsafe code outside of an unsafe block + = help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable + = help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite + +error: this macro expands metavariables in an unsafe block + --> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:232:9 + | +LL | / unsafe { +LL | | +LL | | nested_macro_helper!($v); +LL | | $v; +LL | | } + | |_________^ + | + = note: this allows the user of the macro to write unsafe code outside of an unsafe block + = help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable + = help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite + +error: aborting due to 14 previous errors + diff --git a/src/tools/clippy/tests/ui-toml/macro_metavars_in_unsafe/private/clippy.toml b/src/tools/clippy/tests/ui-toml/macro_metavars_in_unsafe/private/clippy.toml new file mode 100644 index 0000000000000..d4bbc2a1be894 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/macro_metavars_in_unsafe/private/clippy.toml @@ -0,0 +1 @@ +warn-unsafe-macro-metavars-in-private-macros = true diff --git a/src/tools/clippy/tests/ui-toml/macro_metavars_in_unsafe/private/test.rs b/src/tools/clippy/tests/ui-toml/macro_metavars_in_unsafe/private/test.rs new file mode 100644 index 0000000000000..2bbe1fa7b7f94 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/macro_metavars_in_unsafe/private/test.rs @@ -0,0 +1,15 @@ +//! Tests macro_metavars_in_unsafe with private (non-exported) macros +#![warn(clippy::macro_metavars_in_unsafe)] + +macro_rules! mac { + ($v:expr) => { + unsafe { + //~^ ERROR: this macro expands metavariables in an unsafe block + dbg!($v); + } + }; +} + +fn main() { + mac!(1); +} diff --git a/src/tools/clippy/tests/ui-toml/macro_metavars_in_unsafe/private/test.stderr b/src/tools/clippy/tests/ui-toml/macro_metavars_in_unsafe/private/test.stderr new file mode 100644 index 0000000000000..f9c418b221887 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/macro_metavars_in_unsafe/private/test.stderr @@ -0,0 +1,17 @@ +error: this macro expands metavariables in an unsafe block + --> tests/ui-toml/macro_metavars_in_unsafe/private/test.rs:6:9 + | +LL | / unsafe { +LL | | +LL | | dbg!($v); +LL | | } + | |_________^ + | + = note: this allows the user of the macro to write unsafe code outside of an unsafe block + = help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable + = help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite + = note: `-D clippy::macro-metavars-in-unsafe` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::macro_metavars_in_unsafe)]` + +error: aborting due to 1 previous error + diff --git a/src/tools/clippy/tests/ui-toml/panic/clippy.toml b/src/tools/clippy/tests/ui-toml/panic/clippy.toml new file mode 100644 index 0000000000000..5d6230d092c07 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/panic/clippy.toml @@ -0,0 +1 @@ +allow-panic-in-tests = true diff --git a/src/tools/clippy/tests/ui-toml/panic/panic.rs b/src/tools/clippy/tests/ui-toml/panic/panic.rs new file mode 100644 index 0000000000000..618a37ddfc555 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/panic/panic.rs @@ -0,0 +1,54 @@ +//@compile-flags: --test +#![warn(clippy::panic)] + +fn main() { + enum Enam { + A, + } + let a = Enam::A; + match a { + Enam::A => {}, + _ => panic!(""), + } +} + +#[test] +fn lonely_test() { + enum Enam { + A, + } + let a = Enam::A; + match a { + Enam::A => {}, + _ => panic!(""), + } +} + +#[cfg(test)] +mod tests { + // should not lint in `#[cfg(test)]` modules + #[test] + fn test_fn() { + enum Enam { + A, + } + let a = Enam::A; + match a { + Enam::A => {}, + _ => panic!(""), + } + + bar(); + } + + fn bar() { + enum Enam { + A, + } + let a = Enam::A; + match a { + Enam::A => {}, + _ => panic!(""), + } + } +} diff --git a/src/tools/clippy/tests/ui-toml/panic/panic.stderr b/src/tools/clippy/tests/ui-toml/panic/panic.stderr new file mode 100644 index 0000000000000..bf7503e086c9d --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/panic/panic.stderr @@ -0,0 +1,11 @@ +error: `panic` should not be present in production code + --> tests/ui-toml/panic/panic.rs:11:14 + | +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 + diff --git a/src/tools/clippy/tests/ui-toml/renamed_function_params/default/clippy.toml b/src/tools/clippy/tests/ui-toml/renamed_function_params/default/clippy.toml new file mode 100644 index 0000000000000..5381e70a93915 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/renamed_function_params/default/clippy.toml @@ -0,0 +1,2 @@ +# Ignore `From`, `TryFrom`, `FromStr` by default +# allow-renamed-params-for = [] diff --git a/src/tools/clippy/tests/ui-toml/renamed_function_params/extend/clippy.toml b/src/tools/clippy/tests/ui-toml/renamed_function_params/extend/clippy.toml new file mode 100644 index 0000000000000..9b3853e769615 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/renamed_function_params/extend/clippy.toml @@ -0,0 +1,2 @@ +# Ignore `From`, `TryFrom`, `FromStr` by default +allow-renamed-params-for = [ "..", "std::ops::Add", "renamed_function_params::MyTrait" ] diff --git a/src/tools/clippy/tests/ui-toml/renamed_function_params/renamed_function_params.default.stderr b/src/tools/clippy/tests/ui-toml/renamed_function_params/renamed_function_params.default.stderr new file mode 100644 index 0000000000000..2d700f6075926 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/renamed_function_params/renamed_function_params.default.stderr @@ -0,0 +1,46 @@ +error: renamed function parameter of trait impl + --> tests/ui-toml/renamed_function_params/renamed_function_params.rs:30:18 + | +LL | fn eq(&self, rhs: &Self) -> bool { + | ^^^ help: consider using the default name: `other` + | + = note: `-D clippy::renamed-function-params` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::renamed_function_params)]` + +error: renamed function parameter of trait impl + --> tests/ui-toml/renamed_function_params/renamed_function_params.rs:34:18 + | +LL | fn ne(&self, rhs: &Self) -> bool { + | ^^^ help: consider using the default name: `other` + +error: renamed function parameter of trait impl + --> tests/ui-toml/renamed_function_params/renamed_function_params.rs:48:19 + | +LL | fn foo(&self, i_dont_wanna_use_your_name: u8) {} // only lint in `extend` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using the default name: `val` + +error: renamed function parameter of trait impl + --> tests/ui-toml/renamed_function_params/renamed_function_params.rs:55:31 + | +LL | fn hash(&self, states: &mut H) { + | ^^^^^^ help: consider using the default name: `state` + +error: renamed function parameters of trait impl + --> tests/ui-toml/renamed_function_params/renamed_function_params.rs:59:30 + | +LL | fn hash_slice(date: &[Self], states: &mut H) { + | ^^^^ ^^^^^^ + | +help: consider using the default names + | +LL | fn hash_slice(data: &[Self], state: &mut H) { + | ~~~~ ~~~~~ + +error: renamed function parameter of trait impl + --> tests/ui-toml/renamed_function_params/renamed_function_params.rs:80:18 + | +LL | fn add(self, b: B) -> C { + | ^ help: consider using the default name: `rhs` + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui-toml/renamed_function_params/renamed_function_params.extend.stderr b/src/tools/clippy/tests/ui-toml/renamed_function_params/renamed_function_params.extend.stderr new file mode 100644 index 0000000000000..e57554fa613ac --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/renamed_function_params/renamed_function_params.extend.stderr @@ -0,0 +1,34 @@ +error: renamed function parameter of trait impl + --> tests/ui-toml/renamed_function_params/renamed_function_params.rs:30:18 + | +LL | fn eq(&self, rhs: &Self) -> bool { + | ^^^ help: consider using the default name: `other` + | + = note: `-D clippy::renamed-function-params` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::renamed_function_params)]` + +error: renamed function parameter of trait impl + --> tests/ui-toml/renamed_function_params/renamed_function_params.rs:34:18 + | +LL | fn ne(&self, rhs: &Self) -> bool { + | ^^^ help: consider using the default name: `other` + +error: renamed function parameter of trait impl + --> tests/ui-toml/renamed_function_params/renamed_function_params.rs:55:31 + | +LL | fn hash(&self, states: &mut H) { + | ^^^^^^ help: consider using the default name: `state` + +error: renamed function parameters of trait impl + --> tests/ui-toml/renamed_function_params/renamed_function_params.rs:59:30 + | +LL | fn hash_slice(date: &[Self], states: &mut H) { + | ^^^^ ^^^^^^ + | +help: consider using the default names + | +LL | fn hash_slice(data: &[Self], state: &mut H) { + | ~~~~ ~~~~~ + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui-toml/renamed_function_params/renamed_function_params.rs b/src/tools/clippy/tests/ui-toml/renamed_function_params/renamed_function_params.rs new file mode 100644 index 0000000000000..f3eb910abbd67 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/renamed_function_params/renamed_function_params.rs @@ -0,0 +1,110 @@ +//@no-rustfix +//@revisions: default extend +//@[default] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/renamed_function_params/default +//@[extend] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/renamed_function_params/extend +#![warn(clippy::renamed_function_params)] +#![allow(clippy::partialeq_ne_impl, clippy::to_string_trait_impl)] +#![allow(unused)] + +use std::hash::{Hash, Hasher}; + +struct A; +impl From for String { + fn from(_value: A) -> Self { + String::new() + } +} +impl ToString for A { + fn to_string(&self) -> String { + String::new() + } +} + +struct B(u32); +impl std::convert::From for String { + fn from(b: B) -> Self { + b.0.to_string() + } +} +impl PartialEq for B { + fn eq(&self, rhs: &Self) -> bool { + //~^ ERROR: renamed function parameter of trait impl + self.0 == rhs.0 + } + fn ne(&self, rhs: &Self) -> bool { + //~^ ERROR: renamed function parameter of trait impl + self.0 != rhs.0 + } +} + +trait MyTrait { + fn foo(&self, val: u8); + fn bar(a: u8, b: u8); + fn baz(self, _val: u8); + fn quz(&self, _: u8); +} + +impl MyTrait for B { + fn foo(&self, i_dont_wanna_use_your_name: u8) {} // only lint in `extend` + fn bar(_a: u8, _: u8) {} + fn baz(self, val: u8) {} + fn quz(&self, val: u8) {} +} + +impl Hash for B { + fn hash(&self, states: &mut H) { + //~^ ERROR: renamed function parameter of trait impl + self.0.hash(states); + } + fn hash_slice(date: &[Self], states: &mut H) { + //~^ ERROR: renamed function parameters of trait impl + for d in date { + d.hash(states); + } + } +} + +impl B { + fn totally_irrelevant(&self, right: bool) {} + fn some_fn(&self, other: impl MyTrait) {} +} + +#[derive(Copy, Clone)] +enum C { + A, + B(u32), +} + +impl std::ops::Add for C { + type Output = C; + fn add(self, b: B) -> C { + // only lint in `extend` + C::B(b.0) + } +} + +impl From for C { + fn from(_: A) -> C { + C::A + } +} + +trait CustomTraitA { + fn foo(&self, other: u32); +} +trait CustomTraitB { + fn bar(&self, value: u8); +} + +macro_rules! impl_trait { + ($impl_for:ident, $tr:ty, $fn_name:ident, $t:ty) => { + impl $tr for $impl_for { + fn $fn_name(&self, v: $t) {} + } + }; +} + +impl_trait!(C, CustomTraitA, foo, u32); +impl_trait!(C, CustomTraitB, bar, u8); + +fn main() {} diff --git a/src/tools/clippy/tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs b/src/tools/clippy/tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs index 7f28efd676f2c..f02bd07cfe7bf 100644 --- a/src/tools/clippy/tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs +++ b/src/tools/clippy/tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs @@ -40,3 +40,9 @@ fn main() { let _ = HashMap; let _: usize = 64_usize; } + +mod useless_attribute { + // Regression test for https://github.com/rust-lang/rust-clippy/issues/12753 + #[allow(clippy::disallowed_types)] + use std::collections::HashMap; +} diff --git a/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index 722e9b3bc8d46..5cf9c0fb27102 100644 --- a/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -8,8 +8,10 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect allow-expect-in-tests allow-mixed-uninlined-format-args allow-one-hash-in-raw-strings + allow-panic-in-tests allow-print-in-tests allow-private-module-inception + allow-renamed-params-for allow-unwrap-in-tests allow-useless-vec-in-tests allowed-dotfiles @@ -74,6 +76,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect vec-box-size-threshold verbose-bit-mask-threshold warn-on-all-wildcard-imports + warn-unsafe-macro-metavars-in-private-macros --> $DIR/tests/ui-toml/toml_unknown_key/clippy.toml:2:1 | LL | foobar = 42 @@ -89,8 +92,10 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect allow-expect-in-tests allow-mixed-uninlined-format-args allow-one-hash-in-raw-strings + allow-panic-in-tests allow-print-in-tests allow-private-module-inception + allow-renamed-params-for allow-unwrap-in-tests allow-useless-vec-in-tests allowed-dotfiles @@ -155,6 +160,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect vec-box-size-threshold verbose-bit-mask-threshold warn-on-all-wildcard-imports + warn-unsafe-macro-metavars-in-private-macros --> $DIR/tests/ui-toml/toml_unknown_key/clippy.toml:4:1 | LL | barfoo = 53 @@ -170,8 +176,10 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni allow-expect-in-tests allow-mixed-uninlined-format-args allow-one-hash-in-raw-strings + allow-panic-in-tests allow-print-in-tests allow-private-module-inception + allow-renamed-params-for allow-unwrap-in-tests allow-useless-vec-in-tests allowed-dotfiles @@ -236,6 +244,7 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni vec-box-size-threshold verbose-bit-mask-threshold warn-on-all-wildcard-imports + warn-unsafe-macro-metavars-in-private-macros --> $DIR/tests/ui-toml/toml_unknown_key/clippy.toml:7:1 | LL | allow_mixed_uninlined_format_args = true diff --git a/src/tools/clippy/tests/ui/assigning_clones.fixed b/src/tools/clippy/tests/ui/assigning_clones.fixed index 8387c7d6156b5..70ab43b49b3a7 100644 --- a/src/tools/clippy/tests/ui/assigning_clones.fixed +++ b/src/tools/clippy/tests/ui/assigning_clones.fixed @@ -62,6 +62,16 @@ fn clone_method_rhs_complex(mut_thing: &mut HasCloneFrom, ref_thing: &HasCloneFr mut_thing.clone_from(ref_thing + ref_thing); } +fn clone_method_macro() { + let mut s = String::from(""); + s.clone_from(&format!("{} {}", "hello", "world")); +} + +fn clone_function_macro() { + let mut s = String::from(""); + Clone::clone_from(&mut s, &format!("{} {}", "hello", "world")); +} + fn assign_to_init_mut_var(b: HasCloneFrom) -> HasCloneFrom { let mut a = HasCloneFrom; for _ in 1..10 { @@ -86,6 +96,12 @@ fn assign_to_uninit_mut_var(b: HasCloneFrom) { a = b.clone(); } +fn late_init_let_tuple() { + let (p, q): (String, String); + p = "ghi".to_string(); + q = p.clone(); +} + #[derive(Clone)] pub struct HasDeriveClone; @@ -208,6 +224,16 @@ fn owned_function_val(mut mut_thing: String, ref_str: &str) { ToOwned::clone_into(ref_str, &mut mut_thing); } +fn owned_method_macro() { + let mut s = String::from(""); + format!("{} {}", "hello", "world").clone_into(&mut s); +} + +fn owned_function_macro() { + let mut s = String::from(""); + ToOwned::clone_into(&format!("{} {}", "hello", "world"), &mut s); +} + struct FakeToOwned; impl FakeToOwned { /// This looks just like `ToOwned::to_owned` diff --git a/src/tools/clippy/tests/ui/assigning_clones.rs b/src/tools/clippy/tests/ui/assigning_clones.rs index 6f4da9f652c99..9699fed100c8f 100644 --- a/src/tools/clippy/tests/ui/assigning_clones.rs +++ b/src/tools/clippy/tests/ui/assigning_clones.rs @@ -62,6 +62,16 @@ fn clone_method_rhs_complex(mut_thing: &mut HasCloneFrom, ref_thing: &HasCloneFr *mut_thing = (ref_thing + ref_thing).clone(); } +fn clone_method_macro() { + let mut s = String::from(""); + s = format!("{} {}", "hello", "world").clone(); +} + +fn clone_function_macro() { + let mut s = String::from(""); + s = Clone::clone(&format!("{} {}", "hello", "world")); +} + fn assign_to_init_mut_var(b: HasCloneFrom) -> HasCloneFrom { let mut a = HasCloneFrom; for _ in 1..10 { @@ -86,6 +96,12 @@ fn assign_to_uninit_mut_var(b: HasCloneFrom) { a = b.clone(); } +fn late_init_let_tuple() { + let (p, q): (String, String); + p = "ghi".to_string(); + q = p.clone(); +} + #[derive(Clone)] pub struct HasDeriveClone; @@ -208,6 +224,16 @@ fn owned_function_val(mut mut_thing: String, ref_str: &str) { mut_thing = ToOwned::to_owned(ref_str); } +fn owned_method_macro() { + let mut s = String::from(""); + s = format!("{} {}", "hello", "world").to_owned(); +} + +fn owned_function_macro() { + let mut s = String::from(""); + s = ToOwned::to_owned(&format!("{} {}", "hello", "world")); +} + struct FakeToOwned; impl FakeToOwned { /// This looks just like `ToOwned::to_owned` diff --git a/src/tools/clippy/tests/ui/assigning_clones.stderr b/src/tools/clippy/tests/ui/assigning_clones.stderr index 793927bd1cb10..a68516376abb5 100644 --- a/src/tools/clippy/tests/ui/assigning_clones.stderr +++ b/src/tools/clippy/tests/ui/assigning_clones.stderr @@ -62,64 +62,88 @@ LL | *mut_thing = (ref_thing + ref_thing).clone(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `mut_thing.clone_from(ref_thing + ref_thing)` error: assigning the result of `Clone::clone()` may be inefficient - --> tests/ui/assigning_clones.rs:68:9 + --> tests/ui/assigning_clones.rs:67:5 + | +LL | s = format!("{} {}", "hello", "world").clone(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `s.clone_from(&format!("{} {}", "hello", "world"))` + +error: assigning the result of `Clone::clone()` may be inefficient + --> tests/ui/assigning_clones.rs:72:5 + | +LL | s = Clone::clone(&format!("{} {}", "hello", "world")); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `Clone::clone_from(&mut s, &format!("{} {}", "hello", "world"))` + +error: assigning the result of `Clone::clone()` may be inefficient + --> tests/ui/assigning_clones.rs:78:9 | LL | a = b.clone(); | ^^^^^^^^^^^^^ help: use `clone_from()`: `a.clone_from(&b)` error: assigning the result of `Clone::clone()` may be inefficient - --> tests/ui/assigning_clones.rs:133:5 + --> tests/ui/assigning_clones.rs:149:5 | LL | a = b.clone(); | ^^^^^^^^^^^^^ help: use `clone_from()`: `a.clone_from(&b)` error: assigning the result of `Clone::clone()` may be inefficient - --> tests/ui/assigning_clones.rs:140:5 + --> tests/ui/assigning_clones.rs:156:5 | LL | a = b.clone(); | ^^^^^^^^^^^^^ help: use `clone_from()`: `a.clone_from(&b)` error: assigning the result of `ToOwned::to_owned()` may be inefficient - --> tests/ui/assigning_clones.rs:141:5 + --> tests/ui/assigning_clones.rs:157:5 | LL | a = c.to_owned(); | ^^^^^^^^^^^^^^^^ help: use `clone_into()`: `c.clone_into(&mut a)` error: assigning the result of `ToOwned::to_owned()` may be inefficient - --> tests/ui/assigning_clones.rs:171:5 + --> tests/ui/assigning_clones.rs:187:5 | LL | *mut_string = ref_str.to_owned(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ref_str.clone_into(mut_string)` error: assigning the result of `ToOwned::to_owned()` may be inefficient - --> tests/ui/assigning_clones.rs:175:5 + --> tests/ui/assigning_clones.rs:191:5 | LL | mut_string = ref_str.to_owned(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ref_str.clone_into(&mut mut_string)` error: assigning the result of `ToOwned::to_owned()` may be inefficient - --> tests/ui/assigning_clones.rs:196:5 + --> tests/ui/assigning_clones.rs:212:5 | LL | **mut_box_string = ref_str.to_owned(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ref_str.clone_into(&mut (*mut_box_string))` error: assigning the result of `ToOwned::to_owned()` may be inefficient - --> tests/ui/assigning_clones.rs:200:5 + --> tests/ui/assigning_clones.rs:216:5 | LL | **mut_box_string = ref_str.to_owned(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ref_str.clone_into(&mut (*mut_box_string))` error: assigning the result of `ToOwned::to_owned()` may be inefficient - --> tests/ui/assigning_clones.rs:204:5 + --> tests/ui/assigning_clones.rs:220:5 | LL | *mut_thing = ToOwned::to_owned(ref_str); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ToOwned::clone_into(ref_str, mut_thing)` error: assigning the result of `ToOwned::to_owned()` may be inefficient - --> tests/ui/assigning_clones.rs:208:5 + --> tests/ui/assigning_clones.rs:224:5 | LL | mut_thing = ToOwned::to_owned(ref_str); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ToOwned::clone_into(ref_str, &mut mut_thing)` -error: aborting due to 20 previous errors +error: assigning the result of `ToOwned::to_owned()` may be inefficient + --> tests/ui/assigning_clones.rs:229:5 + | +LL | s = format!("{} {}", "hello", "world").to_owned(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `format!("{} {}", "hello", "world").clone_into(&mut s)` + +error: assigning the result of `ToOwned::to_owned()` may be inefficient + --> tests/ui/assigning_clones.rs:234:5 + | +LL | s = ToOwned::to_owned(&format!("{} {}", "hello", "world")); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ToOwned::clone_into(&format!("{} {}", "hello", "world"), &mut s)` + +error: aborting due to 24 previous errors diff --git a/src/tools/clippy/tests/ui/doc/doc_lazy_blockquote.fixed b/src/tools/clippy/tests/ui/doc/doc_lazy_blockquote.fixed new file mode 100644 index 0000000000000..9877991f183a5 --- /dev/null +++ b/src/tools/clippy/tests/ui/doc/doc_lazy_blockquote.fixed @@ -0,0 +1,47 @@ +#![warn(clippy::doc_lazy_continuation)] + +/// > blockquote with +/// > lazy continuation +//~^ ERROR: doc quote missing `>` marker +fn first() {} + +/// > blockquote with no +/// > lazy continuation +fn first_nowarn() {} + +/// > blockquote with no +/// +/// lazy continuation +fn two_nowarn() {} + +/// > nest here +/// > +/// > > nest here +/// > > lazy continuation +//~^ ERROR: doc quote missing `>` marker +fn two() {} + +/// > nest here +/// > +/// > > nest here +/// > > lazy continuation +//~^ ERROR: doc quote missing `>` marker +fn three() {} + +/// > * > nest here +/// > > lazy continuation +//~^ ERROR: doc quote missing `>` marker +fn four() {} + +/// > * > nest here +/// > > lazy continuation +//~^ ERROR: doc quote missing `>` marker +fn four_point_1() {} + +/// * > nest here lazy continuation +fn five() {} + +/// 1. > nest here +/// > lazy continuation (this results in strange indentation, but still works) +//~^ ERROR: doc quote missing `>` marker +fn six() {} diff --git a/src/tools/clippy/tests/ui/doc/doc_lazy_blockquote.rs b/src/tools/clippy/tests/ui/doc/doc_lazy_blockquote.rs new file mode 100644 index 0000000000000..587b2fdd533ce --- /dev/null +++ b/src/tools/clippy/tests/ui/doc/doc_lazy_blockquote.rs @@ -0,0 +1,47 @@ +#![warn(clippy::doc_lazy_continuation)] + +/// > blockquote with +/// lazy continuation +//~^ ERROR: doc quote missing `>` marker +fn first() {} + +/// > blockquote with no +/// > lazy continuation +fn first_nowarn() {} + +/// > blockquote with no +/// +/// lazy continuation +fn two_nowarn() {} + +/// > nest here +/// > +/// > > nest here +/// > lazy continuation +//~^ ERROR: doc quote missing `>` marker +fn two() {} + +/// > nest here +/// > +/// > > nest here +/// lazy continuation +//~^ ERROR: doc quote missing `>` marker +fn three() {} + +/// > * > nest here +/// lazy continuation +//~^ ERROR: doc quote missing `>` marker +fn four() {} + +/// > * > nest here +/// lazy continuation +//~^ ERROR: doc quote missing `>` marker +fn four_point_1() {} + +/// * > nest here lazy continuation +fn five() {} + +/// 1. > nest here +/// lazy continuation (this results in strange indentation, but still works) +//~^ ERROR: doc quote missing `>` marker +fn six() {} diff --git a/src/tools/clippy/tests/ui/doc/doc_lazy_blockquote.stderr b/src/tools/clippy/tests/ui/doc/doc_lazy_blockquote.stderr new file mode 100644 index 0000000000000..975184a01c3f0 --- /dev/null +++ b/src/tools/clippy/tests/ui/doc/doc_lazy_blockquote.stderr @@ -0,0 +1,76 @@ +error: doc quote missing `>` marker + --> tests/ui/doc/doc_lazy_blockquote.rs:4:5 + | +LL | /// lazy continuation + | ^ + | + = help: if this not intended to be a quote at all, escape it with `\>` + = note: `-D clippy::doc-lazy-continuation` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::doc_lazy_continuation)]` +help: add markers to start of line + | +LL | /// > lazy continuation + | + + +error: doc quote missing `>` marker + --> tests/ui/doc/doc_lazy_blockquote.rs:20:5 + | +LL | /// > lazy continuation + | ^^ + | + = help: if this not intended to be a quote at all, escape it with `\>` +help: add markers to start of line + | +LL | /// > > lazy continuation + | + + +error: doc quote missing `>` marker + --> tests/ui/doc/doc_lazy_blockquote.rs:27:5 + | +LL | /// lazy continuation + | ^ + | + = help: if this not intended to be a quote at all, escape it with `\>` +help: add markers to start of line + | +LL | /// > > lazy continuation + | +++ + +error: doc quote missing `>` marker + --> tests/ui/doc/doc_lazy_blockquote.rs:32:5 + | +LL | /// lazy continuation + | ^ + | + = help: if this not intended to be a quote at all, escape it with `\>` +help: add markers to start of line + | +LL | /// > > lazy continuation + | +++++++ + +error: doc quote missing `>` marker + --> tests/ui/doc/doc_lazy_blockquote.rs:37:5 + | +LL | /// lazy continuation + | ^ + | + = help: if this not intended to be a quote at all, escape it with `\>` +help: add markers to start of line + | +LL | /// > > lazy continuation + | +++++ + +error: doc quote missing `>` marker + --> tests/ui/doc/doc_lazy_blockquote.rs:45:5 + | +LL | /// lazy continuation (this results in strange indentation, but still works) + | ^ + | + = help: if this not intended to be a quote at all, escape it with `\>` +help: add markers to start of line + | +LL | /// > lazy continuation (this results in strange indentation, but still works) + | + + +error: aborting due to 6 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 new file mode 100644 index 0000000000000..409e6b0bc227f --- /dev/null +++ b/src/tools/clippy/tests/ui/doc/doc_lazy_list.fixed @@ -0,0 +1,77 @@ +#![warn(clippy::doc_lazy_continuation)] + +/// 1. nest here +/// lazy continuation +//~^ ERROR: doc list item missing indentation +fn one() {} + +/// 1. first line +/// lazy list continuations don't make warnings with this lint +//~^ ERROR: doc list item missing indentation +/// because they don't have the +//~^ ERROR: doc list item missing indentation +fn two() {} + +/// - nest here +/// lazy continuation +//~^ ERROR: doc list item missing indentation +fn three() {} + +/// - first line +/// lazy list continuations don't make warnings with this lint +//~^ ERROR: doc list item missing indentation +/// because they don't have the +//~^ ERROR: doc list item missing indentation +fn four() {} + +/// - nest here +/// lazy continuation +//~^ ERROR: doc list item missing indentation +fn five() {} + +/// - - first line +/// this will warn on the lazy continuation +//~^ ERROR: doc list item missing indentation +/// and so should this +//~^ ERROR: doc list item missing indentation +fn six() {} + +/// - - first line +/// +/// this is not a lazy continuation +fn seven() {} + +#[rustfmt::skip] +// https://github.com/rust-lang/rust-clippy/pull/12770#issuecomment-2118601768 +/// Returns a list of ProtocolDescriptors from a Serde JSON input. +/// +/// Defined Protocol Identifiers for the Protocol Descriptor +/// We intentionally omit deprecated profile identifiers. +/// From Bluetooth Assigned Numbers: +/// https://www.bluetooth.com/specifications/assigned-numbers/service-discovery +/// +/// # Arguments +/// * `protocol_descriptors`: A Json Representation of the ProtocolDescriptors +/// to set up. Example: +/// 'protocol_descriptors': [ +//~^ ERROR: doc list item missing indentation +/// { +/// 'protocol': 25, # u64 Representation of ProtocolIdentifier::AVDTP +/// 'params': [ +/// { +/// 'data': 0x0103 # to indicate 1.3 +/// }, +/// { +/// 'data': 0x0105 # to indicate 1.5 +/// } +/// ] +/// }, +/// { +/// 'protocol': 1, # u64 Representation of ProtocolIdentifier::SDP +/// 'params': [{ +/// 'data': 0x0019 +/// }] +/// } +/// ] +//~^ ERROR: doc list item missing indentation +fn eight() {} diff --git a/src/tools/clippy/tests/ui/doc/doc_lazy_list.rs b/src/tools/clippy/tests/ui/doc/doc_lazy_list.rs new file mode 100644 index 0000000000000..30ab448a1130c --- /dev/null +++ b/src/tools/clippy/tests/ui/doc/doc_lazy_list.rs @@ -0,0 +1,77 @@ +#![warn(clippy::doc_lazy_continuation)] + +/// 1. nest here +/// lazy continuation +//~^ ERROR: doc list item missing indentation +fn one() {} + +/// 1. first line +/// lazy list continuations don't make warnings with this lint +//~^ ERROR: doc list item missing indentation +/// because they don't have the +//~^ ERROR: doc list item missing indentation +fn two() {} + +/// - nest here +/// lazy continuation +//~^ ERROR: doc list item missing indentation +fn three() {} + +/// - first line +/// lazy list continuations don't make warnings with this lint +//~^ ERROR: doc list item missing indentation +/// because they don't have the +//~^ ERROR: doc list item missing indentation +fn four() {} + +/// - nest here +/// lazy continuation +//~^ ERROR: doc list item missing indentation +fn five() {} + +/// - - first line +/// this will warn on the lazy continuation +//~^ ERROR: doc list item missing indentation +/// and so should this +//~^ ERROR: doc list item missing indentation +fn six() {} + +/// - - first line +/// +/// this is not a lazy continuation +fn seven() {} + +#[rustfmt::skip] +// https://github.com/rust-lang/rust-clippy/pull/12770#issuecomment-2118601768 +/// Returns a list of ProtocolDescriptors from a Serde JSON input. +/// +/// Defined Protocol Identifiers for the Protocol Descriptor +/// We intentionally omit deprecated profile identifiers. +/// From Bluetooth Assigned Numbers: +/// https://www.bluetooth.com/specifications/assigned-numbers/service-discovery +/// +/// # Arguments +/// * `protocol_descriptors`: A Json Representation of the ProtocolDescriptors +/// to set up. Example: +/// 'protocol_descriptors': [ +//~^ ERROR: doc list item missing indentation +/// { +/// 'protocol': 25, # u64 Representation of ProtocolIdentifier::AVDTP +/// 'params': [ +/// { +/// 'data': 0x0103 # to indicate 1.3 +/// }, +/// { +/// 'data': 0x0105 # to indicate 1.5 +/// } +/// ] +/// }, +/// { +/// 'protocol': 1, # u64 Representation of ProtocolIdentifier::SDP +/// 'params': [{ +/// 'data': 0x0019 +/// }] +/// } +/// ] +//~^ ERROR: doc list item missing indentation +fn eight() {} diff --git a/src/tools/clippy/tests/ui/doc/doc_lazy_list.stderr b/src/tools/clippy/tests/ui/doc/doc_lazy_list.stderr new file mode 100644 index 0000000000000..ddfdc49340c45 --- /dev/null +++ b/src/tools/clippy/tests/ui/doc/doc_lazy_list.stderr @@ -0,0 +1,136 @@ +error: doc list item missing indentation + --> tests/ui/doc/doc_lazy_list.rs:4:5 + | +LL | /// lazy continuation + | ^ + | + = help: if this is supposed to be its own paragraph, add a blank line + = note: `-D clippy::doc-lazy-continuation` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::doc_lazy_continuation)]` +help: indent this line + | +LL | /// lazy continuation + | +++ + +error: doc list item missing indentation + --> tests/ui/doc/doc_lazy_list.rs:9:5 + | +LL | /// lazy list continuations don't make warnings with this lint + | ^ + | + = help: if this is supposed to be its own paragraph, add a blank line +help: indent this line + | +LL | /// lazy list continuations don't make warnings with this lint + | +++ + +error: doc list item missing indentation + --> tests/ui/doc/doc_lazy_list.rs:11:5 + | +LL | /// because they don't have the + | ^ + | + = 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 missing indentation + --> tests/ui/doc/doc_lazy_list.rs:16:5 + | +LL | /// lazy continuation + | ^ + | + = help: if this is supposed to be its own paragraph, add a blank line +help: indent this line + | +LL | /// lazy continuation + | ++++ + +error: doc list item missing indentation + --> tests/ui/doc/doc_lazy_list.rs:21:5 + | +LL | /// lazy list continuations don't make warnings with this lint + | ^ + | + = help: if this is supposed to be its own paragraph, add a blank line +help: indent this line + | +LL | /// lazy list continuations don't make warnings with this lint + | ++++ + +error: doc list item missing indentation + --> tests/ui/doc/doc_lazy_list.rs:23:5 + | +LL | /// because they don't have the + | ^ + | + = 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 missing indentation + --> tests/ui/doc/doc_lazy_list.rs:28:5 + | +LL | /// lazy continuation + | ^ + | + = help: if this is supposed to be its own paragraph, add a blank line +help: indent this line + | +LL | /// lazy continuation + | ++++ + +error: doc list item missing indentation + --> tests/ui/doc/doc_lazy_list.rs:33:5 + | +LL | /// this will warn on the lazy continuation + | ^ + | + = help: if this is supposed to be its own paragraph, add a blank line +help: indent this line + | +LL | /// this will warn on the lazy continuation + | ++++++ + +error: doc list item missing indentation + --> tests/ui/doc/doc_lazy_list.rs:35:5 + | +LL | /// and so should this + | ^^^^ + | + = 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 missing indentation + --> tests/ui/doc/doc_lazy_list.rs:56:5 + | +LL | /// 'protocol_descriptors': [ + | ^ + | + = help: if this is supposed to be its own paragraph, add a blank line +help: indent this line + | +LL | /// 'protocol_descriptors': [ + | + + +error: doc list item missing indentation + --> tests/ui/doc/doc_lazy_list.rs:75:5 + | +LL | /// ] + | ^ + | + = help: if this is supposed to be its own paragraph, add a blank line +help: indent this line + | +LL | /// ] + | + + +error: aborting due to 11 previous errors + diff --git a/src/tools/clippy/tests/ui/duplicated_attributes.rs b/src/tools/clippy/tests/ui/duplicated_attributes.rs index d51e7e37beb67..97cf4a69682d7 100644 --- a/src/tools/clippy/tests/ui/duplicated_attributes.rs +++ b/src/tools/clippy/tests/ui/duplicated_attributes.rs @@ -1,5 +1,5 @@ //@aux-build:proc_macro_attr.rs - +#![feature(rustc_attrs)] #![warn(clippy::duplicated_attributes)] #![cfg(any(unix, windows))] #![allow(dead_code)] @@ -20,6 +20,10 @@ fn foo() {} #[cfg(unix)] // cfgs are not handled fn bar() {} +// No warning: +#[rustc_on_unimplemented(on(_Self = "&str", label = "`a"), on(_Self = "alloc::string::String", label = "a"))] +trait Abc {} + #[proc_macro_attr::duplicated_attr()] // Should not warn! fn babar() {} diff --git a/src/tools/clippy/tests/ui/from_str_radix_10.fixed b/src/tools/clippy/tests/ui/from_str_radix_10.fixed index 8c253bfd99a57..f9ce1defda17c 100644 --- a/src/tools/clippy/tests/ui/from_str_radix_10.fixed +++ b/src/tools/clippy/tests/ui/from_str_radix_10.fixed @@ -1,3 +1,4 @@ +#![feature(const_int_from_str)] #![warn(clippy::from_str_radix_10)] mod some_mod { @@ -59,3 +60,13 @@ fn main() -> Result<(), Box> { Ok(()) } + +fn issue_12732() { + const A: Result = u32::from_str_radix("123", 10); + const B: () = { + let _ = u32::from_str_radix("123", 10); + }; + const fn foo() { + let _ = u32::from_str_radix("123", 10); + } +} diff --git a/src/tools/clippy/tests/ui/from_str_radix_10.rs b/src/tools/clippy/tests/ui/from_str_radix_10.rs index e9d02215710cb..2d5b351f8da3e 100644 --- a/src/tools/clippy/tests/ui/from_str_radix_10.rs +++ b/src/tools/clippy/tests/ui/from_str_radix_10.rs @@ -1,3 +1,4 @@ +#![feature(const_int_from_str)] #![warn(clippy::from_str_radix_10)] mod some_mod { @@ -59,3 +60,13 @@ fn main() -> Result<(), Box> { Ok(()) } + +fn issue_12732() { + const A: Result = u32::from_str_radix("123", 10); + const B: () = { + let _ = u32::from_str_radix("123", 10); + }; + const fn foo() { + let _ = u32::from_str_radix("123", 10); + } +} diff --git a/src/tools/clippy/tests/ui/from_str_radix_10.stderr b/src/tools/clippy/tests/ui/from_str_radix_10.stderr index 4aa84eca26120..01a1bf8940a12 100644 --- a/src/tools/clippy/tests/ui/from_str_radix_10.stderr +++ b/src/tools/clippy/tests/ui/from_str_radix_10.stderr @@ -1,5 +1,5 @@ error: this call to `from_str_radix` can be replaced with a call to `str::parse` - --> tests/ui/from_str_radix_10.rs:28:5 + --> tests/ui/from_str_radix_10.rs:29:5 | LL | u32::from_str_radix("30", 10)?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"30".parse::()` @@ -8,43 +8,43 @@ LL | u32::from_str_radix("30", 10)?; = help: to override `-D warnings` add `#[allow(clippy::from_str_radix_10)]` error: this call to `from_str_radix` can be replaced with a call to `str::parse` - --> tests/ui/from_str_radix_10.rs:31:5 + --> tests/ui/from_str_radix_10.rs:32:5 | LL | i64::from_str_radix("24", 10)?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"24".parse::()` error: this call to `from_str_radix` can be replaced with a call to `str::parse` - --> tests/ui/from_str_radix_10.rs:33:5 + --> tests/ui/from_str_radix_10.rs:34:5 | LL | isize::from_str_radix("100", 10)?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"100".parse::()` error: this call to `from_str_radix` can be replaced with a call to `str::parse` - --> tests/ui/from_str_radix_10.rs:35:5 + --> tests/ui/from_str_radix_10.rs:36:5 | LL | u8::from_str_radix("7", 10)?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"7".parse::()` error: this call to `from_str_radix` can be replaced with a call to `str::parse` - --> tests/ui/from_str_radix_10.rs:37:5 + --> tests/ui/from_str_radix_10.rs:38:5 | LL | u16::from_str_radix(&("10".to_owned() + "5"), 10)?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `("10".to_owned() + "5").parse::()` error: this call to `from_str_radix` can be replaced with a call to `str::parse` - --> tests/ui/from_str_radix_10.rs:39:5 + --> tests/ui/from_str_radix_10.rs:40:5 | LL | i128::from_str_radix(Test + Test, 10)?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(Test + Test).parse::()` error: this call to `from_str_radix` can be replaced with a call to `str::parse` - --> tests/ui/from_str_radix_10.rs:43:5 + --> tests/ui/from_str_radix_10.rs:44:5 | LL | i32::from_str_radix(string, 10)?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `string.parse::()` error: this call to `from_str_radix` can be replaced with a call to `str::parse` - --> tests/ui/from_str_radix_10.rs:47:5 + --> tests/ui/from_str_radix_10.rs:48:5 | LL | i32::from_str_radix(&stringier, 10)?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `stringier.parse::()` diff --git a/src/tools/clippy/tests/ui/into_iter_on_ref.fixed b/src/tools/clippy/tests/ui/into_iter_on_ref.fixed index c03d91c797c8d..72b39c982bf71 100644 --- a/src/tools/clippy/tests/ui/into_iter_on_ref.fixed +++ b/src/tools/clippy/tests/ui/into_iter_on_ref.fixed @@ -11,7 +11,6 @@ fn main() { let _ = vec![1, 2, 3].into_iter(); let _ = (&vec![1, 2, 3]).iter(); //~ ERROR: equivalent to `.iter() - let _ = vec![1, 2, 3].into_boxed_slice().iter(); //~ ERROR: equivalent to `.iter() let _ = std::rc::Rc::from(&[X][..]).iter(); //~ ERROR: equivalent to `.iter() let _ = std::sync::Arc::from(&[X][..]).iter(); //~ ERROR: equivalent to `.iter() diff --git a/src/tools/clippy/tests/ui/into_iter_on_ref.rs b/src/tools/clippy/tests/ui/into_iter_on_ref.rs index 93c732fd6ccf9..5ba224720d340 100644 --- a/src/tools/clippy/tests/ui/into_iter_on_ref.rs +++ b/src/tools/clippy/tests/ui/into_iter_on_ref.rs @@ -11,7 +11,6 @@ fn main() { let _ = vec![1, 2, 3].into_iter(); let _ = (&vec![1, 2, 3]).into_iter(); //~ ERROR: equivalent to `.iter() - let _ = vec![1, 2, 3].into_boxed_slice().into_iter(); //~ ERROR: equivalent to `.iter() let _ = std::rc::Rc::from(&[X][..]).into_iter(); //~ ERROR: equivalent to `.iter() let _ = std::sync::Arc::from(&[X][..]).into_iter(); //~ ERROR: equivalent to `.iter() diff --git a/src/tools/clippy/tests/ui/into_iter_on_ref.stderr b/src/tools/clippy/tests/ui/into_iter_on_ref.stderr index 0e9d485f1a9d9..64d814074da46 100644 --- a/src/tools/clippy/tests/ui/into_iter_on_ref.stderr +++ b/src/tools/clippy/tests/ui/into_iter_on_ref.stderr @@ -8,160 +8,154 @@ LL | let _ = (&vec![1, 2, 3]).into_iter(); = help: to override `-D warnings` add `#[allow(clippy::into_iter_on_ref)]` error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `slice` - --> tests/ui/into_iter_on_ref.rs:14:46 - | -LL | let _ = vec![1, 2, 3].into_boxed_slice().into_iter(); - | ^^^^^^^^^ help: call directly: `iter` - -error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `slice` - --> tests/ui/into_iter_on_ref.rs:15:41 + --> tests/ui/into_iter_on_ref.rs:14:41 | LL | let _ = std::rc::Rc::from(&[X][..]).into_iter(); | ^^^^^^^^^ help: call directly: `iter` error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `slice` - --> tests/ui/into_iter_on_ref.rs:16:44 + --> tests/ui/into_iter_on_ref.rs:15:44 | LL | let _ = std::sync::Arc::from(&[X][..]).into_iter(); | ^^^^^^^^^ help: call directly: `iter` error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `array` - --> tests/ui/into_iter_on_ref.rs:18:32 + --> tests/ui/into_iter_on_ref.rs:17:32 | LL | let _ = (&&&&&&&[1, 2, 3]).into_iter(); | ^^^^^^^^^ help: call directly: `iter` error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `array` - --> tests/ui/into_iter_on_ref.rs:19:36 + --> tests/ui/into_iter_on_ref.rs:18:36 | LL | let _ = (&&&&mut &&&[1, 2, 3]).into_iter(); | ^^^^^^^^^ help: call directly: `iter` error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not consume the `array` - --> tests/ui/into_iter_on_ref.rs:20:40 + --> tests/ui/into_iter_on_ref.rs:19:40 | LL | let _ = (&mut &mut &mut [1, 2, 3]).into_iter(); | ^^^^^^^^^ help: call directly: `iter_mut` error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `Option` - --> tests/ui/into_iter_on_ref.rs:22:24 + --> tests/ui/into_iter_on_ref.rs:21:24 | LL | let _ = (&Some(4)).into_iter(); | ^^^^^^^^^ help: call directly: `iter` error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not consume the `Option` - --> tests/ui/into_iter_on_ref.rs:23:28 + --> tests/ui/into_iter_on_ref.rs:22:28 | LL | let _ = (&mut Some(5)).into_iter(); | ^^^^^^^^^ help: call directly: `iter_mut` error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `Result` - --> tests/ui/into_iter_on_ref.rs:24:32 + --> tests/ui/into_iter_on_ref.rs:23:32 | LL | let _ = (&Ok::<_, i32>(6)).into_iter(); | ^^^^^^^^^ help: call directly: `iter` error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not consume the `Result` - --> tests/ui/into_iter_on_ref.rs:25:37 + --> tests/ui/into_iter_on_ref.rs:24:37 | LL | let _ = (&mut Err::(7)).into_iter(); | ^^^^^^^^^ help: call directly: `iter_mut` error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `Vec` - --> tests/ui/into_iter_on_ref.rs:26:34 + --> tests/ui/into_iter_on_ref.rs:25:34 | LL | let _ = (&Vec::::new()).into_iter(); | ^^^^^^^^^ help: call directly: `iter` error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not consume the `Vec` - --> tests/ui/into_iter_on_ref.rs:27:38 + --> tests/ui/into_iter_on_ref.rs:26:38 | LL | let _ = (&mut Vec::::new()).into_iter(); | ^^^^^^^^^ help: call directly: `iter_mut` error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `BTreeMap` - --> tests/ui/into_iter_on_ref.rs:28:44 + --> tests/ui/into_iter_on_ref.rs:27:44 | LL | let _ = (&BTreeMap::::new()).into_iter(); | ^^^^^^^^^ help: call directly: `iter` error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not consume the `BTreeMap` - --> tests/ui/into_iter_on_ref.rs:29:48 + --> tests/ui/into_iter_on_ref.rs:28:48 | LL | let _ = (&mut BTreeMap::::new()).into_iter(); | ^^^^^^^^^ help: call directly: `iter_mut` error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `VecDeque` - --> tests/ui/into_iter_on_ref.rs:30:39 + --> tests/ui/into_iter_on_ref.rs:29:39 | LL | let _ = (&VecDeque::::new()).into_iter(); | ^^^^^^^^^ help: call directly: `iter` error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not consume the `VecDeque` - --> tests/ui/into_iter_on_ref.rs:31:43 + --> tests/ui/into_iter_on_ref.rs:30:43 | LL | let _ = (&mut VecDeque::::new()).into_iter(); | ^^^^^^^^^ help: call directly: `iter_mut` error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `LinkedList` - --> tests/ui/into_iter_on_ref.rs:32:41 + --> tests/ui/into_iter_on_ref.rs:31:41 | LL | let _ = (&LinkedList::::new()).into_iter(); | ^^^^^^^^^ help: call directly: `iter` error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not consume the `LinkedList` - --> tests/ui/into_iter_on_ref.rs:33:45 + --> tests/ui/into_iter_on_ref.rs:32:45 | LL | let _ = (&mut LinkedList::::new()).into_iter(); | ^^^^^^^^^ help: call directly: `iter_mut` error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `HashMap` - --> tests/ui/into_iter_on_ref.rs:34:43 + --> tests/ui/into_iter_on_ref.rs:33:43 | LL | let _ = (&HashMap::::new()).into_iter(); | ^^^^^^^^^ help: call directly: `iter` error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not consume the `HashMap` - --> tests/ui/into_iter_on_ref.rs:35:47 + --> tests/ui/into_iter_on_ref.rs:34:47 | LL | let _ = (&mut HashMap::::new()).into_iter(); | ^^^^^^^^^ help: call directly: `iter_mut` error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `BTreeSet` - --> tests/ui/into_iter_on_ref.rs:37:39 + --> tests/ui/into_iter_on_ref.rs:36:39 | LL | let _ = (&BTreeSet::::new()).into_iter(); | ^^^^^^^^^ help: call directly: `iter` error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `BinaryHeap` - --> tests/ui/into_iter_on_ref.rs:38:41 + --> tests/ui/into_iter_on_ref.rs:37:41 | LL | let _ = (&BinaryHeap::::new()).into_iter(); | ^^^^^^^^^ help: call directly: `iter` error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `HashSet` - --> tests/ui/into_iter_on_ref.rs:39:38 + --> tests/ui/into_iter_on_ref.rs:38:38 | LL | let _ = (&HashSet::::new()).into_iter(); | ^^^^^^^^^ help: call directly: `iter` error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `Path` - --> tests/ui/into_iter_on_ref.rs:40:43 + --> tests/ui/into_iter_on_ref.rs:39:43 | LL | let _ = std::path::Path::new("12/34").into_iter(); | ^^^^^^^^^ help: call directly: `iter` error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `PathBuf` - --> tests/ui/into_iter_on_ref.rs:41:47 + --> tests/ui/into_iter_on_ref.rs:40:47 | LL | let _ = std::path::PathBuf::from("12/34").into_iter(); | ^^^^^^^^^ help: call directly: `iter` error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `array` - --> tests/ui/into_iter_on_ref.rs:43:26 + --> tests/ui/into_iter_on_ref.rs:42:26 | LL | let _ = (&[1, 2, 3]).into_iter().next(); | ^^^^^^^^^ help: call directly: `iter` -error: aborting due to 27 previous errors +error: aborting due to 26 previous errors diff --git a/src/tools/clippy/tests/ui/match_same_arms.stderr b/src/tools/clippy/tests/ui/match_same_arms.stderr index a926570b60ada..3c0382767c3fc 100644 --- a/src/tools/clippy/tests/ui/match_same_arms.stderr +++ b/src/tools/clippy/tests/ui/match_same_arms.stderr @@ -2,7 +2,7 @@ error: this match arm has an identical body to the `_` wildcard arm --> tests/ui/match_same_arms.rs:12:9 | LL | Abc::A => 0, - | ^^^^^^^^^^^ help: try removing the arm + | ^^^^^^^^^^^^^ help: try removing the arm | = help: or try changing either arm body note: `_` wildcard arm here @@ -17,106 +17,114 @@ error: this match arm has an identical body to another arm --> tests/ui/match_same_arms.rs:18:9 | LL | (1, .., 3) => 42, - | ----------^^^^^^ - | | - | help: try merging the arm patterns: `(1, .., 3) | (.., 3)` + | ^^^^^^^^^^^^^^^^ | - = help: or try changing either arm body -note: other arm here - --> tests/ui/match_same_arms.rs:19:9 + = help: try changing either arm body +help: or try merging the arm patterns + | +LL | (1, .., 3) | (.., 3) => 42, + | ~~~~~~~~~~~~~~~~~~~~ +help: and remove this obsolete arm + | +LL - (.., 3) => 42, | -LL | (.., 3) => 42, - | ^^^^^^^^^^^^^ error: this match arm has an identical body to another arm --> tests/ui/match_same_arms.rs:25:9 | LL | 51 => 1, - | --^^^^^ - | | - | help: try merging the arm patterns: `51 | 42` + | ^^^^^^^ | - = help: or try changing either arm body -note: other arm here - --> tests/ui/match_same_arms.rs:24:9 + = help: try changing either arm body +help: or try merging the arm patterns + | +LL | 51 | 42 => 1, + | ~~~~~~~ +help: and remove this obsolete arm + | +LL - 42 => 1, | -LL | 42 => 1, - | ^^^^^^^ error: this match arm has an identical body to another arm --> tests/ui/match_same_arms.rs:26:9 | LL | 41 => 2, - | --^^^^^ - | | - | help: try merging the arm patterns: `41 | 52` + | ^^^^^^^ | - = help: or try changing either arm body -note: other arm here - --> tests/ui/match_same_arms.rs:27:9 + = help: try changing either arm body +help: or try merging the arm patterns + | +LL | 41 | 52 => 2, + | ~~~~~~~ +help: and remove this obsolete arm + | +LL - 52 => 2, | -LL | 52 => 2, - | ^^^^^^^ error: this match arm has an identical body to another arm --> tests/ui/match_same_arms.rs:33:9 | LL | 2 => 2, - | -^^^^^ - | | - | help: try merging the arm patterns: `2 | 1` + | ^^^^^^ | - = help: or try changing either arm body -note: other arm here - --> tests/ui/match_same_arms.rs:32:9 + = help: try changing either arm body +help: or try merging the arm patterns + | +LL | 2 | 1 => 2, + | ~~~~~ +help: and remove this obsolete arm + | +LL - 1 => 2, | -LL | 1 => 2, - | ^^^^^^ error: this match arm has an identical body to another arm --> tests/ui/match_same_arms.rs:35:9 | LL | 3 => 2, - | -^^^^^ - | | - | help: try merging the arm patterns: `3 | 1` + | ^^^^^^ | - = help: or try changing either arm body -note: other arm here - --> tests/ui/match_same_arms.rs:32:9 + = help: try changing either arm body +help: or try merging the arm patterns + | +LL | 3 | 1 => 2, + | ~~~~~ +help: and remove this obsolete arm + | +LL - 1 => 2, | -LL | 1 => 2, - | ^^^^^^ error: this match arm has an identical body to another arm --> tests/ui/match_same_arms.rs:33:9 | LL | 2 => 2, - | -^^^^^ - | | - | help: try merging the arm patterns: `2 | 3` + | ^^^^^^ | - = help: or try changing either arm body -note: other arm here - --> tests/ui/match_same_arms.rs:35:9 + = help: try changing either arm body +help: or try merging the arm patterns + | +LL | 2 | 3 => 2, + | ~~~~~ +help: and remove this obsolete arm + | +LL - 3 => 2, +LL + | -LL | 3 => 2, - | ^^^^^^ error: this match arm has an identical body to another arm --> tests/ui/match_same_arms.rs:52:17 | LL | CommandInfo::External { name, .. } => name.to_string(), - | ----------------------------------^^^^^^^^^^^^^^^^^^^^ - | | - | help: try merging the arm patterns: `CommandInfo::External { name, .. } | CommandInfo::BuiltIn { name, .. }` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: or try changing either arm body -note: other arm here - --> tests/ui/match_same_arms.rs:51:17 + = help: try changing either arm body +help: or try merging the arm patterns + | +LL | CommandInfo::External { name, .. } | CommandInfo::BuiltIn { name, .. } => name.to_string(), + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +help: and remove this obsolete arm + | +LL - CommandInfo::BuiltIn { name, .. } => name.to_string(), | -LL | CommandInfo::BuiltIn { name, .. } => name.to_string(), - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 8 previous errors diff --git a/src/tools/clippy/tests/ui/match_same_arms2.fixed b/src/tools/clippy/tests/ui/match_same_arms2.fixed new file mode 100644 index 0000000000000..fba0cf33b3c26 --- /dev/null +++ b/src/tools/clippy/tests/ui/match_same_arms2.fixed @@ -0,0 +1,241 @@ +#![warn(clippy::match_same_arms)] +#![allow( + clippy::disallowed_names, + clippy::diverging_sub_expression, + clippy::uninlined_format_args, + clippy::match_single_binding, + clippy::match_like_matches_macro +)] +fn bar(_: T) {} +fn foo() -> bool { + unimplemented!() +} + +fn match_same_arms() { + let _ = match 42 { + _ => { + foo(); + let mut a = 42 + [23].len() as i32; + if true { + a += 7; + } + a = -31 - a; + a + }, + }; + //~^^^^^^^^^^^^^^^^^^^ ERROR: this match arm has an identical body to the `_` wildcard arm + + let _ = match 42 { + 51 | 42 => foo(), //~ ERROR: this match arm has an identical body to another arm + _ => true, + }; + + let _ = match Some(42) { + None | Some(_) => 24, //~ ERROR: this match arm has an identical body to another arm + }; + + let _ = match Some(42) { + Some(foo) => 24, + None => 24, + }; + + let _ = match Some(42) { + Some(42) => 24, + Some(a) => 24, // bindings are different + None => 0, + }; + + let _ = match Some(42) { + Some(a) if a > 0 => 24, + Some(a) => 24, // one arm has a guard + None => 0, + }; + + match (Some(42), Some(42)) { + (None, Some(a)) | (Some(a), None) => bar(a), //~ ERROR: this match arm has an identical body to another arm + _ => (), + } + + // No warning because guards are different + let _ = match Some(42) { + Some(a) if a == 42 => a, + Some(a) if a == 24 => a, + Some(_) => 24, + None => 0, + }; + + let _ = match (Some(42), Some(42)) { + (None, Some(a)) | (Some(a), None) if a == 42 => a, //~ ERROR: this match arm has an identical body to another arm + _ => 0, + }; + + match (Some(42), Some(42)) { + (Some(a), ..) | (.., Some(a)) => bar(a), //~ ERROR: this match arm has an identical body to another arm + _ => (), + } + + let _ = match Some(()) { + Some(()) => 0.0, + None => -0.0, + }; + + match (Some(42), Some("")) { + (Some(a), None) => bar(a), + (None, Some(a)) => bar(a), // bindings have different types + _ => (), + } + + let x: Result = Ok(3); + + // No warning because of the guard. + match x { + Ok(x) if x * x == 64 => println!("ok"), + Ok(_) => println!("ok"), + Err(_) => println!("err"), + } + + // This used to be a false positive; see issue #1996. + match x { + Ok(3) => println!("ok"), + Ok(x) if x * x == 64 => println!("ok 64"), + Ok(_) => println!("ok"), + Err(_) => println!("err"), + } + + match (x, Some(1i32)) { + (Ok(x), Some(_)) | (Ok(_), Some(x)) => println!("ok {}", x), //~ ERROR: this match arm has an identical body to another arm + _ => println!("err"), + } + + // No warning; different types for `x`. + match (x, Some(1.0f64)) { + (Ok(x), Some(_)) => println!("ok {}", x), + (Ok(_), Some(x)) => println!("ok {}", x), + _ => println!("err"), + } + + // False negative #2251. + match x { + Ok(_tmp) => println!("ok"), + Ok(_) | Ok(3) => println!("ok"), //~ ERROR: this match arm has an identical body to another arm + Err(_) => { + unreachable!(); + }, + } + + // False positive #1390 + macro_rules! empty { + ($e:expr) => {}; + } + match 0 { + 0 => { + empty!(0); + }, + 1 => { + empty!(1); + }, + x => { + empty!(x); + }, + }; + + // still lint if the tokens are the same + match 0 { + 1 | 0 => { + empty!(0); + }, + x => { + empty!(x); + }, + } + //~^^^^^^^ ERROR: this match arm has an identical body to another arm + + match_expr_like_matches_macro_priority(); +} + +fn match_expr_like_matches_macro_priority() { + enum E { + A, + B, + C, + } + let x = E::A; + let _ans = match x { + E::A => false, + E::B => false, + _ => true, + }; +} + +fn main() { + let _ = match Some(0) { + Some(0) => 0, + Some(1) => 1, + #[cfg(feature = "foo")] + Some(2) => 2, + _ => 1, + }; + + enum Foo { + X(u32), + Y(u32), + Z(u32), + } + + // Don't lint. `Foo::X(0)` and `Foo::Z(_)` overlap with the arm in between. + let _ = match Foo::X(0) { + Foo::X(0) => 1, + Foo::X(_) | Foo::Y(_) | Foo::Z(0) => 2, + Foo::Z(_) => 1, + _ => 0, + }; + + // Suggest moving `Foo::Z(_)` up. + let _ = match Foo::X(0) { + Foo::X(0) | Foo::Z(_) => 1, //~ ERROR: this match arm has an identical body to another arm + Foo::X(_) | Foo::Y(_) => 2, + _ => 0, + }; + + // Suggest moving `Foo::X(0)` down. + let _ = match Foo::X(0) { + Foo::Y(_) | Foo::Z(0) => 2, + Foo::Z(_) | Foo::X(0) => 1, //~ ERROR: this match arm has an identical body to another arm + _ => 0, + }; + + // Don't lint. + let _ = match 0 { + -2 => 1, + -5..=50 => 2, + -150..=88 => 1, + _ => 3, + }; + + struct Bar { + x: u32, + y: u32, + z: u32, + } + + // Lint. + let _ = match None { + Some(Bar { y: 10, z: 0, .. }) => 2, + None => 50, + Some(Bar { y: 0, x: 5, .. }) | Some(Bar { x: 0, y: 5, .. }) => 1, //~ ERROR: this match arm has an identical body to another arm + _ => 200, + }; + + let _ = match 0 { + 0 => todo!(), + 1 => todo!(), + 2 => core::convert::identity::(todo!()), + 3 => core::convert::identity::(todo!()), + _ => 5, + }; + + let _ = match 0 { + 1 | 0 => cfg!(not_enable), + _ => false, + }; +} diff --git a/src/tools/clippy/tests/ui/match_same_arms2.rs b/src/tools/clippy/tests/ui/match_same_arms2.rs index 85ad0962eb4bf..8a4e3b325bbff 100644 --- a/src/tools/clippy/tests/ui/match_same_arms2.rs +++ b/src/tools/clippy/tests/ui/match_same_arms2.rs @@ -2,9 +2,10 @@ #![allow( clippy::disallowed_names, clippy::diverging_sub_expression, - clippy::uninlined_format_args + clippy::uninlined_format_args, + clippy::match_single_binding, + clippy::match_like_matches_macro )] -//@no-rustfix fn bar(_: T) {} fn foo() -> bool { unimplemented!() diff --git a/src/tools/clippy/tests/ui/match_same_arms2.stderr b/src/tools/clippy/tests/ui/match_same_arms2.stderr index f4c38c1af8975..3d15176ccf997 100644 --- a/src/tools/clippy/tests/ui/match_same_arms2.stderr +++ b/src/tools/clippy/tests/ui/match_same_arms2.stderr @@ -1,18 +1,18 @@ error: this match arm has an identical body to the `_` wildcard arm - --> tests/ui/match_same_arms2.rs:15:9 + --> tests/ui/match_same_arms2.rs:16:9 | LL | / 42 => { LL | | foo(); LL | | let mut a = 42 + [23].len() as i32; LL | | if true { ... | -LL | | a LL | | }, - | |_________^ help: try removing the arm +LL | | _ => { + | |________^ help: try removing the arm | = help: or try changing either arm body note: `_` wildcard arm here - --> tests/ui/match_same_arms2.rs:24:9 + --> tests/ui/match_same_arms2.rs:25:9 | LL | / _ => { LL | | foo(); @@ -26,203 +26,200 @@ LL | | }, = help: to override `-D warnings` add `#[allow(clippy::match_same_arms)]` error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:38:9 + --> tests/ui/match_same_arms2.rs:39:9 | LL | 51 => foo(), - | --^^^^^^^^^ - | | - | help: try merging the arm patterns: `51 | 42` + | ^^^^^^^^^^^ | - = help: or try changing either arm body -note: other arm here - --> tests/ui/match_same_arms2.rs:37:9 + = help: try changing either arm body +help: or try merging the arm patterns + | +LL | 51 | 42 => foo(), + | ~~~~~~~ +help: and remove this obsolete arm + | +LL - 42 => foo(), | -LL | 42 => foo(), - | ^^^^^^^^^^^ error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:44:9 + --> tests/ui/match_same_arms2.rs:45:9 | LL | None => 24, - | ----^^^^^^ - | | - | help: try merging the arm patterns: `None | Some(_)` + | ^^^^^^^^^^ | - = help: or try changing either arm body -note: other arm here - --> tests/ui/match_same_arms2.rs:43:9 + = help: try changing either arm body +help: or try merging the arm patterns + | +LL | None | Some(_) => 24, + | ~~~~~~~~~~~~~~ +help: and remove this obsolete arm + | +LL - Some(_) => 24, | -LL | Some(_) => 24, - | ^^^^^^^^^^^^^ error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:66:9 + --> tests/ui/match_same_arms2.rs:67:9 | LL | (None, Some(a)) => bar(a), - | ---------------^^^^^^^^^^ - | | - | help: try merging the arm patterns: `(None, Some(a)) | (Some(a), None)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: or try changing either arm body -note: other arm here - --> tests/ui/match_same_arms2.rs:65:9 + = help: try changing either arm body +help: or try merging the arm patterns + | +LL | (None, Some(a)) | (Some(a), None) => bar(a), + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +help: and remove this obsolete arm + | +LL - (Some(a), None) => bar(a), | -LL | (Some(a), None) => bar(a), - | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:80:9 + --> tests/ui/match_same_arms2.rs:81:9 | LL | (None, Some(a)) if a == 42 => a, - | ---------------^^^^^^^^^^^^^^^^ - | | - | help: try merging the arm patterns: `(None, Some(a)) | (Some(a), None)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: or try changing either arm body -note: other arm here - --> tests/ui/match_same_arms2.rs:79:9 + = help: try changing either arm body +help: or try merging the arm patterns + | +LL | (None, Some(a)) | (Some(a), None) if a == 42 => a, + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +help: and remove this obsolete arm + | +LL - (Some(a), None) if a == 42 => a, | -LL | (Some(a), None) if a == 42 => a, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:85:9 + --> tests/ui/match_same_arms2.rs:86:9 | LL | (Some(a), ..) => bar(a), - | -------------^^^^^^^^^^ - | | - | help: try merging the arm patterns: `(Some(a), ..) | (.., Some(a))` + | ^^^^^^^^^^^^^^^^^^^^^^^ | - = help: or try changing either arm body -note: other arm here - --> tests/ui/match_same_arms2.rs:86:9 + = help: try changing either arm body +help: or try merging the arm patterns + | +LL | (Some(a), ..) | (.., Some(a)) => bar(a), + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +help: and remove this obsolete arm + | +LL - (.., Some(a)) => bar(a), | -LL | (.., Some(a)) => bar(a), - | ^^^^^^^^^^^^^^^^^^^^^^^ error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:119:9 + --> tests/ui/match_same_arms2.rs:120:9 | LL | (Ok(x), Some(_)) => println!("ok {}", x), - | ----------------^^^^^^^^^^^^^^^^^^^^^^^^ - | | - | help: try merging the arm patterns: `(Ok(x), Some(_)) | (Ok(_), Some(x))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: or try changing either arm body -note: other arm here - --> tests/ui/match_same_arms2.rs:120:9 + = help: try changing either arm body +help: or try merging the arm patterns + | +LL | (Ok(x), Some(_)) | (Ok(_), Some(x)) => println!("ok {}", x), + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +help: and remove this obsolete arm + | +LL - (Ok(_), Some(x)) => println!("ok {}", x), | -LL | (Ok(_), Some(x)) => println!("ok {}", x), - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:135:9 + --> tests/ui/match_same_arms2.rs:136:9 | LL | Ok(_) => println!("ok"), - | -----^^^^^^^^^^^^^^^^^^ - | | - | help: try merging the arm patterns: `Ok(_) | Ok(3)` + | ^^^^^^^^^^^^^^^^^^^^^^^ | - = help: or try changing either arm body -note: other arm here - --> tests/ui/match_same_arms2.rs:134:9 + = help: try changing either arm body +help: or try merging the arm patterns + | +LL | Ok(_) | Ok(3) => println!("ok"), + | ~~~~~~~~~~~~~ +help: and remove this obsolete arm + | +LL - Ok(3) => println!("ok"), | -LL | Ok(3) => println!("ok"), - | ^^^^^^^^^^^^^^^^^^^^^^^ error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:162:9 + --> tests/ui/match_same_arms2.rs:163:9 | -LL | 1 => { - | ^ help: try merging the arm patterns: `1 | 0` - | _________| - | | +LL | / 1 => { LL | | empty!(0); LL | | }, | |_________^ | - = help: or try changing either arm body -note: other arm here - --> tests/ui/match_same_arms2.rs:159:9 + = help: try changing either arm body +help: or try merging the arm patterns + | +LL | 1 | 0 => { + | ~~~~~ +help: and remove this obsolete arm + | +LL - 0 => { +LL - empty!(0); +LL - }, | -LL | / 0 => { -LL | | empty!(0); -LL | | }, - | |_________^ - -error: match expression looks like `matches!` macro - --> tests/ui/match_same_arms2.rs:181:16 - | -LL | let _ans = match x { - | ________________^ -LL | | E::A => false, -LL | | E::B => false, -LL | | _ => true, -LL | | }; - | |_____^ help: try: `!matches!(x, E::A | E::B)` - | - = note: `-D clippy::match-like-matches-macro` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::match_like_matches_macro)]` error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:213:9 + --> tests/ui/match_same_arms2.rs:214:9 | LL | Foo::X(0) => 1, - | ---------^^^^^ - | | - | help: try merging the arm patterns: `Foo::X(0) | Foo::Z(_)` + | ^^^^^^^^^^^^^^ | - = help: or try changing either arm body -note: other arm here - --> tests/ui/match_same_arms2.rs:215:9 + = help: try changing either arm body +help: or try merging the arm patterns + | +LL | Foo::X(0) | Foo::Z(_) => 1, + | ~~~~~~~~~~~~~~~~~~~~~ +help: and remove this obsolete arm + | +LL - Foo::Z(_) => 1, | -LL | Foo::Z(_) => 1, - | ^^^^^^^^^^^^^^ error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:223:9 + --> tests/ui/match_same_arms2.rs:224:9 | LL | Foo::Z(_) => 1, - | ---------^^^^^ - | | - | help: try merging the arm patterns: `Foo::Z(_) | Foo::X(0)` + | ^^^^^^^^^^^^^^ | - = help: or try changing either arm body -note: other arm here - --> tests/ui/match_same_arms2.rs:221:9 + = help: try changing either arm body +help: or try merging the arm patterns + | +LL | Foo::Z(_) | Foo::X(0) => 1, + | ~~~~~~~~~~~~~~~~~~~~~ +help: and remove this obsolete arm + | +LL - Foo::X(0) => 1, | -LL | Foo::X(0) => 1, - | ^^^^^^^^^^^^^^ error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:246:9 + --> tests/ui/match_same_arms2.rs:247:9 | LL | Some(Bar { y: 0, x: 5, .. }) => 1, - | ----------------------------^^^^^ - | | - | help: try merging the arm patterns: `Some(Bar { y: 0, x: 5, .. }) | Some(Bar { x: 0, y: 5, .. })` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: or try changing either arm body -note: other arm here - --> tests/ui/match_same_arms2.rs:243:9 + = help: try changing either arm body +help: or try merging the arm patterns + | +LL | Some(Bar { y: 0, x: 5, .. }) | Some(Bar { x: 0, y: 5, .. }) => 1, + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +help: and remove this obsolete arm + | +LL - Some(Bar { x: 0, y: 5, .. }) => 1, | -LL | Some(Bar { x: 0, y: 5, .. }) => 1, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:260:9 + --> tests/ui/match_same_arms2.rs:261:9 | LL | 1 => cfg!(not_enable), - | -^^^^^^^^^^^^^^^^^^^^ - | | - | help: try merging the arm patterns: `1 | 0` + | ^^^^^^^^^^^^^^^^^^^^^ | - = help: or try changing either arm body -note: other arm here - --> tests/ui/match_same_arms2.rs:259:9 + = help: try changing either arm body +help: or try merging the arm patterns + | +LL | 1 | 0 => cfg!(not_enable), + | ~~~~~ +help: and remove this obsolete arm + | +LL - 0 => cfg!(not_enable), | -LL | 0 => cfg!(not_enable), - | ^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 14 previous errors +error: aborting due to 13 previous errors diff --git a/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.fixed b/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.fixed new file mode 100644 index 0000000000000..804c0a869a9ff --- /dev/null +++ b/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.fixed @@ -0,0 +1,61 @@ +#![feature(non_exhaustive_omitted_patterns_lint)] +#![warn(clippy::match_same_arms)] +#![no_main] +use std::sync::atomic::Ordering; // #[non_exhaustive] enum + +fn repeat() -> ! { + panic!() +} + +pub fn f(x: Ordering) { + #[deny(non_exhaustive_omitted_patterns)] + match x { + Ordering::Relaxed => println!("relaxed"), + Ordering::Release => println!("release"), + Ordering::Acquire => println!("acquire"), + Ordering::AcqRel | Ordering::SeqCst => repeat(), + _ => repeat(), + } +} + +mod f { + #![deny(non_exhaustive_omitted_patterns)] + + use super::*; + + pub fn f(x: Ordering) { + match x { + Ordering::Relaxed => println!("relaxed"), + Ordering::Release => println!("release"), + Ordering::Acquire => println!("acquire"), + Ordering::AcqRel | Ordering::SeqCst => repeat(), + _ => repeat(), + } + } +} + +// Below should still lint + +pub fn g(x: Ordering) { + match x { + Ordering::Relaxed => println!("relaxed"), + Ordering::Release => println!("release"), + Ordering::Acquire => println!("acquire"), + //~^ ERROR: this match arm has an identical body to the `_` wildcard arm + _ => repeat(), + } +} + +mod g { + use super::*; + + pub fn g(x: Ordering) { + match x { + Ordering::Relaxed => println!("relaxed"), + Ordering::Release => println!("release"), + Ordering::Acquire => println!("acquire"), + //~^ ERROR: this match arm has an identical body to the `_` wildcard arm + _ => repeat(), + } + } +} diff --git a/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.rs b/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.rs index 5c277f925a8f1..e50663932a1a6 100644 --- a/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.rs +++ b/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.rs @@ -1,7 +1,6 @@ #![feature(non_exhaustive_omitted_patterns_lint)] #![warn(clippy::match_same_arms)] #![no_main] -//@no-rustfix use std::sync::atomic::Ordering; // #[non_exhaustive] enum fn repeat() -> ! { diff --git a/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.stderr b/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.stderr index cf2a75354e15c..aa7f8c95dce84 100644 --- a/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.stderr +++ b/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.stderr @@ -1,12 +1,13 @@ error: this match arm has an identical body to the `_` wildcard arm - --> tests/ui/match_same_arms_non_exhaustive.rs:45:9 + --> tests/ui/match_same_arms_non_exhaustive.rs:44:9 | -LL | Ordering::AcqRel | Ordering::SeqCst => repeat(), - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try removing the arm +LL | / Ordering::AcqRel | Ordering::SeqCst => repeat(), +LL | | + | |________^ help: try removing the arm | = help: or try changing either arm body note: `_` wildcard arm here - --> tests/ui/match_same_arms_non_exhaustive.rs:47:9 + --> tests/ui/match_same_arms_non_exhaustive.rs:46:9 | LL | _ => repeat(), | ^^^^^^^^^^^^^ @@ -14,14 +15,15 @@ LL | _ => repeat(), = help: to override `-D warnings` add `#[allow(clippy::match_same_arms)]` error: this match arm has an identical body to the `_` wildcard arm - --> tests/ui/match_same_arms_non_exhaustive.rs:59:13 + --> tests/ui/match_same_arms_non_exhaustive.rs:58:13 | -LL | Ordering::AcqRel | Ordering::SeqCst => repeat(), - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try removing the arm +LL | / Ordering::AcqRel | Ordering::SeqCst => repeat(), +LL | | + | |____________^ help: try removing the arm | = help: or try changing either arm body note: `_` wildcard arm here - --> tests/ui/match_same_arms_non_exhaustive.rs:61:13 + --> tests/ui/match_same_arms_non_exhaustive.rs:60:13 | LL | _ => repeat(), | ^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/cant_be_const.rs b/src/tools/clippy/tests/ui/missing_const_for_fn/cant_be_const.rs index d026e009684a4..2750e0cdf3f70 100644 --- a/src/tools/clippy/tests/ui/missing_const_for_fn/cant_be_const.rs +++ b/src/tools/clippy/tests/ui/missing_const_for_fn/cant_be_const.rs @@ -161,7 +161,23 @@ union U { f: u32, } -// Do not lint because accessing union fields from const functions is unstable +// Do not lint because accessing union fields from const functions is unstable in 1.55 +#[clippy::msrv = "1.55"] fn h(u: U) -> u32 { unsafe { u.f } } + +mod msrv { + struct Foo(*const u8, *mut u8); + + impl Foo { + #[clippy::msrv = "1.57"] + fn deref_ptr_cannot_be_const(self) -> usize { + unsafe { *self.0 as usize } + } + #[clippy::msrv = "1.58"] + fn deref_mut_ptr_cannot_be_const(self) -> usize { + unsafe { *self.1 as usize } + } + } +} diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs index 12a8320c8f329..06dbbeb31c0d5 100644 --- a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs +++ b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs @@ -113,3 +113,31 @@ impl const Drop for D { // Lint this, since it can be dropped in const contexts // FIXME(effects) fn d(this: D) {} + +mod msrv { + struct Foo(*const u8, &'static u8); + + impl Foo { + #[clippy::msrv = "1.58"] + fn deref_ptr_can_be_const(self) -> usize { + //~^ ERROR: this could be a `const fn` + unsafe { *self.0 as usize } + } + + fn deref_copied_val(self) -> usize { + //~^ ERROR: this could be a `const fn` + *self.1 as usize + } + } + + union Bar { + val: u8, + } + + #[clippy::msrv = "1.56"] + fn union_access_can_be_const() { + //~^ ERROR: this could be a `const fn` + let bar = Bar { val: 1 }; + let _ = unsafe { bar.val }; + } +} diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.stderr b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.stderr index 082459fd82120..b2cade3056373 100644 --- a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.stderr +++ b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.stderr @@ -102,5 +102,33 @@ LL | | 46 LL | | } | |_^ -error: aborting due to 11 previous errors +error: this could be a `const fn` + --> tests/ui/missing_const_for_fn/could_be_const.rs:122:9 + | +LL | / fn deref_ptr_can_be_const(self) -> usize { +LL | | +LL | | unsafe { *self.0 as usize } +LL | | } + | |_________^ + +error: this could be a `const fn` + --> tests/ui/missing_const_for_fn/could_be_const.rs:127:9 + | +LL | / fn deref_copied_val(self) -> usize { +LL | | +LL | | *self.1 as usize +LL | | } + | |_________^ + +error: this could be a `const fn` + --> tests/ui/missing_const_for_fn/could_be_const.rs:138:5 + | +LL | / fn union_access_can_be_const() { +LL | | +LL | | let bar = Bar { val: 1 }; +LL | | let _ = unsafe { bar.val }; +LL | | } + | |_____^ + +error: aborting due to 14 previous errors diff --git a/src/tools/clippy/tests/ui/missing_panics_doc.rs b/src/tools/clippy/tests/ui/missing_panics_doc.rs index 0e1533fc1ab10..b0fa8e9885988 100644 --- a/src/tools/clippy/tests/ui/missing_panics_doc.rs +++ b/src/tools/clippy/tests/ui/missing_panics_doc.rs @@ -191,3 +191,11 @@ fn from_declared_macro_should_lint_at_macrosite() { // Not here. some_macro_that_panics!() } + +pub fn issue_12760() { + const { + if N == 0 { + panic!(); + } + } +} diff --git a/src/tools/clippy/tests/ui/needless_borrows_for_generic_args.fixed b/src/tools/clippy/tests/ui/needless_borrows_for_generic_args.fixed index bd7a9a0b9840d..5478372cbe00f 100644 --- a/src/tools/clippy/tests/ui/needless_borrows_for_generic_args.fixed +++ b/src/tools/clippy/tests/ui/needless_borrows_for_generic_args.fixed @@ -141,8 +141,8 @@ fn main() { let f = |arg| { let loc = "loc".to_owned(); let _ = std::fs::write("x", &env); // Don't lint. In environment - let _ = std::fs::write("x", arg); - let _ = std::fs::write("x", loc); + let _ = std::fs::write("x", &arg); + let _ = std::fs::write("x", &loc); }; let _ = std::fs::write("x", &env); // Don't lint. Borrowed by `f` f(arg); @@ -158,13 +158,13 @@ fn main() { fn f(_: impl Debug) {} let x = X; - f(&x); // Don't lint. Has significant drop + f(&x); // Don't lint, not copy, passed by a reference to a variable } { fn f(_: impl AsRef) {} let x = String::new(); - f(x); + f(&x); } { fn f(_: impl AsRef) {} @@ -299,4 +299,38 @@ fn main() { check_str(&owner.0); // Don't lint. `owner` can't be partially moved because it impl Drop } } + { + #[derive(Debug)] + struct X(Vec); + + fn f(_: impl Debug) {} + + let x = X(vec![]); + f(&x); // Don't lint, makes x unavailable later + } + { + #[derive(Debug)] + struct X; + + impl Drop for X { + fn drop(&mut self) {} + } + + fn f(_: impl Debug) {} + + #[derive(Debug)] + struct Y(X); + + let y = Y(X); + f(&y); // Don't lint. Not copy, passed by a reference to value + } + { + fn f(_: impl AsRef) {} + let x = String::new(); + f(&x); // Don't lint, not a copy, makes it unavailable later + f(String::new()); // Lint, makes no difference + let y = "".to_owned(); + f(&y); // Don't lint + f("".to_owned()); // Lint + } } diff --git a/src/tools/clippy/tests/ui/needless_borrows_for_generic_args.rs b/src/tools/clippy/tests/ui/needless_borrows_for_generic_args.rs index 5cfd4ce30cc54..2643815d939b5 100644 --- a/src/tools/clippy/tests/ui/needless_borrows_for_generic_args.rs +++ b/src/tools/clippy/tests/ui/needless_borrows_for_generic_args.rs @@ -158,7 +158,7 @@ fn main() { fn f(_: impl Debug) {} let x = X; - f(&x); // Don't lint. Has significant drop + f(&x); // Don't lint, not copy, passed by a reference to a variable } { fn f(_: impl AsRef) {} @@ -299,4 +299,38 @@ fn main() { check_str(&owner.0); // Don't lint. `owner` can't be partially moved because it impl Drop } } + { + #[derive(Debug)] + struct X(Vec); + + fn f(_: impl Debug) {} + + let x = X(vec![]); + f(&x); // Don't lint, makes x unavailable later + } + { + #[derive(Debug)] + struct X; + + impl Drop for X { + fn drop(&mut self) {} + } + + fn f(_: impl Debug) {} + + #[derive(Debug)] + struct Y(X); + + let y = Y(X); + f(&y); // Don't lint. Not copy, passed by a reference to value + } + { + fn f(_: impl AsRef) {} + let x = String::new(); + f(&x); // Don't lint, not a copy, makes it unavailable later + f(&String::new()); // Lint, makes no difference + let y = "".to_owned(); + f(&y); // Don't lint + f(&"".to_owned()); // Lint + } } diff --git a/src/tools/clippy/tests/ui/needless_borrows_for_generic_args.stderr b/src/tools/clippy/tests/ui/needless_borrows_for_generic_args.stderr index 83c076f8d863a..fba0755d14b5f 100644 --- a/src/tools/clippy/tests/ui/needless_borrows_for_generic_args.stderr +++ b/src/tools/clippy/tests/ui/needless_borrows_for_generic_args.stderr @@ -50,28 +50,22 @@ LL | let _ = Command::new("ls").args(&["-a", "-l"]).status().unwrap(); | ^^^^^^^^^^^^^ help: change this to: `["-a", "-l"]` error: the borrowed expression implements the required traits - --> tests/ui/needless_borrows_for_generic_args.rs:144:41 - | -LL | let _ = std::fs::write("x", &arg); - | ^^^^ help: change this to: `arg` - -error: the borrowed expression implements the required traits - --> tests/ui/needless_borrows_for_generic_args.rs:145:41 + --> tests/ui/needless_borrows_for_generic_args.rs:247:13 | -LL | let _ = std::fs::write("x", &loc); - | ^^^^ help: change this to: `loc` +LL | foo(&a); + | ^^ help: change this to: `a` error: the borrowed expression implements the required traits - --> tests/ui/needless_borrows_for_generic_args.rs:167:11 + --> tests/ui/needless_borrows_for_generic_args.rs:331:11 | -LL | f(&x); - | ^^ help: change this to: `x` +LL | f(&String::new()); // Lint, makes no difference + | ^^^^^^^^^^^^^^ help: change this to: `String::new()` error: the borrowed expression implements the required traits - --> tests/ui/needless_borrows_for_generic_args.rs:247:13 + --> tests/ui/needless_borrows_for_generic_args.rs:334:11 | -LL | foo(&a); - | ^^ help: change this to: `a` +LL | f(&"".to_owned()); // Lint + | ^^^^^^^^^^^^^^ help: change this to: `"".to_owned()` -error: aborting due to 12 previous errors +error: aborting due to 11 previous errors diff --git a/src/tools/clippy/tests/ui/needless_late_init.stderr b/src/tools/clippy/tests/ui/needless_late_init.stderr index 1695784030d02..ce64861fa40a7 100644 --- a/src/tools/clippy/tests/ui/needless_late_init.stderr +++ b/src/tools/clippy/tests/ui/needless_late_init.stderr @@ -8,10 +8,11 @@ LL | a = "zero"; | = note: `-D clippy::needless-late-init` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::needless_late_init)]` -help: declare `a` here +help: move the declaration `a` here + | +LL ~ +LL ~ let a = "zero"; | -LL | let a = "zero"; - | ~~~~~ error: unneeded late initialization --> tests/ui/needless_late_init.rs:30:5 @@ -22,10 +23,12 @@ LL | let c; LL | b = 1; | ^^^^^ initialised here | -help: declare `b` here +help: move the declaration `b` here + | +LL ~ +LL | let c; +LL ~ let b = 1; | -LL | let b = 1; - | ~~~~~ error: unneeded late initialization --> tests/ui/needless_late_init.rs:31:5 @@ -36,10 +39,12 @@ LL | b = 1; LL | c = 2; | ^^^^^ initialised here | -help: declare `c` here +help: move the declaration `c` here + | +LL ~ +LL | b = 1; +LL ~ let c = 2; | -LL | let c = 2; - | ~~~~~ error: unneeded late initialization --> tests/ui/needless_late_init.rs:35:5 @@ -49,10 +54,11 @@ LL | let d: usize; LL | d = 1; | ^^^^^ initialised here | -help: declare `d` here +help: move the declaration `d` here + | +LL ~ +LL ~ let d: usize = 1; | -LL | let d: usize = 1; - | ~~~~~~~~~~~~ error: unneeded late initialization --> tests/ui/needless_late_init.rs:38:5 @@ -62,10 +68,11 @@ LL | let e; LL | e = format!("{}", d); | ^^^^^^^^^^^^^^^^^^^^ initialised here | -help: declare `e` here +help: move the declaration `e` here + | +LL ~ +LL ~ let e = format!("{}", d); | -LL | let e = format!("{}", d); - | ~~~~~ error: unneeded late initialization --> tests/ui/needless_late_init.rs:43:5 @@ -73,20 +80,17 @@ error: unneeded late initialization LL | let a; | ^^^^^^ | -help: declare `a` here - | -LL | let a = match n { - | +++++++ -help: remove the assignments from the `match` arms +help: move the declaration `a` here and remove the assignments from the `match` arms | +LL ~ +LL | let n = 1; +LL ~ let a = match n { LL ~ 1 => "one", LL | _ => { LL ~ "two" +LL | }, +LL ~ }; | -help: add a semicolon after the `match` expression - | -LL | }; - | + error: unneeded late initialization --> tests/ui/needless_late_init.rs:52:5 @@ -94,20 +98,15 @@ error: unneeded late initialization LL | let b; | ^^^^^^ | -help: declare `b` here - | -LL | let b = if n == 3 { - | +++++++ -help: remove the assignments from the branches +help: move the declaration `b` here and remove the assignments from the branches | +LL ~ +LL ~ let b = if n == 3 { LL ~ "four" LL | } else { LL ~ "five" +LL ~ }; | -help: add a semicolon after the `if` expression - | -LL | }; - | + error: unneeded late initialization --> tests/ui/needless_late_init.rs:59:5 @@ -115,20 +114,16 @@ error: unneeded late initialization LL | let d; | ^^^^^^ | -help: declare `d` here - | -LL | let d = if true { - | +++++++ -help: remove the assignments from the branches +help: move the declaration `d` here and remove the assignments from the branches | +LL ~ +LL ~ let d = if true { +LL | let temp = 5; LL ~ temp LL | } else { LL ~ 15 +LL ~ }; | -help: add a semicolon after the `if` expression - | -LL | }; - | + error: unneeded late initialization --> tests/ui/needless_late_init.rs:67:5 @@ -136,20 +131,15 @@ error: unneeded late initialization LL | let e; | ^^^^^^ | -help: declare `e` here - | -LL | let e = if true { - | +++++++ -help: remove the assignments from the branches +help: move the declaration `e` here and remove the assignments from the branches | +LL ~ +LL ~ let e = if true { LL ~ format!("{} {}", a, b) LL | } else { LL ~ format!("{}", n) +LL ~ }; | -help: add a semicolon after the `if` expression - | -LL | }; - | + error: unneeded late initialization --> tests/ui/needless_late_init.rs:74:5 @@ -157,14 +147,11 @@ error: unneeded late initialization LL | let f; | ^^^^^^ | -help: declare `f` here - | -LL | let f = match 1 { - | +++++++ -help: remove the assignments from the `match` arms +help: move the declaration `f` here and remove the assignments from the `match` arms | -LL - 1 => f = "three", -LL + 1 => "three", +LL ~ +LL ~ let f = match 1 { +LL ~ 1 => "three", | error: unneeded late initialization @@ -173,19 +160,15 @@ error: unneeded late initialization LL | let g: usize; | ^^^^^^^^^^^^^ | -help: declare `g` here - | -LL | let g: usize = if true { - | ++++++++++++++ -help: remove the assignments from the branches - | -LL - g = 5; -LL + 5 +help: move the declaration `g` here and remove the assignments from the branches | -help: add a semicolon after the `if` expression +LL ~ +LL ~ let g: usize = if true { +LL ~ 5 +LL | } else { +LL | panic!(); +LL ~ }; | -LL | }; - | + error: unneeded late initialization --> tests/ui/needless_late_init.rs:88:5 @@ -196,10 +179,12 @@ LL | let y = SignificantDrop; LL | x = 1; | ^^^^^ initialised here | -help: declare `x` here +help: move the declaration `x` here + | +LL ~ +LL | let y = SignificantDrop; +LL ~ let x = 1; | -LL | let x = 1; - | ~~~~~ error: unneeded late initialization --> tests/ui/needless_late_init.rs:92:5 @@ -210,10 +195,12 @@ LL | let y = 1; LL | x = SignificantDrop; | ^^^^^^^^^^^^^^^^^^^ initialised here | -help: declare `x` here +help: move the declaration `x` here + | +LL ~ +LL | let y = 1; +LL ~ let x = SignificantDrop; | -LL | let x = SignificantDrop; - | ~~~~~ error: unneeded late initialization --> tests/ui/needless_late_init.rs:96:5 @@ -224,10 +211,14 @@ LL | let x; LL | x = SignificantDrop; | ^^^^^^^^^^^^^^^^^^^ initialised here | -help: declare `x` here +help: move the declaration `x` here + | +LL ~ +LL | // types that should be considered insignificant + ... +LL | let y = Box::new(4); +LL ~ let x = SignificantDrop; | -LL | let x = SignificantDrop; - | ~~~~~ error: unneeded late initialization --> tests/ui/needless_late_init.rs:115:5 @@ -235,20 +226,17 @@ error: unneeded late initialization LL | let a; | ^^^^^^ | -help: declare `a` here - | -LL | let a = match n { - | +++++++ -help: remove the assignments from the `match` arms +help: move the declaration `a` here and remove the assignments from the `match` arms | +LL ~ +LL | let n = 1; +LL ~ let a = match n { LL ~ 1 => f().await, LL | _ => { LL ~ "two" +LL | }, +LL ~ }; | -help: add a semicolon after the `match` expression - | -LL | }; - | + error: unneeded late initialization --> tests/ui/needless_late_init.rs:132:5 @@ -256,20 +244,17 @@ error: unneeded late initialization LL | let a; | ^^^^^^ | -help: declare `a` here - | -LL | let a = match n { - | +++++++ -help: remove the assignments from the `match` arms +help: move the declaration `a` here and remove the assignments from the `match` arms | +LL ~ +LL | let n = 1; +LL ~ let a = match n { LL ~ 1 => f(), LL | _ => { LL ~ "two" +LL | }, +LL ~ }; | -help: add a semicolon after the `match` expression - | -LL | }; - | + error: aborting due to 16 previous errors diff --git a/src/tools/clippy/tests/ui/no_effect.rs b/src/tools/clippy/tests/ui/no_effect.rs index dabeda72f0c8c..0ea911c343488 100644 --- a/src/tools/clippy/tests/ui/no_effect.rs +++ b/src/tools/clippy/tests/ui/no_effect.rs @@ -1,6 +1,5 @@ #![feature(fn_traits, unboxed_closures)] #![warn(clippy::no_effect_underscore_binding)] -#![allow(dead_code, path_statements)] #![allow( clippy::deref_addrof, clippy::redundant_field_names, @@ -33,7 +32,6 @@ impl Neg for Cout { } } -struct Unit; struct Tuple(i32); struct Struct { field: i32, @@ -42,10 +40,6 @@ enum Enum { Tuple(i32), Struct { field: i32 }, } -struct DropUnit; -impl Drop for DropUnit { - fn drop(&mut self) {} -} struct DropStruct { field: i32, } @@ -117,15 +111,9 @@ impl FnOnce<(&str,)> for GreetStruct3 { fn main() { let s = get_struct(); - let s2 = get_struct(); 0; //~^ ERROR: statement with no effect - //~| NOTE: `-D clippy::no-effect` implied by `-D warnings` - s2; - //~^ ERROR: statement with no effect - Unit; - //~^ ERROR: statement with no effect Tuple(0); //~^ ERROR: statement with no effect Struct { field: 0 }; @@ -192,7 +180,6 @@ fn main() { unsafe { unsafe_fn() }; let _used = get_struct(); let _x = vec![1]; - DropUnit; DropStruct { field: 0 }; DropTuple(0); DropEnum::Tuple(0); diff --git a/src/tools/clippy/tests/ui/no_effect.stderr b/src/tools/clippy/tests/ui/no_effect.stderr index c7c8eecd054bc..48ec997d938cd 100644 --- a/src/tools/clippy/tests/ui/no_effect.stderr +++ b/src/tools/clippy/tests/ui/no_effect.stderr @@ -1,5 +1,5 @@ error: statement with no effect - --> tests/ui/no_effect.rs:122:5 + --> tests/ui/no_effect.rs:115:5 | LL | 0; | ^^ @@ -8,151 +8,139 @@ LL | 0; = help: to override `-D warnings` add `#[allow(clippy::no_effect)]` error: statement with no effect - --> tests/ui/no_effect.rs:125:5 - | -LL | s2; - | ^^^ - -error: statement with no effect - --> tests/ui/no_effect.rs:127:5 - | -LL | Unit; - | ^^^^^ - -error: statement with no effect - --> tests/ui/no_effect.rs:129:5 + --> tests/ui/no_effect.rs:117:5 | LL | Tuple(0); | ^^^^^^^^^ error: statement with no effect - --> tests/ui/no_effect.rs:131:5 + --> tests/ui/no_effect.rs:119:5 | LL | Struct { field: 0 }; | ^^^^^^^^^^^^^^^^^^^^ error: statement with no effect - --> tests/ui/no_effect.rs:133:5 + --> tests/ui/no_effect.rs:121:5 | LL | Struct { ..s }; | ^^^^^^^^^^^^^^^ error: statement with no effect - --> tests/ui/no_effect.rs:135:5 + --> tests/ui/no_effect.rs:123:5 | LL | Union { a: 0 }; | ^^^^^^^^^^^^^^^ error: statement with no effect - --> tests/ui/no_effect.rs:137:5 + --> tests/ui/no_effect.rs:125:5 | LL | Enum::Tuple(0); | ^^^^^^^^^^^^^^^ error: statement with no effect - --> tests/ui/no_effect.rs:139:5 + --> tests/ui/no_effect.rs:127:5 | LL | Enum::Struct { field: 0 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: statement with no effect - --> tests/ui/no_effect.rs:141:5 + --> tests/ui/no_effect.rs:129:5 | LL | 5 + 6; | ^^^^^^ error: statement with no effect - --> tests/ui/no_effect.rs:143:5 + --> tests/ui/no_effect.rs:131:5 | LL | *&42; | ^^^^^ error: statement with no effect - --> tests/ui/no_effect.rs:145:5 + --> tests/ui/no_effect.rs:133:5 | LL | &6; | ^^^ error: statement with no effect - --> tests/ui/no_effect.rs:147:5 + --> tests/ui/no_effect.rs:135:5 | LL | (5, 6, 7); | ^^^^^^^^^^ error: statement with no effect - --> tests/ui/no_effect.rs:149:5 + --> tests/ui/no_effect.rs:137:5 | LL | ..; | ^^^ error: statement with no effect - --> tests/ui/no_effect.rs:151:5 + --> tests/ui/no_effect.rs:139:5 | LL | 5..; | ^^^^ error: statement with no effect - --> tests/ui/no_effect.rs:153:5 + --> tests/ui/no_effect.rs:141:5 | LL | ..5; | ^^^^ error: statement with no effect - --> tests/ui/no_effect.rs:155:5 + --> tests/ui/no_effect.rs:143:5 | LL | 5..6; | ^^^^^ error: statement with no effect - --> tests/ui/no_effect.rs:157:5 + --> tests/ui/no_effect.rs:145:5 | LL | 5..=6; | ^^^^^^ error: statement with no effect - --> tests/ui/no_effect.rs:159:5 + --> tests/ui/no_effect.rs:147:5 | LL | [42, 55]; | ^^^^^^^^^ error: statement with no effect - --> tests/ui/no_effect.rs:161:5 + --> tests/ui/no_effect.rs:149:5 | LL | [42, 55][1]; | ^^^^^^^^^^^^ error: statement with no effect - --> tests/ui/no_effect.rs:163:5 + --> tests/ui/no_effect.rs:151:5 | LL | (42, 55).1; | ^^^^^^^^^^^ error: statement with no effect - --> tests/ui/no_effect.rs:165:5 + --> tests/ui/no_effect.rs:153:5 | LL | [42; 55]; | ^^^^^^^^^ error: statement with no effect - --> tests/ui/no_effect.rs:167:5 + --> tests/ui/no_effect.rs:155:5 | LL | [42; 55][13]; | ^^^^^^^^^^^^^ error: statement with no effect - --> tests/ui/no_effect.rs:170:5 + --> tests/ui/no_effect.rs:158:5 | LL | || x += 5; | ^^^^^^^^^^ error: statement with no effect - --> tests/ui/no_effect.rs:173:5 + --> tests/ui/no_effect.rs:161:5 | LL | FooString { s: s }; | ^^^^^^^^^^^^^^^^^^^ error: binding to `_` prefixed variable with no side-effect - --> tests/ui/no_effect.rs:175:9 + --> tests/ui/no_effect.rs:163:9 | LL | let _unused = 1; | ^^^^^^^ @@ -161,22 +149,22 @@ LL | let _unused = 1; = help: to override `-D warnings` add `#[allow(clippy::no_effect_underscore_binding)]` error: binding to `_` prefixed variable with no side-effect - --> tests/ui/no_effect.rs:178:9 + --> tests/ui/no_effect.rs:166:9 | LL | let _penguin = || println!("Some helpful closure"); | ^^^^^^^^ error: binding to `_` prefixed variable with no side-effect - --> tests/ui/no_effect.rs:180:9 + --> tests/ui/no_effect.rs:168:9 | LL | let _duck = Struct { field: 0 }; | ^^^^^ error: binding to `_` prefixed variable with no side-effect - --> tests/ui/no_effect.rs:182:9 + --> tests/ui/no_effect.rs:170:9 | LL | let _cat = [2, 4, 6, 8][2]; | ^^^^ -error: aborting due to 29 previous errors +error: aborting due to 27 previous errors diff --git a/src/tools/clippy/tests/ui/ptr_arg.rs b/src/tools/clippy/tests/ui/ptr_arg.rs index fcd716f41444f..5d6e488972cdc 100644 --- a/src/tools/clippy/tests/ui/ptr_arg.rs +++ b/src/tools/clippy/tests/ui/ptr_arg.rs @@ -282,7 +282,7 @@ mod issue_9218 { todo!() } - // These two's return types don't use use 'a so it's not okay + // These two's return types don't use 'a so it's not okay fn cow_bad_ret_ty_1<'a>(input: &'a Cow<'a, str>) -> &'static str { //~^ ERROR: using a reference to `Cow` is not recommended todo!() diff --git a/src/tools/clippy/tests/ui/significant_drop_in_scrutinee.rs b/src/tools/clippy/tests/ui/significant_drop_in_scrutinee.rs index 0305d895fc585..7fc89bb95380e 100644 --- a/src/tools/clippy/tests/ui/significant_drop_in_scrutinee.rs +++ b/src/tools/clippy/tests/ui/significant_drop_in_scrutinee.rs @@ -675,4 +675,60 @@ fn should_not_trigger_on_significant_iterator_drop() { } } +// https://github.com/rust-lang/rust-clippy/issues/9072 +fn should_not_trigger_lint_if_place_expr_has_significant_drop() { + let x = Mutex::new(vec![1, 2, 3]); + let x_guard = x.lock().unwrap(); + + match x_guard[0] { + 1 => println!("1!"), + x => println!("{x}"), + } + + match x_guard.len() { + 1 => println!("1!"), + x => println!("{x}"), + } +} + +struct Guard<'a, T>(MutexGuard<'a, T>); + +struct Ref<'a, T>(&'a T); + +impl<'a, T> Guard<'a, T> { + fn guard(&self) -> &MutexGuard { + &self.0 + } + + fn guard_ref(&self) -> Ref> { + Ref(&self.0) + } + + fn take(self) -> Box> { + Box::new(self.0) + } +} + +fn should_not_trigger_for_significant_drop_ref() { + let mutex = Mutex::new(vec![1, 2]); + let guard = Guard(mutex.lock().unwrap()); + + match guard.guard().len() { + 0 => println!("empty"), + _ => println!("not empty"), + } + + match guard.guard_ref().0.len() { + 0 => println!("empty"), + _ => println!("not empty"), + } + + match guard.take().len() { + //~^ ERROR: temporary with significant `Drop` in `match` scrutinee will live until the + //~| NOTE: this might lead to deadlocks or other unexpected behavior + 0 => println!("empty"), + _ => println!("not empty"), + }; +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/significant_drop_in_scrutinee.stderr b/src/tools/clippy/tests/ui/significant_drop_in_scrutinee.stderr index 7d5b1acc7f00a..811bb06552792 100644 --- a/src/tools/clippy/tests/ui/significant_drop_in_scrutinee.stderr +++ b/src/tools/clippy/tests/ui/significant_drop_in_scrutinee.stderr @@ -28,6 +28,9 @@ LL | match s.lock_m().get_the_value() { LL | println!("{}", s.lock_m().get_the_value()); | ---------- another value with significant `Drop` created here ... +LL | println!("{}", s.lock_m().get_the_value()); + | ---------- another value with significant `Drop` created here +... LL | } | - temporary lives until here | @@ -47,6 +50,9 @@ LL | match s.lock_m_m().get_the_value() { LL | println!("{}", s.lock_m().get_the_value()); | ---------- another value with significant `Drop` created here ... +LL | println!("{}", s.lock_m().get_the_value()); + | ---------- another value with significant `Drop` created here +... LL | } | - temporary lives until here | @@ -360,7 +366,7 @@ LL | match s.lock().deref().deref() { | ^^^^^^^^^^^^^^^^^^^^^^^^ ... LL | _ => println!("Value is {}", s.lock().deref()), - | ---------------- another value with significant `Drop` created here + | -------- another value with significant `Drop` created here LL | }; | - temporary lives until here | @@ -378,7 +384,7 @@ LL | match s.lock().deref().deref() { | ^^^^^^^^^^^^^^^^^^^^^^^^ ... LL | matcher => println!("Value is {}", s.lock().deref()), - | ---------------- another value with significant `Drop` created here + | -------- another value with significant `Drop` created here LL | _ => println!("Value was not a match"), LL | }; | - temporary lives until here @@ -499,5 +505,21 @@ LL ~ let value = mutex.lock().unwrap().foo(); LL ~ match value { | -error: aborting due to 26 previous errors +error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression + --> tests/ui/significant_drop_in_scrutinee.rs:726:11 + | +LL | match guard.take().len() { + | ^^^^^^^^^^^^^^^^^^ +... +LL | }; + | - temporary lives until here + | + = note: this might lead to deadlocks or other unexpected behavior +help: try moving the temporary above the match + | +LL ~ let value = guard.take().len(); +LL ~ match value { + | + +error: aborting due to 27 previous errors diff --git a/src/tools/clippy/tests/ui/unnecessary_iter_cloned.fixed b/src/tools/clippy/tests/ui/unnecessary_iter_cloned.fixed index ad0e5fab029ee..2c582c90ba8ce 100644 --- a/src/tools/clippy/tests/ui/unnecessary_iter_cloned.fixed +++ b/src/tools/clippy/tests/ui/unnecessary_iter_cloned.fixed @@ -21,6 +21,8 @@ fn main() { let _ = check_files_ref_mut(&[(FileType::Account, path)]); let _ = check_files_self_and_arg(&[(FileType::Account, path)]); let _ = check_files_mut_path_buf(&[(FileType::Account, std::path::PathBuf::new())]); + + check_mut_iteratee_and_modify_inner_variable(); } // `check_files` and its variants are based on: @@ -138,3 +140,33 @@ fn check_files_mut_path_buf(files: &[(FileType, std::path::PathBuf)]) -> bool { fn get_file_path(_file_type: &FileType) -> Result { Ok(std::path::PathBuf::new()) } + +// Issue 12098 +// https://github.com/rust-lang/rust-clippy/issues/12098 +// no message emits +fn check_mut_iteratee_and_modify_inner_variable() { + struct Test { + list: Vec, + mut_this: bool, + } + + impl Test { + fn list(&self) -> &[String] { + &self.list + } + } + + let mut test = Test { + list: vec![String::from("foo"), String::from("bar")], + mut_this: false, + }; + + for _item in test.list().to_vec() { + println!("{}", _item); + + test.mut_this = true; + { + test.mut_this = true; + } + } +} diff --git a/src/tools/clippy/tests/ui/unnecessary_iter_cloned.rs b/src/tools/clippy/tests/ui/unnecessary_iter_cloned.rs index d3d59c4c70f5c..a28ccd1efef26 100644 --- a/src/tools/clippy/tests/ui/unnecessary_iter_cloned.rs +++ b/src/tools/clippy/tests/ui/unnecessary_iter_cloned.rs @@ -21,6 +21,8 @@ fn main() { let _ = check_files_ref_mut(&[(FileType::Account, path)]); let _ = check_files_self_and_arg(&[(FileType::Account, path)]); let _ = check_files_mut_path_buf(&[(FileType::Account, std::path::PathBuf::new())]); + + check_mut_iteratee_and_modify_inner_variable(); } // `check_files` and its variants are based on: @@ -138,3 +140,33 @@ fn check_files_mut_path_buf(files: &[(FileType, std::path::PathBuf)]) -> bool { fn get_file_path(_file_type: &FileType) -> Result { Ok(std::path::PathBuf::new()) } + +// Issue 12098 +// https://github.com/rust-lang/rust-clippy/issues/12098 +// no message emits +fn check_mut_iteratee_and_modify_inner_variable() { + struct Test { + list: Vec, + mut_this: bool, + } + + impl Test { + fn list(&self) -> &[String] { + &self.list + } + } + + let mut test = Test { + list: vec![String::from("foo"), String::from("bar")], + mut_this: false, + }; + + for _item in test.list().to_vec() { + println!("{}", _item); + + test.mut_this = true; + { + test.mut_this = true; + } + } +} diff --git a/src/tools/clippy/tests/ui/unnecessary_iter_cloned.stderr b/src/tools/clippy/tests/ui/unnecessary_iter_cloned.stderr index 9d3591e0dbfc8..fb98cfddc262f 100644 --- a/src/tools/clippy/tests/ui/unnecessary_iter_cloned.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_iter_cloned.stderr @@ -1,5 +1,5 @@ error: unnecessary use of `copied` - --> tests/ui/unnecessary_iter_cloned.rs:29:22 + --> tests/ui/unnecessary_iter_cloned.rs:31:22 | LL | for (t, path) in files.iter().copied() { | ^^^^^^^^^^^^^^^^^^^^^ @@ -17,7 +17,7 @@ LL + let other = match get_file_path(t) { | error: unnecessary use of `copied` - --> tests/ui/unnecessary_iter_cloned.rs:44:22 + --> tests/ui/unnecessary_iter_cloned.rs:46:22 | LL | for (t, path) in files.iter().copied() { | ^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/useless_attribute.fixed b/src/tools/clippy/tests/ui/useless_attribute.fixed index 81759086f79e4..231fc0a892ad1 100644 --- a/src/tools/clippy/tests/ui/useless_attribute.fixed +++ b/src/tools/clippy/tests/ui/useless_attribute.fixed @@ -86,8 +86,51 @@ mod module { #[rustfmt::skip] #[allow(unused_import_braces)] +#[allow(unused_braces)] use module::{Struct}; fn main() { test_indented_attr(); } + +// Regression test for https://github.com/rust-lang/rust-clippy/issues/4467 +#[allow(dead_code)] +use std::collections as puppy_doggy; + +// Regression test for https://github.com/rust-lang/rust-clippy/issues/11595 +pub mod hidden_glob_reexports { + #![allow(unreachable_pub)] + + mod my_prelude { + pub struct MyCoolTypeInternal; + pub use MyCoolTypeInternal as MyCoolType; + } + + mod my_uncool_type { + pub(crate) struct MyUncoolType; + } + + // This exports `MyCoolType`. + pub use my_prelude::*; + + // This hides `my_prelude::MyCoolType`. + #[allow(hidden_glob_reexports)] + use my_uncool_type::MyUncoolType as MyCoolType; +} + +// Regression test for https://github.com/rust-lang/rust-clippy/issues/10878 +pub mod ambiguous_glob_exports { + #![allow(unreachable_pub)] + + mod my_prelude { + pub struct MyType; + } + + mod my_type { + pub struct MyType; + } + + #[allow(ambiguous_glob_reexports)] + pub use my_prelude::*; + pub use my_type::*; +} diff --git a/src/tools/clippy/tests/ui/useless_attribute.rs b/src/tools/clippy/tests/ui/useless_attribute.rs index 59a9dcf093bca..8dfcd2110a4bb 100644 --- a/src/tools/clippy/tests/ui/useless_attribute.rs +++ b/src/tools/clippy/tests/ui/useless_attribute.rs @@ -86,8 +86,51 @@ mod module { #[rustfmt::skip] #[allow(unused_import_braces)] +#[allow(unused_braces)] use module::{Struct}; fn main() { test_indented_attr(); } + +// Regression test for https://github.com/rust-lang/rust-clippy/issues/4467 +#[allow(dead_code)] +use std::collections as puppy_doggy; + +// Regression test for https://github.com/rust-lang/rust-clippy/issues/11595 +pub mod hidden_glob_reexports { + #![allow(unreachable_pub)] + + mod my_prelude { + pub struct MyCoolTypeInternal; + pub use MyCoolTypeInternal as MyCoolType; + } + + mod my_uncool_type { + pub(crate) struct MyUncoolType; + } + + // This exports `MyCoolType`. + pub use my_prelude::*; + + // This hides `my_prelude::MyCoolType`. + #[allow(hidden_glob_reexports)] + use my_uncool_type::MyUncoolType as MyCoolType; +} + +// Regression test for https://github.com/rust-lang/rust-clippy/issues/10878 +pub mod ambiguous_glob_exports { + #![allow(unreachable_pub)] + + mod my_prelude { + pub struct MyType; + } + + mod my_type { + pub struct MyType; + } + + #[allow(ambiguous_glob_reexports)] + pub use my_prelude::*; + pub use my_type::*; +} diff --git a/src/tools/clippy/tests/ui/while_float.rs b/src/tools/clippy/tests/ui/while_float.rs new file mode 100644 index 0000000000000..a3b0618948e6b --- /dev/null +++ b/src/tools/clippy/tests/ui/while_float.rs @@ -0,0 +1,14 @@ +#[deny(clippy::while_float)] +fn main() { + let mut x = 0.0_f32; + while x < 42.0_f32 { + x += 0.5; + } + while x < 42.0 { + x += 1.0; + } + let mut x = 0; + while x < 42 { + x += 1; + } +} diff --git a/src/tools/clippy/tests/ui/while_float.stderr b/src/tools/clippy/tests/ui/while_float.stderr new file mode 100644 index 0000000000000..b8e934b97c6c4 --- /dev/null +++ b/src/tools/clippy/tests/ui/while_float.stderr @@ -0,0 +1,20 @@ +error: while condition comparing floats + --> tests/ui/while_float.rs:4:11 + | +LL | while x < 42.0_f32 { + | ^^^^^^^^^^^^ + | +note: the lint level is defined here + --> tests/ui/while_float.rs:1:8 + | +LL | #[deny(clippy::while_float)] + | ^^^^^^^^^^^^^^^^^^^ + +error: while condition comparing floats + --> tests/ui/while_float.rs:7:11 + | +LL | while x < 42.0 { + | ^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/util/gh-pages/script.js b/src/tools/clippy/util/gh-pages/script.js index c63edd5bf7098..7fd779fe9a46e 100644 --- a/src/tools/clippy/util/gh-pages/script.js +++ b/src/tools/clippy/util/gh-pages/script.js @@ -406,7 +406,7 @@ } // Search by id - if (lint.id.indexOf(searchStr.replace("-", "_")) !== -1) { + if (lint.id.indexOf(searchStr.replaceAll("-", "_")) !== -1) { return true; } diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs index afbcc3e92bcb2..7ff45edd4b26b 100644 --- a/src/tools/compiletest/src/common.rs +++ b/src/tools/compiletest/src/common.rs @@ -250,7 +250,7 @@ pub struct Config { /// Only run tests that match these filters pub filters: Vec, - /// Skip tests tests matching these substrings. Corresponds to + /// Skip tests matching these substrings. Corresponds to /// `test::TestOpts::skip`. `filter_exact` does not apply to these flags. pub skip: Vec, @@ -381,7 +381,7 @@ pub struct Config { /// Whether to rerun tests even if the inputs are unchanged. pub force_rerun: bool, - /// Only rerun the tests that result has been modified accoring to Git status + /// Only rerun the tests that result has been modified according to Git status pub only_modified: bool, pub target_cfgs: OnceLock, diff --git a/src/tools/compiletest/src/lib.rs b/src/tools/compiletest/src/lib.rs index 2e45caec46cd6..99bde107f3a47 100644 --- a/src/tools/compiletest/src/lib.rs +++ b/src/tools/compiletest/src/lib.rs @@ -950,7 +950,7 @@ fn is_android_gdb_target(target: &str) -> bool { ) } -/// Returns `true` if the given target is a MSVC target for the purpouses of CDB testing. +/// Returns `true` if the given target is a MSVC target for the purposes of CDB testing. fn is_pc_windows_msvc_target(target: &str) -> bool { target.ends_with("-pc-windows-msvc") } diff --git a/src/tools/jsondoclint/src/validator.rs b/src/tools/jsondoclint/src/validator.rs index 9e08f7e5f9be8..904c2b614f3f0 100644 --- a/src/tools/jsondoclint/src/validator.rs +++ b/src/tools/jsondoclint/src/validator.rs @@ -21,7 +21,7 @@ const LOCAL_CRATE_ID: u32 = 0; /// it is well formed. This involves calling `check_*` functions on /// fields of that item, and `add_*` functions on [`Id`]s. /// - `add_*`: These add an [`Id`] to the worklist, after validating it to check if -/// the `Id` is a kind expected in this suituation. +/// the `Id` is a kind expected in this situation. #[derive(Debug)] pub struct Validator<'a> { pub(crate) errs: Vec, diff --git a/src/tools/lld-wrapper/src/main.rs b/src/tools/lld-wrapper/src/main.rs index da94e686f3824..79e279a8614b9 100644 --- a/src/tools/lld-wrapper/src/main.rs +++ b/src/tools/lld-wrapper/src/main.rs @@ -1,6 +1,6 @@ //! Script to invoke the bundled rust-lld with the correct flavor. //! -//! lld supports multiple command line interfaces. If `-flavor ` are passed as the first +//! `lld` supports multiple command line interfaces. If `-flavor ` are passed as the first //! two arguments the `` command line interface is used to process the remaining arguments. //! If no `-flavor` argument is present the flavor is determined by the executable name. //! diff --git a/src/tools/miri/cargo-miri/src/util.rs b/src/tools/miri/cargo-miri/src/util.rs index d99957d9c2208..28a824e54f65c 100644 --- a/src/tools/miri/cargo-miri/src/util.rs +++ b/src/tools/miri/cargo-miri/src/util.rs @@ -269,7 +269,7 @@ pub fn get_target_dir(meta: &Metadata) -> PathBuf { output } -/// Determines where the sysroot of this exeuction is +/// Determines where the sysroot of this execution is /// /// Either in a user-specified spot by an envar, or in a default cache location. pub fn get_sysroot_dir() -> PathBuf { diff --git a/src/tools/miri/miri-script/src/commands.rs b/src/tools/miri/miri-script/src/commands.rs index 8061f99b5d653..be0fdbc177a52 100644 --- a/src/tools/miri/miri-script/src/commands.rs +++ b/src/tools/miri/miri-script/src/commands.rs @@ -253,7 +253,7 @@ impl Command { cmd!(sh, "git fetch http://localhost:{JOSH_PORT}/rust-lang/rust.git@{commit}{JOSH_FILTER}.git") .run() .map_err(|e| { - // Try to un-do the previous `git commit`, to leave the repo in the state we found it it. + // Try to un-do the previous `git commit`, to leave the repo in the state we found it. cmd!(sh, "git reset --hard HEAD^") .run() .expect("FAILED to clean up again after failed `git fetch`, sorry for that"); diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 207ef6c5de744..01580139be9de 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -6579ed89f0fcc26da71afdd11d30d63f6f812a0a +b54dd08a84f3c07efbc2aaf63c3df219ae680a03 diff --git a/src/tools/miri/src/borrow_tracker/stacked_borrows/stack.rs b/src/tools/miri/src/borrow_tracker/stacked_borrows/stack.rs index 55ff09c53fede..3b6c29b5eb154 100644 --- a/src/tools/miri/src/borrow_tracker/stacked_borrows/stack.rs +++ b/src/tools/miri/src/borrow_tracker/stacked_borrows/stack.rs @@ -9,7 +9,7 @@ use crate::borrow_tracker::{ }; use crate::ProvenanceExtra; -/// Exactly what cache size we should use is a difficult tradeoff. There will always be some +/// Exactly what cache size we should use is a difficult trade-off. There will always be some /// workload which has a `BorTag` working set which exceeds the size of the cache, and ends up /// falling back to linear searches of the borrow stack very often. /// The cost of making this value too large is that the loop in `Stack::insert` which ensures the diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs index b9f0b5bc17a2c..8abc8530f7c46 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs @@ -390,7 +390,7 @@ struct DisplayFmtWrapper { warning_text: S, } -/// Formating of the permissions on each range. +/// Formatting of the permissions on each range. /// /// Example: /// ```rust,ignore (private type) @@ -422,7 +422,7 @@ struct DisplayFmtPermission { range_sep: S, } -/// Formating of the tree structure. +/// Formatting of the tree structure. /// /// Example: /// ```rust,ignore (private type) @@ -487,7 +487,7 @@ struct DisplayFmtAccess { meh: S, } -/// All parameters to determine how the tree is formated. +/// All parameters to determine how the tree is formatted. struct DisplayFmt { wrapper: DisplayFmtWrapper, perm: DisplayFmtPermission, 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 bec51c7cdf2eb..fb3a4c8dad91c 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs @@ -17,6 +17,7 @@ enum PermissionPriv { /// is relevant /// - `conflicted` is set on foreign reads, /// - `conflicted` must not be set on child writes (there is UB otherwise). + /// /// This is so that the behavior of `Reserved` adheres to the rules of `noalias`: /// - foreign-read then child-write is UB due to `conflicted`, /// - child-write then foreign-read is UB since child-write will activate and then @@ -202,7 +203,7 @@ impl Permission { Self { inner: Frozen } } - /// Default initial permission of the root of a new tre at out-of-bounds positions. + /// Default initial permission of the root of a new tree at out-of-bounds positions. /// Must *only* be used for the root, this is not in general an "initial" permission! pub fn new_disabled() -> Self { Self { inner: Disabled } @@ -339,15 +340,15 @@ pub mod diagnostics { /// This function assumes that its arguments apply to the same location /// and that they were obtained during a normal execution. It will panic otherwise. /// - all transitions involved in `self` and `err` should be increasing - /// (Reserved < Active < Frozen < Disabled); + /// (Reserved < Active < Frozen < Disabled); /// - between `self` and `err` the permission should also be increasing, - /// so all permissions inside `err` should be greater than `self.1`; + /// so all permissions inside `err` should be greater than `self.1`; /// - `Active` and `Reserved(conflicted=false)` cannot cause an error - /// due to insufficient permissions, so `err` cannot be a `ChildAccessForbidden(_)` - /// of either of them; + /// due to insufficient permissions, so `err` cannot be a `ChildAccessForbidden(_)` + /// of either of them; /// - `err` should not be `ProtectedDisabled(Disabled)`, because the protected - /// tag should not have been `Disabled` in the first place (if this occurs it means - /// we have unprotected tags that become protected) + /// tag should not have been `Disabled` in the first place (if this occurs it means + /// we have unprotected tags that become protected) pub(in super::super) fn is_relevant(&self, err: TransitionError) -> bool { // NOTE: `super::super` is the visibility of `TransitionError` assert!(self.is_possible()); diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/tree/tests.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/tree/tests.rs index 6777f41ac2d0d..73717014e8995 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/tree/tests.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/tree/tests.rs @@ -230,6 +230,7 @@ mod spurious_read { /// - any access to the same location /// - end of one of them being protected /// - a retag that would change their relative position + /// /// The type `TestEvent` models these kinds of events. /// /// In order to prevent `x` or `y` from losing their protector, @@ -483,7 +484,7 @@ mod spurious_read { /// that causes UB in the target but not in the source. /// This implementation simply explores the reachable space /// by all sequences of `TestEvent`. - /// This function can be instanciated with `RetX` and `RetY` + /// This function can be instantiated with `RetX` and `RetY` /// among `NoRet` or `AllowRet` to resp. forbid/allow `x`/`y` to lose their /// protector. fn distinguishable(&self, other: &Self) -> bool diff --git a/src/tools/miri/src/concurrency/data_race.rs b/src/tools/miri/src/concurrency/data_race.rs index f2bec972b18b2..3e8703d5cfc89 100644 --- a/src/tools/miri/src/concurrency/data_race.rs +++ b/src/tools/miri/src/concurrency/data_race.rs @@ -1106,9 +1106,9 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> { /// Temporarily allow data-races to occur. This should only be used in /// one of these cases: /// - One of the appropriate `validate_atomic` functions will be called to - /// to treat a memory access as atomic. + /// treat a memory access as atomic. /// - The memory being accessed should be treated as internal state, that - /// cannot be accessed by the interpreted program. + /// cannot be accessed by the interpreted program. /// - Execution of the interpreted program execution has halted. #[inline] fn allow_data_races_ref(&self, op: impl FnOnce(&MiriInterpCx<'mir, 'tcx>) -> R) -> R { diff --git a/src/tools/miri/src/concurrency/thread.rs b/src/tools/miri/src/concurrency/thread.rs index 6953ce81c5e82..24e2c25385236 100644 --- a/src/tools/miri/src/concurrency/thread.rs +++ b/src/tools/miri/src/concurrency/thread.rs @@ -111,7 +111,7 @@ pub enum BlockReason { Condvar(CondvarId), /// Blocked on a reader-writer lock. RwLock(RwLockId), - /// Blocled on a Futex variable. + /// Blocked on a Futex variable. Futex { addr: u64 }, /// Blocked on an InitOnce. InitOnce(InitOnceId), diff --git a/src/tools/miri/src/concurrency/weak_memory.rs b/src/tools/miri/src/concurrency/weak_memory.rs index 574962c48d43e..8c71eeb27aa64 100644 --- a/src/tools/miri/src/concurrency/weak_memory.rs +++ b/src/tools/miri/src/concurrency/weak_memory.rs @@ -48,7 +48,7 @@ //! One consequence of this difference is that safe/sound Rust allows for more operations on atomic locations //! than the C++20 atomic API was intended to allow, such as non-atomically accessing //! a previously atomically accessed location, or accessing previously atomically accessed locations with a differently sized operation -//! (such as accessing the top 16 bits of an AtomicU32). These scenarios are generally undiscussed in formalisations of C++ memory model. +//! (such as accessing the top 16 bits of an AtomicU32). These scenarios are generally undiscussed in formalizations of C++ memory model. //! In Rust, these operations can only be done through a `&mut AtomicFoo` reference or one derived from it, therefore these operations //! can only happen after all previous accesses on the same locations. This implementation is adapted to allow these operations. //! A mixed atomicity read that races with writes, or a write that races with reads or writes will still cause UBs to be thrown. diff --git a/src/tools/miri/src/intrinsics/simd.rs b/src/tools/miri/src/intrinsics/simd.rs index 4cde364fbc47c..e178187ffa621 100644 --- a/src/tools/miri/src/intrinsics/simd.rs +++ b/src/tools/miri/src/intrinsics/simd.rs @@ -42,6 +42,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { | "flog2" | "flog10" | "ctlz" + | "ctpop" | "cttz" | "bswap" | "bitreverse" @@ -68,6 +69,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { "round" => Op::Round(rustc_apfloat::Round::NearestTiesToAway), "trunc" => Op::Round(rustc_apfloat::Round::TowardZero), "ctlz" => Op::Numeric(sym::ctlz), + "ctpop" => Op::Numeric(sym::ctpop), "cttz" => Op::Numeric(sym::cttz), "bswap" => Op::Numeric(sym::bswap), "bitreverse" => Op::Numeric(sym::bitreverse), diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs index 54770873b8472..cbf02d701bcf5 100644 --- a/src/tools/miri/src/machine.rs +++ b/src/tools/miri/src/machine.rs @@ -205,11 +205,11 @@ pub enum Provenance { /// whether *some* exposed pointer could have done what we want to do, and if the answer is yes /// then we allow the access. This allows too much code in two ways: /// - The same wildcard pointer can "take the role" of multiple different exposed pointers on - /// subsequenct memory accesses. + /// subsequent memory accesses. /// - In the aliasing model, we don't just have to know the borrow tag of the pointer used for /// the access, we also have to update the aliasing state -- and that update can be very /// different depending on which borrow tag we pick! Stacked Borrows has support for this by - /// switching to a stack that is only approximately known, i.e. we overapproximate the effect + /// switching to a stack that is only approximately known, i.e. we over-approximate the effect /// of using *any* exposed pointer for this access, and only keep information about the borrow /// stack that would be true with all possible choices. Wildcard, diff --git a/src/tools/miri/src/shims/unix/mem.rs b/src/tools/miri/src/shims/unix/mem.rs index 38e689a3c3419..89d04bb737ef0 100644 --- a/src/tools/miri/src/shims/unix/mem.rs +++ b/src/tools/miri/src/shims/unix/mem.rs @@ -11,7 +11,7 @@ //! calls to munmap, but for a very different reason. In principle, according to the man pages, it //! is possible to unmap arbitrary regions of address space. But in a high-level language like Rust //! this amounts to partial deallocation, which LLVM does not support. So any attempt to call our -//! munmap shim which would partily unmap a region of address space previously mapped by mmap will +//! munmap shim which would partially unmap a region of address space previously mapped by mmap will //! report UB. use crate::*; diff --git a/src/tools/miri/src/shims/unix/sync.rs b/src/tools/miri/src/shims/unix/sync.rs index f24f279ab0f9d..0ba03d4ab7805 100644 --- a/src/tools/miri/src/shims/unix/sync.rs +++ b/src/tools/miri/src/shims/unix/sync.rs @@ -390,7 +390,7 @@ fn reacquire_cond_mutex<'mir, 'tcx: 'mir>( Ok(()) } -/// After a thread waiting on a condvar was signalled: +/// After a thread waiting on a condvar was signaled: /// Reacquire the conditional variable and remove the timeout callback if any /// was registered. fn post_cond_signal<'mir, 'tcx: 'mir>( diff --git a/src/tools/miri/src/shims/x86/mod.rs b/src/tools/miri/src/shims/x86/mod.rs index 1f7138ca338c7..48b7222917b51 100644 --- a/src/tools/miri/src/shims/x86/mod.rs +++ b/src/tools/miri/src/shims/x86/mod.rs @@ -757,7 +757,7 @@ fn int_abs<'tcx>( Ok(()) } -/// Splits `op` (which must be a SIMD vector) into 128-bit chuncks. +/// Splits `op` (which must be a SIMD vector) into 128-bit chunks. /// /// Returns a tuple where: /// * The first element is the number of 128-bit chunks (let's call it `N`). @@ -788,7 +788,7 @@ fn split_simd_to_128bit_chunks<'tcx, P: Projectable<'tcx, Provenance>>( Ok((num_chunks, items_per_chunk, chunked_op)) } -/// Horizontaly performs `which` operation on adjacent values of +/// Horizontally performs `which` operation on adjacent values of /// `left` and `right` SIMD vectors and stores the result in `dest`. /// "Horizontal" means that the i-th output element is calculated /// from the elements 2*i and 2*i+1 of the concatenation of `left` and @@ -1256,7 +1256,7 @@ fn packusdw<'tcx>( /// Negates elements from `left` when the corresponding element in /// `right` is negative. If an element from `right` is zero, zero -/// is writen to the corresponding output element. +/// is written to the corresponding output element. /// In other words, multiplies `left` with `right.signum()`. fn psign<'tcx>( this: &mut crate::MiriInterpCx<'_, 'tcx>, diff --git a/src/tools/miri/tests/fail/shims/backtrace/bad-backtrace-decl.rs b/src/tools/miri/tests/fail/shims/backtrace/bad-backtrace-decl.rs index 97a70103e6461..a20539ee7c708 100644 --- a/src/tools/miri/tests/fail/shims/backtrace/bad-backtrace-decl.rs +++ b/src/tools/miri/tests/fail/shims/backtrace/bad-backtrace-decl.rs @@ -5,7 +5,7 @@ extern "Rust" { fn main() { let frames = unsafe { miri_get_backtrace(0) }; - for frame in frames.into_iter() { + for frame in frames.iter() { unsafe { miri_resolve_frame(*frame, 0); //~ ERROR: Undefined Behavior: bad declaration of miri_resolve_frame - should return a struct with 5 fields } diff --git a/src/tools/miri/tests/pass/backtrace/backtrace-api-v0.rs b/src/tools/miri/tests/pass/backtrace/backtrace-api-v0.rs index 8d3173da400f8..3fff7921aff77 100644 --- a/src/tools/miri/tests/pass/backtrace/backtrace-api-v0.rs +++ b/src/tools/miri/tests/pass/backtrace/backtrace-api-v0.rs @@ -27,7 +27,7 @@ fn func_d() -> Box<[*mut ()]> { fn main() { let mut seen_main = false; let frames = func_a(); - for frame in frames.into_iter() { + for frame in frames.iter() { let miri_frame = unsafe { miri_resolve_frame(*frame, 0) }; let name = String::from_utf8(miri_frame.name.into()).unwrap(); let filename = String::from_utf8(miri_frame.filename.into()).unwrap(); diff --git a/src/tools/miri/tests/pass/backtrace/backtrace-api-v1.rs b/src/tools/miri/tests/pass/backtrace/backtrace-api-v1.rs index ad05271ca519b..a3060abc39402 100644 --- a/src/tools/miri/tests/pass/backtrace/backtrace-api-v1.rs +++ b/src/tools/miri/tests/pass/backtrace/backtrace-api-v1.rs @@ -32,7 +32,7 @@ fn func_d() -> Box<[*mut ()]> { fn main() { let mut seen_main = false; let frames = func_a(); - for frame in frames.into_iter() { + for frame in frames.iter() { let miri_frame = unsafe { miri_resolve_frame(*frame, 1) }; let mut name = vec![0; miri_frame.name_len]; diff --git a/src/tools/miri/tests/pass/intrinsics/portable-simd.rs b/src/tools/miri/tests/pass/intrinsics/portable-simd.rs index 1fc713d48dcc4..248a57d68504d 100644 --- a/src/tools/miri/tests/pass/intrinsics/portable-simd.rs +++ b/src/tools/miri/tests/pass/intrinsics/portable-simd.rs @@ -505,6 +505,21 @@ fn simd_intrinsics() { assert!(simd_reduce_all(i32x4::splat(-1))); assert!(!simd_reduce_all(i32x2::from_array([0, -1]))); + assert_eq!( + simd_ctlz(i32x4::from_array([0, i32::MAX, i32::MIN, -1_i32])), + i32x4::from_array([32, 1, 0, 0]) + ); + + assert_eq!( + simd_ctpop(i32x4::from_array([0, i32::MAX, i32::MIN, -1_i32])), + i32x4::from_array([0, 31, 1, 32]) + ); + + assert_eq!( + simd_cttz(i32x4::from_array([0, i32::MAX, i32::MIN, -1_i32])), + i32x4::from_array([32, 0, 31, 0]) + ); + assert_eq!( simd_select(i8x4::from_array([0, -1, -1, 0]), a, b), i32x4::from_array([1, 10, 10, 4]) diff --git a/src/tools/opt-dist/Cargo.toml b/src/tools/opt-dist/Cargo.toml index 1ff410e723afe..88e8640d56abb 100644 --- a/src/tools/opt-dist/Cargo.toml +++ b/src/tools/opt-dist/Cargo.toml @@ -13,8 +13,6 @@ humansize = "2" sysinfo = { version = "0.30", default-features = false } fs_extra = "1" camino = "1" -reqwest = { version = "0.11", features = ["blocking"] } -zip = { version = "0.6", default-features = false, features = ["deflate"] } tar = "0.4" xz = { version = "0.1", package = "xz2" } serde = { version = "1", features = ["derive"] } diff --git a/src/tools/opt-dist/src/main.rs b/src/tools/opt-dist/src/main.rs index bd0a3815855d5..a709076f24580 100644 --- a/src/tools/opt-dist/src/main.rs +++ b/src/tools/opt-dist/src/main.rs @@ -3,10 +3,7 @@ use anyhow::Context; use camino::{Utf8Path, Utf8PathBuf}; use clap::Parser; use log::LevelFilter; -use std::io::Cursor; -use std::time::Duration; use utils::io; -use zip::ZipArchive; use crate::environment::{Environment, EnvironmentBuilder}; use crate::exec::{cmd, Bootstrap}; @@ -17,9 +14,9 @@ use crate::training::{ rustc_benchmarks, }; use crate::utils::artifact_size::print_binary_sizes; -use crate::utils::io::{copy_directory, move_directory, reset_directory}; +use crate::utils::io::{copy_directory, reset_directory}; use crate::utils::{ - clear_llvm_files, format_env_variables, print_free_disk_space, retry_action, with_log_group, + clear_llvm_files, format_env_variables, print_free_disk_space, with_log_group, write_timer_to_summary, }; @@ -69,7 +66,12 @@ enum EnvironmentCmd { #[arg(long, default_value = "opt-artifacts")] artifact_dir: Utf8PathBuf, - /// Checkout directory of `rustc-perf`, it will be fetched automatically if unspecified. + /// Checkout directory of `rustc-perf`. + /// + /// If unspecified, defaults to the rustc-perf submodule in the rustc checkout dir + /// (`src/tools/rustc-perf`), which should have been initialized when building this tool. + // FIXME: Move update_submodule into build_helper, that way we can also ensure the submodule + // is updated when _running_ opt-dist, rather than building. #[arg(long)] rustc_perf_checkout_dir: Option, @@ -146,8 +148,6 @@ fn create_environment(args: Args) -> anyhow::Result<(Environment, Vec)> .host_llvm_dir(Utf8PathBuf::from("/rustroot")) .artifact_dir(Utf8PathBuf::from("/tmp/tmp-multistage/opt-artifacts")) .build_dir(checkout_dir.join("obj")) - // /tmp/rustc-perf comes from the x64 dist Dockerfile - .prebuilt_rustc_perf(Some(Utf8PathBuf::from("/tmp/rustc-perf"))) .shared_llvm(true) .use_bolt(true) .skipped_tests(vec![ @@ -191,9 +191,12 @@ fn execute_pipeline( ) -> anyhow::Result<()> { reset_directory(&env.artifact_dir())?; - with_log_group("Building rustc-perf", || match env.prebuilt_rustc_perf() { - Some(dir) => copy_rustc_perf(env, &dir), - None => download_rustc_perf(env), + with_log_group("Building rustc-perf", || { + let rustc_perf_checkout_dir = match env.prebuilt_rustc_perf() { + Some(dir) => dir, + None => env.checkout_path().join("src").join("tools").join("rustc-perf"), + }; + copy_rustc_perf(env, &rustc_perf_checkout_dir) })?; // Stage 1: Build PGO instrumented rustc @@ -409,36 +412,6 @@ fn copy_rustc_perf(env: &Environment, dir: &Utf8Path) -> anyhow::Result<()> { build_rustc_perf(env) } -// Download and build rustc-perf into the given environment. -fn download_rustc_perf(env: &Environment) -> anyhow::Result<()> { - reset_directory(&env.rustc_perf_dir())?; - - // FIXME: add some mechanism for synchronization of this commit SHA with - // Linux (which builds rustc-perf in a Dockerfile) - // rustc-perf version from 2023-10-22 - const PERF_COMMIT: &str = "4f313add609f43e928e98132358e8426ed3969ae"; - - let url = format!("https://ci-mirrors.rust-lang.org/rustc/rustc-perf-{PERF_COMMIT}.zip"); - let client = reqwest::blocking::Client::builder() - .timeout(Duration::from_secs(60 * 2)) - .connect_timeout(Duration::from_secs(60 * 2)) - .build()?; - let response = retry_action( - || Ok(client.get(&url).send()?.error_for_status()?.bytes()?.to_vec()), - "Download rustc-perf archive", - 5, - )?; - - let mut archive = ZipArchive::new(Cursor::new(response))?; - archive.extract(env.rustc_perf_dir())?; - move_directory( - &env.rustc_perf_dir().join(format!("rustc-perf-{PERF_COMMIT}")), - &env.rustc_perf_dir(), - )?; - - build_rustc_perf(env) -} - fn build_rustc_perf(env: &Environment) -> anyhow::Result<()> { cmd(&[env.cargo_stage_0().as_str(), "build", "-p", "collector"]) .workdir(&env.rustc_perf_dir()) diff --git a/src/tools/publish_toolstate.py b/src/tools/publish_toolstate.py index f9421117eaa2e..860d21876de0f 100755 --- a/src/tools/publish_toolstate.py +++ b/src/tools/publish_toolstate.py @@ -3,8 +3,7 @@ # This script computes the new "current" toolstate for the toolstate repo (not to be # confused with publishing the test results, which happens in `src/bootstrap/toolstate.rs`). -# It gets called from `src/ci/publish_toolstate.sh` when a new commit lands on `master` -# (i.e., after it passed all checks on `auto`). +# It gets called from `src/ci/publish_toolstate.sh` at the end of an `auto` build. from __future__ import print_function diff --git a/src/tools/rust-analyzer/.git-blame-ignore-revs b/src/tools/rust-analyzer/.git-blame-ignore-revs index a302e23781ae9..d5951a9420916 100644 --- a/src/tools/rust-analyzer/.git-blame-ignore-revs +++ b/src/tools/rust-analyzer/.git-blame-ignore-revs @@ -6,3 +6,10 @@ # prettier format f247090558c9ba3c551566eae5882b7ca865225f + +# subtree syncs +932d85b52946d917deab2c23ead552f7f713b828 +3e358a6827d83e8d6473913a5e304734aadfed04 +9d2cb42a413e51deb50b36794a2e1605381878fc +f532576ac53ddcc666bc8d59e0b6437065e2f599 +c48062fe2ab9a2d913d1985a6b0aec4bf936bfc1 diff --git a/src/tools/rust-analyzer/.github/workflows/ci.yaml b/src/tools/rust-analyzer/.github/workflows/ci.yaml index a10345a7060a0..87a1729d2b418 100644 --- a/src/tools/rust-analyzer/.github/workflows/ci.yaml +++ b/src/tools/rust-analyzer/.github/workflows/ci.yaml @@ -63,15 +63,15 @@ jobs: - name: Install Rust toolchain run: | rustup update --no-self-update ${{ env.RUST_CHANNEL }} - rustup component add --toolchain ${{ env.RUST_CHANNEL }} rustfmt rust-src rustup default ${{ env.RUST_CHANNEL }} + rustup component add --toolchain ${{ env.RUST_CHANNEL }} rustfmt rust-src # https://github.com/actions-rust-lang/setup-rust-toolchain/blob/main/rust.json - name: Install Rust Problem Matcher if: matrix.os == 'ubuntu-latest' run: echo "::add-matcher::.github/rust.json" - name: Cache Dependencies - uses: Swatinem/rust-cache@640a22190e7a783d4c409684cea558f081f92012 + uses: Swatinem/rust-cache@9bdad043e88c75890e36ad3bbc8d27f0090dd609 with: key: ${{ env.RUST_CHANNEL }} @@ -140,7 +140,7 @@ jobs: rustup target add ${{ env.targets }} ${{ env.targets_ide }} - name: Cache Dependencies - uses: Swatinem/rust-cache@640a22190e7a783d4c409684cea558f081f92012 + uses: Swatinem/rust-cache@9bdad043e88c75890e36ad3bbc8d27f0090dd609 - name: Check run: | diff --git a/src/tools/rust-analyzer/.github/workflows/metrics.yaml b/src/tools/rust-analyzer/.github/workflows/metrics.yaml index b6cd4a795a8d6..a4146d602185e 100644 --- a/src/tools/rust-analyzer/.github/workflows/metrics.yaml +++ b/src/tools/rust-analyzer/.github/workflows/metrics.yaml @@ -11,34 +11,21 @@ env: RUSTUP_MAX_RETRIES: 10 jobs: - setup_cargo: + build_metrics: if: github.repository == 'rust-lang/rust-analyzer' runs-on: ubuntu-latest + steps: - name: Install Rust toolchain run: | rustup update --no-self-update stable - rustup component add rustfmt rust-src rustup default stable - - name: Cache cargo - uses: actions/cache@v4 - with: - path: | - ~/.cargo/bin/ - ~/.cargo/registry/index/ - ~/.cargo/registry/cache/ - ~/.cargo/git/db/ - key: ${{ runner.os }}-cargo-${{ github.sha }} + rustup component add --toolchain stable rust-src - build_metrics: - runs-on: ubuntu-latest - needs: setup_cargo - - steps: - name: Checkout repository uses: actions/checkout@v4 - - name: Restore cargo cache + - name: Cache cargo uses: actions/cache@v4 with: path: | @@ -69,22 +56,18 @@ jobs: matrix: names: [self, ripgrep-13.0.0, webrender-2022, diesel-1.4.8, hyper-0.14.18] runs-on: ubuntu-latest - needs: [setup_cargo, build_metrics] + needs: build_metrics steps: + - name: Install Rust toolchain + run: | + rustup update --no-self-update stable + rustup default stable + rustup component add --toolchain stable rust-src + - name: Checkout repository uses: actions/checkout@v4 - - name: Restore cargo cache - uses: actions/cache@v4 - with: - path: | - ~/.cargo/bin/ - ~/.cargo/registry/index/ - ~/.cargo/registry/cache/ - ~/.cargo/git/db/ - key: ${{ runner.os }}-cargo-${{ github.sha }} - - name: Restore target cache uses: actions/cache@v4 with: diff --git a/src/tools/rust-analyzer/.github/workflows/rustdoc.yaml b/src/tools/rust-analyzer/.github/workflows/rustdoc.yaml index 12a1a791fda2e..f975bbaa51056 100644 --- a/src/tools/rust-analyzer/.github/workflows/rustdoc.yaml +++ b/src/tools/rust-analyzer/.github/workflows/rustdoc.yaml @@ -26,7 +26,7 @@ jobs: run: cargo doc --all --no-deps - name: Deploy Docs - uses: peaceiris/actions-gh-pages@364c31d33bb99327c77b3a5438a83a357a6729ad # v3.4.0 + uses: peaceiris/actions-gh-pages@4f9cc6602d3f66b9c108549d475ec49e8ef4d45e # v4.0.0 with: github_token: ${{ secrets.GITHUB_TOKEN }} publish_branch: gh-pages diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index a6e460134f21c..8eb872514a5d1 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -28,9 +28,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.80" +version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ad32ce52e4161730f7098c077cd2ed6229b5804ccf99e5366be1ab72a98b4e1" +checksum = "25bdb32cbbdce2b519a9cd7df3a678443100e265d5e25ca763b7572a5104f5f3" [[package]] name = "arbitrary" @@ -46,15 +46,15 @@ checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" [[package]] name = "autocfg" -version = "1.1.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "backtrace" -version = "0.3.69" +version = "0.3.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" dependencies = [ "addr2line", "cc", @@ -91,9 +91,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.2" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" [[package]] name = "byteorder" @@ -112,9 +112,9 @@ dependencies = [ [[package]] name = "cargo-platform" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "694c8807f2ae16faecc43dc17d74b3eb042482789fd0eb64b39a2e04e087053f" +checksum = "24b1f0365a6c6bb4020cd05806fd0d33c44d38046b8bd7f0e40814b9763cabfc" dependencies = [ "serde", ] @@ -135,9 +135,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.90" +version = "1.0.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5" +checksum = "099a5357d84c4c61eb35fc8eafa9a79a902c2f76911e5747ced4e032edd8d9b4" [[package]] name = "cfg" @@ -159,6 +159,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cfg_aliases" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" + [[package]] name = "chalk-derive" version = "0.97.0" @@ -177,7 +183,7 @@ version = "0.97.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db18493569b190f7266a04901e520fc3a5c00564475154287906f8a27302c119" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.5.0", "chalk-derive", ] @@ -282,11 +288,11 @@ checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" [[package]] name = "ctrlc" -version = "3.4.2" +version = "3.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b467862cc8610ca6fc9a1532d7777cee0804e678ab45410897b9396495994a0b" +checksum = "672465ae37dc1bc6380a6547a8883d5dd397b0f1faaad4f265726cc7042a5345" dependencies = [ - "nix 0.27.1", + "nix 0.28.0", "windows-sys 0.52.0", ] @@ -323,11 +329,32 @@ dependencies = [ "syn", ] +[[package]] +name = "directories" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a49173b84e034382284f27f1af4dcbbd231ffa358c0fe316541a7337f376a35" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.48.0", +] + [[package]] name = "dissimilar" -version = "1.0.7" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86e3bdc80eee6e16b2b6b0f87fbc98c04bee3455e35174c0de1a125d0688c632" +checksum = "59f8e79d1fbf76bdfbde321e902714bf6c49df88a7dda6fc682fc2979226962d" [[package]] name = "dot" @@ -343,15 +370,15 @@ checksum = "9bda8e21c04aca2ae33ffc2fd8c23134f3cac46db123ba97bd9d3f3b8a4a85e1" [[package]] name = "either" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" +checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" [[package]] name = "ena" -version = "0.14.2" +version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c533630cf40e9caa44bd91aadc88a75d75a4c3a12b4cfde353cbed41daa1e1f1" +checksum = "3d248bdd43ce613d87415282f69b9bb99d947d290b10962dd6c56233312c2ad5" dependencies = [ "log", ] @@ -364,9 +391,9 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "expect-test" -version = "1.4.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30d9eafeadd538e68fb28016364c9732d78e420b9ff8853fa5e4058861e9f8d3" +checksum = "9e0be0a561335815e06dab7c62e50353134c796e7a6155402a64bcff66b6a5e0" dependencies = [ "dissimilar", "once_cell", @@ -380,7 +407,7 @@ checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.4.1", "windows-sys 0.52.0", ] @@ -392,9 +419,9 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flate2" -version = "1.0.28" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" +checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae" dependencies = [ "crc32fast", "miniz_oxide", @@ -442,9 +469,9 @@ checksum = "7ab85b9b05e3978cc9a9cf8fea7f01b494e1a09ed3037e16ba39edc7a29eb61a" [[package]] name = "getrandom" -version = "0.2.12" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", @@ -459,9 +486,9 @@ checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" [[package]] name = "hashbrown" -version = "0.14.3" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" [[package]] name = "heck" @@ -504,7 +531,7 @@ version = "0.0.0" dependencies = [ "arrayvec", "base-db", - "bitflags 2.4.2", + "bitflags 2.5.0", "cfg", "cov-mark", "dashmap", @@ -568,7 +595,7 @@ version = "0.0.0" dependencies = [ "arrayvec", "base-db", - "bitflags 2.4.2", + "bitflags 2.5.0", "chalk-derive", "chalk-ir", "chalk-recursive", @@ -589,7 +616,7 @@ dependencies = [ "oorandom", "project-model", "ra-ap-rustc_abi", - "ra-ap-rustc_index", + "ra-ap-rustc_index 0.53.0", "ra-ap-rustc_pattern_analysis", "rustc-hash", "scoped-tls", @@ -695,7 +722,7 @@ version = "0.0.0" dependencies = [ "arrayvec", "base-db", - "bitflags 2.4.2", + "bitflags 2.5.0", "cov-mark", "crossbeam-channel", "either", @@ -776,9 +803,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.2.5" +version = "2.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b0b929d511467233429c45a44ac1dcaa21ba0f5ba11e4879e6ed28ddb4f9df4" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" dependencies = [ "equivalent", "hashbrown", @@ -826,9 +853,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "jod-thread" @@ -874,9 +901,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.153" +version = "0.2.154" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" [[package]] name = "libloading" @@ -885,19 +912,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" dependencies = [ "cfg-if", - "windows-targets 0.52.4", + "windows-targets 0.52.5", ] [[package]] name = "libmimalloc-sys" -version = "0.1.35" +version = "0.1.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3979b5c37ece694f1f5e51e7ecc871fdb0f517ed04ee45f88d15d6d553cb9664" +checksum = "81eb4061c0582dedea1cbc7aff2240300dd6982e0239d1c99e65c1dbf4a30ba7" dependencies = [ "cc", "libc", ] +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags 2.5.0", + "libc", +] + [[package]] name = "limit" version = "0.0.0" @@ -948,9 +985,9 @@ dependencies = [ [[package]] name = "lock_api" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ "autocfg", "scopeguard", @@ -1001,9 +1038,9 @@ dependencies = [ [[package]] name = "lz4_flex" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "912b45c753ff5f7f5208307e8ace7d2a2e30d024e26d3509f3dce546c044ce15" +checksum = "75761162ae2b0e580d7e7c390558127e5f01b4194debd6221fd8c207fc80e3f5" [[package]] name = "mbe" @@ -1023,9 +1060,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.1" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" [[package]] name = "memmap2" @@ -1038,18 +1075,18 @@ dependencies = [ [[package]] name = "memoffset" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" dependencies = [ "autocfg", ] [[package]] name = "mimalloc" -version = "0.1.39" +version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa01922b5ea280a911e323e4d2fd24b7fe5cc4042e0d2cda3c40775cdc4bdc9c" +checksum = "9f41a2280ded0da56c8cf898babb86e8f10651a34adcfff190ae9a1159c6908d" dependencies = [ "libmimalloc-sys", ] @@ -1097,12 +1134,13 @@ dependencies = [ [[package]] name = "nix" -version = "0.27.1" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" +checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.5.0", "cfg-if", + "cfg_aliases", "libc", ] @@ -1118,7 +1156,7 @@ version = "6.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.5.0", "crossbeam-channel", "filetime", "fsevent-sys", @@ -1186,11 +1224,17 @@ version = "11.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + [[package]] name = "parking_lot" -version = "0.12.1" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb" dependencies = [ "lock_api", "parking_lot_core", @@ -1198,15 +1242,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.9" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.5.1", "smallvec", - "windows-targets 0.48.5", + "windows-targets 0.52.5", ] [[package]] @@ -1224,9 +1268,9 @@ dependencies = [ [[package]] name = "paste" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "paths" @@ -1262,9 +1306,9 @@ dependencies = [ [[package]] name = "petgraph" -version = "0.6.4" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" +checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset", "indexmap", @@ -1272,9 +1316,9 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" [[package]] name = "powerfmt" @@ -1346,9 +1390,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.78" +version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b" dependencies = [ "unicode-ident", ] @@ -1365,7 +1409,7 @@ dependencies = [ "perf-event", "tikv-jemalloc-ctl", "tracing", - "winapi", + "windows-sys 0.52.0", ] [[package]] @@ -1417,7 +1461,7 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57206b407293d2bcd3af849ce869d52068623f19e1b5ff8e8778e3309439682b" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.5.0", "memchr", "unicase", ] @@ -1433,21 +1477,21 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.35" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] [[package]] name = "ra-ap-rustc_abi" -version = "0.44.0" +version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8709df2a746f055316bc0c62bd30948695a25e734863bf6e1f9755403e010ab" +checksum = "80b1d613eee933486c0613a7bc26e515e46f43adf479d1edd5e537f983e9ce46" dependencies = [ - "bitflags 2.4.2", - "ra-ap-rustc_index", + "bitflags 2.5.0", + "ra-ap-rustc_index 0.53.0", "tracing", ] @@ -1458,7 +1502,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ad68bacffb87dcdbb23a3ce11261375078aaa06b85d348c49f39ffd5510dc20" dependencies = [ "arrayvec", - "ra-ap-rustc_index_macros", + "ra-ap-rustc_index_macros 0.44.0", + "smallvec", +] + +[[package]] +name = "ra-ap-rustc_index" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f072060ac77e9e1a02cc20028095993af7e72cc0804779c68bcbf47b16de49c9" +dependencies = [ + "arrayvec", + "ra-ap-rustc_index_macros 0.53.0", "smallvec", ] @@ -1474,11 +1529,23 @@ dependencies = [ "synstructure", ] +[[package]] +name = "ra-ap-rustc_index_macros" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82f3d6dcb30a66905388e14756b8f2216131d9f8004922c07f13335840e058d1" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + [[package]] name = "ra-ap-rustc_lexer" -version = "0.44.0" +version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aab683fc8579d09eb72033bd5dc9ba6d701aa9645b5fed087ef19af71184dff3" +checksum = "dbd8a2b0bdcba9892cbce0b25f6c953d31b0febc1f3420fc692884fce5a23ad8" dependencies = [ "unicode-properties", "unicode-xid", @@ -1486,11 +1553,11 @@ dependencies = [ [[package]] name = "ra-ap-rustc_parse_format" -version = "0.44.0" +version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bcf9ff5edbf784b67b8ad5e03a068f1300fcc24062c0d476b3018965135d933" +checksum = "70dad7a491c2554590222e0c9212dcb7c2e7aceb668875075012a35ea780d135" dependencies = [ - "ra-ap-rustc_index", + "ra-ap-rustc_index 0.53.0", "ra-ap-rustc_lexer", ] @@ -1500,7 +1567,7 @@ version = "0.44.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d63d1e1d5b2a13273cee1a10011147418f40e12b70f70578ce1dee0f1cafc334" dependencies = [ - "ra-ap-rustc_index", + "ra-ap-rustc_index 0.44.0", "rustc-hash", "rustc_apfloat", "smallvec", @@ -1539,9 +1606,9 @@ dependencies = [ [[package]] name = "rayon" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4963ed1bc86e4f3ee217022bd855b297cef07fb9eac5dfa1f788b220b49b3bd" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" dependencies = [ "either", "rayon-core", @@ -1566,6 +1633,26 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "redox_syscall" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" +dependencies = [ + "bitflags 2.5.0", +] + +[[package]] +name = "redox_users" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" +dependencies = [ + "getrandom", + "libredox", + "thiserror", +] + [[package]] name = "rowan" version = "0.15.15" @@ -1634,16 +1721,16 @@ dependencies = [ "vfs", "vfs-notify", "walkdir", - "winapi", + "windows-sys 0.52.0", "xflags", "xshell", ] [[package]] name = "rustc-demangle" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustc-hash" @@ -1663,9 +1750,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "salsa" @@ -1729,27 +1816,27 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "semver" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" dependencies = [ "serde", ] [[package]] name = "serde" -version = "1.0.197" +version = "1.0.201" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" +checksum = "780f1cebed1629e4753a1a38a3c72d30b97ec044f0aef68cb26650a3c5cf363c" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.197" +version = "1.0.201" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" +checksum = "c5e405930b9796f1c00bee880d03fc7e0bb4b9a11afc776885ffe84320da2865" dependencies = [ "proc-macro2", "quote", @@ -1758,9 +1845,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.114" +version = "1.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" +checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" dependencies = [ "indexmap", "itoa", @@ -1770,9 +1857,9 @@ dependencies = [ [[package]] name = "serde_repr" -version = "0.1.18" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b2e6b945e9d3df726b65d6ee24060aff8e3533d431f677a9695db04eff9dfdb" +checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", "quote", @@ -1799,9 +1886,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.13.1" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "smol_str" @@ -1856,14 +1943,14 @@ dependencies = [ "jod-thread", "libc", "miow", - "winapi", + "windows-sys 0.52.0", ] [[package]] name = "syn" -version = "2.0.52" +version = "2.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07" +checksum = "bf5be731623ca1a1fb7d8be6f261a3be6d3e2337b8a1f97be944d020c8fcb704" dependencies = [ "proc-macro2", "quote", @@ -1946,18 +2033,18 @@ checksum = "f18aa187839b2bdb1ad2fa35ead8c4c2976b64e4363c386d45ac0f7ee85c9233" [[package]] name = "thiserror" -version = "1.0.57" +version = "1.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e45bcbe8ed29775f228095caf2cd67af7a4ccf756ebff23a306bf3e8b47b24b" +checksum = "579e9083ca58dd9dcf91a9923bb9054071b9ebbd800b342194c9feb0ee89fc18" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.57" +version = "1.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81" +checksum = "e2470041c06ec3ac1ab38d0356a6119054dedaea53e12fbefc0de730a1c08524" dependencies = [ "proc-macro2", "quote", @@ -2007,9 +2094,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.34" +version = "0.3.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" dependencies = [ "deranged", "num-conv", @@ -2041,9 +2128,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "toml" -version = "0.8.8" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1a195ec8c9da26928f773888e0742ca3ca1040c6cd859c919c9f59c1954ab35" +checksum = "e9dd1545e8208b4a5af1aa9bbd0b4cf7e9ea08fabc5d0a5c67fcaafa17433aa3" dependencies = [ "serde", "serde_spanned", @@ -2062,9 +2149,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.21.0" +version = "0.22.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03" +checksum = "d3328d4f68a705b2a4498da1d580585d39a6510f98318a2cec3018a7ec61ddef" dependencies = [ "indexmap", "serde", @@ -2255,6 +2342,7 @@ dependencies = [ "paths", "rustc-hash", "stdx", + "tracing", ] [[package]] @@ -2304,11 +2392,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.6" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" dependencies = [ - "winapi", + "windows-sys 0.52.0", ] [[package]] @@ -2332,7 +2420,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.4", + "windows-targets 0.52.5", ] [[package]] @@ -2352,17 +2440,18 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" dependencies = [ - "windows_aarch64_gnullvm 0.52.4", - "windows_aarch64_msvc 0.52.4", - "windows_i686_gnu 0.52.4", - "windows_i686_msvc 0.52.4", - "windows_x86_64_gnu 0.52.4", - "windows_x86_64_gnullvm 0.52.4", - "windows_x86_64_msvc 0.52.4", + "windows_aarch64_gnullvm 0.52.5", + "windows_aarch64_msvc 0.52.5", + "windows_i686_gnu 0.52.5", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.5", + "windows_x86_64_gnu 0.52.5", + "windows_x86_64_gnullvm 0.52.5", + "windows_x86_64_msvc 0.52.5", ] [[package]] @@ -2373,9 +2462,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" [[package]] name = "windows_aarch64_msvc" @@ -2385,9 +2474,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" [[package]] name = "windows_i686_gnu" @@ -2397,9 +2486,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.4" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" [[package]] name = "windows_i686_msvc" @@ -2409,9 +2504,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" [[package]] name = "windows_x86_64_gnu" @@ -2421,9 +2516,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" [[package]] name = "windows_x86_64_gnullvm" @@ -2433,9 +2528,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" [[package]] name = "windows_x86_64_msvc" @@ -2445,15 +2540,15 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" [[package]] name = "winnow" -version = "0.5.32" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8434aeec7b290e8da5c3f0d628cb0eac6cabcb31d14bb74f779a08109a5914d6" +checksum = "c3c52e9c97a68071b23e836c9380edae937f17b9c4667bd021973efc689f618d" dependencies = [ "memchr", ] @@ -2481,24 +2576,25 @@ checksum = "672423d4fea7ffa2f6c25ba60031ea13dc6258070556f125cc4d790007d4a155" [[package]] name = "xshell" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce2107fe03e558353b4c71ad7626d58ed82efaf56c54134228608893c77023ad" +checksum = "6db0ab86eae739efd1b054a8d3d16041914030ac4e01cd1dca0cf252fd8b6437" dependencies = [ "xshell-macros", ] [[package]] name = "xshell-macros" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e2c411759b501fb9501aac2b1b2d287a6e93e5bdcf13c25306b23e1b716dd0e" +checksum = "9d422e8e38ec76e2f06ee439ccc765e9c6a9638b9e7c9f2e8255e4d41e8bd852" [[package]] name = "xtask" version = "0.1.0" dependencies = [ "anyhow", + "directories", "flate2", "itertools", "proc-macro2", diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml index f7e3ae51dfd00..3108c1b3dfe5d 100644 --- a/src/tools/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/Cargo.toml @@ -4,7 +4,7 @@ exclude = ["crates/proc-macro-srv/proc-macro-test/imp"] resolver = "2" [workspace.package] -rust-version = "1.76" +rust-version = "1.78" edition = "2021" license = "MIT OR Apache-2.0" authors = ["rust-analyzer team"] @@ -85,10 +85,10 @@ 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.44.0", default-features = false } -ra-ap-rustc_parse_format = { version = "0.44.0", default-features = false } -ra-ap-rustc_index = { version = "0.44.0", default-features = false } -ra-ap-rustc_abi = { version = "0.44.0", default-features = false } +ra-ap-rustc_lexer = { version = "0.53.0", default-features = false } +ra-ap-rustc_parse_format = { version = "0.53.0", default-features = false } +ra-ap-rustc_index = { version = "0.53.0", default-features = false } +ra-ap-rustc_abi = { version = "0.53.0", default-features = false } ra-ap-rustc_pattern_analysis = { version = "0.44.0", default-features = false } # local crates that aren't published to crates.io. These should not have versions. 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 f202a885e278f..927b2108a6ce8 100644 --- a/src/tools/rust-analyzer/crates/base-db/src/change.rs +++ b/src/tools/rust-analyzer/crates/base-db/src/change.rs @@ -51,7 +51,7 @@ impl FileChange { } pub fn apply(self, db: &mut dyn SourceDatabaseExt) { - let _p = tracing::span!(tracing::Level::INFO, "RootDatabase::apply_change").entered(); + let _p = tracing::span!(tracing::Level::INFO, "FileChange::apply").entered(); if let Some(roots) = self.roots { for (idx, root) in roots.into_iter().enumerate() { let root_id = SourceRootId(idx as u32); 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 240af7925ccca..b2c3f38ab4f8a 100644 --- a/src/tools/rust-analyzer/crates/base-db/src/input.rs +++ b/src/tools/rust-analyzer/crates/base-db/src/input.rs @@ -324,21 +324,27 @@ pub struct Dependency { pub crate_id: CrateId, pub name: CrateName, prelude: bool, + sysroot: bool, } impl Dependency { pub fn new(name: CrateName, crate_id: CrateId) -> Self { - Self { name, crate_id, prelude: true } + Self { name, crate_id, prelude: true, sysroot: false } } - pub fn with_prelude(name: CrateName, crate_id: CrateId, prelude: bool) -> Self { - Self { name, crate_id, prelude } + pub fn with_prelude(name: CrateName, crate_id: CrateId, prelude: bool, sysroot: bool) -> Self { + Self { name, crate_id, prelude, sysroot } } /// Whether this dependency is to be added to the depending crate's extern prelude. pub fn is_prelude(&self) -> bool { self.prelude } + + /// Whether this dependency is a sysroot injected one. + pub fn is_sysroot(&self) -> bool { + self.sysroot + } } impl CrateGraph { 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 2b64a07a5a95f..2c13eed56c3e1 100644 --- a/src/tools/rust-analyzer/crates/base-db/src/lib.rs +++ b/src/tools/rust-analyzer/crates/base-db/src/lib.rs @@ -8,7 +8,7 @@ mod input; use std::panic; use salsa::Durability; -use syntax::{ast, Parse, SourceFile}; +use syntax::{ast, Parse, SourceFile, SyntaxError}; use triomphe::Arc; pub use crate::{ @@ -51,6 +51,7 @@ pub trait FileLoader { /// Text of the file. fn file_text(&self, file_id: FileId) -> Arc; fn resolve_path(&self, path: AnchoredPath<'_>) -> Option; + /// Crates whose root's source root is the same as the source root of `file_id` fn relevant_crates(&self, file_id: FileId) -> Arc<[CrateId]>; } @@ -61,6 +62,9 @@ pub trait SourceDatabase: FileLoader + std::fmt::Debug { /// Parses the file into the syntax tree. fn parse(&self, file_id: FileId) -> Parse; + /// Returns the set of errors obtained from parsing the file including validation errors. + fn parse_errors(&self, file_id: FileId) -> Option>; + /// The crate graph. #[salsa::input] fn crate_graph(&self) -> Arc; @@ -81,12 +85,20 @@ fn toolchain_channel(db: &dyn SourceDatabase, krate: CrateId) -> Option Parse { - let _p = tracing::span!(tracing::Level::INFO, "parse_query", ?file_id).entered(); + let _p = tracing::span!(tracing::Level::INFO, "parse", ?file_id).entered(); let text = db.file_text(file_id); // FIXME: Edition based parsing SourceFile::parse(&text, span::Edition::CURRENT) } +fn parse_errors(db: &dyn SourceDatabase, file_id: FileId) -> Option> { + let errors = db.parse(file_id).errors(); + match &*errors { + [] => None, + [..] => Some(errors.into()), + } +} + /// We don't want to give HIR knowledge of source roots, hence we extract these /// methods into a separate DB. #[salsa::query_group(SourceDatabaseExtStorage)] @@ -104,6 +116,7 @@ pub trait SourceDatabaseExt: SourceDatabase { #[salsa::input] fn source_root(&self, id: SourceRootId) -> Arc; + /// Crates whose root fool is in `id`. fn source_root_crates(&self, id: SourceRootId) -> Arc<[CrateId]>; } diff --git a/src/tools/rust-analyzer/crates/cfg/src/tests.rs b/src/tools/rust-analyzer/crates/cfg/src/tests.rs index a1ae15fcddade..dddaf2cce1823 100644 --- a/src/tools/rust-analyzer/crates/cfg/src/tests.rs +++ b/src/tools/rust-analyzer/crates/cfg/src/tests.rs @@ -1,6 +1,6 @@ use arbitrary::{Arbitrary, Unstructured}; use expect_test::{expect, Expect}; -use mbe::{syntax_node_to_token_tree, DummyTestSpanMap, DUMMY}; +use mbe::{syntax_node_to_token_tree, DocCommentDesugarMode, DummyTestSpanMap, DUMMY}; use syntax::{ast, AstNode, Edition}; use crate::{CfgAtom, CfgExpr, CfgOptions, DnfExpr}; @@ -8,7 +8,12 @@ use crate::{CfgAtom, CfgExpr, CfgOptions, DnfExpr}; fn assert_parse_result(input: &str, expected: CfgExpr) { let source_file = ast::SourceFile::parse(input, Edition::CURRENT).ok().unwrap(); let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); - let tt = syntax_node_to_token_tree(tt.syntax(), DummyTestSpanMap, DUMMY); + let tt = syntax_node_to_token_tree( + tt.syntax(), + DummyTestSpanMap, + DUMMY, + DocCommentDesugarMode::ProcMacro, + ); let cfg = CfgExpr::parse(&tt); assert_eq!(cfg, expected); } @@ -16,7 +21,12 @@ fn assert_parse_result(input: &str, expected: CfgExpr) { fn check_dnf(input: &str, expect: Expect) { let source_file = ast::SourceFile::parse(input, Edition::CURRENT).ok().unwrap(); let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); - let tt = syntax_node_to_token_tree(tt.syntax(), DummyTestSpanMap, DUMMY); + let tt = syntax_node_to_token_tree( + tt.syntax(), + DummyTestSpanMap, + DUMMY, + DocCommentDesugarMode::ProcMacro, + ); let cfg = CfgExpr::parse(&tt); let actual = format!("#![cfg({})]", DnfExpr::new(cfg)); expect.assert_eq(&actual); @@ -25,7 +35,12 @@ fn check_dnf(input: &str, expect: Expect) { fn check_why_inactive(input: &str, opts: &CfgOptions, expect: Expect) { let source_file = ast::SourceFile::parse(input, Edition::CURRENT).ok().unwrap(); let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); - let tt = syntax_node_to_token_tree(tt.syntax(), DummyTestSpanMap, DUMMY); + let tt = syntax_node_to_token_tree( + tt.syntax(), + DummyTestSpanMap, + DUMMY, + DocCommentDesugarMode::ProcMacro, + ); let cfg = CfgExpr::parse(&tt); let dnf = DnfExpr::new(cfg); let why_inactive = dnf.why_inactive(opts).unwrap().to_string(); @@ -36,7 +51,12 @@ fn check_why_inactive(input: &str, opts: &CfgOptions, expect: Expect) { fn check_enable_hints(input: &str, opts: &CfgOptions, expected_hints: &[&str]) { let source_file = ast::SourceFile::parse(input, Edition::CURRENT).ok().unwrap(); let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); - let tt = syntax_node_to_token_tree(tt.syntax(), DummyTestSpanMap, DUMMY); + let tt = syntax_node_to_token_tree( + tt.syntax(), + DummyTestSpanMap, + DUMMY, + DocCommentDesugarMode::ProcMacro, + ); let cfg = CfgExpr::parse(&tt); let dnf = DnfExpr::new(cfg); let hints = dnf.compute_enable_hints(opts).map(|diff| diff.to_string()).collect::>(); diff --git a/src/tools/rust-analyzer/crates/flycheck/src/lib.rs b/src/tools/rust-analyzer/crates/flycheck/src/lib.rs index 5dfaaf774209c..6d5ca8321e5dc 100644 --- a/src/tools/rust-analyzer/crates/flycheck/src/lib.rs +++ b/src/tools/rust-analyzer/crates/flycheck/src/lib.rs @@ -125,8 +125,10 @@ impl FlycheckHandle { config: FlycheckConfig, sysroot_root: Option, workspace_root: AbsPathBuf, + manifest_path: Option, ) -> FlycheckHandle { - let actor = FlycheckActor::new(id, sender, config, sysroot_root, workspace_root); + let actor = + FlycheckActor::new(id, sender, config, sysroot_root, workspace_root, manifest_path); let (sender, receiver) = unbounded::(); let thread = stdx::thread::Builder::new(stdx::thread::ThreadIntent::Worker) .name("Flycheck".to_owned()) @@ -205,6 +207,7 @@ struct FlycheckActor { id: usize, sender: Box, config: FlycheckConfig, + manifest_path: Option, /// Either the workspace root of the workspace we are flychecking, /// or the project root of the project. root: AbsPathBuf, @@ -233,6 +236,7 @@ impl FlycheckActor { config: FlycheckConfig, sysroot_root: Option, workspace_root: AbsPathBuf, + manifest_path: Option, ) -> FlycheckActor { tracing::info!(%id, ?workspace_root, "Spawning flycheck"); FlycheckActor { @@ -241,6 +245,7 @@ impl FlycheckActor { config, sysroot_root, root: workspace_root, + manifest_path, command_handle: None, command_receiver: None, } @@ -388,8 +393,13 @@ impl FlycheckActor { "--message-format=json" }); - cmd.arg("--manifest-path"); - cmd.arg(self.root.join("Cargo.toml")); + if let Some(manifest_path) = &self.manifest_path { + cmd.arg("--manifest-path"); + cmd.arg(manifest_path); + if manifest_path.extension().map_or(false, |ext| ext == "rs") { + cmd.arg("-Zscript"); + } + } options.apply_on_command(&mut cmd); (cmd, options.extra_args.clone()) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/attr/tests.rs b/src/tools/rust-analyzer/crates/hir-def/src/attr/tests.rs index 9b68797fbf70e..727f4429802cd 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/attr/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/attr/tests.rs @@ -5,7 +5,7 @@ use triomphe::Arc; use base_db::FileId; use hir_expand::span_map::{RealSpanMap, SpanMap}; -use mbe::syntax_node_to_token_tree; +use mbe::{syntax_node_to_token_tree, DocCommentDesugarMode}; use syntax::{ast, AstNode, TextRange}; use crate::attr::{DocAtom, DocExpr}; @@ -18,6 +18,7 @@ fn assert_parse_result(input: &str, expected: DocExpr) { tt.syntax(), map.as_ref(), map.span_for_range(TextRange::empty(0.into())), + DocCommentDesugarMode::ProcMacro, ); let cfg = DocExpr::parse(&tt); assert_eq!(cfg, expected); 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 c9f1add275107..d2f4d7b7e56e9 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body.rs @@ -10,9 +10,10 @@ use std::ops::Index; use base_db::CrateId; use cfg::{CfgExpr, CfgOptions}; -use hir_expand::{name::Name, HirFileId, InFile}; +use hir_expand::{name::Name, InFile}; use la_arena::{Arena, ArenaMap}; use rustc_hash::FxHashMap; +use span::MacroFileId; use syntax::{ast, AstPtr, SyntaxNodePtr}; use triomphe::Arc; @@ -98,7 +99,7 @@ pub struct BodySourceMap { format_args_template_map: FxHashMap>, - expansions: FxHashMap>, HirFileId>, + expansions: FxHashMap>, MacroFileId>, /// Diagnostics accumulated during body lowering. These contain `AstPtr`s and so are stored in /// the source map (since they're just as volatile). @@ -349,11 +350,17 @@ impl BodySourceMap { self.expr_map.get(&src).cloned() } - pub fn node_macro_file(&self, node: InFile<&ast::MacroCall>) -> Option { + pub fn node_macro_file(&self, node: InFile<&ast::MacroCall>) -> Option { let src = node.map(AstPtr::new); self.expansions.get(&src).cloned() } + pub fn macro_calls( + &self, + ) -> impl Iterator>, MacroFileId)> + '_ { + self.expansions.iter().map(|(&a, &b)| (a, b)) + } + pub fn pat_syntax(&self, pat: PatId) -> Result { self.pat_map_back.get(pat).cloned().ok_or(SyntheticSyntax) } 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 340e95dbc2f43..82f89393add6f 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 @@ -1006,7 +1006,9 @@ impl ExprCollector<'_> { Some((mark, expansion)) => { // Keep collecting even with expansion errors so we can provide completions and // other services in incomplete macro expressions. - self.source_map.expansions.insert(macro_call_ptr, self.expander.current_file_id()); + self.source_map + .expansions + .insert(macro_call_ptr, self.expander.current_file_id().macro_file().unwrap()); let prev_ast_id_map = mem::replace( &mut self.ast_id_map, self.db.ast_id_map(self.expander.current_file_id()), @@ -1869,42 +1871,45 @@ impl ExprCollector<'_> { ) -> ExprId { match count { Some(FormatCount::Literal(n)) => { - match LangItem::FormatCount.ty_rel_path(self.db, self.krate, name![Is]) { - Some(count_is) => { - let count_is = self.alloc_expr_desugared(Expr::Path(count_is)); - let args = self.alloc_expr_desugared(Expr::Literal(Literal::Uint( - *n as u128, - Some(BuiltinUint::Usize), - ))); - self.alloc_expr_desugared(Expr::Call { - callee: count_is, - args: Box::new([args]), - is_assignee_expr: false, - }) - } - None => self.missing_expr(), - } + let args = self.alloc_expr_desugared(Expr::Literal(Literal::Uint( + *n as u128, + Some(BuiltinUint::Usize), + ))); + let count_is = + match LangItem::FormatCount.ty_rel_path(self.db, self.krate, name![Is]) { + Some(count_is) => self.alloc_expr_desugared(Expr::Path(count_is)), + None => self.missing_expr(), + }; + self.alloc_expr_desugared(Expr::Call { + callee: count_is, + args: Box::new([args]), + is_assignee_expr: false, + }) } Some(FormatCount::Argument(arg)) => { if let Ok(arg_index) = arg.index { let (i, _) = argmap.insert_full((arg_index, ArgumentType::Usize)); - match LangItem::FormatCount.ty_rel_path(self.db, self.krate, name![Param]) { - Some(count_param) => { - let count_param = self.alloc_expr_desugared(Expr::Path(count_param)); - let args = self.alloc_expr_desugared(Expr::Literal(Literal::Uint( - i as u128, - Some(BuiltinUint::Usize), - ))); - self.alloc_expr_desugared(Expr::Call { - callee: count_param, - args: Box::new([args]), - is_assignee_expr: false, - }) - } + let args = self.alloc_expr_desugared(Expr::Literal(Literal::Uint( + i as u128, + Some(BuiltinUint::Usize), + ))); + let count_param = match LangItem::FormatCount.ty_rel_path( + self.db, + self.krate, + name![Param], + ) { + Some(count_param) => self.alloc_expr_desugared(Expr::Path(count_param)), None => self.missing_expr(), - } + }; + self.alloc_expr_desugared(Expr::Call { + callee: count_param, + args: Box::new([args]), + is_assignee_expr: false, + }) } else { + // FIXME: This drops arg causing it to potentially not be resolved/type checked + // when typing? self.missing_expr() } } @@ -1925,7 +1930,8 @@ impl ExprCollector<'_> { fn make_argument(&mut self, arg: ExprId, ty: ArgumentType) -> ExprId { use ArgumentType::*; use FormatTrait::*; - match LangItem::FormatArgument.ty_rel_path( + + let new_fn = match LangItem::FormatArgument.ty_rel_path( self.db, self.krate, match ty { @@ -1941,16 +1947,14 @@ impl ExprCollector<'_> { Usize => name![from_usize], }, ) { - Some(new_fn) => { - let new_fn = self.alloc_expr_desugared(Expr::Path(new_fn)); - self.alloc_expr_desugared(Expr::Call { - callee: new_fn, - args: Box::new([arg]), - is_assignee_expr: false, - }) - } + Some(new_fn) => self.alloc_expr_desugared(Expr::Path(new_fn)), None => self.missing_expr(), - } + }; + self.alloc_expr_desugared(Expr::Call { + callee: new_fn, + args: Box::new([arg]), + is_assignee_expr: false, + }) } // endregion: format } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/scope.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/scope.rs index 0020e4eac3072..fd685235e17db 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body/scope.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body/scope.rs @@ -7,7 +7,7 @@ use crate::{ body::Body, db::DefDatabase, hir::{Binding, BindingId, Expr, ExprId, LabelId, Pat, PatId, Statement}, - BlockId, DefWithBodyId, + BlockId, ConstBlockId, DefWithBodyId, }; pub type ScopeId = Idx; @@ -46,7 +46,9 @@ pub struct ScopeData { impl ExprScopes { pub(crate) fn expr_scopes_query(db: &dyn DefDatabase, def: DefWithBodyId) -> Arc { let body = db.body(def); - let mut scopes = ExprScopes::new(&body); + let mut scopes = ExprScopes::new(&body, |const_block| { + db.lookup_intern_anonymous_const(const_block).root + }); scopes.shrink_to_fit(); Arc::new(scopes) } @@ -89,7 +91,10 @@ fn empty_entries(idx: usize) -> IdxRange { } impl ExprScopes { - fn new(body: &Body) -> ExprScopes { + fn new( + body: &Body, + resolve_const_block: impl (Fn(ConstBlockId) -> ExprId) + Copy, + ) -> ExprScopes { let mut scopes = ExprScopes { scopes: Arena::default(), scope_entries: Arena::default(), @@ -100,7 +105,7 @@ impl ExprScopes { scopes.add_bindings(body, root, self_param); } scopes.add_params_bindings(body, root, &body.params); - compute_expr_scopes(body.body_expr, body, &mut scopes, &mut root); + compute_expr_scopes(body.body_expr, body, &mut scopes, &mut root, resolve_const_block); scopes } @@ -183,35 +188,46 @@ fn compute_block_scopes( body: &Body, scopes: &mut ExprScopes, scope: &mut ScopeId, + resolve_const_block: impl (Fn(ConstBlockId) -> ExprId) + Copy, ) { for stmt in statements { match stmt { Statement::Let { pat, initializer, else_branch, .. } => { if let Some(expr) = initializer { - compute_expr_scopes(*expr, body, scopes, scope); + compute_expr_scopes(*expr, body, scopes, scope, resolve_const_block); } if let Some(expr) = else_branch { - compute_expr_scopes(*expr, body, scopes, scope); + compute_expr_scopes(*expr, body, scopes, scope, resolve_const_block); } *scope = scopes.new_scope(*scope); scopes.add_pat_bindings(body, *scope, *pat); } Statement::Expr { expr, .. } => { - compute_expr_scopes(*expr, body, scopes, scope); + compute_expr_scopes(*expr, body, scopes, scope, resolve_const_block); } Statement::Item => (), } } if let Some(expr) = tail { - compute_expr_scopes(expr, body, scopes, scope); + compute_expr_scopes(expr, body, scopes, scope, resolve_const_block); } } -fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut ExprScopes, scope: &mut ScopeId) { +fn compute_expr_scopes( + expr: ExprId, + body: &Body, + scopes: &mut ExprScopes, + scope: &mut ScopeId, + resolve_const_block: impl (Fn(ConstBlockId) -> ExprId) + Copy, +) { let make_label = |label: &Option| label.map(|label| (label, body.labels[label].name.clone())); + let compute_expr_scopes = |scopes: &mut ExprScopes, expr: ExprId, scope: &mut ScopeId| { + compute_expr_scopes(expr, body, scopes, scope, resolve_const_block) + }; + scopes.set_scope(expr, *scope); match &body[expr] { Expr::Block { statements, tail, id, label } => { @@ -219,53 +235,54 @@ fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut ExprScopes, scope // Overwrite the old scope for the block expr, so that every block scope can be found // via the block itself (important for blocks that only contain items, no expressions). scopes.set_scope(expr, scope); - compute_block_scopes(statements, *tail, body, scopes, &mut scope); + compute_block_scopes(statements, *tail, body, scopes, &mut scope, resolve_const_block); } - Expr::Const(_) => { - // FIXME: This is broken. + Expr::Const(id) => { + let mut scope = scopes.root_scope(); + compute_expr_scopes(scopes, resolve_const_block(*id), &mut scope); } Expr::Unsafe { id, statements, tail } | Expr::Async { id, statements, tail } => { let mut scope = scopes.new_block_scope(*scope, *id, None); // Overwrite the old scope for the block expr, so that every block scope can be found // via the block itself (important for blocks that only contain items, no expressions). scopes.set_scope(expr, scope); - compute_block_scopes(statements, *tail, body, scopes, &mut scope); + compute_block_scopes(statements, *tail, body, scopes, &mut scope, resolve_const_block); } Expr::Loop { body: body_expr, label } => { let mut scope = scopes.new_labeled_scope(*scope, make_label(label)); - compute_expr_scopes(*body_expr, body, scopes, &mut scope); + compute_expr_scopes(scopes, *body_expr, &mut scope); } Expr::Closure { args, body: body_expr, .. } => { let mut scope = scopes.new_scope(*scope); scopes.add_params_bindings(body, scope, args); - compute_expr_scopes(*body_expr, body, scopes, &mut scope); + compute_expr_scopes(scopes, *body_expr, &mut scope); } Expr::Match { expr, arms } => { - compute_expr_scopes(*expr, body, scopes, scope); + compute_expr_scopes(scopes, *expr, scope); for arm in arms.iter() { let mut scope = scopes.new_scope(*scope); scopes.add_pat_bindings(body, scope, arm.pat); if let Some(guard) = arm.guard { scope = scopes.new_scope(scope); - compute_expr_scopes(guard, body, scopes, &mut scope); + compute_expr_scopes(scopes, guard, &mut scope); } - compute_expr_scopes(arm.expr, body, scopes, &mut scope); + compute_expr_scopes(scopes, arm.expr, &mut scope); } } &Expr::If { condition, then_branch, else_branch } => { let mut then_branch_scope = scopes.new_scope(*scope); - compute_expr_scopes(condition, body, scopes, &mut then_branch_scope); - compute_expr_scopes(then_branch, body, scopes, &mut then_branch_scope); + compute_expr_scopes(scopes, condition, &mut then_branch_scope); + compute_expr_scopes(scopes, then_branch, &mut then_branch_scope); if let Some(else_branch) = else_branch { - compute_expr_scopes(else_branch, body, scopes, scope); + compute_expr_scopes(scopes, else_branch, scope); } } &Expr::Let { pat, expr } => { - compute_expr_scopes(expr, body, scopes, scope); + compute_expr_scopes(scopes, expr, scope); *scope = scopes.new_scope(*scope); scopes.add_pat_bindings(body, *scope, pat); } - e => e.walk_child_exprs(|e| compute_expr_scopes(e, body, scopes, scope)), + e => e.walk_child_exprs(|e| compute_expr_scopes(scopes, e, scope)), }; } 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 a27ffe216750e..4c8a54f7c8c72 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 @@ -318,18 +318,20 @@ fn f() { expect![[r#" fn f() { - $crate::panicking::panic_fmt( - builtin#lang(Arguments::new_v1_formatted)( - &[ - "cc", - ], - &[], - &[], - unsafe { - builtin#lang(UnsafeArg::new)() - }, - ), - ); + { + $crate::panicking::panic_fmt( + builtin#lang(Arguments::new_v1_formatted)( + &[ + "cc", + ], + &[], + &[], + unsafe { + builtin#lang(UnsafeArg::new)() + }, + ), + ); + }; }"#]] .assert_eq(&body.pretty_print(&db, def)) } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/data.rs b/src/tools/rust-analyzer/crates/hir-def/src/data.rs index e3d750d33cad5..51a4dd6f42a85 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/data.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/data.rs @@ -229,7 +229,7 @@ pub struct TraitData { /// method calls to this trait's methods when the receiver is an array and the crate edition is /// 2015 or 2018. // box it as the vec is usually empty anyways - pub attribute_calls: Option, MacroCallId)>>>, + pub macro_calls: Option, MacroCallId)>>>, } impl TraitData { @@ -258,12 +258,12 @@ impl TraitData { let mut collector = AssocItemCollector::new(db, module_id, tree_id.file_id(), ItemContainerId::TraitId(tr)); collector.collect(&item_tree, tree_id.tree_id(), &tr_def.items); - let (items, attribute_calls, diagnostics) = collector.finish(); + let (items, macro_calls, diagnostics) = collector.finish(); ( Arc::new(TraitData { name, - attribute_calls, + macro_calls, items, is_auto, is_unsafe, @@ -298,7 +298,7 @@ impl TraitData { } pub fn attribute_calls(&self) -> impl Iterator, MacroCallId)> + '_ { - self.attribute_calls.iter().flat_map(|it| it.iter()).copied() + self.macro_calls.iter().flat_map(|it| it.iter()).copied() } } @@ -319,7 +319,7 @@ impl TraitAliasData { } } -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq)] pub struct ImplData { pub target_trait: Option>, pub self_ty: Interned, @@ -327,7 +327,7 @@ pub struct ImplData { pub is_negative: bool, pub is_unsafe: bool, // box it as the vec is usually empty anyways - pub attribute_calls: Option, MacroCallId)>>>, + pub macro_calls: Option, MacroCallId)>>>, } impl ImplData { @@ -354,7 +354,7 @@ impl ImplData { AssocItemCollector::new(db, module_id, tree_id.file_id(), ItemContainerId::ImplId(id)); collector.collect(&item_tree, tree_id.tree_id(), &impl_def.items); - let (items, attribute_calls, diagnostics) = collector.finish(); + let (items, macro_calls, diagnostics) = collector.finish(); let items = items.into_iter().map(|(_, item)| item).collect(); ( @@ -364,14 +364,14 @@ impl ImplData { items, is_negative, is_unsafe, - attribute_calls, + macro_calls, }), DefDiagnostics::new(diagnostics), ) } pub fn attribute_calls(&self) -> impl Iterator, MacroCallId)> + '_ { - self.attribute_calls.iter().flat_map(|it| it.iter()).copied() + self.macro_calls.iter().flat_map(|it| it.iter()).copied() } } @@ -573,7 +573,7 @@ struct AssocItemCollector<'a> { expander: Expander, items: Vec<(Name, AssocItemId)>, - attr_calls: Vec<(AstId, MacroCallId)>, + macro_calls: Vec<(AstId, MacroCallId)>, } impl<'a> AssocItemCollector<'a> { @@ -590,7 +590,7 @@ impl<'a> AssocItemCollector<'a> { container, expander: Expander::new(db, file_id, module_id), items: Vec::new(), - attr_calls: Vec::new(), + macro_calls: Vec::new(), diagnostics: Vec::new(), } } @@ -604,7 +604,7 @@ impl<'a> AssocItemCollector<'a> { ) { ( self.items, - if self.attr_calls.is_empty() { None } else { Some(Box::new(self.attr_calls)) }, + if self.macro_calls.is_empty() { None } else { Some(Box::new(self.macro_calls)) }, self.diagnostics, ) } @@ -662,11 +662,11 @@ impl<'a> AssocItemCollector<'a> { } } - self.attr_calls.push((ast_id, call_id)); + self.macro_calls.push((ast_id, call_id)); let res = self.expander.enter_expand_id::(self.db, call_id); - self.collect_macro_items(res, &|| loc.kind.clone()); + self.collect_macro_items(res); continue 'items; } Ok(_) => (), @@ -698,24 +698,22 @@ impl<'a> AssocItemCollector<'a> { match item { AssocItem::Function(id) => { let item = &item_tree[id]; - let def = FunctionLoc { container, id: ItemTreeId::new(tree_id, id) }.intern(self.db); self.items.push((item.name.clone(), def.into())); } - AssocItem::Const(id) => { - let item = &item_tree[id]; - let Some(name) = item.name.clone() else { return }; - let def = ConstLoc { container, id: ItemTreeId::new(tree_id, id) }.intern(self.db); - self.items.push((name, def.into())); - } AssocItem::TypeAlias(id) => { let item = &item_tree[id]; - let def = TypeAliasLoc { container, id: ItemTreeId::new(tree_id, id) }.intern(self.db); self.items.push((item.name.clone(), def.into())); } + AssocItem::Const(id) => { + let item = &item_tree[id]; + let Some(name) = item.name.clone() else { return }; + let def = ConstLoc { container, id: ItemTreeId::new(tree_id, id) }.intern(self.db); + self.items.push((name, def.into())); + } AssocItem::MacroCall(call) => { let file_id = self.expander.current_file_id(); let MacroCall { ast_id, expand_to, ctxt, ref path } = item_tree[call]; @@ -745,11 +743,8 @@ impl<'a> AssocItemCollector<'a> { Ok(Some(call_id)) => { let res = self.expander.enter_expand_id::(self.db, call_id); - self.collect_macro_items(res, &|| hir_expand::MacroCallKind::FnLike { - ast_id: InFile::new(file_id, ast_id), - expand_to: hir_expand::ExpandTo::Items, - eager: None, - }); + self.macro_calls.push((InFile::new(file_id, ast_id.upcast()), call_id)); + self.collect_macro_items(res); } Ok(None) => (), Err(_) => { @@ -768,39 +763,8 @@ impl<'a> AssocItemCollector<'a> { } } - fn collect_macro_items( - &mut self, - ExpandResult { value, err }: ExpandResult)>>, - error_call_kind: &dyn Fn() -> hir_expand::MacroCallKind, - ) { - let Some((mark, parse)) = value else { return }; - - if let Some(err) = err { - let diag = match err { - // why is this reported here? - hir_expand::ExpandError::UnresolvedProcMacro(krate) => { - DefDiagnostic::unresolved_proc_macro( - self.module_id.local_id, - error_call_kind(), - krate, - ) - } - _ => DefDiagnostic::macro_error( - self.module_id.local_id, - error_call_kind(), - err.to_string(), - ), - }; - self.diagnostics.push(diag); - } - let errors = parse.errors(); - if !errors.is_empty() { - self.diagnostics.push(DefDiagnostic::macro_expansion_parse_error( - self.module_id.local_id, - error_call_kind(), - errors.into_boxed_slice(), - )); - } + fn collect_macro_items(&mut self, res: ExpandResult)>>) { + let Some((mark, _parse)) = res.value else { return }; let tree_id = item_tree::TreeId::new(self.expander.current_file_id(), None); let item_tree = tree_id.item_tree(self.db); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs b/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs index bf728a7107936..4e57845a694d1 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs @@ -1177,6 +1177,8 @@ pub mod fmt { //- /main.rs crate:main deps:alloc,std #![no_std] +extern crate alloc; + $0 //- /std.rs crate:std deps:alloc diff --git a/src/tools/rust-analyzer/crates/hir-def/src/generics.rs b/src/tools/rust-analyzer/crates/hir-def/src/generics.rs index acc60e1d9e4e8..10a1d65bb9337 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/generics.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/generics.rs @@ -20,7 +20,7 @@ use triomphe::Arc; use crate::{ db::DefDatabase, expander::Expander, - item_tree::{GenericsItemTreeNode, ItemTree}, + item_tree::{AttrOwner, FileItemTreeId, GenericModItem, GenericsItemTreeNode, ItemTree}, lower::LowerCtx, nameres::{DefMap, MacroSubNs}, type_ref::{ConstRef, LifetimeRef, TypeBound, TypeRef}, @@ -339,6 +339,7 @@ impl GenericParamsCollector { target: Either, ) { let bound = TypeBound::from_ast(lower_ctx, bound); + self.fill_impl_trait_bounds(lower_ctx.take_impl_traits_bounds()); let predicate = match (target, bound) { (Either::Left(type_ref), bound) => match hrtb_lifetimes { Some(hrtb_lifetimes) => WherePredicate::ForLifetime { @@ -359,7 +360,24 @@ impl GenericParamsCollector { self.where_predicates.push(predicate); } - pub(crate) fn fill_implicit_impl_trait_args( + fn fill_impl_trait_bounds(&mut self, impl_bounds: Vec>>) { + for bounds in impl_bounds { + let param = TypeParamData { + name: None, + default: None, + provenance: TypeParamProvenance::ArgumentImplTrait, + }; + let param_id = self.type_or_consts.alloc(param.into()); + for bound in bounds { + self.where_predicates.push(WherePredicate::TypeBound { + target: WherePredicateTypeTarget::TypeOrConstParam(param_id), + bound, + }); + } + } + } + + fn fill_implicit_impl_trait_args( &mut self, db: &dyn DefDatabase, exp: &mut Lazy<(Arc, Expander), impl FnOnce() -> (Arc, Expander)>, @@ -456,56 +474,67 @@ impl GenericParams { let cfg_options = &cfg_options[krate].cfg_options; // Returns the generic parameters that are enabled under the current `#[cfg]` options - let enabled_params = |params: &Interned, item_tree: &ItemTree| { - let enabled = |param| item_tree.attrs(db, krate, param).is_cfg_enabled(cfg_options); - - // In the common case, no parameters will by disabled by `#[cfg]` attributes. - // Therefore, make a first pass to check if all parameters are enabled and, if so, - // clone the `Interned` instead of recreating an identical copy. - let all_type_or_consts_enabled = - params.type_or_consts.iter().all(|(idx, _)| enabled(idx.into())); - let all_lifetimes_enabled = params.lifetimes.iter().all(|(idx, _)| enabled(idx.into())); - - if all_type_or_consts_enabled && all_lifetimes_enabled { - params.clone() - } else { - Interned::new(GenericParams { - type_or_consts: all_type_or_consts_enabled - .then(|| params.type_or_consts.clone()) - .unwrap_or_else(|| { - params - .type_or_consts - .iter() - .filter(|(idx, _)| enabled((*idx).into())) - .map(|(_, param)| param.clone()) - .collect() - }), - lifetimes: all_lifetimes_enabled - .then(|| params.lifetimes.clone()) - .unwrap_or_else(|| { - params - .lifetimes - .iter() - .filter(|(idx, _)| enabled((*idx).into())) - .map(|(_, param)| param.clone()) - .collect() - }), - where_predicates: params.where_predicates.clone(), - }) - } - }; + let enabled_params = + |params: &Interned, item_tree: &ItemTree, parent: GenericModItem| { + let enabled = |param| item_tree.attrs(db, krate, param).is_cfg_enabled(cfg_options); + let attr_owner_ct = |param| AttrOwner::TypeOrConstParamData(parent, param); + let attr_owner_lt = |param| AttrOwner::LifetimeParamData(parent, param); + + // In the common case, no parameters will by disabled by `#[cfg]` attributes. + // Therefore, make a first pass to check if all parameters are enabled and, if so, + // clone the `Interned` instead of recreating an identical copy. + let all_type_or_consts_enabled = + params.type_or_consts.iter().all(|(idx, _)| enabled(attr_owner_ct(idx))); + let all_lifetimes_enabled = + params.lifetimes.iter().all(|(idx, _)| enabled(attr_owner_lt(idx))); + + if all_type_or_consts_enabled && all_lifetimes_enabled { + params.clone() + } else { + Interned::new(GenericParams { + type_or_consts: all_type_or_consts_enabled + .then(|| params.type_or_consts.clone()) + .unwrap_or_else(|| { + params + .type_or_consts + .iter() + .filter(|&(idx, _)| enabled(attr_owner_ct(idx))) + .map(|(_, param)| param.clone()) + .collect() + }), + lifetimes: all_lifetimes_enabled + .then(|| params.lifetimes.clone()) + .unwrap_or_else(|| { + params + .lifetimes + .iter() + .filter(|&(idx, _)| enabled(attr_owner_lt(idx))) + .map(|(_, param)| param.clone()) + .collect() + }), + where_predicates: params.where_predicates.clone(), + }) + } + }; fn id_to_generics( db: &dyn DefDatabase, id: impl for<'db> Lookup< Database<'db> = dyn DefDatabase + 'db, Data = impl ItemTreeLoc, >, - enabled_params: impl Fn(&Interned, &ItemTree) -> Interned, - ) -> Interned { + enabled_params: impl Fn( + &Interned, + &ItemTree, + GenericModItem, + ) -> Interned, + ) -> Interned + where + FileItemTreeId: Into, + { let id = id.lookup(db).item_tree_id(); let tree = id.item_tree(db); let item = &tree[id.value]; - enabled_params(item.generic_params(), &tree) + enabled_params(item.generic_params(), &tree, id.value.into()) } match def { @@ -514,7 +543,8 @@ impl GenericParams { let tree = loc.id.item_tree(db); let item = &tree[loc.id.value]; - let enabled_params = enabled_params(&item.explicit_generic_params, &tree); + let enabled_params = + enabled_params(&item.explicit_generic_params, &tree, loc.id.value.into()); let module = loc.container.module(db); let func_data = db.function_data(id); 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 ac0caaf0dc898..2f7ebbfec13ae 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs @@ -136,15 +136,15 @@ impl From for Literal { Literal::Float(FloatTypeWrapper::new(lit.value().unwrap_or(Default::default())), ty) } LiteralKind::ByteString(bs) => { - let text = bs.value().map(Box::from).unwrap_or_else(Default::default); + let text = bs.value().map_or_else(|_| Default::default(), Box::from); Literal::ByteString(text) } LiteralKind::String(s) => { - let text = s.value().map(Box::from).unwrap_or_else(Default::default); + let text = s.value().map_or_else(|_| Default::default(), Box::from); Literal::String(text) } LiteralKind::CString(s) => { - let text = s.value().map(Box::from).unwrap_or_else(Default::default); + let text = s.value().map_or_else(|_| Default::default(), Box::from); Literal::CString(text) } LiteralKind::Byte(b) => { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs index a60b9f9f3ab75..54cd57110ea84 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs @@ -234,6 +234,14 @@ impl ItemScope { self.impls.iter().copied() } + pub fn all_macro_calls(&self) -> impl Iterator + '_ { + self.macro_invocations.values().copied().chain(self.attr_macros.values().copied()).chain( + self.derive_macros.values().flat_map(|it| { + it.iter().flat_map(|it| it.derive_call_ids.iter().copied().flatten()) + }), + ) + } + pub(crate) fn modules_in_scope(&self) -> impl Iterator + '_ { self.types.values().copied().filter_map(|(def, vis, _)| match def { ModuleDefId::ModuleId(module) => Some((module, vis)), diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs index 610480736ccda..acda64c41fb24 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs @@ -29,6 +29,7 @@ //! //! In general, any item in the `ItemTree` stores its `AstId`, which allows mapping it back to its //! surface syntax. +#![allow(unexpected_cfgs)] mod lower; mod pretty; @@ -57,21 +58,21 @@ use triomphe::Arc; use crate::{ attr::Attrs, db::DefDatabase, - generics::{GenericParams, LifetimeParamData, TypeOrConstParamData}, + generics::GenericParams, path::{GenericArgs, ImportAlias, ModPath, Path, PathKind}, type_ref::{Mutability, TraitRef, TypeBound, TypeRef}, visibility::{RawVisibility, VisibilityExplicitness}, - BlockId, Lookup, + BlockId, LocalLifetimeParamId, LocalTypeOrConstParamId, Lookup, }; #[derive(Copy, Clone, Eq, PartialEq)] pub struct RawVisibilityId(u32); impl RawVisibilityId { - pub const PUB: Self = RawVisibilityId(u32::max_value()); - pub const PRIV_IMPLICIT: Self = RawVisibilityId(u32::max_value() - 1); - pub const PRIV_EXPLICIT: Self = RawVisibilityId(u32::max_value() - 2); - pub const PUB_CRATE: Self = RawVisibilityId(u32::max_value() - 3); + pub const PUB: Self = RawVisibilityId(u32::MAX); + pub const PRIV_IMPLICIT: Self = RawVisibilityId(u32::MAX - 1); + pub const PRIV_EXPLICIT: Self = RawVisibilityId(u32::MAX - 2); + pub const PUB_CRATE: Self = RawVisibilityId(u32::MAX - 3); } impl fmt::Debug for RawVisibilityId { @@ -293,8 +294,8 @@ pub enum AttrOwner { Variant(FileItemTreeId), Field(Idx), Param(Idx), - TypeOrConstParamData(Idx), - LifetimeParamData(Idx), + TypeOrConstParamData(GenericModItem, LocalTypeOrConstParamId), + LifetimeParamData(GenericModItem, LocalLifetimeParamId), } macro_rules! from_attrs { @@ -314,8 +315,6 @@ from_attrs!( Variant(FileItemTreeId), Field(Idx), Param(Idx), - TypeOrConstParamData(Idx), - LifetimeParamData(Idx), ); /// Trait implemented by all nodes in the item tree. @@ -465,12 +464,49 @@ macro_rules! mod_items { )+ } + #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] + pub enum GenericModItem { + $( + $( + #[cfg_attr(ignore_fragment, $generic_params)] + $typ(FileItemTreeId<$typ>), + )? + )+ + } + + impl From for ModItem { + fn from(id: GenericModItem) -> ModItem { + match id { + $( + $( + #[cfg_attr(ignore_fragment, $generic_params)] + GenericModItem::$typ(id) => ModItem::$typ(id), + )? + )+ + } + } + } + + impl From for AttrOwner { + fn from(t: GenericModItem) -> AttrOwner { + AttrOwner::ModItem(t.into()) + } + } + $( impl From> for ModItem { fn from(id: FileItemTreeId<$typ>) -> ModItem { ModItem::$typ(id) } } + $( + #[cfg_attr(ignore_fragment, $generic_params)] + impl From> for GenericModItem { + fn from(id: FileItemTreeId<$typ>) -> GenericModItem { + GenericModItem::$typ(id) + } + } + )? )+ $( diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs index 4b5ef56d782e9..199b8daa37efe 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs @@ -4,6 +4,7 @@ use std::collections::hash_map::Entry; use hir_expand::{mod_path::path, name, name::AsName, span_map::SpanMapRef, HirFileId}; use la_arena::Arena; +use rustc_hash::FxHashMap; use span::{AstIdMap, SyntaxContextId}; use syntax::{ ast::{self, HasModuleItem, HasName, HasTypeBounds, IsString}, @@ -16,11 +17,11 @@ use crate::{ generics::{GenericParams, GenericParamsCollector, TypeParamData, TypeParamProvenance}, item_tree::{ AssocItem, AttrOwner, Const, Either, Enum, ExternBlock, ExternCrate, Field, FieldAstId, - Fields, FileItemTreeId, FnFlags, Function, GenericArgs, Idx, IdxRange, Impl, ImportAlias, - Interned, ItemTree, ItemTreeData, ItemTreeNode, Macro2, MacroCall, MacroRules, Mod, - ModItem, ModKind, ModPath, Mutability, Name, Param, ParamAstId, Path, Range, RawAttrs, - RawIdx, RawVisibilityId, Static, Struct, StructKind, Trait, TraitAlias, TypeAlias, Union, - Use, UseTree, UseTreeKind, Variant, + Fields, FileItemTreeId, FnFlags, Function, GenericArgs, GenericModItem, Idx, IdxRange, + Impl, ImportAlias, Interned, ItemTree, ItemTreeData, ItemTreeNode, Macro2, MacroCall, + MacroRules, Mod, ModItem, ModKind, ModPath, Mutability, Name, Param, ParamAstId, Path, + Range, RawAttrs, RawIdx, RawVisibilityId, Static, Struct, StructKind, Trait, TraitAlias, + TypeAlias, Union, Use, UseTree, UseTreeKind, Variant, }, path::AssociatedTypeBinding, type_ref::{LifetimeRef, TraitBoundModifier, TraitRef, TypeBound, TypeRef}, @@ -36,6 +37,8 @@ pub(super) struct Ctx<'a> { db: &'a dyn DefDatabase, tree: ItemTree, source_ast_id_map: Arc, + generic_param_attr_buffer: + FxHashMap, RawAttrs>, body_ctx: crate::lower::LowerCtx<'a>, } @@ -44,6 +47,7 @@ impl<'a> Ctx<'a> { Self { db, tree: ItemTree::default(), + generic_param_attr_buffer: FxHashMap::default(), source_ast_id_map: db.ast_id_map(file), body_ctx: crate::lower::LowerCtx::new(db, file), } @@ -56,6 +60,7 @@ impl<'a> Ctx<'a> { pub(super) fn lower_module_items(mut self, item_owner: &dyn HasModuleItem) -> ItemTree { self.tree.top_level = item_owner.items().flat_map(|item| self.lower_mod_item(&item)).collect(); + assert!(self.generic_param_attr_buffer.is_empty()); self.tree } @@ -89,6 +94,7 @@ impl<'a> Ctx<'a> { } } + assert!(self.generic_param_attr_buffer.is_empty()); self.tree } @@ -117,6 +123,7 @@ impl<'a> Ctx<'a> { } } + assert!(self.generic_param_attr_buffer.is_empty()); self.tree } @@ -185,10 +192,12 @@ impl<'a> Ctx<'a> { let visibility = self.lower_visibility(strukt); let name = strukt.name()?.as_name(); let ast_id = self.source_ast_id_map.ast_id(strukt); - let generic_params = self.lower_generic_params(HasImplicitSelf::No, strukt); let fields = self.lower_fields(&strukt.kind()); + let generic_params = self.lower_generic_params(HasImplicitSelf::No, strukt); let res = Struct { name, visibility, generic_params, fields, ast_id }; - Some(id(self.data().structs.alloc(res))) + let id = id(self.data().structs.alloc(res)); + self.write_generic_params_attributes(id.into()); + Some(id) } fn lower_fields(&mut self, strukt_kind: &ast::StructKind) -> Fields { @@ -252,28 +261,32 @@ impl<'a> Ctx<'a> { let visibility = self.lower_visibility(union); let name = union.name()?.as_name(); let ast_id = self.source_ast_id_map.ast_id(union); - let generic_params = self.lower_generic_params(HasImplicitSelf::No, union); let fields = match union.record_field_list() { Some(record_field_list) => self.lower_fields(&StructKind::Record(record_field_list)), None => Fields::Record(IdxRange::new(self.next_field_idx()..self.next_field_idx())), }; + let generic_params = self.lower_generic_params(HasImplicitSelf::No, union); let res = Union { name, visibility, generic_params, fields, ast_id }; - Some(id(self.data().unions.alloc(res))) + let id = id(self.data().unions.alloc(res)); + self.write_generic_params_attributes(id.into()); + Some(id) } fn lower_enum(&mut self, enum_: &ast::Enum) -> Option> { let visibility = self.lower_visibility(enum_); let name = enum_.name()?.as_name(); let ast_id = self.source_ast_id_map.ast_id(enum_); - let generic_params = self.lower_generic_params(HasImplicitSelf::No, enum_); let variants = match &enum_.variant_list() { Some(variant_list) => self.lower_variants(variant_list), None => { FileItemTreeId(self.next_variant_idx())..FileItemTreeId(self.next_variant_idx()) } }; + let generic_params = self.lower_generic_params(HasImplicitSelf::No, enum_); let res = Enum { name, visibility, generic_params, variants, ast_id }; - Some(id(self.data().enums.alloc(res))) + let id = id(self.data().enums.alloc(res)); + self.write_generic_params_attributes(id.into()); + Some(id) } fn lower_variants(&mut self, variants: &ast::VariantList) -> Range> { @@ -414,7 +427,9 @@ impl<'a> Ctx<'a> { flags, }; - Some(id(self.data().functions.alloc(res))) + let id = id(self.data().functions.alloc(res)); + self.write_generic_params_attributes(id.into()); + Some(id) } fn lower_type_alias( @@ -428,7 +443,9 @@ impl<'a> Ctx<'a> { let ast_id = self.source_ast_id_map.ast_id(type_alias); let generic_params = self.lower_generic_params(HasImplicitSelf::No, type_alias); let res = TypeAlias { name, visibility, bounds, generic_params, type_ref, ast_id }; - Some(id(self.data().type_aliases.alloc(res))) + let id = id(self.data().type_aliases.alloc(res)); + self.write_generic_params_attributes(id.into()); + Some(id) } fn lower_static(&mut self, static_: &ast::Static) -> Option> { @@ -475,8 +492,6 @@ impl<'a> Ctx<'a> { let name = trait_def.name()?.as_name(); let visibility = self.lower_visibility(trait_def); let ast_id = self.source_ast_id_map.ast_id(trait_def); - let generic_params = - self.lower_generic_params(HasImplicitSelf::Yes(trait_def.type_bound_list()), trait_def); let is_auto = trait_def.auto_token().is_some(); let is_unsafe = trait_def.unsafe_token().is_some(); @@ -487,8 +502,12 @@ impl<'a> Ctx<'a> { .filter_map(|item_node| self.lower_assoc_item(&item_node)) .collect(); + let generic_params = + self.lower_generic_params(HasImplicitSelf::Yes(trait_def.type_bound_list()), trait_def); let def = Trait { name, visibility, generic_params, is_auto, is_unsafe, items, ast_id }; - Some(id(self.data().traits.alloc(def))) + let id = id(self.data().traits.alloc(def)); + self.write_generic_params_attributes(id.into()); + Some(id) } fn lower_trait_alias( @@ -504,19 +523,18 @@ impl<'a> Ctx<'a> { ); let alias = TraitAlias { name, visibility, generic_params, ast_id }; - Some(id(self.data().trait_aliases.alloc(alias))) + let id = id(self.data().trait_aliases.alloc(alias)); + self.write_generic_params_attributes(id.into()); + Some(id) } fn lower_impl(&mut self, impl_def: &ast::Impl) -> Option> { let ast_id = self.source_ast_id_map.ast_id(impl_def); - // Note that trait impls don't get implicit `Self` unlike traits, because here they are a - // type alias rather than a type parameter, so this is handled by the resolver. - let generic_params = self.lower_generic_params(HasImplicitSelf::No, impl_def); // FIXME: If trait lowering fails, due to a non PathType for example, we treat this impl // as if it was an non-trait impl. Ideally we want to create a unique missing ref that only // equals itself. - let target_trait = impl_def.trait_().and_then(|tr| self.lower_trait_ref(&tr)); let self_ty = self.lower_type_ref(&impl_def.self_ty()?); + let target_trait = impl_def.trait_().and_then(|tr| self.lower_trait_ref(&tr)); let is_negative = impl_def.excl_token().is_some(); let is_unsafe = impl_def.unsafe_token().is_some(); @@ -527,9 +545,14 @@ impl<'a> Ctx<'a> { .flat_map(|it| it.assoc_items()) .filter_map(|item| self.lower_assoc_item(&item)) .collect(); + // Note that trait impls don't get implicit `Self` unlike traits, because here they are a + // type alias rather than a type parameter, so this is handled by the resolver. + let generic_params = self.lower_generic_params(HasImplicitSelf::No, impl_def); let res = Impl { generic_params, target_trait, self_ty, is_negative, is_unsafe, items, ast_id }; - Some(id(self.data().impls.alloc(res))) + let id = id(self.data().impls.alloc(res)); + self.write_generic_params_attributes(id.into()); + Some(id) } fn lower_use(&mut self, use_item: &ast::Use) -> Option> { @@ -616,11 +639,30 @@ impl<'a> Ctx<'a> { id(self.data().extern_blocks.alloc(res)) } + fn write_generic_params_attributes(&mut self, parent: GenericModItem) { + self.generic_param_attr_buffer.drain().for_each(|(idx, attrs)| { + self.tree.attrs.insert( + match idx { + Either::Left(id) => AttrOwner::TypeOrConstParamData(parent, id), + Either::Right(id) => AttrOwner::LifetimeParamData(parent, id), + }, + attrs, + ); + }) + } + fn lower_generic_params( &mut self, has_implicit_self: HasImplicitSelf, node: &dyn ast::HasGenericParams, ) -> Interned { + debug_assert!(self.generic_param_attr_buffer.is_empty(),); + let add_param_attrs = |item: Either, + param| { + let attrs = RawAttrs::new(self.db.upcast(), ¶m, self.body_ctx.span_map()); + debug_assert!(self.generic_param_attr_buffer.insert(item, attrs).is_none()); + }; + self.body_ctx.take_impl_traits_bounds(); let mut generics = GenericParamsCollector::default(); if let HasImplicitSelf::Yes(bounds) = has_implicit_self { @@ -635,28 +677,13 @@ impl<'a> Ctx<'a> { ); // add super traits as bounds on Self // i.e., `trait Foo: Bar` is equivalent to `trait Foo where Self: Bar` - let self_param = TypeRef::Path(name![Self].into()); - generics.fill_bounds(&self.body_ctx, bounds, Either::Left(self_param)); + generics.fill_bounds( + &self.body_ctx, + bounds, + Either::Left(TypeRef::Path(name![Self].into())), + ); } - let add_param_attrs = |item: Either, - param| { - let attrs = RawAttrs::new(self.db.upcast(), ¶m, self.body_ctx.span_map()); - // This is identical to the body of `Ctx::add_attrs()` but we can't call that here - // because it requires `&mut self` and the call to `generics.fill()` below also - // references `self`. - match self.tree.attrs.entry(match item { - Either::Right(id) => id.into(), - Either::Left(id) => id.into(), - }) { - Entry::Occupied(mut entry) => { - *entry.get_mut() = entry.get().merge(attrs); - } - Entry::Vacant(entry) => { - entry.insert(attrs); - } - } - }; generics.fill(&self.body_ctx, node, add_param_attrs); Interned::new(generics.finish()) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs index cef2a3fb86637..2803678a33063 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs @@ -8,8 +8,8 @@ use crate::{ generics::{TypeOrConstParamData, WherePredicate, WherePredicateTypeTarget}, item_tree::{ AttrOwner, Const, DefDatabase, Enum, ExternBlock, ExternCrate, Field, FieldAstId, Fields, - FileItemTreeId, FnFlags, Function, GenericParams, Impl, Interned, ItemTree, Macro2, - MacroCall, MacroRules, Mod, ModItem, ModKind, Param, ParamAstId, Path, RawAttrs, + FileItemTreeId, FnFlags, Function, GenericModItem, GenericParams, Impl, Interned, ItemTree, + Macro2, MacroCall, MacroRules, Mod, ModItem, ModKind, Param, ParamAstId, Path, RawAttrs, RawVisibilityId, Static, Struct, Trait, TraitAlias, TypeAlias, TypeBound, TypeRef, Union, Use, UseTree, UseTreeKind, Variant, }, @@ -276,7 +276,7 @@ impl Printer<'_> { w!(self, "extern \"{}\" ", abi); } w!(self, "fn {}", name.display(self.db.upcast())); - self.print_generic_params(explicit_generic_params); + self.print_generic_params(explicit_generic_params, it.into()); w!(self, "("); if !params.is_empty() { self.indented(|this| { @@ -316,7 +316,7 @@ impl Printer<'_> { self.print_ast_id(ast_id.erase()); self.print_visibility(*visibility); w!(self, "struct {}", name.display(self.db.upcast())); - self.print_generic_params(generic_params); + self.print_generic_params(generic_params, it.into()); self.print_fields_and_where_clause(fields, generic_params); if matches!(fields, Fields::Record(_)) { wln!(self); @@ -329,7 +329,7 @@ impl Printer<'_> { self.print_ast_id(ast_id.erase()); self.print_visibility(*visibility); w!(self, "union {}", name.display(self.db.upcast())); - self.print_generic_params(generic_params); + self.print_generic_params(generic_params, it.into()); self.print_fields_and_where_clause(fields, generic_params); if matches!(fields, Fields::Record(_)) { wln!(self); @@ -342,7 +342,7 @@ impl Printer<'_> { self.print_ast_id(ast_id.erase()); self.print_visibility(*visibility); w!(self, "enum {}", name.display(self.db.upcast())); - self.print_generic_params(generic_params); + self.print_generic_params(generic_params, it.into()); self.print_where_clause_and_opening_brace(generic_params); self.indented(|this| { for variant in FileItemTreeId::range_iter(variants.clone()) { @@ -394,7 +394,7 @@ impl Printer<'_> { w!(self, "auto "); } w!(self, "trait {}", name.display(self.db.upcast())); - self.print_generic_params(generic_params); + self.print_generic_params(generic_params, it.into()); self.print_where_clause_and_opening_brace(generic_params); self.indented(|this| { for item in &**items { @@ -408,7 +408,7 @@ impl Printer<'_> { self.print_ast_id(ast_id.erase()); self.print_visibility(*visibility); w!(self, "trait {}", name.display(self.db.upcast())); - self.print_generic_params(generic_params); + self.print_generic_params(generic_params, it.into()); w!(self, " = "); self.print_where_clause(generic_params); w!(self, ";"); @@ -429,7 +429,7 @@ impl Printer<'_> { w!(self, "unsafe"); } w!(self, "impl"); - self.print_generic_params(generic_params); + self.print_generic_params(generic_params, it.into()); w!(self, " "); if *is_negative { w!(self, "!"); @@ -453,7 +453,7 @@ impl Printer<'_> { self.print_ast_id(ast_id.erase()); self.print_visibility(*visibility); w!(self, "type {}", name.display(self.db.upcast())); - self.print_generic_params(generic_params); + self.print_generic_params(generic_params, it.into()); if !bounds.is_empty() { w!(self, ": "); self.print_type_bounds(bounds); @@ -525,7 +525,7 @@ impl Printer<'_> { print_path(self.db, path, self).unwrap(); } - fn print_generic_params(&mut self, params: &GenericParams) { + fn print_generic_params(&mut self, params: &GenericParams, parent: GenericModItem) { if params.is_empty() { return; } @@ -537,7 +537,7 @@ impl Printer<'_> { w!(self, ", "); } first = false; - self.print_attrs_of(idx, " "); + self.print_attrs_of(AttrOwner::LifetimeParamData(parent, idx), " "); w!(self, "{}", lt.name.display(self.db.upcast())); } for (idx, x) in params.type_or_consts.iter() { @@ -545,7 +545,7 @@ impl Printer<'_> { w!(self, ", "); } first = false; - self.print_attrs_of(idx, " "); + self.print_attrs_of(AttrOwner::TypeOrConstParamData(parent, idx), " "); match x { TypeOrConstParamData::TypeParamData(ty) => match &ty.name { Some(name) => w!(self, "{}", name.display(self.db.upcast())), diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/tests.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/tests.rs index 48da876ac1581..79bab11998bb6 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/tests.rs @@ -427,10 +427,18 @@ fn generics_with_attributes() { check( r#" struct S<#[cfg(never)] T>; +struct S; +struct S; "#, expect![[r#" // AstId: 1 pub(self) struct S<#[cfg(never)] T>; + + // AstId: 2 + pub(self) struct S; + + // AstId: 3 + pub(self) struct S; "#]], ) } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/lower.rs index d574d80a8e0d4..ecd8d79f20bef 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lower.rs @@ -1,26 +1,34 @@ //! Context for lowering paths. -use std::cell::OnceCell; +use std::cell::{OnceCell, RefCell}; use hir_expand::{ span_map::{SpanMap, SpanMapRef}, AstId, HirFileId, InFile, }; +use intern::Interned; use span::{AstIdMap, AstIdNode}; use syntax::ast; use triomphe::Arc; -use crate::{db::DefDatabase, path::Path}; +use crate::{db::DefDatabase, path::Path, type_ref::TypeBound}; pub struct LowerCtx<'a> { pub db: &'a dyn DefDatabase, file_id: HirFileId, span_map: OnceCell, ast_id_map: OnceCell>, + impl_trait_bounds: RefCell>>>, } impl<'a> LowerCtx<'a> { pub fn new(db: &'a dyn DefDatabase, file_id: HirFileId) -> Self { - LowerCtx { db, file_id, span_map: OnceCell::new(), ast_id_map: OnceCell::new() } + LowerCtx { + db, + file_id, + span_map: OnceCell::new(), + ast_id_map: OnceCell::new(), + impl_trait_bounds: RefCell::new(Vec::new()), + } } pub fn with_span_map_cell( @@ -28,7 +36,13 @@ impl<'a> LowerCtx<'a> { file_id: HirFileId, span_map: OnceCell, ) -> Self { - LowerCtx { db, file_id, span_map, ast_id_map: OnceCell::new() } + LowerCtx { + db, + file_id, + span_map, + ast_id_map: OnceCell::new(), + impl_trait_bounds: RefCell::new(Vec::new()), + } } pub(crate) fn span_map(&self) -> SpanMapRef<'_> { @@ -45,4 +59,12 @@ impl<'a> LowerCtx<'a> { self.ast_id_map.get_or_init(|| self.db.ast_id_map(self.file_id)).ast_id(item), ) } + + pub fn update_impl_traits_bounds(&self, bounds: Vec>) { + self.impl_trait_bounds.borrow_mut().push(bounds); + } + + pub fn take_impl_traits_bounds(&self) -> Vec>> { + self.impl_trait_bounds.take() + } } 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 a4864c74d77e4..6f605c0cb33db 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 @@ -186,3 +186,33 @@ fn#0:1@45..47#0# foo#0:1@48..51#0#(#0:1@51..52#0#�:1@52..53#0#self#0:1@53..57# }#0:1@76..77#0#"#]], ); } + +#[test] +fn attribute_macro_doc_desugaring() { + check( + r#" +//- proc_macros: identity +#[proc_macros::identity] +/// doc string \n with newline +/** + MultiLines Doc + MultiLines Doc +*/ +#[doc = "doc attr"] +struct S; +"#, + expect![[r##" +#[proc_macros::identity] +/// doc string \n with newline +/** + MultiLines Doc + MultiLines Doc +*/ +#[doc = "doc attr"] +struct S; + +#[doc = " doc string \\n with newline"] +#[doc = "\n MultiLines Doc\n MultiLines Doc\n"] +#[doc = "doc attr"] struct S;"##]], + ); +} 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 0a6cd0fe9ed5f..262bc538b9415 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs @@ -5,7 +5,7 @@ use std::{cmp::Ordering, iter, mem, ops::Not}; -use base_db::{CrateId, Dependency, FileId}; +use base_db::{CrateId, CrateOrigin, Dependency, FileId, LangCrateOrigin}; use cfg::{CfgExpr, CfgOptions}; use either::Either; use hir_expand::{ @@ -15,15 +15,13 @@ use hir_expand::{ builtin_fn_macro::find_builtin_macro, name::{name, AsName, Name}, proc_macro::CustomProcMacroExpander, - ExpandResult, ExpandTo, HirFileId, InFile, MacroCallId, MacroCallKind, MacroCallLoc, - MacroDefId, MacroDefKind, + ExpandTo, HirFileId, InFile, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind, }; use itertools::{izip, Itertools}; use la_arena::Idx; use limit::Limit; use rustc_hash::{FxHashMap, FxHashSet}; use span::{Edition, ErasedFileAstId, FileAstId, Span, SyntaxContextId}; -use stdx::always; use syntax::ast; use triomphe::Arc; @@ -279,7 +277,8 @@ impl DefCollector<'_> { fn seed_with_top_level(&mut self) { let _p = tracing::span!(tracing::Level::INFO, "seed_with_top_level").entered(); - let file_id = self.db.crate_graph()[self.def_map.krate].root_file_id; + let crate_graph = self.db.crate_graph(); + let file_id = crate_graph[self.def_map.krate].root_file_id; let item_tree = self.db.file_item_tree(file_id.into()); let attrs = item_tree.top_level_attrs(self.db, self.def_map.krate); let crate_data = Arc::get_mut(&mut self.def_map.data).unwrap(); @@ -288,19 +287,14 @@ impl DefCollector<'_> { crate_data.proc_macro_loading_error = Some(e.clone()); } - for (name, dep) in &self.deps { - if dep.is_prelude() { - crate_data - .extern_prelude - .insert(name.clone(), (CrateRootModuleId { krate: dep.crate_id }, None)); - } - } + let mut process = true; // Process other crate-level attributes. for attr in &*attrs { if let Some(cfg) = attr.cfg() { if self.cfg_options.check(&cfg) == Some(false) { - return; + process = false; + break; } } let Some(attr_name) = attr.path.as_ident() else { continue }; @@ -350,9 +344,38 @@ impl DefCollector<'_> { } } - crate_data.shrink_to_fit(); + for (name, dep) in &self.deps { + if dep.is_prelude() { + // This is a bit confusing but the gist is that `no_core` and `no_std` remove the + // sysroot dependence on `core` and `std` respectively. Our `CrateGraph` is eagerly + // constructed with them in place no matter what though, since at that point we + // don't do pre-configured attribute resolution yet. + // So here check if we are no_core / no_std and we are trying to add the + // corresponding dep from the sysroot + let skip = match crate_graph[dep.crate_id].origin { + CrateOrigin::Lang(LangCrateOrigin::Core) => { + crate_data.no_core && dep.is_sysroot() + } + CrateOrigin::Lang(LangCrateOrigin::Std) => { + crate_data.no_std && dep.is_sysroot() + } + _ => false, + }; + if skip { + continue; + } + crate_data + .extern_prelude + .insert(name.clone(), (CrateRootModuleId { krate: dep.crate_id }, None)); + } + } + self.inject_prelude(); + if !process { + return; + } + ModCollector { def_collector: self, macro_depth: 0, @@ -362,6 +385,7 @@ impl DefCollector<'_> { mod_dir: ModDir::root(), } .collect_in_top_module(item_tree.top_level_items()); + Arc::get_mut(&mut self.def_map.data).unwrap().shrink_to_fit(); } fn seed_with_inner(&mut self, tree_id: TreeId) { @@ -519,15 +543,12 @@ impl DefCollector<'_> { let krate = if self.def_map.data.no_std { name![core] + } else if self.def_map.extern_prelude().any(|(name, _)| *name == name![std]) { + name![std] } else { - let std = name![std]; - if self.def_map.extern_prelude().any(|(name, _)| *name == std) { - std - } else { - // If `std` does not exist for some reason, fall back to core. This mostly helps - // keep r-a's own tests minimal. - name![core] - } + // If `std` does not exist for some reason, fall back to core. This mostly helps + // keep r-a's own tests minimal. + name![core] }; let edition = match self.def_map.data.edition { @@ -1389,31 +1410,6 @@ impl DefCollector<'_> { } let file_id = macro_call_id.as_file(); - // First, fetch the raw expansion result for purposes of error reporting. This goes through - // `parse_macro_expansion_error` to avoid depending on the full expansion result (to improve - // incrementality). - // FIXME: This kind of error fetching feels a bit odd? - let ExpandResult { value: errors, err } = - self.db.parse_macro_expansion_error(macro_call_id); - if let Some(err) = err { - let loc: MacroCallLoc = self.db.lookup_intern_macro_call(macro_call_id); - let diag = match err { - // why is this reported here? - hir_expand::ExpandError::UnresolvedProcMacro(krate) => { - always!(krate == loc.def.krate); - DefDiagnostic::unresolved_proc_macro(module_id, loc.kind.clone(), loc.def.krate) - } - _ => DefDiagnostic::macro_error(module_id, loc.kind, err.to_string()), - }; - - self.def_map.diagnostics.push(diag); - } - if !errors.is_empty() { - let loc: MacroCallLoc = self.db.lookup_intern_macro_call(macro_call_id); - let diag = DefDiagnostic::macro_expansion_parse_error(module_id, loc.kind, errors); - self.def_map.diagnostics.push(diag); - } - // Then, fetch and process the item tree. This will reuse the expansion result from above. let item_tree = self.db.file_item_tree(file_id); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/diagnostics.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/diagnostics.rs index 8c7fdaaf58b32..523a4c107b358 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/diagnostics.rs @@ -6,7 +6,7 @@ use base_db::CrateId; use cfg::{CfgExpr, CfgOptions}; use hir_expand::{attrs::AttrId, ErasedAstId, MacroCallKind}; use la_arena::Idx; -use syntax::{ast, SyntaxError}; +use syntax::ast; use crate::{ item_tree::{self, ItemTreeId}, @@ -23,8 +23,6 @@ pub enum DefDiagnosticKind { UnconfiguredCode { ast: ErasedAstId, cfg: CfgExpr, opts: CfgOptions }, UnresolvedProcMacro { ast: MacroCallKind, krate: CrateId }, UnresolvedMacroCall { ast: MacroCallKind, path: ModPath }, - MacroError { ast: MacroCallKind, message: String }, - MacroExpansionParseError { ast: MacroCallKind, errors: Box<[SyntaxError]> }, UnimplementedBuiltinMacro { ast: AstId }, InvalidDeriveTarget { ast: AstId, id: usize }, MalformedDerive { ast: AstId, id: usize }, @@ -98,7 +96,7 @@ impl DefDiagnostic { // FIXME: This is used for a lot of things, unresolved proc macros, disabled proc macros, etc // yet the diagnostic handler in ide-diagnostics has to figure out what happened because this // struct loses all that information! - pub(crate) fn unresolved_proc_macro( + pub fn unresolved_proc_macro( container: LocalModuleId, ast: MacroCallKind, krate: CrateId, @@ -106,25 +104,6 @@ impl DefDiagnostic { Self { in_module: container, kind: DefDiagnosticKind::UnresolvedProcMacro { ast, krate } } } - pub(crate) fn macro_error( - container: LocalModuleId, - ast: MacroCallKind, - message: String, - ) -> Self { - Self { in_module: container, kind: DefDiagnosticKind::MacroError { ast, message } } - } - - pub(crate) fn macro_expansion_parse_error( - container: LocalModuleId, - ast: MacroCallKind, - errors: Box<[SyntaxError]>, - ) -> Self { - Self { - in_module: container, - kind: DefDiagnosticKind::MacroExpansionParseError { ast, errors }, - } - } - // FIXME: Whats the difference between this and unresolved_proc_macro pub(crate) fn unresolved_macro_call( container: LocalModuleId, diff --git a/src/tools/rust-analyzer/crates/hir-def/src/path/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/path/lower.rs index b3c41a073c600..6af5261411168 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/path/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/path/lower.rs @@ -208,6 +208,13 @@ pub(super) fn lower_generic_args( .and_then(|args| lower_generic_args(lower_ctx, args)) .map(Interned::new); let type_ref = assoc_type_arg.ty().map(|it| TypeRef::from_ast(lower_ctx, it)); + let type_ref = type_ref.inspect(|tr| { + tr.walk(&mut |tr| { + if let TypeRef::ImplTrait(bounds) = tr { + lower_ctx.update_impl_traits_bounds(bounds.clone()); + } + }); + }); let bounds = if let Some(l) = assoc_type_arg.type_bound_list() { l.bounds() .map(|it| Interned::new(TypeBound::from_ast(lower_ctx, it))) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs index fadab858aa149..1602b173858d9 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs @@ -1,5 +1,5 @@ //! Name resolution façade. -use std::{fmt, hash::BuildHasherDefault, mem}; +use std::{fmt, hash::BuildHasherDefault, iter, mem}; use base_db::CrateId; use hir_expand::{ @@ -591,13 +591,13 @@ impl Resolver { pub fn where_predicates_in_scope( &self, - ) -> impl Iterator { + ) -> impl Iterator { self.scopes() .filter_map(|scope| match scope { - Scope::GenericParams { params, .. } => Some(params), + Scope::GenericParams { params, def } => Some((params, def)), _ => None, }) - .flat_map(|params| params.where_predicates.iter()) + .flat_map(|(params, def)| params.where_predicates.iter().zip(iter::repeat(def))) } pub fn generic_def(&self) -> Option { diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs b/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs index f8bf88d83cd9c..85ec02ae07313 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs @@ -5,7 +5,7 @@ use base_db::CrateId; use cfg::CfgExpr; use either::Either; use intern::Interned; -use mbe::{syntax_node_to_token_tree, DelimiterKind, Punct}; +use mbe::{syntax_node_to_token_tree, DelimiterKind, DocCommentDesugarMode, Punct}; use smallvec::{smallvec, SmallVec}; use span::{Span, SyntaxContextId}; use syntax::unescape; @@ -239,7 +239,12 @@ impl Attr { span, }))) } else if let Some(tt) = ast.token_tree() { - let tree = syntax_node_to_token_tree(tt.syntax(), span_map, span); + let tree = syntax_node_to_token_tree( + tt.syntax(), + span_map, + span, + DocCommentDesugarMode::ProcMacro, + ); Some(Interned::new(AttrInput::TokenTree(Box::new(tree)))) } else { None @@ -247,8 +252,18 @@ impl Attr { Some(Attr { id, path, input, ctxt: span.ctx }) } - fn from_tt(db: &dyn ExpandDatabase, tt: &[tt::TokenTree], id: AttrId) -> Option { - let ctxt = tt.first()?.first_span().ctx; + fn from_tt(db: &dyn ExpandDatabase, mut tt: &[tt::TokenTree], id: AttrId) -> Option { + if matches!(tt, + [tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { text, .. })), ..] + if text == "unsafe" + ) { + match tt.get(1) { + Some(tt::TokenTree::Subtree(subtree)) => tt = &subtree.token_trees, + _ => return None, + } + } + let first = &tt.first()?; + let ctxt = first.first_span().ctx; let path_end = tt .iter() .position(|tt| { @@ -430,7 +445,7 @@ fn inner_attributes( // Input subtree is: `(cfg, $(attr),+)` // Split it up into a `cfg` subtree and the `attr` subtrees. -pub fn parse_cfg_attr_input( +fn parse_cfg_attr_input( subtree: &Subtree, ) -> Option<(&[tt::TokenTree], impl Iterator)> { let mut parts = subtree diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin_derive_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin_derive_macro.rs index 94681b42a9b11..c7cdc5e92200f 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin_derive_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin_derive_macro.rs @@ -1,6 +1,7 @@ //! Builtin derives. use itertools::izip; +use mbe::DocCommentDesugarMode; use rustc_hash::FxHashSet; use span::{MacroCallId, Span}; use stdx::never; @@ -262,7 +263,12 @@ fn parse_adt(tt: &tt::Subtree, call_site: Span) -> Result { param_type_set.insert(it.as_name()); - mbe::syntax_node_to_token_tree(it.syntax(), tm, call_site) + mbe::syntax_node_to_token_tree( + it.syntax(), + tm, + call_site, + DocCommentDesugarMode::ProcMacro, + ) } None => { tt::Subtree::empty(::tt::DelimSpan { open: call_site, close: call_site }) @@ -270,15 +276,27 @@ fn parse_adt(tt: &tt::Subtree, call_site: Span) -> Result it - .type_bound_list() - .map(|it| mbe::syntax_node_to_token_tree(it.syntax(), tm, call_site)), + ast::TypeOrConstParam::Type(it) => it.type_bound_list().map(|it| { + mbe::syntax_node_to_token_tree( + it.syntax(), + tm, + call_site, + DocCommentDesugarMode::ProcMacro, + ) + }), ast::TypeOrConstParam::Const(_) => None, }; let ty = if let ast::TypeOrConstParam::Const(param) = param { let ty = param .ty() - .map(|ty| mbe::syntax_node_to_token_tree(ty.syntax(), tm, call_site)) + .map(|ty| { + mbe::syntax_node_to_token_tree( + ty.syntax(), + tm, + call_site, + DocCommentDesugarMode::ProcMacro, + ) + }) .unwrap_or_else(|| { tt::Subtree::empty(::tt::DelimSpan { open: call_site, close: call_site }) }); @@ -292,7 +310,14 @@ fn parse_adt(tt: &tt::Subtree, call_site: Span) -> Result Result Option<(String, Span)> { let span = lit.span; let lit = ast::make::tokens::literal(&lit.to_string()); let token = ast::String::cast(lit)?; - token.value().map(|it| (it.into_owned(), span)) + token.value().ok().map(|it| (it.into_owned(), span)) } fn unquote_char(lit: &tt::Literal) -> Option<(char, Span)> { let span = lit.span; let lit = ast::make::tokens::literal(&lit.to_string()); let token = ast::Char::cast(lit)?; - token.value().zip(Some(span)) + token.value().ok().zip(Some(span)) } fn unquote_byte_string(lit: &tt::Literal) -> Option<(Vec, Span)> { let span = lit.span; let lit = ast::make::tokens::literal(&lit.to_string()); let token = ast::ByteString::cast(lit)?; - token.value().map(|it| (it.into_owned(), span)) + token.value().ok().map(|it| (it.into_owned(), span)) } fn compile_error_expand( diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs index d7233a8923fee..2e5fa6131a779 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs @@ -3,7 +3,7 @@ use base_db::{salsa, CrateId, FileId, SourceDatabase}; use either::Either; use limit::Limit; -use mbe::{syntax_node_to_token_tree, MatchedArmIndex}; +use mbe::{syntax_node_to_token_tree, DocCommentDesugarMode, MatchedArmIndex}; use rustc_hash::FxHashSet; use span::{AstIdMap, Span, SyntaxContextData, SyntaxContextId}; use syntax::{ast, AstNode, Parse, SyntaxElement, SyntaxError, SyntaxNode, SyntaxToken, T}; @@ -132,7 +132,7 @@ pub trait ExpandDatabase: SourceDatabase { fn parse_macro_expansion_error( &self, macro_call: MacroCallId, - ) -> ExpandResult>; + ) -> Option>>>; } /// This expands the given macro call, but with different arguments. This is @@ -156,11 +156,25 @@ pub fn expand_speculative( // Build the subtree and token mapping for the speculative args let (mut tt, undo_info) = match loc.kind { MacroCallKind::FnLike { .. } => ( - mbe::syntax_node_to_token_tree(speculative_args, span_map, span), + mbe::syntax_node_to_token_tree( + speculative_args, + span_map, + span, + if loc.def.is_proc_macro() { + DocCommentDesugarMode::ProcMacro + } else { + DocCommentDesugarMode::Mbe + }, + ), SyntaxFixupUndoInfo::NONE, ), MacroCallKind::Attr { .. } if loc.def.is_attribute_derive() => ( - mbe::syntax_node_to_token_tree(speculative_args, span_map, span), + mbe::syntax_node_to_token_tree( + speculative_args, + span_map, + span, + DocCommentDesugarMode::ProcMacro, + ), SyntaxFixupUndoInfo::NONE, ), MacroCallKind::Derive { derive_attr_index: index, .. } @@ -176,7 +190,12 @@ pub fn expand_speculative( let censor_cfg = cfg_process::process_cfg_attrs(db, speculative_args, &loc).unwrap_or_default(); - let mut fixups = fixup::fixup_syntax(span_map, speculative_args, span); + let mut fixups = fixup::fixup_syntax( + span_map, + speculative_args, + span, + DocCommentDesugarMode::ProcMacro, + ); fixups.append.retain(|it, _| match it { syntax::NodeOrToken::Token(_) => true, it => !censor.contains(it) && !censor_cfg.contains(it), @@ -191,6 +210,7 @@ pub fn expand_speculative( fixups.append, fixups.remove, span, + DocCommentDesugarMode::ProcMacro, ), fixups.undo_info, ) @@ -212,7 +232,12 @@ pub fn expand_speculative( }?; match attr.token_tree() { Some(token_tree) => { - let mut tree = syntax_node_to_token_tree(token_tree.syntax(), span_map, span); + let mut tree = syntax_node_to_token_tree( + token_tree.syntax(), + span_map, + span, + DocCommentDesugarMode::ProcMacro, + ); tree.delimiter = tt::Delimiter::invisible_spanned(span); Some(tree) @@ -332,9 +357,14 @@ fn parse_macro_expansion( fn parse_macro_expansion_error( db: &dyn ExpandDatabase, macro_call_id: MacroCallId, -) -> ExpandResult> { - db.parse_macro_expansion(MacroFileId { macro_call_id }) - .map(|it| it.0.errors().into_boxed_slice()) +) -> Option>>> { + let e: ExpandResult> = + db.parse_macro_expansion(MacroFileId { macro_call_id }).map(|it| Arc::from(it.0.errors())); + if e.value.is_empty() && e.err.is_none() { + None + } else { + Some(Arc::new(e)) + } } pub(crate) fn parse_with_map( @@ -432,7 +462,16 @@ fn macro_arg(db: &dyn ExpandDatabase, id: MacroCallId) -> MacroArgResult { return dummy_tt(kind); } - let mut tt = mbe::syntax_node_to_token_tree(tt.syntax(), map.as_ref(), span); + let mut tt = mbe::syntax_node_to_token_tree( + tt.syntax(), + map.as_ref(), + span, + if loc.def.is_proc_macro() { + DocCommentDesugarMode::ProcMacro + } else { + DocCommentDesugarMode::Mbe + }, + ); if loc.def.is_proc_macro() { // proc macros expect their inputs without parentheses, MBEs expect it with them included tt.delimiter.kind = tt::DelimiterKind::Invisible; @@ -469,7 +508,8 @@ fn macro_arg(db: &dyn ExpandDatabase, id: MacroCallId) -> MacroArgResult { let (mut tt, undo_info) = { let syntax = item_node.syntax(); let censor_cfg = cfg_process::process_cfg_attrs(db, syntax, &loc).unwrap_or_default(); - let mut fixups = fixup::fixup_syntax(map.as_ref(), syntax, span); + let mut fixups = + fixup::fixup_syntax(map.as_ref(), syntax, span, DocCommentDesugarMode::ProcMacro); fixups.append.retain(|it, _| match it { syntax::NodeOrToken::Token(_) => true, it => !censor.contains(it) && !censor_cfg.contains(it), @@ -484,6 +524,7 @@ fn macro_arg(db: &dyn ExpandDatabase, id: MacroCallId) -> MacroArgResult { fixups.append, fixups.remove, span, + DocCommentDesugarMode::ProcMacro, ), fixups.undo_info, ) 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 66465ce60057f..7c3bf995b1240 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs @@ -2,6 +2,7 @@ use std::sync::OnceLock; use base_db::{CrateId, VersionReq}; +use mbe::DocCommentDesugarMode; use span::{Edition, MacroCallId, Span, SyntaxContextId}; use stdx::TupleExt; use syntax::{ast, AstNode}; @@ -158,6 +159,7 @@ impl DeclarativeMacroExpander { map.span_for_range( macro_rules.macro_rules_token().unwrap().text_range(), ), + DocCommentDesugarMode::Mbe, ); mbe::DeclarativeMacro::parse_macro_rules(&tt, edition, new_meta_vars) @@ -175,6 +177,7 @@ impl DeclarativeMacroExpander { arg.syntax(), map.as_ref(), map.span_for_range(macro_def.macro_token().unwrap().text_range()), + DocCommentDesugarMode::Mbe, ); mbe::DeclarativeMacro::parse_macro2(&tt, edition, new_meta_vars) diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs b/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs index 8b147c88c13c2..64e04bc08f525 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs @@ -19,6 +19,7 @@ //! //! See the full discussion : use base_db::CrateId; +use mbe::DocCommentDesugarMode; use span::SyntaxContextId; use syntax::{ted, Parse, SyntaxElement, SyntaxNode, TextSize, WalkEvent}; use triomphe::Arc; @@ -80,7 +81,12 @@ pub fn expand_eager_macro_input( return ExpandResult { value: None, err }; }; - let mut subtree = mbe::syntax_node_to_token_tree(&expanded_eager_input, arg_map, span); + let mut subtree = mbe::syntax_node_to_token_tree( + &expanded_eager_input, + arg_map, + span, + DocCommentDesugarMode::Mbe, + ); subtree.delimiter.kind = crate::tt::DelimiterKind::Invisible; diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs b/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs index 711acfeb3d853..9ec2a83162a62 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs @@ -1,6 +1,7 @@ //! To make attribute macros work reliably when typing, we need to take care to //! fix up syntax errors in the code we're passing to them. +use mbe::DocCommentDesugarMode; use rustc_hash::{FxHashMap, FxHashSet}; use smallvec::SmallVec; use span::{ErasedFileAstId, Span, SpanAnchor, FIXUP_ERASED_FILE_AST_ID_MARKER}; @@ -49,6 +50,7 @@ pub(crate) fn fixup_syntax( span_map: SpanMapRef<'_>, node: &SyntaxNode, call_site: Span, + mode: DocCommentDesugarMode, ) -> SyntaxFixups { let mut append = FxHashMap::::default(); let mut remove = FxHashSet::::default(); @@ -70,7 +72,7 @@ pub(crate) fn fixup_syntax( if can_handle_error(&node) && has_error_to_handle(&node) { remove.insert(node.clone().into()); // the node contains an error node, we have to completely replace it by something valid - let original_tree = mbe::syntax_node_to_token_tree(&node, span_map, call_site); + let original_tree = mbe::syntax_node_to_token_tree(&node, span_map, call_site, mode); let idx = original.len() as u32; original.push(original_tree); let span = span_map.span_for_range(node_range); @@ -360,6 +362,7 @@ fn reverse_fixups_(tt: &mut Subtree, undo_info: &[Subtree]) { mod tests { use base_db::FileId; use expect_test::{expect, Expect}; + use mbe::DocCommentDesugarMode; use syntax::TextRange; use triomphe::Arc; @@ -402,6 +405,7 @@ mod tests { span_map.as_ref(), &parsed.syntax_node(), span_map.span_for_range(TextRange::empty(0.into())), + DocCommentDesugarMode::Mbe, ); let mut tt = mbe::syntax_node_to_token_tree_modified( &parsed.syntax_node(), @@ -409,6 +413,7 @@ mod tests { fixups.append, fixups.remove, span_map.span_for_range(TextRange::empty(0.into())), + DocCommentDesugarMode::Mbe, ); let actual = format!("{tt}\n"); @@ -436,6 +441,7 @@ mod tests { &parsed.syntax_node(), span_map.as_ref(), span_map.span_for_range(TextRange::empty(0.into())), + DocCommentDesugarMode::Mbe, ); assert!( check_subtree_eq(&tt, &original_as_tt), 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 338bd25ede31d..4ab989bec2f04 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs @@ -132,13 +132,13 @@ pub enum ExpandError { MacroDefinition, Mbe(mbe::ExpandError), RecursionOverflow, - Other(Box>), - ProcMacroPanic(Box>), + Other(Arc>), + ProcMacroPanic(Arc>), } impl ExpandError { pub fn other(msg: impl Into>) -> Self { - ExpandError::Other(Box::new(msg.into())) + ExpandError::Other(Arc::new(msg.into())) } } 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 abed16fecdebc..def2578b0e362 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 @@ -8,6 +8,7 @@ use rustc_hash::FxHashMap; use span::Span; use stdx::never; use syntax::SmolStr; +use triomphe::Arc; use crate::{db::ExpandDatabase, tt, ExpandError, ExpandResult}; @@ -157,7 +158,7 @@ impl CustomProcMacroExpander { ProcMacroExpansionError::System(text) | ProcMacroExpansionError::Panic(text) => ExpandResult::new( tt::Subtree::empty(tt::DelimSpan { open: call_site, close: call_site }), - ExpandError::ProcMacroPanic(Box::new(text.into_boxed_str())), + ExpandError::ProcMacroPanic(Arc::new(text.into_boxed_str())), ), }, } 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 46612242b090c..84ac8740ecd54 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 @@ -806,7 +806,7 @@ pub(crate) fn impl_datum_query( krate: CrateId, impl_id: ImplId, ) -> Arc { - let _p = tracing::span!(tracing::Level::INFO, "impl_datum").entered(); + let _p = tracing::span!(tracing::Level::INFO, "impl_datum_query").entered(); debug!("impl_datum {:?}", impl_id); let impl_: hir_def::ImplId = from_chalk(db, impl_id); impl_def_datum(db, krate, impl_id, impl_) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs index 0bf01b0bc6ae2..d99ef6679e51f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs @@ -27,6 +27,7 @@ pub trait TyExt { fn is_scalar(&self) -> bool; fn is_floating_point(&self) -> bool; fn is_never(&self) -> bool; + fn is_str(&self) -> bool; fn is_unknown(&self) -> bool; fn contains_unknown(&self) -> bool; fn is_ty_var(&self) -> bool; @@ -87,6 +88,10 @@ impl TyExt for Ty { matches!(self.kind(Interner), TyKind::Never) } + fn is_str(&self) -> bool { + matches!(self.kind(Interner), TyKind::Str) + } + fn is_unknown(&self) -> bool { matches!(self.kind(Interner), TyKind::Error) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs index a357e85035159..c010f5d22b681 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -797,8 +797,20 @@ impl HirDisplay for Ty { c.hir_fmt(f)?; write!(f, "]")?; } - TyKind::Raw(m, t) | TyKind::Ref(m, _, t) => { - if matches!(self.kind(Interner), TyKind::Raw(..)) { + kind @ (TyKind::Raw(m, t) | TyKind::Ref(m, _, t)) => { + if let TyKind::Ref(_, l, _) = kind { + f.write_char('&')?; + if cfg!(test) { + // rendering these unconditionally is probably too much (at least for inlay + // hints) so we gate it to testing only for the time being + l.hir_fmt(f)?; + f.write_char(' ')?; + } + match m { + Mutability::Not => (), + Mutability::Mut => f.write_str("mut ")?, + } + } else { write!( f, "*{}", @@ -807,15 +819,6 @@ impl HirDisplay for Ty { Mutability::Mut => "mut ", } )?; - } else { - write!( - f, - "&{}", - match m { - Mutability::Not => "", - Mutability::Mut => "mut ", - } - )?; } // FIXME: all this just to decide whether to use parentheses... @@ -1330,7 +1333,18 @@ fn hir_fmt_generics( } let parameters_to_write = generic_args_sans_defaults(f, generic_def, parameters); - if !parameters_to_write.is_empty() { + + // FIXME: Remote this + // most of our lifetimes will be errors as we lack elision and inference + // so don't render them for now + let only_err_lifetimes = !cfg!(test) + && parameters_to_write.iter().all(|arg| { + matches!( + arg.data(Interner), + chalk_ir::GenericArgData::Lifetime(it) if *it.data(Interner) == LifetimeData::Error + ) + }); + if !parameters_to_write.is_empty() && !only_err_lifetimes { write!(f, "<")?; hir_fmt_generic_arguments(f, parameters_to_write)?; write!(f, ">")?; @@ -1403,6 +1417,18 @@ fn hir_fmt_generic_arguments( None => (parameters, &[][..]), }; for generic_arg in lifetimes.iter().chain(ty_or_const) { + // FIXME: Remove this + // most of our lifetimes will be errors as we lack elision and inference + // so don't render them for now + if !cfg!(test) + && matches!( + generic_arg.lifetime(Interner), + Some(l) if ***l.interned() == LifetimeData::Error + ) + { + continue; + } + if !first { write!(f, ", ")?; } @@ -1728,9 +1754,9 @@ impl HirDisplay for LifetimeData { LifetimeData::BoundVar(idx) => idx.hir_fmt(f), LifetimeData::InferenceVar(_) => write!(f, "_"), LifetimeData::Static => write!(f, "'static"), - LifetimeData::Error => write!(f, "'{{error}}"), - LifetimeData::Erased => Ok(()), - LifetimeData::Phantom(_, _) => Ok(()), + LifetimeData::Error => write!(f, "'?"), + LifetimeData::Erased => write!(f, "'"), + LifetimeData::Phantom(void, _) => match *void {}, } } } 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 281386e1364b4..6f2f70dd40a78 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -198,6 +198,7 @@ pub enum InferenceDiagnostic { NoSuchField { field: ExprOrPatId, private: bool, + variant: VariantId, }, PrivateField { expr: ExprId, 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 d011a62e77a34..38076fce8f8f5 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 @@ -563,6 +563,7 @@ impl InferenceContext<'_> { InferenceDiagnostic::NoSuchField { field: field.expr.into(), private: true, + variant: def, }, ); } @@ -572,6 +573,7 @@ impl InferenceContext<'_> { self.push_diagnostic(InferenceDiagnostic::NoSuchField { field: field.expr.into(), private: false, + variant: def, }); None } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs index 1b354935a5bf0..dac5a5ea6995d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs @@ -177,6 +177,7 @@ impl InferenceContext<'_> { self.push_diagnostic(InferenceDiagnostic::NoSuchField { field: inner.into(), private: true, + variant: def, }); } let f = field_types[local_id].clone(); @@ -190,6 +191,7 @@ impl InferenceContext<'_> { self.push_diagnostic(InferenceDiagnostic::NoSuchField { field: inner.into(), private: false, + variant: def, }); self.err_ty() } 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 4d0516ead67c8..04ace38202191 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -345,51 +345,47 @@ impl<'a> TyLoweringContext<'a> { } ImplTraitLoweringState::Param(counter) => { let idx = counter.get(); - // FIXME we're probably doing something wrong here + // Count the number of `impl Trait` things that appear within our bounds. + // Since t hose have been emitted as implicit type args already. counter.set(idx + count_impl_traits(type_ref) as u16); - if let Some(generics) = self.generics() { - let param = generics - .iter() - .filter(|(_, data)| { - matches!( - data, - GenericParamDataRef::TypeParamData(data) - if data.provenance == TypeParamProvenance::ArgumentImplTrait - ) - }) - .nth(idx as usize) - .map_or(TyKind::Error, |(id, _)| { - if let GenericParamId::TypeParamId(id) = id { - TyKind::Placeholder(to_placeholder_idx(self.db, id.into())) - } else { - // we just filtered them out - unreachable!("Unexpected lifetime or const argument"); - } - }); - param.intern(Interner) - } else { - TyKind::Error.intern(Interner) - } + let kind = self + .generics() + .expect("param impl trait lowering must be in a generic def") + .iter() + .filter_map(|(id, data)| match (id, data) { + ( + GenericParamId::TypeParamId(id), + GenericParamDataRef::TypeParamData(data), + ) if data.provenance == TypeParamProvenance::ArgumentImplTrait => { + Some(id) + } + _ => None, + }) + .nth(idx as usize) + .map_or(TyKind::Error, |id| { + TyKind::Placeholder(to_placeholder_idx(self.db, id.into())) + }); + kind.intern(Interner) } ImplTraitLoweringState::Variable(counter) => { let idx = counter.get(); - // FIXME we're probably doing something wrong here + // Count the number of `impl Trait` things that appear within our bounds. + // Since t hose have been emitted as implicit type args already. counter.set(idx + count_impl_traits(type_ref) as u16); let ( _parent_params, self_params, - list_params, + type_params, const_params, _impl_trait_params, _lifetime_params, - ) = if let Some(generics) = self.generics() { - generics.provenance_split() - } else { - (0, 0, 0, 0, 0, 0) - }; + ) = self + .generics() + .expect("variable impl trait lowering must be in a generic def") + .provenance_split(); TyKind::BoundVar(BoundVar::new( self.in_binders, - idx as usize + self_params + list_params + const_params, + idx as usize + self_params + type_params + const_params, )) .intern(Interner) } @@ -1010,6 +1006,7 @@ impl<'a> TyLoweringContext<'a> { pub(crate) fn lower_where_predicate<'b>( &'b self, where_predicate: &'b WherePredicate, + &def: &GenericDefId, ignore_bindings: bool, ) -> impl Iterator + 'b { match where_predicate { @@ -1018,7 +1015,6 @@ impl<'a> TyLoweringContext<'a> { let self_ty = match target { WherePredicateTypeTarget::TypeRef(type_ref) => self.lower_ty(type_ref), &WherePredicateTypeTarget::TypeOrConstParam(local_id) => { - let def = self.resolver.generic_def().expect("generics in scope"); let param_id = hir_def::TypeOrConstParamId { parent: def, local_id }; match self.type_param_mode { ParamLoweringMode::Placeholder => { @@ -1056,23 +1052,7 @@ impl<'a> TyLoweringContext<'a> { 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 - .clone() - .filter(|tr| { - // ignore `T: Drop` or `T: Destruct` bounds. - // - `T: ~const Drop` has a special meaning in Rust 1.61 that we don't implement. - // (So ideally, we'd only ignore `~const Drop` here) - // - `Destruct` impls are built-in in 1.62 (current nightly as of 08-04-2022), so until - // the builtin impls are supported by Chalk, we ignore them here. - if let Some(lang) = self.db.lang_attr(tr.hir_trait_id().into()) { - if matches!(lang, LangItem::Drop | LangItem::Destruct) { - return false; - } - } - true - }) - .map(WhereClause::Implemented) - .map(crate::wrap_empty_binders) + trait_ref.clone().map(WhereClause::Implemented).map(crate::wrap_empty_binders) } TypeBound::Path(path, TraitBoundModifier::Maybe) => { let sized_trait = self @@ -1166,84 +1146,77 @@ impl<'a> TyLoweringContext<'a> { binding.type_ref.as_ref().map_or(0, |_| 1) + binding.bounds.len(), ); if let Some(type_ref) = &binding.type_ref { - if let ( - TypeRef::ImplTrait(bounds), - ImplTraitLoweringState::Param(_) - | ImplTraitLoweringState::Variable(_) - | ImplTraitLoweringState::Disallowed, - ) = (type_ref, &self.impl_trait_mode) - { - for bound in bounds { - predicates.extend( - self.lower_type_bound( - bound, - TyKind::Alias(AliasTy::Projection(projection_ty.clone())) - .intern(Interner), - false, - ), - ); + match (type_ref, &self.impl_trait_mode) { + (TypeRef::ImplTrait(_), ImplTraitLoweringState::Disallowed) => (), + ( + _, + ImplTraitLoweringState::Disallowed | ImplTraitLoweringState::Opaque(_), + ) => { + let ty = self.lower_ty(type_ref); + let alias_eq = + AliasEq { alias: AliasTy::Projection(projection_ty.clone()), ty }; + predicates + .push(crate::wrap_empty_binders(WhereClause::AliasEq(alias_eq))); } - } else { - let ty = 'ty: { - if matches!( - self.impl_trait_mode, - ImplTraitLoweringState::Param(_) - | ImplTraitLoweringState::Variable(_) - ) { - // Find the generic index for the target of our `bound` - let target_param_idx = self - .resolver - .where_predicates_in_scope() - .find_map(|p| match p { - WherePredicate::TypeBound { - target: WherePredicateTypeTarget::TypeOrConstParam(idx), - bound: b, - } if b == bound => Some(idx), - _ => None, - }); - if let Some(target_param_idx) = target_param_idx { - let mut counter = 0; - let generics = self.generics().expect("generics in scope"); - for (idx, data) in generics.params.type_or_consts.iter() { - // Count the number of `impl Trait` things that appear before - // the target of our `bound`. - // Our counter within `impl_trait_mode` should be that number - // to properly lower each types within `type_ref` - if data.type_param().is_some_and(|p| { - p.provenance == TypeParamProvenance::ArgumentImplTrait - }) { - counter += 1; - } - if idx == *target_param_idx { - break; - } + ( + _, + ImplTraitLoweringState::Param(_) | ImplTraitLoweringState::Variable(_), + ) => { + // Find the generic index for the target of our `bound` + let target_param_idx = self + .resolver + .where_predicates_in_scope() + .find_map(|(p, _)| match p { + WherePredicate::TypeBound { + target: WherePredicateTypeTarget::TypeOrConstParam(idx), + bound: b, + } if b == bound => Some(idx), + _ => None, + }); + let ty = if let Some(target_param_idx) = target_param_idx { + let mut counter = 0; + let generics = self.generics().expect("generics in scope"); + for (idx, data) in generics.params.type_or_consts.iter() { + // Count the number of `impl Trait` things that appear before + // the target of our `bound`. + // Our counter within `impl_trait_mode` should be that number + // to properly lower each types within `type_ref` + if data.type_param().is_some_and(|p| { + p.provenance == TypeParamProvenance::ArgumentImplTrait + }) { + counter += 1; } - let mut ext = TyLoweringContext::new_maybe_unowned( - self.db, - self.resolver, - self.owner, - ) - .with_type_param_mode(self.type_param_mode); - match &self.impl_trait_mode { - ImplTraitLoweringState::Param(_) => { - ext.impl_trait_mode = - ImplTraitLoweringState::Param(Cell::new(counter)); - } - ImplTraitLoweringState::Variable(_) => { - ext.impl_trait_mode = ImplTraitLoweringState::Variable( - Cell::new(counter), - ); - } - _ => unreachable!(), + if idx == *target_param_idx { + break; } - break 'ty ext.lower_ty(type_ref); } - } - self.lower_ty(type_ref) - }; - let alias_eq = - AliasEq { alias: AliasTy::Projection(projection_ty.clone()), ty }; - predicates.push(crate::wrap_empty_binders(WhereClause::AliasEq(alias_eq))); + let mut ext = TyLoweringContext::new_maybe_unowned( + self.db, + self.resolver, + self.owner, + ) + .with_type_param_mode(self.type_param_mode); + match &self.impl_trait_mode { + ImplTraitLoweringState::Param(_) => { + ext.impl_trait_mode = + ImplTraitLoweringState::Param(Cell::new(counter)); + } + ImplTraitLoweringState::Variable(_) => { + ext.impl_trait_mode = + ImplTraitLoweringState::Variable(Cell::new(counter)); + } + _ => unreachable!(), + } + ext.lower_ty(type_ref) + } else { + self.lower_ty(type_ref) + }; + + let alias_eq = + AliasEq { alias: AliasTy::Projection(projection_ty.clone()), ty }; + predicates + .push(crate::wrap_empty_binders(WhereClause::AliasEq(alias_eq))); + } } } for bound in binding.bounds.iter() { @@ -1338,11 +1311,10 @@ impl<'a> TyLoweringContext<'a> { bounds, lifetime: match lifetime { Some(it) => match it.bound_var(Interner) { - Some(bound_var) => LifetimeData::BoundVar(BoundVar::new( - DebruijnIndex::INNERMOST, - bound_var.index, - )) - .intern(Interner), + Some(bound_var) => bound_var + .shifted_out_to(DebruijnIndex::new(2)) + .map(|bound_var| LifetimeData::BoundVar(bound_var).intern(Interner)) + .unwrap_or(it), None => it, }, None => static_lifetime(), @@ -1410,16 +1382,6 @@ impl<'a> TyLoweringContext<'a> { } } -fn count_impl_traits(type_ref: &TypeRef) -> usize { - let mut count = 0; - type_ref.walk(&mut |type_ref| { - if matches!(type_ref, TypeRef::ImplTrait(_)) { - count += 1; - } - }); - count -} - /// Build the signature of a callable item (function, struct or enum variant). pub(crate) fn callable_item_sig(db: &dyn HirDatabase, def: CallableDefId) -> PolyFnSig { match def { @@ -1438,6 +1400,17 @@ pub fn associated_type_shorthand_candidates( named_associated_type_shorthand_candidates(db, def, res, None, |name, _, id| cb(name, id)) } +// FIXME: This does not handle macros! +fn count_impl_traits(type_ref: &TypeRef) -> usize { + let mut count = 0; + type_ref.walk(&mut |type_ref| { + if matches!(type_ref, TypeRef::ImplTrait(_)) { + count += 1; + } + }); + count +} + fn named_associated_type_shorthand_candidates( db: &dyn HirDatabase, // If the type parameter is defined in an impl and we're in a method, there @@ -1575,7 +1548,7 @@ pub(crate) fn generic_predicates_for_param_query( let generics = generics(db.upcast(), def); // we have to filter out all other predicates *first*, before attempting to lower them - let predicate = |pred: &&_| match pred { + let predicate = |(pred, &def): &(&_, _)| match pred { WherePredicate::ForLifetime { target, bound, .. } | WherePredicate::TypeBound { target, bound, .. } => { let invalid_target = match target { @@ -1617,8 +1590,8 @@ pub(crate) fn generic_predicates_for_param_query( let mut predicates: Vec<_> = resolver .where_predicates_in_scope() .filter(predicate) - .flat_map(|pred| { - ctx.lower_where_predicate(pred, true).map(|p| make_binders(db, &generics, p)) + .flat_map(|(pred, def)| { + ctx.lower_where_predicate(pred, def, true).map(|p| make_binders(db, &generics, p)) }) .collect(); @@ -1671,8 +1644,8 @@ pub(crate) fn trait_environment_query( }; let mut traits_in_scope = Vec::new(); let mut clauses = Vec::new(); - for pred in resolver.where_predicates_in_scope() { - for pred in ctx.lower_where_predicate(pred, false) { + for (pred, def) in resolver.where_predicates_in_scope() { + for pred in ctx.lower_where_predicate(pred, def, false) { if let WhereClause::Implemented(tr) = &pred.skip_binders() { traits_in_scope.push((tr.self_type_parameter(Interner).clone(), tr.hir_trait_id())); } @@ -1726,8 +1699,8 @@ pub(crate) fn generic_predicates_query( let mut predicates = resolver .where_predicates_in_scope() - .flat_map(|pred| { - ctx.lower_where_predicate(pred, false).map(|p| make_binders(db, &generics, p)) + .flat_map(|(pred, def)| { + ctx.lower_where_predicate(pred, def, false).map(|p| make_binders(db, &generics, p)) }) .collect::>(); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs index cd723494713b3..cb56a6f0bfe7d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs @@ -368,7 +368,7 @@ pub(crate) fn incoherent_inherent_impl_crates( krate: CrateId, fp: TyFingerprint, ) -> SmallVec<[CrateId; 2]> { - let _p = tracing::span!(tracing::Level::INFO, "inherent_impl_crates_query").entered(); + let _p = tracing::span!(tracing::Level::INFO, "incoherent_inherent_impl_crates").entered(); let mut res = SmallVec::new(); let crate_graph = db.crate_graph(); 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 045ffb418c8de..2de1aa30c9660 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 @@ -428,6 +428,17 @@ impl MirEvalError { } Ok(()) } + + pub fn is_panic(&self) -> Option<&str> { + let mut err = self; + while let MirEvalError::InFunction(e, _) = err { + err = e; + } + match err { + MirEvalError::Panic(msg) => Some(msg), + _ => None, + } + } } impl std::fmt::Debug for MirEvalError { @@ -1138,7 +1149,7 @@ impl Evaluator<'_> { let mut ty = self.operand_ty(lhs, locals)?; while let TyKind::Ref(_, _, z) = ty.kind(Interner) { ty = z.clone(); - let size = if ty.kind(Interner) == &TyKind::Str { + let size = if ty.is_str() { if *op != BinOp::Eq { never!("Only eq is builtin for `str`"); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs index fee3dd3ada852..3438712049e2d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs @@ -49,6 +49,7 @@ impl Evaluator<'_> { if self.not_special_fn_cache.borrow().contains(&def) { return Ok(false); } + let function_data = self.db.function_data(def); let is_intrinsic = match &function_data.abi { Some(abi) => *abi == Interned::new_str("rust-intrinsic"), @@ -131,9 +132,7 @@ impl Evaluator<'_> { return Ok(true); } if let Some(it) = self.detect_lang_function(def) { - let arg_bytes = - args.iter().map(|it| Ok(it.get(self)?.to_owned())).collect::>>()?; - let result = self.exec_lang_item(it, generic_args, &arg_bytes, locals, span)?; + let result = self.exec_lang_item(it, generic_args, args, locals, span)?; destination.write_from_bytes(self, &result)?; return Ok(true); } @@ -311,16 +310,20 @@ impl Evaluator<'_> { fn detect_lang_function(&self, def: FunctionId) -> Option { use LangItem::*; - let candidate = self.db.lang_attr(def.into())?; + let attrs = self.db.attrs(def.into()); + + if attrs.by_key("rustc_const_panic_str").exists() { + // `#[rustc_const_panic_str]` is treated like `lang = "begin_panic"` by rustc CTFE. + return Some(LangItem::BeginPanic); + } + + let candidate = attrs.by_key("lang").string_value().and_then(LangItem::from_str)?; // We want to execute these functions with special logic // `PanicFmt` is not detected here as it's redirected later. if [BeginPanic, SliceLen, DropInPlace].contains(&candidate) { return Some(candidate); } - if self.db.attrs(def.into()).by_key("rustc_const_panic_str").exists() { - // `#[rustc_const_panic_str]` is treated like `lang = "begin_panic"` by rustc CTFE. - return Some(LangItem::BeginPanic); - } + None } @@ -328,18 +331,52 @@ impl Evaluator<'_> { &mut self, it: LangItem, generic_args: &Substitution, - args: &[Vec], + args: &[IntervalAndTy], locals: &Locals, span: MirSpan, ) -> Result> { use LangItem::*; let mut args = args.iter(); match it { - BeginPanic => Err(MirEvalError::Panic("".to_owned())), + BeginPanic => { + let mut arg = args + .next() + .ok_or(MirEvalError::InternalError( + "argument of BeginPanic is not provided".into(), + ))? + .clone(); + while let TyKind::Ref(_, _, ty) = arg.ty.kind(Interner) { + if ty.is_str() { + let (pointee, metadata) = arg.interval.get(self)?.split_at(self.ptr_size()); + let len = from_bytes!(usize, metadata); + + return { + Err(MirEvalError::Panic( + std::str::from_utf8( + self.read_memory(Address::from_bytes(pointee)?, len)?, + ) + .unwrap() + .to_owned(), + )) + }; + } + let size = self.size_of_sized(ty, locals, "begin panic arg")?; + let pointee = arg.interval.get(self)?; + arg = IntervalAndTy { + interval: Interval::new(Address::from_bytes(pointee)?, size), + ty: ty.clone(), + }; + } + Err(MirEvalError::Panic(format!( + "unknown-panic-payload: {:?}", + arg.ty.kind(Interner) + ))) + } SliceLen => { let arg = args.next().ok_or(MirEvalError::InternalError( "argument of <[T]>::len() is not provided".into(), ))?; + let arg = arg.get(self)?; let ptr_size = arg.len() / 2; Ok(arg[ptr_size..].into()) } @@ -353,6 +390,7 @@ impl Evaluator<'_> { let arg = args.next().ok_or(MirEvalError::InternalError( "argument of drop_in_place is not provided".into(), ))?; + let arg = arg.interval.get(self)?.to_owned(); self.run_drop_glue_deep( ty.clone(), 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 381522c9abe62..4abbda56cbb4d 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 @@ -31,6 +31,7 @@ fn eval_main(db: &TestDB, file_id: FileId) -> Result<(String, String), MirEvalEr db.trait_environment(func_id.into()), ) .map_err(|e| MirEvalError::MirLowerError(func_id, e))?; + let (result, output) = interpret_mir(db, body, false, None); result?; Ok((output.stdout().into_owned(), output.stderr().into_owned())) @@ -72,6 +73,13 @@ fn check_pass_and_stdio(ra_fixture: &str, expected_stdout: &str, expected_stderr } } +fn check_panic(ra_fixture: &str, expected_panic: &str) { + let (db, file_ids) = TestDB::with_many_files(ra_fixture); + let file_id = *file_ids.last().unwrap(); + let e = eval_main(&db, file_id).unwrap_err(); + assert_eq!(e.is_panic().unwrap_or_else(|| panic!("unexpected error: {:?}", e)), expected_panic); +} + #[test] fn function_with_extern_c_abi() { check_pass( @@ -87,6 +95,43 @@ fn main() { ); } +#[test] +fn panic_fmt() { + // panic! + // -> panic_2021 (builtin macro redirection) + // -> #[lang = "panic_fmt"] core::panicking::panic_fmt (hooked by CTFE for redirection) + // -> core::panicking::const_panic_fmt + // -> #[rustc_const_panic_str] core::panicking::panic_display (hooked by CTFE for builtin panic) + // -> Err(ConstEvalError::Panic) + check_panic( + r#" +//- minicore: fmt, panic +fn main() { + panic!("hello, world!"); +} + "#, + "hello, world!", + ); +} + +#[test] +fn panic_display() { + // panic! + // -> panic_2021 (builtin macro redirection) + // -> #[rustc_const_panic_str] core::panicking::panic_display (hooked by CTFE for builtin panic) + // -> Err(ConstEvalError::Panic) + check_panic( + r#" +//- minicore: fmt, panic + +fn main() { + panic!("{}", "hello, world!"); +} + "#, + "hello, world!", + ); +} + #[test] fn drop_basic() { check_pass( 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 d56b15b9b741e..526db2af6dccb 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 @@ -186,7 +186,7 @@ fn test() { let x = match 1 { 1 => t as *mut i32, 2 => t as &i32, - //^^^^^^^^^ expected *mut i32, got &i32 + //^^^^^^^^^ expected *mut i32, got &'? i32 _ => t as *const i32, // ^^^^^^^^^^^^^^^ adjustments: Pointer(MutToConstPointer) @@ -307,7 +307,7 @@ fn test() { let foo = Foo; //^^^ type: Foo<{unknown}> let _: &u32 = &Foo; - //^^^^ expected &u32, got &Foo<{unknown}> + //^^^^ expected &'? u32, got &'? Foo<{unknown}> }", ); } @@ -544,9 +544,9 @@ struct Bar(Foo); fn test() { let _: &Foo<[usize]> = &Foo { t: [1, 2, 3] }; - //^^^^^^^^^^^^^^^^^^^^^ expected &Foo<[usize]>, got &Foo<[i32; 3]> + //^^^^^^^^^^^^^^^^^^^^^ expected &'? Foo<[usize]>, got &'? Foo<[i32; 3]> let _: &Bar<[usize]> = &Bar(Foo { t: [1, 2, 3] }); - //^^^^^^^^^^^^^^^^^^^^^^^^^^ expected &Bar<[usize]>, got &Bar<[i32; 3]> + //^^^^^^^^^^^^^^^^^^^^^^^^^^ expected &'? Bar<[usize]>, got &'? Bar<[i32; 3]> } "#, ); @@ -562,7 +562,7 @@ trait Foo {} fn test(f: impl Foo, g: &(impl Foo + ?Sized)) { let _: &dyn Foo = &f; let _: &dyn Foo = g; - //^ expected &dyn Foo, got &impl Foo + ?Sized + //^ expected &'? dyn Foo, got &'? impl Foo + ?Sized } "#, ); @@ -828,11 +828,11 @@ struct V { t: T } fn main() { let a: V<&dyn Tr>; (a,) = V { t: &S }; - //^^^^expected V<&S>, got (V<&dyn Tr>,) + //^^^^expected V<&'? S>, got (V<&'? dyn Tr>,) let mut a: V<&dyn Tr> = V { t: &S }; (a,) = V { t: &S }; - //^^^^expected V<&S>, got (V<&dyn Tr>,) + //^^^^expected V<&'? S>, got (V<&'? dyn Tr>,) } "#, ); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/diagnostics.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/diagnostics.rs index 119de7f050e78..def06f2d59d2a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/diagnostics.rs @@ -8,7 +8,7 @@ fn function_return_type_mismatch_1() { r#" fn test() -> &'static str { 5 - //^ expected &str, got i32 + //^ expected &'static str, got i32 } "#, ); @@ -21,7 +21,7 @@ fn function_return_type_mismatch_2() { fn test(x: bool) -> &'static str { if x { return 1; - //^ expected &str, got i32 + //^ expected &'static str, got i32 } "ok" } @@ -38,7 +38,7 @@ fn test(x: bool) -> &'static str { return "ok"; } 1 - //^ expected &str, got i32 + //^ expected &'static str, got i32 } "#, ); @@ -53,7 +53,7 @@ fn test(x: bool) -> &'static str { "ok" } else { 1 - //^ expected &str, got i32 + //^ expected &'static str, got i32 } } "#, @@ -67,7 +67,7 @@ fn function_return_type_mismatch_5() { fn test(x: bool) -> &'static str { if x { 1 - //^ expected &str, got i32 + //^ expected &'static str, got i32 } else { "ok" } @@ -83,10 +83,10 @@ fn non_unit_block_expr_stmt_no_semi() { fn test(x: bool) { if x { "notok" - //^^^^^^^ expected (), got &str + //^^^^^^^ expected (), got &'static str } else { "ok" - //^^^^ expected (), got &str + //^^^^ expected (), got &'static str } match x { true => true, false => 0 } //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected (), got bool diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/display_source_code.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/display_source_code.rs index e8369caa77140..60c03b52246c4 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/display_source_code.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/display_source_code.rs @@ -67,11 +67,11 @@ trait B: A {} fn test( _: &(dyn A + Send), - //^ &(dyn A + Send) + //^ &'_ (dyn A + Send) _: &(dyn Send + A), - //^ &(dyn A + Send) + //^ &'_ (dyn A + Send) _: &dyn B, - //^ &(dyn B) + //^ &'_ (dyn B) ) {} "#, ); @@ -85,7 +85,7 @@ fn render_dyn_for_ty() { trait Foo<'a> {} fn foo(foo: &dyn for<'a> Foo<'a>) {} - // ^^^ &dyn Foo<'_> + // ^^^ &'_ dyn Foo<'_> "#, ); } @@ -111,11 +111,11 @@ fn test( b; //^ impl Foo c; - //^ &impl Foo + ?Sized + //^ &'_ impl Foo + ?Sized d; //^ S ref_any; - //^^^^^^^ &impl ?Sized + //^^^^^^^ &'_ impl ?Sized empty; } //^^^^^ impl Sized "#, @@ -192,7 +192,7 @@ fn test( b; //^ fn(impl Foo) -> impl Foo c; -} //^ fn(&impl Foo + ?Sized) -> &impl Foo + ?Sized +} //^ fn(&'_ impl Foo + ?Sized) -> &'_ impl Foo + ?Sized "#, ); } 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 2f75338f99436..a0899cb1d632b 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 @@ -200,8 +200,8 @@ fn expr_macro_def_expanded_in_various_places() { 100..119 'for _ ...!() {}': IntoIterator::IntoIter 100..119 'for _ ...!() {}': ! 100..119 'for _ ...!() {}': IntoIterator::IntoIter - 100..119 'for _ ...!() {}': &mut IntoIterator::IntoIter - 100..119 'for _ ...!() {}': fn next>(&mut IntoIterator::IntoIter) -> Option< as Iterator>::Item> + 100..119 'for _ ...!() {}': &'? mut IntoIterator::IntoIter + 100..119 'for _ ...!() {}': fn next>(&'? mut IntoIterator::IntoIter) -> Option< as Iterator>::Item> 100..119 'for _ ...!() {}': Option> 100..119 'for _ ...!() {}': () 100..119 'for _ ...!() {}': () @@ -221,7 +221,7 @@ fn expr_macro_def_expanded_in_various_places() { 281..303 'Spam {...m!() }': {unknown} 309..325 'spam!(...am!()]': {unknown} 350..366 'spam!(... usize': usize - 372..380 '&spam!()': &isize + 372..380 '&spam!()': &'? isize 386..394 '-spam!()': isize 400..416 'spam!(...pam!()': {unknown} 422..439 'spam!(...pam!()': isize @@ -293,8 +293,8 @@ fn expr_macro_rules_expanded_in_various_places() { 114..133 'for _ ...!() {}': IntoIterator::IntoIter 114..133 'for _ ...!() {}': ! 114..133 'for _ ...!() {}': IntoIterator::IntoIter - 114..133 'for _ ...!() {}': &mut IntoIterator::IntoIter - 114..133 'for _ ...!() {}': fn next>(&mut IntoIterator::IntoIter) -> Option< as Iterator>::Item> + 114..133 'for _ ...!() {}': &'? mut IntoIterator::IntoIter + 114..133 'for _ ...!() {}': fn next>(&'? mut IntoIterator::IntoIter) -> Option< as Iterator>::Item> 114..133 'for _ ...!() {}': Option> 114..133 'for _ ...!() {}': () 114..133 'for _ ...!() {}': () @@ -314,7 +314,7 @@ fn expr_macro_rules_expanded_in_various_places() { 295..317 'Spam {...m!() }': {unknown} 323..339 'spam!(...am!()]': {unknown} 364..380 'spam!(... usize': usize - 386..394 '&spam!()': &isize + 386..394 '&spam!()': &'? isize 400..408 '-spam!()': isize 414..430 'spam!(...pam!()': {unknown} 436..453 'spam!(...pam!()': isize @@ -539,7 +539,7 @@ fn test() { let msg = foo::Message(foo::MessageRef); let r = msg.deref(); r; - //^ &MessageRef + //^ &'? MessageRef } //- /lib.rs crate:foo @@ -703,9 +703,9 @@ fn infer_builtin_macros_file() { } "#, expect![[r#" - !0..2 '""': &str + !0..2 '""': &'static str 63..87 '{ ...!(); }': () - 73..74 'x': &str + 73..74 'x': &'static str "#]], ); } @@ -741,9 +741,9 @@ fn infer_builtin_macros_concat() { } "#, expect![[r#" - !0..13 '"helloworld!"': &str + !0..13 '"helloworld!"': &'static str 65..121 '{ ...")); }': () - 75..76 'x': &str + 75..76 'x': &'static str "#]], ); } @@ -820,7 +820,7 @@ macro_rules! include_str {() => {}} fn main() { let a = include_str!("foo.rs"); a; -} //^ &str +} //^ &'static str //- /foo.rs hello @@ -847,7 +847,7 @@ macro_rules! m { fn main() { let a = include_str!(m!(".rs")); a; -} //^ &str +} //^ &'static str //- /foo.rs hello @@ -960,9 +960,9 @@ fn infer_builtin_macros_concat_with_lazy() { } "#, expect![[r#" - !0..13 '"helloworld!"': &str + !0..13 '"helloworld!"': &'static str 103..160 '{ ...")); }': () - 113..114 'x': &str + 113..114 'x': &'static str "#]], ); } @@ -977,7 +977,7 @@ fn infer_builtin_macros_env() { fn main() { let x = env!("foo"); - //^ &str + //^ &'static str } "#, ); @@ -991,7 +991,7 @@ fn infer_builtin_macros_option_env() { //- /main.rs env:foo=bar fn main() { let x = option_env!("foo"); - //^ Option<&str> + //^ Option<&'static str> } "#, ); 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 8609ba410394f..63a83d403fa97 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 @@ -658,7 +658,7 @@ fn infer_call_trait_method_on_generic_param_1() { } "#, expect![[r#" - 29..33 'self': &Self + 29..33 'self': &'? Self 63..64 't': T 69..88 '{ ...d(); }': () 75..76 't': T @@ -679,7 +679,7 @@ fn infer_call_trait_method_on_generic_param_2() { } "#, expect![[r#" - 32..36 'self': &Self + 32..36 'self': &'? Self 70..71 't': T 76..95 '{ ...d(); }': () 82..83 't': T @@ -757,7 +757,7 @@ struct S; impl Clone for S {} impl Clone for &S {} fn test() { (S.clone(), (&S).clone(), (&&S).clone()); } - //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ (S, S, &S) + //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ (S, S, &'? S) "#, ); } @@ -1150,12 +1150,12 @@ fn dyn_trait_super_trait_not_in_scope() { } "#, expect![[r#" - 51..55 'self': &Self + 51..55 'self': &'? Self 64..69 '{ 0 }': u32 66..67 '0': u32 - 176..177 'd': &dyn Trait + 176..177 'd': &'? dyn Trait 191..207 '{ ...o(); }': () - 197..198 'd': &dyn Trait + 197..198 'd': &'? dyn Trait 197..204 'd.foo()': u32 "#]], ); @@ -1182,15 +1182,15 @@ fn test() { } "#, expect![[r#" - 75..79 'self': &S + 75..79 'self': &'? S 89..109 '{ ... }': bool 99..103 'true': bool 123..167 '{ ...o(); }': () - 133..134 's': &S - 137..151 'unsafe { f() }': &S - 146..147 'f': fn f() -> &S - 146..149 'f()': &S - 157..158 's': &S + 133..134 's': &'? S + 137..151 'unsafe { f() }': &'static S + 146..147 'f': fn f() -> &'static S + 146..149 'f()': &'static S + 157..158 's': &'? S 157..164 's.foo()': bool "#]], ); @@ -1601,11 +1601,11 @@ use core::IntoIterator; fn f() { let v = [4].into_iter(); v; - //^ &i32 + //^ &'? i32 let a = [0, 1].into_iter(); a; - //^ &i32 + //^ &'? i32 } //- /main2021.rs crate:main2021 deps:core edition:2021 @@ -1618,7 +1618,7 @@ fn f() { let a = [0, 1].into_iter(); a; - //^ &i32 + //^ &'? i32 } //- /core.rs crate:core @@ -1767,7 +1767,7 @@ fn test() { let a2 = A(make(), 1i32); let _: &str = a2.thing(); a2; - //^^ A, i32> + //^^ A, i32> } "#, ); 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 5d809b823923c..0ccbcf63e2b67 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 @@ -104,7 +104,7 @@ enum Option { None, Some(T) } fn test() { let a = if true { Option::None } else { Option::Some(return) }; a; - //^ Option<&str> + //^ Option<&'static str> match 42 { 42 => a, _ => Option::Some("str"), @@ -317,8 +317,8 @@ fn diverging_expression_2() { 63..81 '{ loop...foo" }': u32 65..72 'loop {}': ! 70..72 '{}': () - 74..79 '"foo"': &str - 74..79: expected u32, got &str + 74..79 '"foo"': &'static str + 74..79: expected u32, got &'static str "#]], ); } @@ -365,8 +365,8 @@ fn diverging_expression_3_break() { 151..172 'for a ...eak; }': {unknown} 151..172 'for a ...eak; }': ! 151..172 'for a ...eak; }': {unknown} - 151..172 'for a ...eak; }': &mut {unknown} - 151..172 'for a ...eak; }': fn next<{unknown}>(&mut {unknown}) -> Option<<{unknown} as Iterator>::Item> + 151..172 'for a ...eak; }': &'? mut {unknown} + 151..172 'for a ...eak; }': fn next<{unknown}>(&'? mut {unknown}) -> Option<<{unknown} as Iterator>::Item> 151..172 'for a ...eak; }': Option<{unknown}> 151..172 'for a ...eak; }': () 151..172 'for a ...eak; }': () @@ -381,8 +381,8 @@ fn diverging_expression_3_break() { 237..250 'for a in b {}': {unknown} 237..250 'for a in b {}': ! 237..250 'for a in b {}': {unknown} - 237..250 'for a in b {}': &mut {unknown} - 237..250 'for a in b {}': fn next<{unknown}>(&mut {unknown}) -> Option<<{unknown} as Iterator>::Item> + 237..250 'for a in b {}': &'? mut {unknown} + 237..250 'for a in b {}': fn next<{unknown}>(&'? mut {unknown}) -> Option<<{unknown} as Iterator>::Item> 237..250 'for a in b {}': Option<{unknown}> 237..250 'for a in b {}': () 237..250 'for a in b {}': () @@ -396,8 +396,8 @@ fn diverging_expression_3_break() { 315..337 'for a ...urn; }': {unknown} 315..337 'for a ...urn; }': ! 315..337 'for a ...urn; }': {unknown} - 315..337 'for a ...urn; }': &mut {unknown} - 315..337 'for a ...urn; }': fn next<{unknown}>(&mut {unknown}) -> Option<<{unknown} as Iterator>::Item> + 315..337 'for a ...urn; }': &'? mut {unknown} + 315..337 'for a ...urn; }': fn next<{unknown}>(&'? mut {unknown}) -> Option<<{unknown} as Iterator>::Item> 315..337 'for a ...urn; }': Option<{unknown}> 315..337 'for a ...urn; }': () 315..337 'for a ...urn; }': () 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 4355881d7299c..5e040a60e29ce 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 @@ -32,27 +32,27 @@ fn infer_pattern() { } "#, expect![[r#" - 8..9 'x': &i32 + 8..9 'x': &'? i32 17..400 '{ ...o_x; }': () - 27..28 'y': &i32 - 31..32 'x': &i32 - 42..44 '&z': &i32 + 27..28 'y': &'? i32 + 31..32 'x': &'? i32 + 42..44 '&z': &'? i32 43..44 'z': i32 - 47..48 'x': &i32 + 47..48 'x': &'? i32 58..59 'a': i32 62..63 'z': i32 - 73..79 '(c, d)': (i32, &str) + 73..79 '(c, d)': (i32, &'static str) 74..75 'c': i32 - 77..78 'd': &str - 82..94 '(1, "hello")': (i32, &str) + 77..78 'd': &'static str + 82..94 '(1, "hello")': (i32, &'static str) 83..84 '1': i32 - 86..93 '"hello"': &str + 86..93 '"hello"': &'static str 101..151 'for (e... }': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter 101..151 'for (e... }': {unknown} 101..151 'for (e... }': ! 101..151 'for (e... }': {unknown} - 101..151 'for (e... }': &mut {unknown} - 101..151 'for (e... }': fn next<{unknown}>(&mut {unknown}) -> Option<<{unknown} as Iterator>::Item> + 101..151 'for (e... }': &'? mut {unknown} + 101..151 'for (e... }': fn next<{unknown}>(&'? mut {unknown}) -> Option<<{unknown} as Iterator>::Item> 101..151 'for (e... }': Option<({unknown}, {unknown})> 101..151 'for (e... }': () 101..151 'for (e... }': () @@ -74,10 +74,10 @@ fn infer_pattern() { 194..197 'val': {unknown} 210..236 'if let...rue {}': () 213..233 'let x ... &true': bool - 217..225 'x @ true': &bool + 217..225 'x @ true': &'? bool 221..225 'true': bool 221..225 'true': bool - 228..233 '&true': &bool + 228..233 '&true': &'? bool 229..233 'true': bool 234..236 '{}': () 246..252 'lambda': impl Fn(u64, u64, i32) -> i32 @@ -90,14 +90,14 @@ fn infer_pattern() { 277..282 'a + b': u64 281..282 'b': u64 284..285 'c': i32 - 298..310 'ref ref_to_x': &&i32 - 313..314 'x': &i32 - 324..333 'mut mut_x': &i32 - 336..337 'x': &i32 - 347..367 'ref mu...f_to_x': &mut &i32 - 370..371 'x': &i32 - 381..382 'k': &mut &i32 - 385..397 'mut_ref_to_x': &mut &i32 + 298..310 'ref ref_to_x': &'? &'? i32 + 313..314 'x': &'? i32 + 324..333 'mut mut_x': &'? i32 + 336..337 'x': &'? i32 + 347..367 'ref mu...f_to_x': &'? mut &'? i32 + 370..371 'x': &'? i32 + 381..382 'k': &'? mut &'? i32 + 385..397 'mut_ref_to_x': &'? mut &'? i32 "#]], ); } @@ -120,14 +120,14 @@ fn infer_literal_pattern() { 17..28 '{ loop {} }': T 19..26 'loop {}': ! 24..26 '{}': () - 37..38 'x': &i32 + 37..38 'x': &'? i32 46..208 '{ ...) {} }': () 52..75 'if let...y() {}': () 55..72 'let "f... any()': bool - 59..64 '"foo"': &str - 59..64 '"foo"': &str - 67..70 'any': fn any<&str>() -> &str - 67..72 'any()': &str + 59..64 '"foo"': &'static str + 59..64 '"foo"': &'static str + 67..70 'any': fn any<&'static str>() -> &'static str + 67..72 'any()': &'static str 73..75 '{}': () 80..99 'if let...y() {}': () 83..96 'let 1 = any()': bool @@ -178,7 +178,7 @@ fn infer_range_pattern() { } "#, expect![[r#" - 8..9 'x': &i32 + 8..9 'x': &'? i32 17..75 '{ ...2 {} }': () 23..45 'if let...u32 {}': () 26..42 'let 1....= 2u32': bool @@ -208,14 +208,14 @@ fn infer_pattern_match_ergonomics() { expect![[r#" 27..78 '{ ...(1); }': () 37..41 'A(n)': A - 39..40 'n': &i32 - 44..49 '&A(1)': &A + 39..40 'n': &'? i32 + 44..49 '&A(1)': &'? A 45..46 'A': extern "rust-call" A(i32) -> A 45..49 'A(1)': A 47..48 '1': i32 59..63 'A(n)': A - 61..62 'n': &mut i32 - 66..75 '&mut A(1)': &mut A + 61..62 'n': &'? mut i32 + 66..75 '&mut A(1)': &'? mut A 71..72 'A': extern "rust-call" A(i32) -> A 71..75 'A(1)': A 73..74 '1': i32 @@ -235,17 +235,17 @@ fn infer_pattern_match_ergonomics_ref() { "#, expect![[r#" 10..56 '{ ...= v; }': () - 20..21 'v': &(i32, &i32) - 24..32 '&(1, &2)': &(i32, &i32) - 25..32 '(1, &2)': (i32, &i32) + 20..21 'v': &'? (i32, &'? i32) + 24..32 '&(1, &2)': &'? (i32, &'? i32) + 25..32 '(1, &2)': (i32, &'? i32) 26..27 '1': i32 - 29..31 '&2': &i32 + 29..31 '&2': &'? i32 30..31 '2': i32 - 42..49 '(_, &w)': (i32, &i32) + 42..49 '(_, &w)': (i32, &'? i32) 43..44 '_': i32 - 46..48 '&w': &i32 + 46..48 '&w': &'? i32 47..48 'w': i32 - 52..53 'v': &(i32, &i32) + 52..53 'v': &'? (i32, &'? i32) "#]], ); } @@ -286,28 +286,28 @@ fn infer_pattern_match_slice() { "#, expect![[r#" 10..209 '{ ... } }': () - 20..25 'slice': &[f64] - 36..42 '&[0.0]': &[f64; 1] + 20..25 'slice': &'? [f64] + 36..42 '&[0.0]': &'? [f64; 1] 37..42 '[0.0]': [f64; 1] 38..41 '0.0': f64 48..207 'match ... }': () - 54..59 'slice': &[f64] - 70..73 '&[]': &[f64] + 54..59 'slice': &'? [f64] + 70..73 '&[]': &'? [f64] 71..73 '[]': [f64] 77..79 '{}': () - 89..93 '&[a]': &[f64] + 89..93 '&[a]': &'? [f64] 90..93 '[a]': [f64] 91..92 'a': f64 97..123 '{ ... }': () 111..112 'a': f64 - 133..140 '&[b, c]': &[f64] + 133..140 '&[b, c]': &'? [f64] 134..140 '[b, c]': [f64] 135..136 'b': f64 138..139 'c': f64 144..185 '{ ... }': () 158..159 'b': f64 173..174 'c': f64 - 194..195 '_': &[f64] + 194..195 '_': &'? [f64] 199..201 '{}': () "#]], ); @@ -327,14 +327,14 @@ fn infer_pattern_match_string_literal() { "#, expect![[r#" 10..98 '{ ... } }': () - 20..21 's': &str - 30..37 '"hello"': &str + 20..21 's': &'? str + 30..37 '"hello"': &'static str 43..96 'match ... }': () - 49..50 's': &str - 61..68 '"hello"': &str - 61..68 '"hello"': &str + 49..50 's': &'? str + 61..68 '"hello"': &'static str + 61..68 '"hello"': &'static str 72..74 '{}': () - 83..84 '_': &str + 83..84 '_': &'? str 88..90 '{}': () "#]], ); @@ -358,27 +358,27 @@ fn infer_pattern_match_byte_string_literal() { } "#, expect![[r#" - 105..109 'self': &[T; N] + 105..109 'self': &'? [T; N] 111..116 'index': {unknown} - 157..180 '{ ... }': &[u8] + 157..180 '{ ... }': &'? [u8] 167..174 'loop {}': ! 172..174 '{}': () 191..192 'v': [u8; 3] 203..261 '{ ...v {} }': () 209..233 'if let...[S] {}': () 212..230 'let b"... &v[S]': bool - 216..222 'b"foo"': &[u8] - 216..222 'b"foo"': &[u8] - 225..230 '&v[S]': &[u8] + 216..222 'b"foo"': &'static [u8] + 216..222 'b"foo"': &'static [u8] + 225..230 '&v[S]': &'? [u8] 226..227 'v': [u8; 3] 226..230 'v[S]': [u8] 228..229 'S': S 231..233 '{}': () 238..259 'if let... &v {}': () 241..256 'let b"foo" = &v': bool - 245..251 'b"foo"': &[u8; 3] - 245..251 'b"foo"': &[u8; 3] - 254..256 '&v': &[u8; 3] + 245..251 'b"foo"': &'static [u8; 3] + 245..251 'b"foo"': &'static [u8; 3] + 254..256 '&v': &'? [u8; 3] 255..256 'v': [u8; 3] 257..259 '{}': () "#]], @@ -399,17 +399,17 @@ fn infer_pattern_match_or() { "#, expect![[r#" 10..108 '{ ... } }': () - 20..21 's': &str - 30..37 '"hello"': &str + 20..21 's': &'? str + 30..37 '"hello"': &'static str 43..106 'match ... }': () - 49..50 's': &str - 61..68 '"hello"': &str - 61..68 '"hello"': &str - 61..78 '"hello...world"': &str - 71..78 '"world"': &str - 71..78 '"world"': &str + 49..50 's': &'? str + 61..68 '"hello"': &'static str + 61..68 '"hello"': &'static str + 61..78 '"hello...world"': &'? str + 71..78 '"world"': &'static str + 71..78 '"world"': &'static str 82..84 '{}': () - 93..94 '_': &str + 93..94 '_': &'? str 98..100 '{}': () "#]], ); @@ -505,10 +505,10 @@ fn infer_adt_pattern() { 216..217 '1': usize 227..231 'E::B': E 235..237 '10': usize - 255..274 'ref d ...{ .. }': &E + 255..274 'ref d ...{ .. }': &'? E 263..274 'E::A { .. }': E 277..278 'e': E - 284..285 'd': &E + 284..285 'd': &'? E "#]], ); } @@ -529,14 +529,14 @@ impl Foo { expect![[r#" 42..151 '{ ... }': () 56..64 'Self(s,)': Foo - 61..62 's': &usize - 67..75 '&Foo(0,)': &Foo + 61..62 's': &'? usize + 67..75 '&Foo(0,)': &'? Foo 68..71 'Foo': extern "rust-call" Foo(usize) -> Foo 68..75 'Foo(0,)': Foo 72..73 '0': usize 89..97 'Self(s,)': Foo - 94..95 's': &mut usize - 100..112 '&mut Foo(0,)': &mut Foo + 94..95 's': &'? mut usize + 100..112 '&mut Foo(0,)': &'? mut Foo 105..108 'Foo': extern "rust-call" Foo(usize) -> Foo 105..112 'Foo(0,)': Foo 109..110 '0': usize @@ -669,7 +669,7 @@ fn main() { } "#, expect![[r#" - 27..31 'self': &S + 27..31 'self': &'? S 41..50 '{ false }': bool 43..48 'false': bool 64..115 '{ ... } }': () @@ -679,7 +679,7 @@ fn main() { 93..94 's': S 93..100 's.foo()': bool 104..106 '()': () - "#]], + "#]], ) } @@ -702,29 +702,29 @@ fn test() { 51..58 'loop {}': ! 56..58 '{}': () 72..171 '{ ... x); }': () - 78..81 'foo': fn foo<&(i32, &str), i32, impl FnOnce(&(i32, &str)) -> i32>(&(i32, &str), impl FnOnce(&(i32, &str)) -> i32) -> i32 + 78..81 'foo': fn foo<&'? (i32, &'? str), i32, impl FnOnce(&'? (i32, &'? str)) -> i32>(&'? (i32, &'? str), impl FnOnce(&'? (i32, &'? str)) -> i32) -> i32 78..105 'foo(&(...y)| x)': i32 - 82..91 '&(1, "a")': &(i32, &str) - 83..91 '(1, "a")': (i32, &str) + 82..91 '&(1, "a")': &'? (i32, &'static str) + 83..91 '(1, "a")': (i32, &'static str) 84..85 '1': i32 - 87..90 '"a"': &str - 93..104 '|&(x, y)| x': impl FnOnce(&(i32, &str)) -> i32 - 94..101 '&(x, y)': &(i32, &str) - 95..101 '(x, y)': (i32, &str) + 87..90 '"a"': &'static str + 93..104 '|&(x, y)| x': impl FnOnce(&'? (i32, &'? str)) -> i32 + 94..101 '&(x, y)': &'? (i32, &'? str) + 95..101 '(x, y)': (i32, &'? str) 96..97 'x': i32 - 99..100 'y': &str + 99..100 'y': &'? str 103..104 'x': i32 - 142..145 'foo': fn foo<&(i32, &str), &i32, impl FnOnce(&(i32, &str)) -> &i32>(&(i32, &str), impl FnOnce(&(i32, &str)) -> &i32) -> &i32 - 142..168 'foo(&(...y)| x)': &i32 - 146..155 '&(1, "a")': &(i32, &str) - 147..155 '(1, "a")': (i32, &str) + 142..145 'foo': fn foo<&'? (i32, &'? str), &'? i32, impl FnOnce(&'? (i32, &'? str)) -> &'? i32>(&'? (i32, &'? str), impl FnOnce(&'? (i32, &'? str)) -> &'? i32) -> &'? i32 + 142..168 'foo(&(...y)| x)': &'? i32 + 146..155 '&(1, "a")': &'? (i32, &'static str) + 147..155 '(1, "a")': (i32, &'static str) 148..149 '1': i32 - 151..154 '"a"': &str - 157..167 '|(x, y)| x': impl FnOnce(&(i32, &str)) -> &i32 - 158..164 '(x, y)': (i32, &str) - 159..160 'x': &i32 - 162..163 'y': &&str - 166..167 'x': &i32 + 151..154 '"a"': &'static str + 157..167 '|(x, y)| x': impl FnOnce(&'? (i32, &'? str)) -> &'? i32 + 158..164 '(x, y)': (i32, &'? str) + 159..160 'x': &'? i32 + 162..163 'y': &'? &'? str + 166..167 'x': &'? i32 "#]], ); } @@ -741,13 +741,13 @@ fn slice_tail_pattern() { } "#, expect![[r#" - 7..13 'params': &[i32] + 7..13 'params': &'? [i32] 23..92 '{ ... } }': () 29..90 'match ... }': () - 35..41 'params': &[i32] + 35..41 'params': &'? [i32] 52..69 '[head,... @ ..]': [i32] - 53..57 'head': &i32 - 59..68 'tail @ ..': &[i32] + 53..57 'head': &'? i32 + 59..68 'tail @ ..': &'? [i32] 66..68 '..': [i32] 73..84 '{ }': () "#]], @@ -1109,7 +1109,7 @@ fn var_args() { #[lang = "va_list"] pub struct VaListImpl<'f>; fn my_fn(foo: ...) {} - //^^^ VaListImpl<'{error}> + //^^^ VaListImpl<'?> "#, ); } @@ -1122,9 +1122,9 @@ fn foo() { let &() = &(); let &mut () = &mut (); let &mut () = &(); - //^^^^^^^ expected &(), got &mut () + //^^^^^^^ expected &'? (), got &'? mut () let &() = &mut (); - //^^^ expected &mut (), got &() + //^^^ expected &'? mut (), got &'? () } "#, ); @@ -1148,7 +1148,7 @@ fn main() { }; if let Wrap::::A { cool, ..} = &wrapped {} - //^^^^ &u32 + //^^^^ &'? u32 } "#, ); @@ -1182,7 +1182,7 @@ fn main() { }; if let Wrap::<::Props>::A { cool, ..} = &wrapped {} - //^^^^ &u32 + //^^^^ &'? u32 } "#, ); @@ -1217,7 +1217,7 @@ fn main() { match &6 { Foo::::TEST_I32_REF => (), Foo::::TEST_I32 => (), - //^^^^^^^^^^^^^^^^^^^^ expected &i32, got i32 + //^^^^^^^^^^^^^^^^^^^^ expected &'? i32, got i32 _ => (), } } 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 c2d2047e6f99b..3aa94be755c74 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 @@ -99,7 +99,7 @@ fn recursive_vars() { 24..31 'unknown': {unknown} 37..44 '[y, &y]': [{unknown}; 2] 38..39 'y': {unknown} - 41..43 '&y': &{unknown} + 41..43 '&y': &'? {unknown} 42..43 'y': {unknown} "#]], ); @@ -117,19 +117,19 @@ fn recursive_vars_2() { "#, expect![[r#" 10..79 '{ ...x)]; }': () - 20..21 'x': &{unknown} - 24..31 'unknown': &{unknown} + 20..21 'x': &'? {unknown} + 24..31 'unknown': &'? {unknown} 41..42 'y': {unknown} 45..52 'unknown': {unknown} - 58..76 '[(x, y..., &x)]': [(&{unknown}, {unknown}); 2] - 59..65 '(x, y)': (&{unknown}, {unknown}) - 60..61 'x': &{unknown} + 58..76 '[(x, y..., &x)]': [(&'? {unknown}, {unknown}); 2] + 59..65 '(x, y)': (&'? {unknown}, {unknown}) + 60..61 'x': &'? {unknown} 63..64 'y': {unknown} - 67..75 '(&y, &x)': (&{unknown}, {unknown}) - 68..70 '&y': &{unknown} + 67..75 '(&y, &x)': (&'? {unknown}, {unknown}) + 68..70 '&y': &'? {unknown} 69..70 'y': {unknown} - 72..74 '&x': &&{unknown} - 73..74 'x': &{unknown} + 72..74 '&x': &'? &'? {unknown} + 73..74 'x': &'? {unknown} "#]], ); } @@ -166,7 +166,7 @@ fn infer_std_crash_1() { 59..136 'match ... }': () 65..82 'someth...nknown': Maybe<{unknown}> 93..123 'Maybe:...thing)': Maybe<{unknown}> - 105..122 'ref mu...ething': &mut {unknown} + 105..122 'ref mu...ething': &'? mut {unknown} 127..129 '()': () "#]], ); @@ -183,7 +183,7 @@ fn infer_std_crash_2() { "#, expect![[r#" 22..52 '{ ...n']; }': () - 28..49 '&[0, b...b'\n']': &[u8; 4] + 28..49 '&[0, b...b'\n']': &'? [u8; 4] 29..49 '[0, b'...b'\n']': [u8; 4] 30..31 '0': u8 33..38 'b'\n'': u8 @@ -269,8 +269,8 @@ fn infer_std_crash_5() { 32..320 'for co... }': {unknown} 32..320 'for co... }': ! 32..320 'for co... }': {unknown} - 32..320 'for co... }': &mut {unknown} - 32..320 'for co... }': fn next<{unknown}>(&mut {unknown}) -> Option<<{unknown} as Iterator>::Item> + 32..320 'for co... }': &'? mut {unknown} + 32..320 'for co... }': fn next<{unknown}>(&'? mut {unknown}) -> Option<<{unknown} as Iterator>::Item> 32..320 'for co... }': Option<{unknown}> 32..320 'for co... }': () 32..320 'for co... }': () @@ -278,22 +278,22 @@ fn infer_std_crash_5() { 36..43 'content': {unknown} 47..60 'doesnt_matter': {unknown} 61..320 '{ ... }': () - 75..79 'name': &{unknown} - 82..166 'if doe... }': &{unknown} + 75..79 'name': &'? {unknown} + 82..166 'if doe... }': &'? {unknown} 85..98 'doesnt_matter': bool - 99..128 '{ ... }': &{unknown} - 113..118 'first': &{unknown} - 134..166 '{ ... }': &{unknown} - 148..156 '&content': &{unknown} + 99..128 '{ ... }': &'? {unknown} + 113..118 'first': &'? {unknown} + 134..166 '{ ... }': &'? {unknown} + 148..156 '&content': &'? {unknown} 149..156 'content': {unknown} - 181..188 'content': &{unknown} - 191..313 'if ICE... }': &{unknown} + 181..188 'content': &'? {unknown} + 191..313 'if ICE... }': &'? {unknown} 194..231 'ICE_RE..._VALUE': {unknown} 194..247 'ICE_RE...&name)': bool - 241..246 '&name': &&{unknown} - 242..246 'name': &{unknown} - 248..276 '{ ... }': &{unknown} - 262..266 'name': &{unknown} + 241..246 '&name': &'? &'? {unknown} + 242..246 'name': &'? {unknown} + 248..276 '{ ... }': &'? {unknown} + 262..266 'name': &'? {unknown} 282..313 '{ ... }': {unknown} 296..303 'content': {unknown} "#]], @@ -318,7 +318,7 @@ fn infer_nested_generics_crash() { expect![[r#" 91..105 'query_response': Canonical> 136..166 '{ ...lue; }': () - 142..163 '&query....value': &QueryResponse + 142..163 '&query....value': &'? QueryResponse 143..157 'query_response': Canonical> 143..163 'query_....value': QueryResponse "#]], @@ -465,12 +465,12 @@ fn issue_3999_slice() { } "#, expect![[r#" - 7..13 'params': &[usize] + 7..13 'params': &'? [usize] 25..80 '{ ... } }': () 31..78 'match ... }': () - 37..43 'params': &[usize] + 37..43 'params': &'? [usize] 54..66 '[ps @ .., _]': [usize] - 55..62 'ps @ ..': &[usize] + 55..62 'ps @ ..': &'? [usize] 60..62 '..': [usize] 64..65 '_': usize 70..72 '{}': () @@ -523,13 +523,13 @@ fn issue_4235_name_conflicts() { "#, expect![[r#" 31..37 'FOO {}': FOO - 63..67 'self': &FOO + 63..67 'self': &'? FOO 69..71 '{}': () 85..119 '{ ...o(); }': () - 95..96 'a': &FOO - 99..103 '&FOO': &FOO + 95..96 'a': &'? FOO + 99..103 '&FOO': &'? FOO 100..103 'FOO': FOO - 109..110 'a': &FOO + 109..110 'a': &'? FOO 109..116 'a.foo()': () "#]], ); @@ -715,12 +715,12 @@ fn issue_4885() { } "#, expect![[r#" - 70..73 'key': &K + 70..73 'key': &'? K 132..148 '{ ...key) }': impl Future>::Bar> - 138..141 'bar': fn bar(&K) -> impl Future>::Bar> + 138..141 'bar': fn bar(&'? K) -> impl Future>::Bar> 138..146 'bar(key)': impl Future>::Bar> - 142..145 'key': &K - 162..165 'key': &K + 142..145 'key': &'? K + 162..165 'key': &'? K 224..227 '{ }': () "#]], ); @@ -771,11 +771,11 @@ fn issue_4800() { } "#, expect![[r#" - 379..383 'self': &mut PeerSet + 379..383 'self': &'? mut PeerSet 401..424 '{ ... }': dyn Future 411..418 'loop {}': ! 416..418 '{}': () - 575..579 'self': &mut Self + 575..579 'self': &'? mut Self "#]], ); } @@ -815,19 +815,19 @@ fn issue_4966() { 225..229 'iter': T 244..246 '{}': Vec 258..402 '{ ...r(); }': () - 268..273 'inner': Map f64> - 276..300 'Map { ... 0.0 }': Map f64> - 285..298 '|_: &f64| 0.0': impl Fn(&f64) -> f64 - 286..287 '_': &f64 + 268..273 'inner': Map f64> + 276..300 'Map { ... 0.0 }': Map f64> + 285..298 '|_: &f64| 0.0': impl Fn(&'? f64) -> f64 + 286..287 '_': &'? f64 295..298 '0.0': f64 - 311..317 'repeat': Repeat f64>> - 320..345 'Repeat...nner }': Repeat f64>> - 338..343 'inner': Map f64> - 356..359 'vec': Vec f64>>>> - 362..371 'from_iter': fn from_iter f64>>>, Repeat f64>>>(Repeat f64>>) -> Vec f64>>>> - 362..379 'from_i...epeat)': Vec f64>>>> - 372..378 'repeat': Repeat f64>> - 386..389 'vec': Vec f64>>>> + 311..317 'repeat': Repeat f64>> + 320..345 'Repeat...nner }': Repeat f64>> + 338..343 'inner': Map f64> + 356..359 'vec': Vec f64>>>> + 362..371 'from_iter': fn from_iter f64>>>, Repeat f64>>>(Repeat f64>>) -> Vec f64>>>> + 362..379 'from_i...epeat)': Vec f64>>>> + 372..378 'repeat': Repeat f64>> + 386..389 'vec': Vec f64>>>> 386..399 'vec.foo_bar()': {unknown} "#]], ); @@ -850,10 +850,10 @@ fn main() { } "#, expect![[r#" - 40..44 'self': &S + 40..44 'self': &'? S 46..48 '_t': T 53..55 '{}': () - 81..85 'self': &S + 81..85 'self': &'? S 87..89 '_f': F 94..96 '{}': () 109..160 '{ ...10); }': () @@ -862,8 +862,8 @@ fn main() { 123..126 'S()': S 132..133 's': S 132..144 's.g(|_x| {})': () - 136..143 '|_x| {}': impl FnOnce(&i32) - 137..139 '_x': &i32 + 136..143 '|_x| {}': impl FnOnce(&'? i32) + 137..139 '_x': &'? i32 141..143 '{}': () 150..151 's': S 150..157 's.f(10)': () @@ -895,14 +895,14 @@ fn flush(&self) { } "#, expect![[r#" - 123..127 'self': &Mutex - 150..152 '{}': MutexGuard<'{error}, T> - 234..238 'self': &{unknown} + 123..127 'self': &'? Mutex + 150..152 '{}': MutexGuard<'?, T> + 234..238 'self': &'? {unknown} 240..290 '{ ...()); }': () - 250..251 'w': &Mutex + 250..251 'w': &'? Mutex 276..287 '*(w.lock())': BufWriter - 278..279 'w': &Mutex - 278..286 'w.lock()': MutexGuard<'{error}, BufWriter> + 278..279 'w': &'? Mutex + 278..286 'w.lock()': MutexGuard<'?, BufWriter> "#]], ); } @@ -1023,20 +1023,20 @@ fn cfg_tail() { expect![[r#" 14..53 '{ ...)] 9 }': () 20..31 '{ "first" }': () - 22..29 '"first"': &str + 22..29 '"first"': &'static str 72..190 '{ ...] 13 }': () 78..88 '{ "fake" }': () - 80..86 '"fake"': &str + 80..86 '"fake"': &'static str 93..103 '{ "fake" }': () - 95..101 '"fake"': &str + 95..101 '"fake"': &'static str 108..120 '{ "second" }': () - 110..118 '"second"': &str + 110..118 '"second"': &'static str 210..273 '{ ... 15; }': () 216..227 '{ "third" }': () - 218..225 '"third"': &str + 218..225 '"third"': &'static str 293..357 '{ ...] 15 }': () - 299..311 '{ "fourth" }': &str - 301..309 '"fourth"': &str + 299..311 '{ "fourth" }': &'static str + 301..309 '"fourth"': &'static str "#]], ) } @@ -1238,8 +1238,8 @@ fn test() { 16..66 'for _ ... }': IntoIterator::IntoIter<()> 16..66 'for _ ... }': ! 16..66 'for _ ... }': IntoIterator::IntoIter<()> - 16..66 'for _ ... }': &mut IntoIterator::IntoIter<()> - 16..66 'for _ ... }': fn next>(&mut IntoIterator::IntoIter<()>) -> Option< as Iterator>::Item> + 16..66 'for _ ... }': &'? mut IntoIterator::IntoIter<()> + 16..66 'for _ ... }': fn next>(&'? mut IntoIterator::IntoIter<()>) -> Option< as Iterator>::Item> 16..66 'for _ ... }': Option> 16..66 'for _ ... }': () 16..66 'for _ ... }': () @@ -1599,85 +1599,6 @@ fn f(s: S) { ); } -#[test] -fn rust_161_option_clone() { - check_types( - r#" -//- minicore: option, drop - -fn test(o: &Option) { - o.my_clone(); - //^^^^^^^^^^^^ Option -} - -pub trait MyClone: Sized { - fn my_clone(&self) -> Self; -} - -impl const MyClone for Option -where - T: ~const MyClone + ~const Drop + ~const Destruct, -{ - fn my_clone(&self) -> Self { - match self { - Some(x) => Some(x.my_clone()), - None => None, - } - } -} - -impl const MyClone for i32 { - fn my_clone(&self) -> Self { - *self - } -} - -pub trait Destruct {} - -impl const Destruct for T {} -"#, - ); -} - -#[test] -fn rust_162_option_clone() { - check_types( - r#" -//- minicore: option, drop - -fn test(o: &Option) { - o.my_clone(); - //^^^^^^^^^^^^ Option -} - -pub trait MyClone: Sized { - fn my_clone(&self) -> Self; -} - -impl const MyClone for Option -where - T: ~const MyClone + ~const Destruct, -{ - fn my_clone(&self) -> Self { - match self { - Some(x) => Some(x.my_clone()), - None => None, - } - } -} - -impl const MyClone for i32 { - fn my_clone(&self) -> Self { - *self - } -} - -#[lang = "destruct"] -pub trait Destruct {} -"#, - ); -} - #[test] fn tuple_struct_pattern_with_unmatched_args_crash() { check_infer( @@ -1727,7 +1648,7 @@ fn dyn_with_unresolved_trait() { r#" fn foo(a: &dyn DoesNotExist) { a.bar(); - //^&{unknown} + //^&'? {unknown} } "#, ); @@ -1851,9 +1772,9 @@ fn foo() { match &E::A { b @ (x @ E::A | x) => { b; - //^ &E + //^ &'? E x; - //^ &E + //^ &'? E } } }", @@ -2040,3 +1961,41 @@ fn main() { "#, ) } + +#[test] +fn cfg_first_trait_param_16141() { + check_no_mismatches( + r#" +//- minicore: sized, coerce_unsized +trait Bar { + fn bar(&self) {} +} + +impl<#[cfg(feature = "a-feature")] A> Bar for (){} +"#, + ) +} + +#[test] +fn nested_anon_generics_and_where_bounds_17173() { + check_types( + r#" +//- minicore: sized, fn +pub trait Lookup { + type Data; + fn lookup(&self) -> Self::Data; +} +pub trait ItemTreeLoc { + type Id; +} +fn id_to_generics(id: impl Lookup>, + //^^ impl Lookup> + enabled_params: impl Fn(), + //^^^^^^^^^^^^^^ impl Fn() + ) +where + (): Sized, +{} +"#, + ); +} 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 a9d28ebfef570..e2cd7fa26b224 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 @@ -115,15 +115,15 @@ fn test(a: u32, b: isize, c: !, d: &str) { 8..9 'a': u32 16..17 'b': isize 26..27 'c': ! - 32..33 'd': &str + 32..33 'd': &'? str 41..120 '{ ...f32; }': () 47..48 'a': u32 54..55 'b': isize 61..62 'c': ! - 68..69 'd': &str + 68..69 'd': &'? str 75..81 '1usize': usize 87..93 '1isize': isize - 99..105 '"test"': &str + 99..105 '"test"': &'static str 111..117 '1.0f32': f32 "#]], ); @@ -344,23 +344,23 @@ fn test(a: &u32, b: &mut u32, c: *const u32, d: *mut u32) { } "#, expect![[r#" - 8..9 'a': &u32 - 17..18 'b': &mut u32 + 8..9 'a': &'? u32 + 17..18 'b': &'? mut u32 30..31 'c': *const u32 45..46 'd': *mut u32 58..149 '{ ... *d; }': () - 64..65 'a': &u32 + 64..65 'a': &'? u32 71..73 '*a': u32 - 72..73 'a': &u32 - 79..81 '&a': &&u32 - 80..81 'a': &u32 - 87..93 '&mut a': &mut &u32 - 92..93 'a': &u32 - 99..100 'b': &mut u32 + 72..73 'a': &'? u32 + 79..81 '&a': &'? &'? u32 + 80..81 'a': &'? u32 + 87..93 '&mut a': &'? mut &'? u32 + 92..93 'a': &'? u32 + 99..100 'b': &'? mut u32 106..108 '*b': u32 - 107..108 'b': &mut u32 - 114..116 '&b': &&mut u32 - 115..116 'b': &mut u32 + 107..108 'b': &'? mut u32 + 114..116 '&b': &'? &'? mut u32 + 115..116 'b': &'? mut u32 122..123 'c': *const u32 129..131 '*c': u32 130..131 'c': *const u32 @@ -425,22 +425,22 @@ h"; 32..36 '5i32': i32 50..54 '5f32': f32 68..72 '5f64': f64 - 86..93 '"hello"': &str - 107..115 'b"bytes"': &[u8; 5] + 86..93 '"hello"': &'static str + 107..115 'b"bytes"': &'static [u8; 5] 129..132 ''c'': char 146..150 'b'b'': u8 164..168 '3.14': f64 182..186 '5000': i32 200..205 'false': bool 219..223 'true': bool - 237..333 'r#" ... "#': &str - 347..357 'br#"yolo"#': &[u8; 4] - 375..376 'a': &[u8; 4] - 379..403 'b"a\x2... c"': &[u8; 4] - 421..422 'b': &[u8; 4] - 425..433 'br"g\ h"': &[u8; 4] - 451..452 'c': &[u8; 6] - 455..467 'br#"x"\"yb"#': &[u8; 6] + 237..333 'r#" ... "#': &'static str + 347..357 'br#"yolo"#': &'static [u8; 4] + 375..376 'a': &'static [u8; 4] + 379..403 'b"a\x2... c"': &'static [u8; 4] + 421..422 'b': &'static [u8; 4] + 425..433 'br"g\ h"': &'static [u8; 4] + 451..452 'c': &'static [u8; 6] + 455..467 'br#"x"\"yb"#': &'static [u8; 6] "##]], ); } @@ -508,9 +508,9 @@ fn test(x: SomeType) { 238..240 '!x': {unknown} 239..240 'x': SomeType 246..254 '-"hello"': {unknown} - 247..254 '"hello"': &str + 247..254 '"hello"': &'static str 260..268 '!"hello"': {unknown} - 261..268 '"hello"': &str + 261..268 '"hello"': &'static str "#]], ); } @@ -535,7 +535,7 @@ fn test() -> &mut &f64 { expect![[r#" 13..14 'x': u32 21..23 '{}': () - 77..230 '{ ...t &c }': &mut &f64 + 77..230 '{ ...t &c }': &'? mut &'? f64 87..88 'a': u32 91..107 'unknow...nction': {unknown} 91..109 'unknow...tion()': u32 @@ -550,8 +550,8 @@ fn test() -> &mut &f64 { 193..194 'c': f64 197..213 'unknow...nction': {unknown} 197..215 'unknow...tion()': f64 - 221..228 '&mut &c': &mut &f64 - 226..228 '&c': &f64 + 221..228 '&mut &c': &'? mut &'? f64 + 226..228 '&c': &'? f64 227..228 'c': f64 "#]], ); @@ -579,12 +579,12 @@ impl S { } "#, expect![[r#" - 33..37 'self': &S + 33..37 'self': &'? S 39..60 '{ ... }': () - 49..53 'self': &S - 74..78 'self': &S + 49..53 'self': &'? S + 74..78 'self': &'? S 87..108 '{ ... }': () - 97..101 'self': &S + 97..101 'self': &'? S 132..152 '{ ... }': S 142..146 'S {}': S 176..199 '{ ... }': S @@ -771,35 +771,35 @@ fn test2(a1: *const A, a2: *mut A) { 64..65 'a': A 71..73 'a1': A 71..75 'a1.b': B - 85..87 'a2': &A - 90..92 '&a': &A + 85..87 'a2': &'? A + 90..92 '&a': &'? A 91..92 'a': A - 98..100 'a2': &A + 98..100 'a2': &'? A 98..102 'a2.b': B - 112..114 'a3': &mut A - 117..123 '&mut a': &mut A + 112..114 'a3': &'? mut A + 117..123 '&mut a': &'? mut A 122..123 'a': A - 129..131 'a3': &mut A + 129..131 'a3': &'? mut A 129..133 'a3.b': B - 143..145 'a4': &&&&&&&A - 148..156 '&&&&&&&a': &&&&&&&A - 149..156 '&&&&&&a': &&&&&&A - 150..156 '&&&&&a': &&&&&A - 151..156 '&&&&a': &&&&A - 152..156 '&&&a': &&&A - 153..156 '&&a': &&A - 154..156 '&a': &A + 143..145 'a4': &'? &'? &'? &'? &'? &'? &'? A + 148..156 '&&&&&&&a': &'? &'? &'? &'? &'? &'? &'? A + 149..156 '&&&&&&a': &'? &'? &'? &'? &'? &'? A + 150..156 '&&&&&a': &'? &'? &'? &'? &'? A + 151..156 '&&&&a': &'? &'? &'? &'? A + 152..156 '&&&a': &'? &'? &'? A + 153..156 '&&a': &'? &'? A + 154..156 '&a': &'? A 155..156 'a': A - 162..164 'a4': &&&&&&&A + 162..164 'a4': &'? &'? &'? &'? &'? &'? &'? A 162..166 'a4.b': B - 176..178 'a5': &mut &&mut &&mut A - 181..199 '&mut &...&mut a': &mut &&mut &&mut A - 186..199 '&&mut &&mut a': &&mut &&mut A - 187..199 '&mut &&mut a': &mut &&mut A - 192..199 '&&mut a': &&mut A - 193..199 '&mut a': &mut A + 176..178 'a5': &'? mut &'? &'? mut &'? &'? mut A + 181..199 '&mut &...&mut a': &'? mut &'? &'? mut &'? &'? mut A + 186..199 '&&mut &&mut a': &'? &'? mut &'? &'? mut A + 187..199 '&mut &&mut a': &'? mut &'? &'? mut A + 192..199 '&&mut a': &'? &'? mut A + 193..199 '&mut a': &'? mut A 198..199 'a': A - 205..207 'a5': &mut &&mut &&mut A + 205..207 'a5': &'? mut &'? &'? mut &'? &'? mut A 205..209 'a5.b': B 223..225 'a1': *const A 237..239 'a2': *mut A @@ -840,22 +840,22 @@ fn test() { } "#, expect![[r#" - 66..70 'self': &A - 78..101 '{ ... }': &T - 88..95 '&self.0': &T - 89..93 'self': &A + 66..70 'self': &'? A + 78..101 '{ ... }': &'? T + 88..95 '&self.0': &'? T + 89..93 'self': &'? A 89..95 'self.0': T - 182..186 'self': &B - 205..228 '{ ... }': &T - 215..222 '&self.0': &T - 216..220 'self': &B + 182..186 'self': &'? B + 205..228 '{ ... }': &'? T + 215..222 '&self.0': &'? T + 216..220 'self': &'? B 216..222 'self.0': T 242..280 '{ ...))); }': () - 252..253 't': &i32 - 256..262 'A::foo': fn foo(&A) -> &i32 - 256..277 'A::foo...42))))': &i32 - 263..276 '&&B(B(A(42)))': &&B>> - 264..276 '&B(B(A(42)))': &B>> + 252..253 't': &'? i32 + 256..262 'A::foo': fn foo(&'? A) -> &'? i32 + 256..277 'A::foo...42))))': &'? i32 + 263..276 '&&B(B(A(42)))': &'? &'? B>> + 264..276 '&B(B(A(42)))': &'? B>> 265..266 'B': extern "rust-call" B>>(B>) -> B>> 265..276 'B(B(A(42)))': B>> 267..268 'B': extern "rust-call" B>(A) -> B> @@ -895,28 +895,28 @@ fn test(a: A) { } "#, expect![[r#" - 71..75 'self': &A - 77..78 'x': &A - 93..114 '{ ... }': &T - 103..108 '&*x.0': &T + 71..75 'self': &'? A + 77..78 'x': &'? A + 93..114 '{ ... }': &'? T + 103..108 '&*x.0': &'? T 104..108 '*x.0': T - 105..106 'x': &A + 105..106 'x': &'? A 105..108 'x.0': *mut T - 195..199 'self': &B - 218..241 '{ ... }': &T - 228..235 '&self.0': &T - 229..233 'self': &B + 195..199 'self': &'? B + 218..241 '{ ... }': &'? T + 228..235 '&self.0': &'? T + 229..233 'self': &'? B 229..235 'self.0': T 253..254 'a': A 264..310 '{ ...))); }': () - 274..275 't': &i32 + 274..275 't': &'? i32 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 + 278..307 'A(0 as...B(a)))': &'? i32 280..281 '0': i32 280..291 '0 as *mut _': *mut i32 - 297..306 '&&B(B(a))': &&B>> - 298..306 '&B(B(a))': &B>> + 297..306 '&&B(B(a))': &'? &'? B>> + 298..306 '&B(B(a))': &'? B>> 299..300 'B': extern "rust-call" B>>(B>) -> B>> 299..306 'B(B(a))': B>> 301..302 'B': extern "rust-call" B>(A) -> B> @@ -1044,7 +1044,7 @@ fn infer_inherent_method() { 31..35 'self': A 37..38 'x': u32 52..54 '{}': i32 - 106..110 'self': &A + 106..110 'self': &'? A 112..113 'x': u64 127..129 '{}': i64 147..148 'a': A @@ -1053,7 +1053,7 @@ fn infer_inherent_method() { 159..167 'a.foo(1)': i32 165..166 '1': u32 173..184 '(&a).bar(1)': i64 - 174..176 '&a': &A + 174..176 '&a': &'? A 175..176 'a': A 182..183 '1': u64 190..191 'a': A @@ -1078,10 +1078,10 @@ fn test() { } "#, expect![[r#" - 67..71 'self': &str + 67..71 'self': &'? str 80..82 '{}': i32 96..116 '{ ...o(); }': () - 102..107 '"foo"': &str + 102..107 '"foo"': &'static str 102..113 '"foo".foo()': i32 "#]], ); @@ -1101,33 +1101,33 @@ fn infer_tuple() { } "#, expect![[r#" - 8..9 'x': &str + 8..9 'x': &'? str 17..18 'y': isize 27..169 '{ ...d"); }': () - 37..38 'a': (u32, &str) - 54..62 '(1, "a")': (u32, &str) + 37..38 'a': (u32, &'? str) + 54..62 '(1, "a")': (u32, &'? str) 55..56 '1': u32 - 58..61 '"a"': &str - 72..73 'b': ((u32, &str), &str) - 76..82 '(a, x)': ((u32, &str), &str) - 77..78 'a': (u32, &str) - 80..81 'x': &str - 92..93 'c': (isize, &str) - 96..102 '(y, x)': (isize, &str) + 58..61 '"a"': &'static str + 72..73 'b': ((u32, &'? str), &'? str) + 76..82 '(a, x)': ((u32, &'? str), &'? str) + 77..78 'a': (u32, &'? str) + 80..81 'x': &'? str + 92..93 'c': (isize, &'? str) + 96..102 '(y, x)': (isize, &'? str) 97..98 'y': isize - 100..101 'x': &str - 112..113 'd': ((isize, &str), &str) - 116..122 '(c, x)': ((isize, &str), &str) - 117..118 'c': (isize, &str) - 120..121 'x': &str - 132..133 'e': (i32, &str) - 136..144 '(1, "e")': (i32, &str) + 100..101 'x': &'? str + 112..113 'd': ((isize, &'? str), &'? str) + 116..122 '(c, x)': ((isize, &'? str), &'? str) + 117..118 'c': (isize, &'? str) + 120..121 'x': &'? str + 132..133 'e': (i32, &'static str) + 136..144 '(1, "e")': (i32, &'static str) 137..138 '1': i32 - 140..143 '"e"': &str - 154..155 'f': ((i32, &str), &str) - 158..166 '(e, "d")': ((i32, &str), &str) - 159..160 'e': (i32, &str) - 162..165 '"d"': &str + 140..143 '"e"': &'static str + 154..155 'f': ((i32, &'static str), &'static str) + 158..166 '(e, "d")': ((i32, &'static str), &'static str) + 159..160 'e': (i32, &'static str) + 162..165 '"d"': &'static str "#]], ); } @@ -1156,20 +1156,20 @@ fn infer_array() { } "#, expect![[r#" - 8..9 'x': &str + 8..9 'x': &'? str 17..18 'y': isize 27..326 '{ ...,4]; }': () - 37..38 'a': [&str; 1] - 41..44 '[x]': [&str; 1] - 42..43 'x': &str - 54..55 'b': [[&str; 1]; 2] - 58..64 '[a, a]': [[&str; 1]; 2] - 59..60 'a': [&str; 1] - 62..63 'a': [&str; 1] - 74..75 'c': [[[&str; 1]; 2]; 2] - 78..84 '[b, b]': [[[&str; 1]; 2]; 2] - 79..80 'b': [[&str; 1]; 2] - 82..83 'b': [[&str; 1]; 2] + 37..38 'a': [&'? str; 1] + 41..44 '[x]': [&'? str; 1] + 42..43 'x': &'? str + 54..55 'b': [[&'? str; 1]; 2] + 58..64 '[a, a]': [[&'? str; 1]; 2] + 59..60 'a': [&'? str; 1] + 62..63 'a': [&'? str; 1] + 74..75 'c': [[[&'? str; 1]; 2]; 2] + 78..84 '[b, b]': [[[&'? str; 1]; 2]; 2] + 79..80 'b': [[&'? str; 1]; 2] + 82..83 'b': [[&'? str; 1]; 2] 95..96 'd': [isize; 4] 99..111 '[y, 1, 2, 3]': [isize; 4] 100..101 'y': isize @@ -1197,15 +1197,15 @@ fn infer_array() { 209..215 '[1, 2]': [i32; 2] 210..211 '1': i32 213..214 '2': i32 - 225..226 'i': [&str; 2] - 229..239 '["a", "b"]': [&str; 2] - 230..233 '"a"': &str - 235..238 '"b"': &str - 250..251 'b': [[&str; 1]; 2] - 254..264 '[a, ["b"]]': [[&str; 1]; 2] - 255..256 'a': [&str; 1] - 258..263 '["b"]': [&str; 1] - 259..262 '"b"': &str + 225..226 'i': [&'? str; 2] + 229..239 '["a", "b"]': [&'? str; 2] + 230..233 '"a"': &'static str + 235..238 '"b"': &'static str + 250..251 'b': [[&'? str; 1]; 2] + 254..264 '[a, ["b"]]': [[&'? str; 1]; 2] + 255..256 'a': [&'? str; 1] + 258..263 '["b"]': [&'? str; 1] + 259..262 '"b"': &'static str 274..275 'x': [u8; 0] 287..289 '[]': [u8; 0] 299..300 'y': [u8; 4] @@ -1279,12 +1279,12 @@ fn infer_tuple_struct_generics() { 92..93 'A': extern "rust-call" A(u128) -> A 92..101 'A(42u128)': A 94..100 '42u128': u128 - 107..111 'Some': extern "rust-call" Some<&str>(&str) -> Option<&str> - 107..116 'Some("x")': Option<&str> - 112..115 '"x"': &str - 122..134 'Option::Some': extern "rust-call" Some<&str>(&str) -> Option<&str> - 122..139 'Option...e("x")': Option<&str> - 135..138 '"x"': &str + 107..111 'Some': extern "rust-call" Some<&'static str>(&'static str) -> Option<&'static str> + 107..116 'Some("x")': Option<&'static str> + 112..115 '"x"': &'static str + 122..134 'Option::Some': extern "rust-call" Some<&'static str>(&'static str) -> Option<&'static str> + 122..139 'Option...e("x")': Option<&'static str> + 135..138 '"x"': &'static str 145..149 'None': Option<{unknown}> 159..160 'x': Option 176..180 'None': Option @@ -1405,15 +1405,15 @@ fn infer_impl_generics_with_autoderef() { } "#, expect![[r#" - 77..81 'self': &Option - 97..99 '{}': Option<&T> + 77..81 'self': &'? Option + 97..99 '{}': Option<&'? T> 110..111 'o': Option 126..164 '{ ...f(); }': () - 132..145 '(&o).as_ref()': Option<&u32> - 133..135 '&o': &Option + 132..145 '(&o).as_ref()': Option<&'? u32> + 133..135 '&o': &'? Option 134..135 'o': Option 151..152 'o': Option - 151..161 'o.as_ref()': Option<&u32> + 151..161 'o.as_ref()': Option<&'? u32> "#]], ); } @@ -1551,16 +1551,16 @@ fn infer_type_alias() { "#, expect![[r#" 115..116 'x': A - 123..124 'y': A<&str, u128> + 123..124 'y': A<&'? str, u128> 137..138 'z': A 153..210 '{ ...z.y; }': () 159..160 'x': A 159..162 'x.x': u32 168..169 'x': A 168..171 'x.y': i128 - 177..178 'y': A<&str, u128> - 177..180 'y.x': &str - 186..187 'y': A<&str, u128> + 177..178 'y': A<&'? str, u128> + 177..180 'y.x': &'? str + 186..187 'y': A<&'? str, u128> 186..189 'y.y': u128 195..196 'z': A 195..198 'z.x': u8 @@ -1572,8 +1572,8 @@ fn infer_type_alias() { 312..328 'm::Ali...Foo(0)': Enum 326..327 '0': u8 338..354 'm::Ali...Foo(x)': Enum - 352..353 'x': &u8 - 357..359 '&e': &Enum + 352..353 'x': &'? u8 + 357..359 '&e': &'? Enum 358..359 'e': Enum "#]], ) @@ -1618,10 +1618,10 @@ fn infer_type_param() { 9..10 'x': T 20..29 '{ x }': T 26..27 'x': T - 43..44 'x': &T + 43..44 'x': &'? T 55..65 '{ *x }': T 61..63 '*x': T - 62..63 'x': &T + 62..63 'x': &'? T 77..157 '{ ...(1); }': () 87..88 'y': u32 91..96 '10u32': u32 @@ -1629,9 +1629,9 @@ fn infer_type_param() { 102..107 'id(y)': u32 105..106 'y': u32 117..118 'x': bool - 127..132 'clone': fn clone(&bool) -> bool + 127..132 'clone': fn clone(&'? bool) -> bool 127..135 'clone(z)': bool - 133..134 'z': &bool + 133..134 'z': &'? bool 141..151 'id::': fn id(i128) -> i128 141..154 'id::(1)': i128 152..153 '1': i128 @@ -1842,7 +1842,7 @@ fn foo() -> &'static str { "" } fn main() { foo(); - //^^^^^ &str + //^^^^^ &'static str }"#, ); } @@ -1940,10 +1940,10 @@ fn closure_return_inferred() { "#, expect![[r#" 16..46 '{ ..." }; }': u32 - 26..27 'x': impl Fn() -> &str - 30..43 '|| { "test" }': impl Fn() -> &str - 33..43 '{ "test" }': &str - 35..41 '"test"': &str + 26..27 'x': impl Fn() -> &'static str + 30..43 '|| { "test" }': impl Fn() -> &'static str + 33..43 '{ "test" }': &'static str + 35..41 '"test"': &'static str "#]], ); } @@ -1975,10 +1975,10 @@ fn test() { 70..71 'v': i64 78..80 '{}': () 91..362 '{ ... } }': () - 101..106 'mut g': |usize| yields i64 -> &str - 109..218 '|r| { ... }': |usize| yields i64 -> &str + 101..106 'mut g': |usize| yields i64 -> &'static str + 109..218 '|r| { ... }': |usize| yields i64 -> &'static str 110..111 'r': usize - 113..218 '{ ... }': &str + 113..218 '{ ... }': &'static str 127..128 'a': usize 131..138 'yield 0': usize 137..138 '0': i64 @@ -1988,22 +1988,22 @@ fn test() { 177..178 'a': usize 181..188 'yield 2': usize 187..188 '2': i64 - 198..212 '"return value"': &str + 198..212 '"return value"': &'static str 225..360 'match ... }': () - 231..239 'Pin::new': fn new<&mut |usize| yields i64 -> &str>(&mut |usize| yields i64 -> &str) -> Pin<&mut |usize| yields i64 -> &str> - 231..247 'Pin::n...mut g)': Pin<&mut |usize| yields i64 -> &str> - 231..262 'Pin::n...usize)': CoroutineState - 240..246 '&mut g': &mut |usize| yields i64 -> &str - 245..246 'g': |usize| yields i64 -> &str + 231..239 'Pin::new': fn new<&'? mut |usize| yields i64 -> &'static str>(&'? mut |usize| yields i64 -> &'static str) -> Pin<&'? mut |usize| yields i64 -> &'static str> + 231..247 'Pin::n...mut g)': Pin<&'? mut |usize| yields i64 -> &'static str> + 231..262 'Pin::n...usize)': CoroutineState + 240..246 '&mut g': &'? mut |usize| yields i64 -> &'static str + 245..246 'g': |usize| yields i64 -> &'static str 255..261 '0usize': usize - 273..299 'Corout...ded(y)': CoroutineState + 273..299 'Corout...ded(y)': CoroutineState 297..298 'y': i64 303..312 '{ f(y); }': () 305..306 'f': fn f(i64) 305..309 'f(y)': () 307..308 'y': i64 - 321..348 'Corout...ete(r)': CoroutineState - 346..347 'r': &str + 321..348 'Corout...ete(r)': CoroutineState + 346..347 'r': &'static str 352..354 '{}': () "#]], ); @@ -2050,7 +2050,7 @@ fn f(x: (&&&&i32, &&&i32)) { _ => loop {}, }; f; - //^ (&&&&i32, &&&i32) + //^ (&'? &'? &'? &'? i32, &'? &'? &'? i32) } "#, ); @@ -2059,10 +2059,10 @@ fn f(x: (&&&&i32, &&&i32)) { fn f() { let x = &&&(&&&2, &&&&&3); let (y, z) = x; - //^ &&&&i32 + //^ &'? &'? &'? &'? i32 let t @ (y, z) = x; t; - //^ &&&(&&&i32, &&&&&i32) + //^ &'? &'? &'? (&'? &'? &'? i32, &'? &'? &'? &'? &'? i32) } "#, ); @@ -2071,10 +2071,10 @@ fn f() { fn f() { let x = &&&(&&&2, &&&&&3); let (y, z) = x; - //^ &&&&i32 + //^ &'? &'? &'? &'? i32 let t @ (y, z) = x; t; - //^ &&&(&&&i32, &&&&&i32) + //^ &'? &'? &'? (&'? &'? &'? i32, &'? &'? &'? &'? &'? i32) } "#, ); @@ -2761,23 +2761,23 @@ impl S { fn f() { let x = S; let c1 = || x.read(); - //^^ impl Fn() -> &S + //^^ impl Fn() -> &'? S let c2 = || x.write(); - //^^ impl FnMut() -> &mut S + //^^ impl FnMut() -> &'? mut S let c3 = || x.consume(); //^^ impl FnOnce() -> S let c3 = || x.consume().consume().consume(); //^^ impl FnOnce() -> S let c3 = || x.consume().write().read(); - //^^ impl FnOnce() -> &S + //^^ impl FnOnce() -> &'? S let x = &mut x; let c1 = || x.write(); - //^^ impl FnMut() -> &mut S + //^^ impl FnMut() -> &'? mut S let x = S; let c1 = || { let ref t = x; t }; - //^^ impl Fn() -> &S + //^^ impl Fn() -> &'? S let c2 = || { let ref mut t = x; t }; - //^^ impl FnMut() -> &mut S + //^^ impl FnMut() -> &'? mut S let c3 = || { let t = x; t }; //^^ impl FnOnce() -> S } @@ -3074,11 +3074,11 @@ fn main() { } "#, expect![[r#" - 104..108 'self': &Box - 188..192 'self': &Box> - 218..220 '{}': &T - 242..246 'self': &Box> - 275..277 '{}': &Foo + 104..108 'self': &'? Box + 188..192 'self': &'a Box> + 218..220 '{}': &'a T + 242..246 'self': &'a Box> + 275..277 '{}': &'a Foo 297..301 'self': Box> 322..324 '{}': Foo 338..559 '{ ...r(); }': () @@ -3088,21 +3088,21 @@ fn main() { 360..363 'Foo': extern "rust-call" Foo(i32) -> Foo 360..370 'Foo(0_i32)': Foo 364..369 '0_i32': i32 - 382..386 'bad1': &i32 + 382..386 'bad1': &'? i32 389..394 'boxed': Box> - 389..406 'boxed....nner()': &i32 - 416..421 'good1': &i32 - 424..438 'Foo::get_inner': fn get_inner(&Box>) -> &i32 - 424..446 'Foo::g...boxed)': &i32 - 439..445 '&boxed': &Box> + 389..406 'boxed....nner()': &'? i32 + 416..421 'good1': &'? i32 + 424..438 'Foo::get_inner': fn get_inner(&'? Box>) -> &'? i32 + 424..446 'Foo::g...boxed)': &'? i32 + 439..445 '&boxed': &'? Box> 440..445 'boxed': Box> - 457..461 'bad2': &Foo + 457..461 'bad2': &'? Foo 464..469 'boxed': Box> - 464..480 'boxed....self()': &Foo - 490..495 'good2': &Foo - 498..511 'Foo::get_self': fn get_self(&Box>) -> &Foo - 498..519 'Foo::g...boxed)': &Foo - 512..518 '&boxed': &Box> + 464..480 'boxed....self()': &'? Foo + 490..495 'good2': &'? Foo + 498..511 'Foo::get_self': fn get_self(&'? Box>) -> &'? Foo + 498..519 'Foo::g...boxed)': &'? Foo + 512..518 '&boxed': &'? Box> 513..518 'boxed': Box> 530..535 'inner': Foo 538..543 'boxed': Box> @@ -3414,31 +3414,31 @@ struct TS(usize); fn main() { let x; [x,] = &[1,]; - //^^^^expected &[i32; 1], got [{unknown}; _] + //^^^^expected &'? [i32; 1], got [{unknown}; _] let x; [(x,),] = &[(1,),]; - //^^^^^^^expected &[(i32,); 1], got [{unknown}; _] + //^^^^^^^expected &'? [(i32,); 1], got [{unknown}; _] let x; ((x,),) = &((1,),); - //^^^^^^^expected &((i32,),), got (({unknown},),) + //^^^^^^^expected &'? ((i32,),), got (({unknown},),) let x; (x,) = &(1,); - //^^^^expected &(i32,), got ({unknown},) + //^^^^expected &'? (i32,), got ({unknown},) let x; (S { a: x },) = &(S { a: 42 },); - //^^^^^^^^^^^^^expected &(S,), got (S,) + //^^^^^^^^^^^^^expected &'? (S,), got (S,) let x; S { a: x } = &S { a: 42 }; - //^^^^^^^^^^expected &S, got S + //^^^^^^^^^^expected &'? S, got S let x; TS(x) = &TS(42); - //^^^^^expected &TS, got TS + //^^^^^expected &'? TS, got TS } "#, ); @@ -3548,17 +3548,17 @@ fn f(t: Ark) { } "#, expect![[r#" - 47..51 'self': &Ark + 47..51 'self': &'? Ark 65..88 '{ ... }': *const T - 75..82 '&self.0': &T - 76..80 'self': &Ark + 75..82 '&self.0': &'? T + 76..80 'self': &'? Ark 76..82 'self.0': T 99..100 't': Ark 110..144 '{ ... (); }': () - 116..124 'Ark::foo': fn foo(&Ark) -> *const T + 116..124 'Ark::foo': fn foo(&'? Ark) -> *const T 116..128 'Ark::foo(&t)': *const T 116..141 'Ark::f...nst ()': *const () - 125..127 '&t': &Ark + 125..127 '&t': &'? Ark 126..127 't': Ark "#]], ); @@ -3632,7 +3632,7 @@ pub struct CStr; fn main() { c"ello"; - //^^^^^^^ &CStr + //^^^^^^^ &'static CStr } "#, ); @@ -3659,7 +3659,25 @@ fn main() { let are = "are"; let count = 10; builtin#format_args("hello {count:02} {} friends, we {are:?} {0}{last}", "fancy", last = "!"); - // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type: Arguments<'{error}> + // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type: Arguments<'?> +} +"#, + ); +} + +#[test] +fn inline_const_expression() { + check( + r#" +fn main() { + let foo = 0; + const { + let bar = 1; + let unresolved = foo; + // ^^^^^^^^^^ type: {unknown} + let resolved = bar; + // ^^^^^^^^ type: i32 + } } "#, ); 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 dfcd322a39c73..18fc8afd183db 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 @@ -238,6 +238,7 @@ fn infer_for_loop() { //- minicore: iterator //- /main.rs crate:main deps:alloc #![no_std] +extern crate alloc; use alloc::collections::Vec; fn test() { @@ -245,7 +246,7 @@ fn test() { v.push("foo"); for x in v { x; - } //^ &str + } //^ &'static str } //- /alloc.rs crate:alloc @@ -575,7 +576,7 @@ fn indexing_arrays() { "fn main() { &mut [9][2]; }", expect![[r#" 10..26 '{ &mut...[2]; }': () - 12..23 '&mut [9][2]': &mut {unknown} + 12..23 '&mut [9][2]': &'? mut {unknown} 17..20 '[9]': [i32; 1] 17..23 '[9][2]': {unknown} 18..19 '9': i32 @@ -873,7 +874,7 @@ impl> O { fn test(o: O) { o.foo(); -} //^^^^^^^ &str +} //^^^^^^^ &'? str "#, ); } @@ -1016,15 +1017,15 @@ fn test(x: impl Trait, y: &impl Trait) { z.foo2(); }"#, expect![[r#" - 29..33 'self': &Self - 54..58 'self': &Self + 29..33 'self': &'? Self + 54..58 'self': &'? Self 77..78 'x': impl Trait 97..99 '{}': () 154..155 'x': impl Trait - 174..175 'y': &impl Trait + 174..175 'y': &'? impl Trait 195..323 '{ ...2(); }': () 201..202 'x': impl Trait - 208..209 'y': &impl Trait + 208..209 'y': &'? impl Trait 219..220 'z': S 223..224 'S': extern "rust-call" S(u16) -> S 223..227 'S(1)': S @@ -1034,13 +1035,13 @@ fn test(x: impl Trait, y: &impl Trait) { 237..238 'z': S 245..246 'x': impl Trait 245..252 'x.foo()': u64 - 258..259 'y': &impl Trait + 258..259 'y': &'? impl Trait 258..265 'y.foo()': u32 271..272 'z': S 271..278 'z.foo()': u16 284..285 'x': impl Trait 284..292 'x.foo2()': i64 - 298..299 'y': &impl Trait + 298..299 'y': &'? impl Trait 298..306 'y.foo2()': i64 312..313 'z': S 312..320 'z.foo2()': i64 @@ -1204,26 +1205,26 @@ fn test(x: impl Trait, y: &impl Trait) { z.foo2(); }"#, expect![[r#" - 29..33 'self': &Self - 54..58 'self': &Self + 29..33 'self': &'? Self + 54..58 'self': &'? Self 98..100 '{}': () 110..111 'x': impl Trait - 130..131 'y': &impl Trait + 130..131 'y': &'? impl Trait 151..268 '{ ...2(); }': () 157..158 'x': impl Trait - 164..165 'y': &impl Trait + 164..165 'y': &'? impl Trait 175..176 'z': impl Trait 179..182 'bar': fn bar() -> impl Trait 179..184 'bar()': impl Trait 190..191 'x': impl Trait 190..197 'x.foo()': u64 - 203..204 'y': &impl Trait + 203..204 'y': &'? impl Trait 203..210 'y.foo()': u64 216..217 'z': impl Trait 216..223 'z.foo()': u64 229..230 'x': impl Trait 229..237 'x.foo2()': i64 - 243..244 'y': &impl Trait + 243..244 'y': &'? impl Trait 243..251 'y.foo2()': i64 257..258 'z': impl Trait 257..265 'z.foo2()': i64 @@ -1328,7 +1329,7 @@ fn test() { a.foo(); }"#, expect![[r#" - 29..33 'self': &Self + 29..33 'self': &'? Self 71..82 '{ loop {} }': ! 73..80 'loop {}': ! 78..80 '{}': () @@ -1366,8 +1367,8 @@ fn test() { d.foo(); }"#, expect![[r#" - 49..53 'self': &mut Self - 101..105 'self': &Self + 49..53 'self': &'? mut Self + 101..105 'self': &'? Self 184..195 '{ loop {} }': ({unknown}, {unknown}) 186..193 'loop {}': ! 191..193 '{}': () @@ -1414,10 +1415,10 @@ fn foo() -> (impl FnOnce(&str, T), impl Trait) { } "#, expect![[r#" - 134..165 '{ ...(C)) }': (impl FnOnce(&str, T), Bar) - 140..163 '(|inpu...ar(C))': (impl FnOnce(&str, T), Bar) - 141..154 '|input, t| {}': impl FnOnce(&str, T) - 142..147 'input': &str + 134..165 '{ ...(C)) }': (impl FnOnce(&'? str, T), Bar) + 140..163 '(|inpu...ar(C))': (impl FnOnce(&'? str, T), Bar) + 141..154 '|input, t| {}': impl FnOnce(&'? str, T) + 142..147 'input': &'? str 149..150 't': T 152..154 '{}': () 156..159 'Bar': extern "rust-call" Bar(u8) -> Bar @@ -1466,26 +1467,26 @@ fn test(x: dyn Trait, y: &dyn Trait) { z.foo2(); }"#, expect![[r#" - 29..33 'self': &Self - 54..58 'self': &Self + 29..33 'self': &'? Self + 54..58 'self': &'? Self 97..99 '{}': dyn Trait 109..110 'x': dyn Trait - 128..129 'y': &dyn Trait + 128..129 'y': &'? dyn Trait 148..265 '{ ...2(); }': () 154..155 'x': dyn Trait - 161..162 'y': &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..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..241 'y': &'? dyn Trait 240..248 'y.foo2()': i64 254..255 'z': dyn Trait 254..262 'z.foo2()': i64 @@ -1514,16 +1515,16 @@ fn test(s: S) { s.bar().baz(); }"#, expect![[r#" - 32..36 'self': &Self - 102..106 'self': &S - 128..139 '{ loop {} }': &dyn Trait + 32..36 'self': &'? Self + 102..106 'self': &'? S + 128..139 '{ loop {} }': &'? dyn Trait 130..137 'loop {}': ! 135..137 '{}': () - 175..179 'self': &Self + 175..179 'self': &'? Self 251..252 's': S 267..289 '{ ...z(); }': () 273..274 's': S - 273..280 's.bar()': &dyn Trait + 273..280 's.bar()': &'? dyn Trait 273..286 's.bar().baz()': (u32, i32) "#]], ); @@ -1548,19 +1549,19 @@ fn test(x: Trait, y: &Trait) -> u64 { z.foo(); }"#, expect![[r#" - 26..30 'self': &Self + 26..30 'self': &'? Self 60..62 '{}': dyn Trait 72..73 'x': dyn Trait - 82..83 'y': &dyn Trait + 82..83 'y': &'? dyn Trait 100..175 '{ ...o(); }': u64 106..107 'x': dyn Trait - 113..114 'y': &dyn Trait + 113..114 'y': &'? dyn Trait 124..125 'z': dyn Trait 128..131 'bar': fn bar() -> dyn Trait 128..133 'bar()': dyn Trait 139..140 'x': dyn Trait 139..146 'x.foo()': u64 - 152..153 'y': &dyn Trait + 152..153 'y': &'? dyn Trait 152..159 'y.foo()': u64 165..166 'z': dyn Trait 165..172 'z.foo()': u64 @@ -1580,14 +1581,14 @@ fn main() { } "#, expect![[r#" - 31..35 'self': &S + 31..35 'self': &'? S 37..39 '{}': () - 47..48 '_': &dyn Fn(S) + 47..48 '_': &'? dyn Fn(S) 58..60 '{}': () 71..105 '{ ...()); }': () - 77..78 'f': fn f(&dyn Fn(S)) + 77..78 'f': fn f(&'? dyn Fn(S)) 77..102 'f(&|nu...foo())': () - 79..101 '&|numb....foo()': &impl Fn(S) + 79..101 '&|numb....foo()': &'? impl Fn(S) 80..101 '|numbe....foo()': impl Fn(S) 81..87 'number': S 89..95 'number': S @@ -1790,7 +1791,7 @@ fn test(x: T, y: U) { y.foo(); }"#, expect![[r#" - 53..57 'self': &Self + 53..57 'self': &'? Self 66..68 '{}': u32 185..186 'x': T 191..192 'y': U @@ -1819,11 +1820,11 @@ fn test(x: &impl Trait1) { x.foo(); }"#, expect![[r#" - 53..57 'self': &Self + 53..57 'self': &'? Self 66..68 '{}': u32 - 119..120 'x': &impl Trait1 + 119..120 'x': &'? impl Trait1 136..152 '{ ...o(); }': () - 142..143 'x': &impl Trait1 + 142..143 'x': &'? impl Trait1 142..149 'x.foo()': u32 "#]], ); @@ -1934,8 +1935,8 @@ fn test() { opt.map(f); }"#, expect![[r#" - 28..32 'self': &Self - 132..136 'self': &Bar + 28..32 'self': &'? Self + 132..136 'self': &'? Bar 149..160 '{ loop {} }': (A1, R) 151..158 'loop {}': ! 156..158 '{}': () @@ -1988,7 +1989,7 @@ fn test() { let r2 = lazy2.foo(); }"#, expect![[r#" - 36..40 'self': &Foo + 36..40 'self': &'? Foo 51..53 '{}': usize 131..132 'f': F 151..153 '{}': Lazy @@ -2262,14 +2263,14 @@ impl Trait for S2 { fn f(&self, x: ::Item) { let y = x; } }"#, expect![[r#" - 40..44 'self': &Self + 40..44 'self': &'? Self 46..47 'x': Trait::Item - 126..130 'self': &S + 126..130 'self': &'? S 132..133 'x': u32 147..161 '{ let y = x; }': () 153..154 'y': u32 157..158 'x': u32 - 228..232 'self': &S2 + 228..232 'self': &'? S2 234..235 'x': i32 251..265 '{ let y = x; }': () 257..258 'y': i32 @@ -2643,12 +2644,12 @@ fn main() { 72..74 '_v': F 117..120 '{ }': () 132..163 '{ ... }); }': () - 138..148 'f::<(), _>': fn f<(), impl FnOnce(&())>(impl FnOnce(&())) + 138..148 'f::<(), _>': fn f<(), impl FnOnce(&'? ())>(impl FnOnce(&'? ())) 138..160 'f::<()... z; })': () - 149..159 '|z| { z; }': impl FnOnce(&()) - 150..151 'z': &() + 149..159 '|z| { z; }': impl FnOnce(&'? ()) + 150..151 'z': &'? () 153..159 '{ z; }': () - 155..156 'z': &() + 155..156 'z': &'? () "#]], ); } @@ -2897,13 +2898,13 @@ fn test(x: &dyn Foo) { foo(x); }"#, expect![[r#" - 21..22 'x': &dyn Foo + 21..22 'x': &'? dyn Foo 34..36 '{}': () - 46..47 'x': &dyn Foo + 46..47 'x': &'? dyn Foo 59..74 '{ foo(x); }': () - 65..68 'foo': fn foo(&dyn Foo) + 65..68 'foo': fn foo(&'? dyn Foo) 65..71 'foo(x)': () - 69..70 'x': &dyn Foo + 69..70 'x': &'? dyn Foo "#]], ); } @@ -2927,7 +2928,7 @@ fn test() { (IsCopy, NotCopy).test(); }"#, expect![[r#" - 78..82 'self': &Self + 78..82 'self': &'? Self 134..235 '{ ...t(); }': () 140..146 'IsCopy': IsCopy 140..153 'IsCopy.test()': bool @@ -2969,7 +2970,7 @@ fn test() { 28..29 'T': {unknown} 36..38 '{}': T 36..38: expected T, got () - 113..117 'self': &Self + 113..117 'self': &'? Self 169..249 '{ ...t(); }': () 175..178 'foo': fn foo() 175..185 'foo.test()': bool @@ -2997,16 +2998,16 @@ fn test(f1: fn(), f2: fn(usize) -> u8, f3: fn(u8, u8) -> &u8) { f3.test(); }"#, expect![[r#" - 22..26 'self': &Self + 22..26 'self': &'? Self 76..78 'f1': fn() 86..88 'f2': fn(usize) -> u8 - 107..109 'f3': fn(u8, u8) -> &u8 + 107..109 'f3': fn(u8, u8) -> &'? u8 130..178 '{ ...t(); }': () 136..138 'f1': fn() 136..145 'f1.test()': bool 151..153 'f2': fn(usize) -> u8 151..160 'f2.test()': bool - 166..168 'f3': fn(u8, u8) -> &u8 + 166..168 'f3': fn(u8, u8) -> &'? u8 166..175 'f3.test()': bool "#]], ); @@ -3027,13 +3028,13 @@ fn test() { (1u8, *"foo").test(); // not Sized }"#, expect![[r#" - 22..26 'self': &Self + 22..26 'self': &'? Self 79..194 '{ ...ized }': () 85..88 '1u8': u8 85..95 '1u8.test()': bool 101..116 '(*"foo").test()': {unknown} 102..108 '*"foo"': str - 103..108 '"foo"': &str + 103..108 '"foo"': &'static str 135..145 '(1u8, 1u8)': (u8, u8) 135..152 '(1u8, ...test()': bool 136..139 '1u8': u8 @@ -3042,7 +3043,7 @@ fn test() { 158..178 '(1u8, ...test()': {unknown} 159..162 '1u8': u8 164..170 '*"foo"': str - 165..170 '"foo"': &str + 165..170 '"foo"': &'static str "#]], ); } @@ -3093,7 +3094,7 @@ fn foo() { 93..94 'x': Option 109..111 '{}': () 117..124 '(&f)(s)': () - 118..120 '&f': &impl Fn(Option) + 118..120 '&f': &'? impl Fn(Option) 119..120 'f': impl Fn(Option) 122..123 's': Option "#]], @@ -3170,25 +3171,25 @@ fn foo() { f(&s); }"#, expect![[r#" - 154..158 'self': &Box - 166..205 '{ ... }': &T - 176..199 'unsafe...nner }': &T - 185..197 '&*self.inner': &T + 154..158 'self': &'? Box + 166..205 '{ ... }': &'? T + 176..199 'unsafe...nner }': &'? T + 185..197 '&*self.inner': &'? T 186..197 '*self.inner': T - 187..191 'self': &Box + 187..191 'self': &'? Box 187..197 'self.inner': *mut T 218..324 '{ ...&s); }': () 228..229 's': Option 232..236 'None': Option - 246..247 'f': Box)> - 281..310 'Box { ... {}) }': Box)> - 294..308 '&mut (|ps| {})': &mut impl FnOnce(&Option) - 300..307 '|ps| {}': impl FnOnce(&Option) - 301..303 'ps': &Option + 246..247 'f': Box)> + 281..310 'Box { ... {}) }': Box)> + 294..308 '&mut (|ps| {})': &'? mut impl FnOnce(&'? Option) + 300..307 '|ps| {}': impl FnOnce(&'? Option) + 301..303 'ps': &'? Option 305..307 '{}': () - 316..317 'f': Box)> + 316..317 'f': Box)> 316..321 'f(&s)': () - 318..320 '&s': &Option + 318..320 '&s': &'? Option 319..320 's': Option "#]], ); @@ -3320,7 +3321,7 @@ fn f() { } }"#, expect![[r#" - 46..50 'self': &Self + 46..50 'self': &'? Self 58..63 '{ 0 }': u8 60..61 '0': u8 115..185 '{ ... } }': () @@ -3595,7 +3596,7 @@ fn take_u32(_: u32) {} fn minimized() { let v = V::default(); let p = v.get(&0); - //^ &u32 + //^ &'? u32 take_u32(42 + p); } "#, @@ -3625,7 +3626,7 @@ fn take_u32(_: u32) {} fn minimized() { let v = V::default(); let p = v.get(); - //^ &{unknown} + //^ &'? {unknown} take_u32(42 + p); } "#, @@ -3684,11 +3685,11 @@ fn main() { } "#, expect![[r#" - 44..48 'self': &Self - 133..137 'self': &[u8; 4] + 44..48 'self': &'? Self + 133..137 'self': &'? [u8; 4] 155..172 '{ ... }': usize 165..166 '2': usize - 236..240 'self': &[u8; 2] + 236..240 'self': &'? [u8; 2] 258..275 '{ ... }': u8 268..269 '2': u8 289..392 '{ ...g(); }': () @@ -3732,11 +3733,11 @@ fn main() { } "#, expect![[r#" - 44..48 'self': &Self - 151..155 'self': &[u8; L] + 44..48 'self': &'? Self + 151..155 'self': &'? [u8; L] 173..194 '{ ... }': [u8; L] 183..188 '*self': [u8; L] - 184..188 'self': &[u8; L] + 184..188 'self': &'? [u8; L] 208..260 '{ ...g(); }': () 218..219 'v': [u8; 2] 222..230 '[0u8; 2]': [u8; 2] @@ -4056,13 +4057,13 @@ fn g(t: &(dyn Sync + T2 + T1 + Send)) { } "#, expect![[r#" - 68..69 't': &{unknown} + 68..69 't': &'? {unknown} 101..103 '{}': () - 109..110 't': &{unknown} + 109..110 't': &'? {unknown} 142..155 '{ f(t); }': () - 148..149 'f': fn f(&{unknown}) + 148..149 'f': fn f(&'? {unknown}) 148..152 'f(t)': () - 150..151 't': &{unknown} + 150..151 't': &'? {unknown} "#]], ); @@ -4105,7 +4106,7 @@ trait Trait { } fn f(t: &dyn Trait) {} - //^&{unknown} + //^&'? {unknown} "#, ); } @@ -4175,27 +4176,27 @@ trait Trait { fn f(v: impl Trait) { let a = v.get::().deref(); - //^ &i32 + //^ &'? i32 let a = v.get::().deref(); - //^ &T + //^ &'? T } fn g<'a, T: 'a>(v: impl Trait = &'a T>) { let a = v.get::(); - //^ &T + //^ &'a T let a = v.get::<()>(); - //^ Trait::Assoc<(), impl Trait = &T>> + //^ Trait::Assoc<(), impl Trait = &'a T>> } fn h<'a>(v: impl Trait = &'a i32> + Trait = &'a i64>) { let a = v.get::(); - //^ &i32 + //^ &'a i32 let a = v.get::(); - //^ &i64 + //^ &'a i64 } fn i<'a>(v: impl Trait = &'a i32, Assoc = &'a i64>) { let a = v.get::(); - //^ &i32 + //^ &'a i32 let a = v.get::(); - //^ &i64 + //^ &'a i64 } "#, ); @@ -4221,12 +4222,12 @@ fn f<'a>(v: &dyn Trait = &'a i32>) { } "#, expect![[r#" - 90..94 'self': &Self - 127..128 'v': &(dyn Trait = &i32>) + 90..94 'self': &'? Self + 127..128 'v': &'? (dyn Trait = &'a i32>) 164..195 '{ ...f(); }': () - 170..171 'v': &(dyn Trait = &i32>) - 170..184 'v.get::()': &i32 - 170..192 'v.get:...eref()': &i32 + 170..171 'v': &'? (dyn Trait = &'a i32>) + 170..184 'v.get::()': &'? i32 + 170..192 'v.get:...eref()': &'? i32 "#]], ); } @@ -4487,19 +4488,19 @@ fn derive_macro_bounds() { let x = (&Copy).clone(); //^ Copy let x = (&NotCopy).clone(); - //^ &NotCopy + //^ &'? NotCopy let x = (&Generic(Copy)).clone(); //^ Generic let x = (&Generic(NotCopy)).clone(); - //^ &Generic + //^ &'? Generic let x: &AssocGeneric = &AssocGeneric(NotCopy); let x = x.clone(); - //^ &AssocGeneric + //^ &'? AssocGeneric // let x: &AssocGeneric2 = &AssocGeneric2(NotCopy); // let x = x.clone(); let x: &AssocGeneric3 = &AssocGeneric3(Generic(NotCopy)); let x = x.clone(); - //^ &AssocGeneric3 + //^ &'? AssocGeneric3 let x = (&R1(Vec())).clone(); //^ R1 let x = (&R2(R1(Vec()))).clone(); @@ -4582,7 +4583,7 @@ impl B for u16 { fn ttt() { let inp = Y; x::(&inp); - //^^^^ expected &X, got &Y + //^^^^ expected &'? X, got &'? Y } "#, ); @@ -4629,7 +4630,7 @@ fn foo() { let mut map = SomeMap; map["a"] = (); map; - //^^^ SomeMap<&str> + //^^^ SomeMap<&'static str> } "#, ); @@ -4764,3 +4765,62 @@ fn test() { "#, ); } + +#[test] +fn associated_type_with_impl_trait_in_tuple() { + check_no_mismatches( + r#" +pub trait Iterator { + type Item; +} + +pub trait Value {} + +fn bar>() {} + +fn foo() { + bar(); +} +"#, + ); +} + +#[test] +fn associated_type_with_impl_trait_in_nested_tuple() { + check_no_mismatches( + r#" +pub trait Iterator { + type Item; +} + +pub trait Value {} + +fn bar>() {} + +fn foo() { + bar(); +} +"#, + ); +} + +#[test] +fn dyn_trait_with_lifetime_in_rpit() { + check_types( + r#" +//- minicore: future +pub struct Box {} + +trait Trait {} + +pub async fn foo_async<'a>() -> Box { + Box {} +} + +fn foo() { + foo_async(); + //^^^^^^^^^^^impl Future> + ?Sized +} +"#, + ) +} diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs index 4518422d27e95..72272934ab7a3 100644 --- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs @@ -9,9 +9,11 @@ use hir_ty::{db::HirDatabase, diagnostics::BodyValidationDiagnostic, InferenceDi use base_db::CrateId; use cfg::{CfgExpr, CfgOptions}; use either::Either; +pub use hir_def::VariantId; use hir_def::{body::SyntheticSyntax, hir::ExprOrPatId, path::ModPath, AssocItemId, DefWithBodyId}; use hir_expand::{name::Name, HirFileId, InFile}; use syntax::{ast, AstPtr, SyntaxError, SyntaxNodePtr, TextRange}; +use triomphe::Arc; use crate::{AssocItem, Field, Local, MacroKind, Trait, Type}; @@ -171,7 +173,7 @@ pub struct MacroError { pub struct MacroExpansionParseError { pub node: InFile, pub precise_location: Option, - pub errors: Box<[SyntaxError]>, + pub errors: Arc<[SyntaxError]>, } #[derive(Debug, Clone, Eq, PartialEq)] @@ -200,6 +202,7 @@ pub struct MalformedDerive { pub struct NoSuchField { pub field: InFile>>, pub private: bool, + pub variant: VariantId, } #[derive(Debug)] @@ -525,7 +528,7 @@ impl AnyDiagnostic { source_map.pat_syntax(pat).inspect_err(|_| tracing::error!("synthetic syntax")).ok() }; Some(match d { - &InferenceDiagnostic::NoSuchField { field: expr, private } => { + &InferenceDiagnostic::NoSuchField { field: expr, private, variant } => { let expr_or_pat = match expr { ExprOrPatId::ExprId(expr) => { source_map.field_syntax(expr).map(AstPtr::wrap_left) @@ -534,7 +537,7 @@ impl AnyDiagnostic { source_map.pat_field_syntax(pat).map(AstPtr::wrap_right) } }; - NoSuchField { field: expr_or_pat, private }.into() + NoSuchField { field: expr_or_pat, private, variant }.into() } &InferenceDiagnostic::MismatchedArgCount { call_expr, expected, found } => { MismatchedArgCount { call_expr: expr_syntax(call_expr)?, expected, found }.into() diff --git a/src/tools/rust-analyzer/crates/hir/src/display.rs b/src/tools/rust-analyzer/crates/hir/src/display.rs index 84f03d111f275..c276e87786dde 100644 --- a/src/tools/rust-analyzer/crates/hir/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir/src/display.rs @@ -188,28 +188,7 @@ impl HirDisplay for Struct { StructKind::Record => { let has_where_clause = write_where_clause(def_id, f)?; if let Some(limit) = f.entity_limit { - let fields = self.fields(f.db); - let count = fields.len().min(limit); - f.write_char(if !has_where_clause { ' ' } else { '\n' })?; - if count == 0 { - if fields.is_empty() { - f.write_str("{}")?; - } else { - f.write_str("{ /* … */ }")?; - } - } else { - f.write_str(" {\n")?; - for field in &fields[..count] { - f.write_str(" ")?; - field.hir_fmt(f)?; - f.write_str(",\n")?; - } - - if fields.len() > count { - f.write_str(" /* … */\n")?; - } - f.write_str("}")?; - } + display_fields(&self.fields(f.db), has_where_clause, limit, false, f)?; } } StructKind::Unit => _ = write_where_clause(def_id, f)?, @@ -226,18 +205,10 @@ impl HirDisplay for Enum { write!(f, "{}", self.name(f.db).display(f.db.upcast()))?; let def_id = GenericDefId::AdtId(AdtId::EnumId(self.id)); write_generic_params(def_id, f)?; - let has_where_clause = write_where_clause(def_id, f)?; - let variants = self.variants(f.db); - if !variants.is_empty() { - f.write_char(if !has_where_clause { ' ' } else { '\n' })?; - f.write_str("{\n")?; - for variant in variants { - f.write_str(" ")?; - variant.hir_fmt(f)?; - f.write_str(",\n")?; - } - f.write_str("}")?; + let has_where_clause = write_where_clause(def_id, f)?; + if let Some(limit) = f.entity_limit { + display_variants(&self.variants(f.db), has_where_clause, limit, f)?; } Ok(()) @@ -251,22 +222,102 @@ impl HirDisplay for Union { write!(f, "{}", self.name(f.db).display(f.db.upcast()))?; let def_id = GenericDefId::AdtId(AdtId::UnionId(self.id)); write_generic_params(def_id, f)?; + let has_where_clause = write_where_clause(def_id, f)?; + if let Some(limit) = f.entity_limit { + display_fields(&self.fields(f.db), has_where_clause, limit, false, f)?; + } + Ok(()) + } +} + +fn display_fields( + fields: &[Field], + has_where_clause: bool, + limit: usize, + in_line: bool, + f: &mut HirFormatter<'_>, +) -> Result<(), HirDisplayError> { + let count = fields.len().min(limit); + let (indent, separator) = if in_line { ("", ' ') } else { (" ", '\n') }; + f.write_char(if !has_where_clause { ' ' } else { separator })?; + if count == 0 { + if fields.is_empty() { + f.write_str("{}")?; + } else { + f.write_str("{ /* … */ }")?; + } + } else { + f.write_char('{')?; - let fields = self.fields(f.db); if !fields.is_empty() { - f.write_char(if !has_where_clause { ' ' } else { '\n' })?; - f.write_str("{\n")?; - for field in self.fields(f.db) { - f.write_str(" ")?; + f.write_char(separator)?; + for field in &fields[..count] { + f.write_str(indent)?; field.hir_fmt(f)?; - f.write_str(",\n")?; + f.write_char(',')?; + f.write_char(separator)?; + } + + if fields.len() > count { + f.write_str(indent)?; + f.write_str("/* … */")?; + f.write_char(separator)?; } - f.write_str("}")?; } - Ok(()) + f.write_str("}")?; + } + + Ok(()) +} + +fn display_variants( + variants: &[Variant], + has_where_clause: bool, + limit: usize, + f: &mut HirFormatter<'_>, +) -> Result<(), HirDisplayError> { + let count = variants.len().min(limit); + f.write_char(if !has_where_clause { ' ' } else { '\n' })?; + if count == 0 { + if variants.is_empty() { + f.write_str("{}")?; + } else { + f.write_str("{ /* … */ }")?; + } + } else { + f.write_str("{\n")?; + for variant in &variants[..count] { + f.write_str(" ")?; + write!(f, "{}", variant.name(f.db).display(f.db.upcast()))?; + match variant.kind(f.db) { + StructKind::Tuple => { + if variant.fields(f.db).is_empty() { + f.write_str("()")?; + } else { + f.write_str("( /* … */ )")?; + } + } + StructKind::Record => { + if variant.fields(f.db).is_empty() { + f.write_str(" {}")?; + } else { + f.write_str(" { /* … */ }")?; + } + } + StructKind::Unit => {} + } + f.write_str(",\n")?; + } + + if variants.len() > count { + f.write_str(" /* … */\n")?; + } + f.write_str("}")?; } + + Ok(()) } impl HirDisplay for Field { @@ -304,21 +355,10 @@ impl HirDisplay for Variant { } f.write_char(')')?; } - VariantData::Record(fields) => { - f.write_str(" {")?; - let mut first = true; - for (_, field) in fields.iter() { - if first { - first = false; - f.write_char(' ')?; - } else { - f.write_str(", ")?; - } - // Enum variant fields must be pub. - write!(f, "{}: ", field.name.display(f.db.upcast()))?; - field.type_ref.hir_fmt(f)?; + VariantData::Record(_) => { + if let Some(limit) = f.entity_limit { + display_fields(&self.fields(f.db), false, limit, true, f)?; } - f.write_str(" }")?; } } Ok(()) diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index bcd94a611a903..85f33a10fcb2d 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -59,7 +59,9 @@ use hir_def::{ ModuleId, StaticId, StructId, TraitAliasId, TraitId, TupleId, TypeAliasId, TypeOrConstParamId, TypeParamId, UnionId, }; -use hir_expand::{attrs::collect_attrs, name::name, proc_macro::ProcMacroKind, MacroCallKind}; +use hir_expand::{ + attrs::collect_attrs, name::name, proc_macro::ProcMacroKind, AstId, MacroCallKind, ValueResult, +}; use hir_ty::{ all_super_traits, autoderef, check_orphan_rules, consteval::{try_const_usize, unknown_const_as_generic, ConstExt}, @@ -79,7 +81,7 @@ use hir_ty::{ use itertools::Itertools; use nameres::diagnostics::DefDiagnosticKind; use rustc_hash::FxHashSet; -use span::Edition; +use span::{Edition, MacroCallId}; use stdx::{impl_from, never}; use syntax::{ ast::{self, HasAttrs as _, HasName}, @@ -559,6 +561,12 @@ impl Module { emit_def_diagnostic(db, acc, diag); } + if !self.id.is_block_module() { + // These are reported by the body of block modules + let scope = &def_map[self.id.local_id].scope; + scope.all_macro_calls().for_each(|it| macro_call_diagnostics(db, it, acc)); + } + for def in self.declarations(db) { match def { ModuleDef::Module(m) => { @@ -577,6 +585,10 @@ impl Module { item.diagnostics(db, acc, style_lints); } + t.all_macro_calls(db) + .iter() + .for_each(|&(_ast, call_id)| macro_call_diagnostics(db, call_id, acc)); + acc.extend(def.diagnostics(db, style_lints)) } ModuleDef::Adt(adt) => { @@ -621,6 +633,11 @@ impl Module { // FIXME: Once we diagnose the inputs to builtin derives, we should at least extract those diagnostics somehow continue; } + impl_def + .all_macro_calls(db) + .iter() + .for_each(|&(_ast, call_id)| macro_call_diagnostics(db, call_id, acc)); + let ast_id_map = db.ast_id_map(file_id); for diag in db.impl_data_with_diagnostics(impl_def.id).1.iter() { @@ -809,6 +826,37 @@ impl Module { } } +fn macro_call_diagnostics( + db: &dyn HirDatabase, + macro_call_id: MacroCallId, + acc: &mut Vec, +) { + let Some(e) = db.parse_macro_expansion_error(macro_call_id) else { + return; + }; + let ValueResult { value: parse_errors, err } = &*e; + if let Some(err) = err { + let loc = db.lookup_intern_macro_call(macro_call_id); + let (node, precise_location, macro_name, kind) = precise_macro_call_location(&loc.kind, db); + let diag = match err { + &hir_expand::ExpandError::UnresolvedProcMacro(krate) => { + UnresolvedProcMacro { node, precise_location, macro_name, kind, krate }.into() + } + err => MacroError { node, precise_location, message: err.to_string() }.into(), + }; + acc.push(diag); + } + + if !parse_errors.is_empty() { + let loc = db.lookup_intern_macro_call(macro_call_id); + let (node, precise_location, _, _) = precise_macro_call_location(&loc.kind, db); + acc.push( + MacroExpansionParseError { node, precise_location, errors: parse_errors.clone() } + .into(), + ) + } +} + fn emit_macro_def_diagnostics(db: &dyn HirDatabase, acc: &mut Vec, m: Macro) { let id = db.macro_def(m.id); if let hir_expand::db::TokenExpander::DeclarativeMacro(expander) = db.macro_expander(id) { @@ -888,16 +936,6 @@ fn emit_def_diagnostic_( .into(), ); } - DefDiagnosticKind::MacroError { ast, message } => { - let (node, precise_location, _, _) = precise_macro_call_location(ast, db); - acc.push(MacroError { node, precise_location, message: message.clone() }.into()); - } - DefDiagnosticKind::MacroExpansionParseError { ast, errors } => { - let (node, precise_location, _, _) = precise_macro_call_location(ast, db); - acc.push( - MacroExpansionParseError { node, precise_location, errors: errors.clone() }.into(), - ); - } DefDiagnosticKind::UnimplementedBuiltinMacro { ast } => { let node = ast.to_node(db.upcast()); // Must have a name, otherwise we wouldn't emit it. @@ -1489,6 +1527,14 @@ impl Adt { .map(|arena| arena.1.clone()) } + pub fn as_struct(&self) -> Option { + if let Self::Struct(v) = self { + Some(*v) + } else { + None + } + } + pub fn as_enum(&self) -> Option { if let Self::Enum(v) = self { Some(*v) @@ -1636,6 +1682,10 @@ impl DefWithBody { Module { id: def_map.module_id(DefMap::ROOT) }.diagnostics(db, acc, style_lints); } + source_map + .macro_calls() + .for_each(|(_ast_id, call_id)| macro_call_diagnostics(db, call_id.macro_call_id, acc)); + for diag in source_map.diagnostics() { acc.push(match diag { BodyDiagnostic::InactiveCode { node, cfg, opts } => { @@ -2437,6 +2487,14 @@ impl Trait { .filter(|(_, ty)| !count_required_only || !ty.has_default()) .count() } + + fn all_macro_calls(&self, db: &dyn HirDatabase) -> Box<[(AstId, MacroCallId)]> { + db.trait_data(self.id) + .macro_calls + .as_ref() + .map(|it| it.as_ref().clone().into_boxed_slice()) + .unwrap_or_default() + } } impl HasVisibility for Trait { @@ -2505,6 +2563,15 @@ impl HasVisibility for TypeAlias { } } +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct StaticLifetime; + +impl StaticLifetime { + pub fn name(self) -> Name { + known::STATIC_LIFETIME + } +} + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct BuiltinType { pub(crate) inner: hir_def::builtin_type::BuiltinType, @@ -2535,6 +2602,20 @@ impl BuiltinType { matches!(self.inner, hir_def::builtin_type::BuiltinType::Float(_)) } + pub fn is_f32(&self) -> bool { + matches!( + self.inner, + hir_def::builtin_type::BuiltinType::Float(hir_def::builtin_type::BuiltinFloat::F32) + ) + } + + pub fn is_f64(&self) -> bool { + matches!( + self.inner, + hir_def::builtin_type::BuiltinType::Float(hir_def::builtin_type::BuiltinFloat::F64) + ) + } + pub fn is_char(&self) -> bool { matches!(self.inner, hir_def::builtin_type::BuiltinType::Char) } @@ -3743,6 +3824,14 @@ impl Impl { pub fn check_orphan_rules(self, db: &dyn HirDatabase) -> bool { check_orphan_rules(db, self.id) } + + fn all_macro_calls(&self, db: &dyn HirDatabase) -> Box<[(AstId, MacroCallId)]> { + db.impl_data(self.id) + .macro_calls + .as_ref() + .map(|it| it.as_ref().clone().into_boxed_slice()) + .unwrap_or_default() + } } #[derive(Clone, PartialEq, Eq, Debug, Hash)] @@ -4495,7 +4584,8 @@ impl Type { name: Option<&Name>, mut callback: impl FnMut(Function) -> Option, ) -> Option { - let _p = tracing::span!(tracing::Level::INFO, "iterate_method_candidates").entered(); + let _p = + tracing::span!(tracing::Level::INFO, "iterate_method_candidates_with_traits").entered(); let mut slot = None; self.iterate_method_candidates_dyn( diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index e792e159acf0a..6c70cc4baf0aa 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -131,7 +131,7 @@ pub struct SemanticsImpl<'db> { pub db: &'db dyn HirDatabase, s2d_cache: RefCell, /// Rootnode to HirFileId cache - cache: RefCell>, + root_to_file_cache: RefCell>, // These 2 caches are mainly useful for semantic highlighting as nothing else descends a lot of tokens // So we might wanna move them out into something specific for semantic highlighting expansion_info_cache: RefCell>, @@ -294,7 +294,7 @@ impl<'db> SemanticsImpl<'db> { SemanticsImpl { db, s2d_cache: Default::default(), - cache: Default::default(), + root_to_file_cache: Default::default(), expansion_info_cache: Default::default(), macro_call_cache: Default::default(), } @@ -690,6 +690,7 @@ impl<'db> SemanticsImpl<'db> { exp_info }); + // FIXME: uncached parse // Create the source analyzer for the macro call scope let Some(sa) = self.analyze_no_infer(&self.parse_or_expand(expansion_info.call_file())) else { @@ -722,7 +723,7 @@ impl<'db> SemanticsImpl<'db> { mut token: SyntaxToken, f: &mut dyn FnMut(InFile) -> ControlFlow<()>, ) { - let _p = tracing::span!(tracing::Level::INFO, "descend_into_macros").entered(); + let _p = tracing::span!(tracing::Level::INFO, "descend_into_macros_impl").entered(); let (sa, span, file_id) = match token.parent().and_then(|parent| self.analyze_no_infer(&parent)) { Some(sa) => match sa.file_id.file_id() { @@ -1025,6 +1026,7 @@ impl<'db> SemanticsImpl<'db> { None => { let call_node = file_id.macro_file()?.call_node(db); // cache the node + // FIXME: uncached parse self.parse_or_expand(call_node.file_id); Some(call_node) } @@ -1370,7 +1372,7 @@ impl<'db> SemanticsImpl<'db> { offset: Option, infer_body: bool, ) -> Option { - let _p = tracing::span!(tracing::Level::INFO, "Semantics::analyze_impl").entered(); + let _p = tracing::span!(tracing::Level::INFO, "SemanticsImpl::analyze_impl").entered(); let node = self.find_file(node); let container = self.with_ctx(|ctx| ctx.find_container(node))?; @@ -1397,7 +1399,7 @@ impl<'db> SemanticsImpl<'db> { fn cache(&self, root_node: SyntaxNode, file_id: HirFileId) { assert!(root_node.parent().is_none()); - let mut cache = self.cache.borrow_mut(); + let mut cache = self.root_to_file_cache.borrow_mut(); let prev = cache.insert(root_node, file_id); assert!(prev.is_none() || prev == Some(file_id)) } @@ -1407,7 +1409,7 @@ impl<'db> SemanticsImpl<'db> { } fn lookup(&self, root_node: &SyntaxNode) -> Option { - let cache = self.cache.borrow(); + let cache = self.root_to_file_cache.borrow(); cache.get(root_node).copied() } @@ -1427,7 +1429,7 @@ impl<'db> SemanticsImpl<'db> { known nodes: {}\n\n", node, root_node, - self.cache + self.root_to_file_cache .borrow() .keys() .map(|it| format!("{it:?}")) diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs b/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs index 434e4b5a0cf12..d2bd8b0e799f7 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 @@ -118,10 +118,10 @@ pub(super) struct SourceToDefCtx<'a, 'b> { impl SourceToDefCtx<'_, '_> { pub(super) fn file_to_def(&self, file: FileId) -> SmallVec<[ModuleId; 1]> { - let _p = tracing::span!(tracing::Level::INFO, "SourceBinder::file_to_module_def").entered(); + let _p = tracing::span!(tracing::Level::INFO, "SourceToDefCtx::file_to_def").entered(); let mut mods = SmallVec::new(); for &crate_id in self.db.relevant_crates(file).iter() { - // FIXME: inner items + // Note: `mod` declarations in block modules cannot be supported here let crate_def_map = self.db.crate_def_map(crate_id); mods.extend( crate_def_map @@ -129,6 +129,9 @@ impl SourceToDefCtx<'_, '_> { .map(|local_id| crate_def_map.module_id(local_id)), ) } + if mods.is_empty() { + // FIXME: detached file + } mods } 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 dc96a1b03d0cf..057b03baef07a 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -28,7 +28,7 @@ use hir_expand::{ mod_path::path, name, name::{AsName, Name}, - HirFileId, InFile, MacroFileId, MacroFileIdExt, + HirFileId, InFile, InMacroFile, MacroFileId, MacroFileIdExt, }; use hir_ty::{ diagnostics::{ @@ -118,7 +118,7 @@ impl SourceAnalyzer { fn expr_id(&self, db: &dyn HirDatabase, expr: &ast::Expr) -> Option { let src = match expr { ast::Expr::MacroExpr(expr) => { - self.expand_expr(db, InFile::new(self.file_id, expr.macro_call()?))? + self.expand_expr(db, InFile::new(self.file_id, expr.macro_call()?))?.into() } _ => InFile::new(self.file_id, expr.clone()), }; @@ -145,20 +145,20 @@ impl SourceAnalyzer { &self, db: &dyn HirDatabase, expr: InFile, - ) -> Option> { + ) -> Option> { let macro_file = self.body_source_map()?.node_macro_file(expr.as_ref())?; - let expanded = db.parse_or_expand(macro_file); + let expanded = db.parse_macro_expansion(macro_file).value.0.syntax_node(); let res = if let Some(stmts) = ast::MacroStmts::cast(expanded.clone()) { match stmts.expr()? { ast::Expr::MacroExpr(mac) => { - self.expand_expr(db, InFile::new(macro_file, mac.macro_call()?))? + self.expand_expr(db, InFile::new(macro_file.into(), mac.macro_call()?))? } - expr => InFile::new(macro_file, expr), + expr => InMacroFile::new(macro_file, expr), } } else if let Some(call) = ast::MacroCall::cast(expanded.clone()) { - self.expand_expr(db, InFile::new(macro_file, call))? + self.expand_expr(db, InFile::new(macro_file.into(), call))? } else { - InFile::new(macro_file, ast::Expr::cast(expanded)?) + InMacroFile::new(macro_file, ast::Expr::cast(expanded)?) }; Some(res) diff --git a/src/tools/rust-analyzer/crates/hir/src/term_search.rs b/src/tools/rust-analyzer/crates/hir/src/term_search.rs index 93e7300491106..5c5ddae19e272 100644 --- a/src/tools/rust-analyzer/crates/hir/src/term_search.rs +++ b/src/tools/rust-analyzer/crates/hir/src/term_search.rs @@ -127,6 +127,13 @@ impl LookupTable { self.types_wishlist.insert(ty.clone()); } + // Collapse suggestions if there are many + if let Some(res) = &res { + if res.len() > self.many_threshold { + return Some(vec![Expr::Many(ty.clone())]); + } + } + res } @@ -158,6 +165,13 @@ impl LookupTable { self.types_wishlist.insert(ty.clone()); } + // Collapse suggestions if there are many + if let Some(res) = &res { + if res.len() > self.many_threshold { + return Some(vec![Expr::Many(ty.clone())]); + } + } + res } @@ -255,13 +269,13 @@ pub struct TermSearchConfig { pub enable_borrowcheck: bool, /// Indicate when to squash multiple trees to `Many` as there are too many to keep track pub many_alternatives_threshold: usize, - /// Depth of the search eg. number of cycles to run - pub depth: usize, + /// Fuel for term search in "units of work" + pub fuel: u64, } impl Default for TermSearchConfig { fn default() -> Self { - Self { enable_borrowcheck: true, many_alternatives_threshold: 1, depth: 6 } + Self { enable_borrowcheck: true, many_alternatives_threshold: 1, fuel: 400 } } } @@ -280,8 +294,7 @@ impl Default for TermSearchConfig { /// transformation tactics. For example functions take as from set of types (arguments) to some /// type (return type). Other transformations include methods on type, type constructors and /// projections to struct fields (field access). -/// 3. Once we manage to find path to type we are interested in we continue for single round to see -/// if we can find more paths that take us to the `goal` type. +/// 3. If we run out of fuel (term search takes too long) we stop iterating. /// 4. Return all the paths (type trees) that take us to the `goal` type. /// /// Note that there are usually more ways we can get to the `goal` type but some are discarded to @@ -297,21 +310,31 @@ pub fn term_search(ctx: &TermSearchCtx<'_, DB>) -> Vec { }); let mut lookup = LookupTable::new(ctx.config.many_alternatives_threshold, ctx.goal.clone()); + let fuel = std::cell::Cell::new(ctx.config.fuel); + + let should_continue = &|| { + let remaining = fuel.get(); + fuel.set(remaining.saturating_sub(1)); + if remaining == 0 { + tracing::debug!("fuel exhausted"); + } + remaining > 0 + }; // Try trivial tactic first, also populates lookup table let mut solutions: Vec = tactics::trivial(ctx, &defs, &mut lookup).collect(); // Use well known types tactic before iterations as it does not depend on other tactics solutions.extend(tactics::famous_types(ctx, &defs, &mut lookup)); - for _ in 0..ctx.config.depth { + while should_continue() { lookup.new_round(); - solutions.extend(tactics::type_constructor(ctx, &defs, &mut lookup)); - solutions.extend(tactics::free_function(ctx, &defs, &mut lookup)); - solutions.extend(tactics::impl_method(ctx, &defs, &mut lookup)); - solutions.extend(tactics::struct_projection(ctx, &defs, &mut lookup)); - solutions.extend(tactics::impl_static_method(ctx, &defs, &mut lookup)); - solutions.extend(tactics::make_tuple(ctx, &defs, &mut lookup)); + solutions.extend(tactics::type_constructor(ctx, &defs, &mut lookup, should_continue)); + solutions.extend(tactics::free_function(ctx, &defs, &mut lookup, should_continue)); + solutions.extend(tactics::impl_method(ctx, &defs, &mut lookup, should_continue)); + solutions.extend(tactics::struct_projection(ctx, &defs, &mut lookup, should_continue)); + solutions.extend(tactics::impl_static_method(ctx, &defs, &mut lookup, should_continue)); + solutions.extend(tactics::make_tuple(ctx, &defs, &mut lookup, should_continue)); // Discard not interesting `ScopeDef`s for speedup for def in lookup.exhausted_scopedefs() { diff --git a/src/tools/rust-analyzer/crates/hir/src/term_search/expr.rs b/src/tools/rust-analyzer/crates/hir/src/term_search/expr.rs index 2d0c5630e10e9..9f56a1ee55d3b 100644 --- a/src/tools/rust-analyzer/crates/hir/src/term_search/expr.rs +++ b/src/tools/rust-analyzer/crates/hir/src/term_search/expr.rs @@ -211,13 +211,13 @@ impl Expr { } } Expr::Method { func, target, params, .. } => { - if target.contains_many_in_illegal_pos() { + if self.contains_many_in_illegal_pos(db) { return Ok(many_formatter(&target.ty(db))); } let func_name = func.name(db).display(db.upcast()).to_string(); let self_param = func.self_param(db).unwrap(); - let target = target.gen_source_code( + let target_str = target.gen_source_code( sema_scope, many_formatter, prefer_no_std, @@ -236,9 +236,12 @@ impl Expr { Some(trait_) => { let trait_name = mod_item_path_str(sema_scope, &ModuleDef::Trait(trait_))?; let target = match self_param.access(db) { - crate::Access::Shared => format!("&{target}"), - crate::Access::Exclusive => format!("&mut {target}"), - crate::Access::Owned => target, + crate::Access::Shared if !target.is_many() => format!("&{target_str}"), + crate::Access::Exclusive if !target.is_many() => { + format!("&mut {target_str}") + } + crate::Access::Owned => target_str, + _ => many_formatter(&target.ty(db)), }; let res = match args.is_empty() { true => format!("{trait_name}::{func_name}({target})",), @@ -246,7 +249,7 @@ impl Expr { }; Ok(res) } - None => Ok(format!("{target}.{func_name}({args})")), + None => Ok(format!("{target_str}.{func_name}({args})")), } } Expr::Variant { variant, generics, params } => { @@ -381,7 +384,7 @@ impl Expr { Ok(res) } Expr::Field { expr, field } => { - if expr.contains_many_in_illegal_pos() { + if expr.contains_many_in_illegal_pos(db) { return Ok(many_formatter(&expr.ty(db))); } @@ -395,7 +398,7 @@ impl Expr { Ok(format!("{strukt}.{field}")) } Expr::Reference(expr) => { - if expr.contains_many_in_illegal_pos() { + if expr.contains_many_in_illegal_pos(db) { return Ok(many_formatter(&expr.ty(db))); } @@ -466,10 +469,15 @@ impl Expr { /// macro!().bar() /// ¯o!() /// ``` - fn contains_many_in_illegal_pos(&self) -> bool { + fn contains_many_in_illegal_pos(&self, db: &dyn HirDatabase) -> bool { match self { - Expr::Method { target, .. } => target.contains_many_in_illegal_pos(), - Expr::Field { expr, .. } => expr.contains_many_in_illegal_pos(), + Expr::Method { target, func, .. } => { + match func.as_assoc_item(db).and_then(|it| it.container_or_implemented_trait(db)) { + Some(_) => false, + None => target.is_many(), + } + } + Expr::Field { expr, .. } => expr.contains_many_in_illegal_pos(db), Expr::Reference(target) => target.is_many(), Expr::Many(_) => true, _ => false, diff --git a/src/tools/rust-analyzer/crates/hir/src/term_search/tactics.rs b/src/tools/rust-analyzer/crates/hir/src/term_search/tactics.rs index 63b2a2506f895..a26728272dc6a 100644 --- a/src/tools/rust-analyzer/crates/hir/src/term_search/tactics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/term_search/tactics.rs @@ -4,6 +4,7 @@ //! * `ctx` - Context for the term search //! * `defs` - Set of items in scope at term search target location //! * `lookup` - Lookup table for types +//! * `should_continue` - Function that indicates when to stop iterating //! And they return iterator that yields type trees that unify with the `goal` type. use std::iter; @@ -97,16 +98,19 @@ pub(super) fn trivial<'a, DB: HirDatabase>( /// * `ctx` - Context for the term search /// * `defs` - Set of items in scope at term search target location /// * `lookup` - Lookup table for types +/// * `should_continue` - Function that indicates when to stop iterating pub(super) fn type_constructor<'a, DB: HirDatabase>( ctx: &'a TermSearchCtx<'a, DB>, defs: &'a FxHashSet, lookup: &'a mut LookupTable, + should_continue: &'a dyn std::ops::Fn() -> bool, ) -> impl Iterator + 'a { let db = ctx.sema.db; let module = ctx.scope.module(); fn variant_helper( db: &dyn HirDatabase, lookup: &mut LookupTable, + should_continue: &dyn std::ops::Fn() -> bool, parent_enum: Enum, variant: Variant, config: &TermSearchConfig, @@ -152,6 +156,7 @@ pub(super) fn type_constructor<'a, DB: HirDatabase>( .chain((non_default_type_params_len == 0).then_some(Vec::new())); generic_params + .filter(|_| should_continue()) .filter_map(move |generics| { // Insert default type params let mut g = generics.into_iter(); @@ -194,8 +199,14 @@ pub(super) fn type_constructor<'a, DB: HirDatabase>( defs.iter() .filter_map(move |def| match def { ScopeDef::ModuleDef(ModuleDef::Variant(it)) => { - let variant_exprs = - variant_helper(db, lookup, it.parent_enum(db), *it, &ctx.config); + let variant_exprs = variant_helper( + db, + lookup, + should_continue, + it.parent_enum(db), + *it, + &ctx.config, + ); if variant_exprs.is_empty() { return None; } @@ -213,7 +224,9 @@ pub(super) fn type_constructor<'a, DB: HirDatabase>( let exprs: Vec<(Type, Vec)> = enum_ .variants(db) .into_iter() - .flat_map(|it| variant_helper(db, lookup, *enum_, it, &ctx.config)) + .flat_map(|it| { + variant_helper(db, lookup, should_continue, *enum_, it, &ctx.config) + }) .collect(); if exprs.is_empty() { @@ -271,6 +284,7 @@ pub(super) fn type_constructor<'a, DB: HirDatabase>( .chain((non_default_type_params_len == 0).then_some(Vec::new())); let exprs = generic_params + .filter(|_| should_continue()) .filter_map(|generics| { // Insert default type params let mut g = generics.into_iter(); @@ -345,10 +359,12 @@ pub(super) fn type_constructor<'a, DB: HirDatabase>( /// * `ctx` - Context for the term search /// * `defs` - Set of items in scope at term search target location /// * `lookup` - Lookup table for types +/// * `should_continue` - Function that indicates when to stop iterating pub(super) fn free_function<'a, DB: HirDatabase>( ctx: &'a TermSearchCtx<'a, DB>, defs: &'a FxHashSet, lookup: &'a mut LookupTable, + should_continue: &'a dyn std::ops::Fn() -> bool, ) -> impl Iterator + 'a { let db = ctx.sema.db; let module = ctx.scope.module(); @@ -390,6 +406,7 @@ pub(super) fn free_function<'a, DB: HirDatabase>( .permutations(non_default_type_params_len); let exprs: Vec<_> = generic_params + .filter(|_| should_continue()) .filter_map(|generics| { // Insert default type params let mut g = generics.into_iter(); @@ -474,10 +491,12 @@ pub(super) fn free_function<'a, DB: HirDatabase>( /// * `ctx` - Context for the term search /// * `defs` - Set of items in scope at term search target location /// * `lookup` - Lookup table for types +/// * `should_continue` - Function that indicates when to stop iterating pub(super) fn impl_method<'a, DB: HirDatabase>( ctx: &'a TermSearchCtx<'a, DB>, _defs: &'a FxHashSet, lookup: &'a mut LookupTable, + should_continue: &'a dyn std::ops::Fn() -> bool, ) -> impl Iterator + 'a { let db = ctx.sema.db; let module = ctx.scope.module(); @@ -554,6 +573,7 @@ pub(super) fn impl_method<'a, DB: HirDatabase>( .permutations(non_default_fn_type_params_len); let exprs: Vec<_> = generic_params + .filter(|_| should_continue()) .filter_map(|generics| { // Insert default type params let mut g = generics.into_iter(); @@ -645,10 +665,12 @@ pub(super) fn impl_method<'a, DB: HirDatabase>( /// * `ctx` - Context for the term search /// * `defs` - Set of items in scope at term search target location /// * `lookup` - Lookup table for types +/// * `should_continue` - Function that indicates when to stop iterating pub(super) fn struct_projection<'a, DB: HirDatabase>( ctx: &'a TermSearchCtx<'a, DB>, _defs: &'a FxHashSet, lookup: &'a mut LookupTable, + should_continue: &'a dyn std::ops::Fn() -> bool, ) -> impl Iterator + 'a { let db = ctx.sema.db; let module = ctx.scope.module(); @@ -656,6 +678,7 @@ pub(super) fn struct_projection<'a, DB: HirDatabase>( .new_types(NewTypesKey::StructProjection) .into_iter() .map(|ty| (ty.clone(), lookup.find(db, &ty).expect("Expr not in lookup"))) + .filter(|_| should_continue()) .flat_map(move |(ty, targets)| { ty.fields(db).into_iter().filter_map(move |(field, filed_ty)| { if !field.is_visible_from(db, module) { @@ -716,10 +739,12 @@ pub(super) fn famous_types<'a, DB: HirDatabase>( /// * `ctx` - Context for the term search /// * `defs` - Set of items in scope at term search target location /// * `lookup` - Lookup table for types +/// * `should_continue` - Function that indicates when to stop iterating pub(super) fn impl_static_method<'a, DB: HirDatabase>( ctx: &'a TermSearchCtx<'a, DB>, _defs: &'a FxHashSet, lookup: &'a mut LookupTable, + should_continue: &'a dyn std::ops::Fn() -> bool, ) -> impl Iterator + 'a { let db = ctx.sema.db; let module = ctx.scope.module(); @@ -728,6 +753,7 @@ pub(super) fn impl_static_method<'a, DB: HirDatabase>( .clone() .into_iter() .chain(iter::once(ctx.goal.clone())) + .filter(|_| should_continue()) .flat_map(|ty| { Impl::all_for_type(db, ty.clone()).into_iter().map(move |imp| (ty.clone(), imp)) }) @@ -801,6 +827,7 @@ pub(super) fn impl_static_method<'a, DB: HirDatabase>( .permutations(non_default_fn_type_params_len); let exprs: Vec<_> = generic_params + .filter(|_| should_continue()) .filter_map(|generics| { // Insert default type params let mut g = generics.into_iter(); @@ -884,10 +911,12 @@ pub(super) fn impl_static_method<'a, DB: HirDatabase>( /// * `ctx` - Context for the term search /// * `defs` - Set of items in scope at term search target location /// * `lookup` - Lookup table for types +/// * `should_continue` - Function that indicates when to stop iterating pub(super) fn make_tuple<'a, DB: HirDatabase>( ctx: &'a TermSearchCtx<'a, DB>, _defs: &'a FxHashSet, lookup: &'a mut LookupTable, + should_continue: &'a dyn std::ops::Fn() -> bool, ) -> impl Iterator + 'a { let db = ctx.sema.db; let module = ctx.scope.module(); @@ -896,6 +925,7 @@ pub(super) fn make_tuple<'a, DB: HirDatabase>( .types_wishlist() .clone() .into_iter() + .filter(|_| should_continue()) .filter(|ty| ty.is_tuple()) .filter_map(move |ty| { // Double check to not contain unknown @@ -915,6 +945,7 @@ pub(super) fn make_tuple<'a, DB: HirDatabase>( let exprs: Vec = param_exprs .into_iter() .multi_cartesian_product() + .filter(|_| should_continue()) .map(|params| { let tys: Vec = params.iter().map(|it| it.ty(db)).collect(); let tuple_ty = Type::new_tuple(module.krate().into(), &tys); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/assist_config.rs b/src/tools/rust-analyzer/crates/ide-assists/src/assist_config.rs index fbe17dbfd7714..5d76cb04323d9 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/assist_config.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/assist_config.rs @@ -16,4 +16,5 @@ pub struct AssistConfig { pub prefer_no_std: bool, pub prefer_prelude: bool, pub assist_emit_must_use: bool, + pub term_search_fuel: u64, } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs index 55e0d7f3b28c7..f178a7e0cec88 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs @@ -8,8 +8,7 @@ use ide_db::{ }; use syntax::{ ast::{self, make, AstNode, Expr::BinExpr, HasArgList}, - ted::{self, Position}, - SyntaxKind, + ted, SyntaxKind, T, }; use crate::{utils::invert_boolean_expression, AssistContext, AssistId, AssistKind, Assists}; @@ -62,7 +61,7 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti let demorganed = bin_expr.clone_subtree().clone_for_update(); ted::replace(demorganed.op_token()?, ast::make::token(inv_token)); - let mut exprs = VecDeque::from(vec![ + let mut exprs = VecDeque::from([ (bin_expr.lhs()?, demorganed.lhs()?), (bin_expr.rhs()?, demorganed.rhs()?), ]); @@ -93,58 +92,38 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti } } - let dm_lhs = demorganed.lhs()?; - acc.add_group( &GroupLabel("Apply De Morgan's law".to_owned()), AssistId("apply_demorgan", AssistKind::RefactorRewrite), "Apply De Morgan's law", op_range, |edit| { + let demorganed = ast::Expr::BinExpr(demorganed); let paren_expr = bin_expr.syntax().parent().and_then(ast::ParenExpr::cast); let neg_expr = paren_expr .clone() .and_then(|paren_expr| paren_expr.syntax().parent()) .and_then(ast::PrefixExpr::cast) - .and_then(|prefix_expr| { - if prefix_expr.op_kind()? == ast::UnaryOp::Not { - Some(prefix_expr) - } else { - None - } - }); + .filter(|prefix_expr| matches!(prefix_expr.op_kind(), Some(ast::UnaryOp::Not))) + .map(ast::Expr::PrefixExpr); if let Some(paren_expr) = paren_expr { if let Some(neg_expr) = neg_expr { cov_mark::hit!(demorgan_double_negation); - edit.replace_ast(ast::Expr::PrefixExpr(neg_expr), demorganed.into()); + let parent = neg_expr.syntax().parent(); + + if parent.is_some_and(|parent| demorganed.needs_parens_in(parent)) { + cov_mark::hit!(demorgan_keep_parens_for_op_precedence2); + edit.replace_ast(neg_expr, make::expr_paren(demorganed)); + } else { + edit.replace_ast(neg_expr, demorganed); + }; } else { cov_mark::hit!(demorgan_double_parens); - ted::insert_all_raw( - Position::before(dm_lhs.syntax()), - vec![ - syntax::NodeOrToken::Token(ast::make::token(SyntaxKind::BANG)), - syntax::NodeOrToken::Token(ast::make::token(SyntaxKind::L_PAREN)), - ], - ); - - ted::append_child_raw( - demorganed.syntax(), - syntax::NodeOrToken::Token(ast::make::token(SyntaxKind::R_PAREN)), - ); - - edit.replace_ast(ast::Expr::ParenExpr(paren_expr), demorganed.into()); + edit.replace_ast(paren_expr.into(), add_bang_paren(demorganed)); } } else { - ted::insert_all_raw( - Position::before(dm_lhs.syntax()), - vec![ - syntax::NodeOrToken::Token(ast::make::token(SyntaxKind::BANG)), - syntax::NodeOrToken::Token(ast::make::token(SyntaxKind::L_PAREN)), - ], - ); - ted::append_child_raw(demorganed.syntax(), ast::make::token(SyntaxKind::R_PAREN)); - edit.replace_ast(bin_expr, demorganed); + edit.replace_ast(bin_expr.into(), add_bang_paren(demorganed)); } }, ) @@ -271,6 +250,11 @@ fn tail_cb_impl(edit: &mut SourceChangeBuilder, e: &ast::Expr) { } } +/// Add bang and parentheses to the expression. +fn add_bang_paren(expr: ast::Expr) -> ast::Expr { + make::expr_prefix(T![!], make::expr_paren(expr)) +} + #[cfg(test)] mod tests { use super::*; @@ -349,16 +333,14 @@ fn f() { !(S <= S || S < S) } check_assist(apply_demorgan, "fn f() { (x ||$0 x) }", "fn f() { !(!x && !x) }") } - // FIXME : This needs to go. - // // https://github.com/rust-lang/rust-analyzer/issues/10963 - // #[test] - // fn demorgan_doesnt_hang() { - // check_assist( - // apply_demorgan, - // "fn f() { 1 || 3 &&$0 4 || 5 }", - // "fn f() { !(!1 || !3 || !4) || 5 }", - // ) - // } + #[test] + fn demorgan_doesnt_hang() { + check_assist( + apply_demorgan, + "fn f() { 1 || 3 &&$0 4 || 5 }", + "fn f() { 1 || !(!3 || !4) || 5 }", + ) + } #[test] fn demorgan_keep_pars_for_op_precedence() { @@ -375,6 +357,21 @@ fn f() { !(S <= S || S < S) } ); } + #[test] + fn demorgan_keep_pars_for_op_precedence2() { + cov_mark::check!(demorgan_keep_parens_for_op_precedence2); + check_assist( + apply_demorgan, + "fn f() { (a && !(b &&$0 c); }", + "fn f() { (a && (!b || !c); }", + ); + } + + #[test] + fn demorgan_keep_pars_for_op_precedence3() { + check_assist(apply_demorgan, "fn f() { (a || !(b &&$0 c); }", "fn f() { (a || !b || !c; }"); + } + #[test] fn demorgan_removes_pars_in_eq_precedence() { check_assist( @@ -384,6 +381,11 @@ fn f() { !(S <= S || S < S) } ) } + #[test] + fn demorgan_removes_pars_for_op_precedence2() { + check_assist(apply_demorgan, "fn f() { (a || !(b ||$0 c); }", "fn f() { (a || !b && !c; }"); + } + #[test] fn demorgan_iterator_any_all_reverse() { check_assist( diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs index 76f021ed912f6..43ff1158864eb 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs @@ -85,7 +85,7 @@ fn edit_struct_def( strukt: &Either, record_fields: ast::RecordFieldList, ) { - // Note that we don't need to consider macro files in this function because this this is + // Note that we don't need to consider macro files in this function because this is // currently not triggered for struct definitions inside macro calls. let tuple_fields = record_fields .fields() diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs index 34326294d2e12..2b8de3443b431 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs @@ -5623,7 +5623,7 @@ fn func(i: Struct<'_, T>) { fun_name(i); } -fn $0fun_name(i: Struct<'_, T>) { +fn $0fun_name(i: Struct) { foo(i); } "#, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs index db94a21a6d2c8..0fc122d623fc6 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs @@ -1,6 +1,6 @@ use hir::{ - Adt, AsAssocItem, HasSource, HirDisplay, HirFileIdExt, Module, PathResolution, Semantics, Type, - TypeInfo, + Adt, AsAssocItem, HasSource, HirDisplay, HirFileIdExt, Module, PathResolution, Semantics, + StructKind, Type, TypeInfo, }; use ide_db::{ base_db::FileId, @@ -15,8 +15,8 @@ use itertools::Itertools; use stdx::to_lower_snake_case; use syntax::{ ast::{ - self, edit::IndentLevel, edit_in_place::Indent, make, AstNode, CallExpr, HasArgList, - HasGenericParams, HasModuleItem, HasTypeBounds, + self, edit::IndentLevel, edit_in_place::Indent, make, AstNode, BlockExpr, CallExpr, + HasArgList, HasGenericParams, HasModuleItem, HasTypeBounds, }, ted, SyntaxKind, SyntaxNode, TextRange, T, }; @@ -66,7 +66,7 @@ fn gen_fn(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { } let fn_name = &*name_ref.text(); - let TargetInfo { target_module, adt_name, target, file } = + let TargetInfo { target_module, adt_info, target, file } = fn_target_info(ctx, path, &call, fn_name)?; if let Some(m) = target_module { @@ -75,15 +75,16 @@ fn gen_fn(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { } } - let function_builder = FunctionBuilder::from_call(ctx, &call, fn_name, target_module, target)?; + let function_builder = + FunctionBuilder::from_call(ctx, &call, fn_name, target_module, target, &adt_info)?; let text_range = call.syntax().text_range(); let label = format!("Generate {} function", function_builder.fn_name); - add_func_to_accumulator(acc, ctx, text_range, function_builder, file, adt_name, label) + add_func_to_accumulator(acc, ctx, text_range, function_builder, file, adt_info, label) } struct TargetInfo { target_module: Option, - adt_name: Option, + adt_info: Option, target: GeneratedFunctionTarget, file: FileId, } @@ -91,11 +92,11 @@ struct TargetInfo { impl TargetInfo { fn new( target_module: Option, - adt_name: Option, + adt_info: Option, target: GeneratedFunctionTarget, file: FileId, ) -> Self { - Self { target_module, adt_name, target, file } + Self { target_module, adt_info, target, file } } } @@ -157,9 +158,9 @@ fn gen_method(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { target, )?; let text_range = call.syntax().text_range(); - let adt_name = if impl_.is_none() { Some(adt.name(ctx.sema.db)) } else { None }; + let adt_info = AdtInfo::new(adt, impl_.is_some()); let label = format!("Generate {} method", function_builder.fn_name); - add_func_to_accumulator(acc, ctx, text_range, function_builder, file, adt_name, label) + add_func_to_accumulator(acc, ctx, text_range, function_builder, file, Some(adt_info), label) } fn add_func_to_accumulator( @@ -168,7 +169,7 @@ fn add_func_to_accumulator( text_range: TextRange, function_builder: FunctionBuilder, file: FileId, - adt_name: Option, + adt_info: Option, label: String, ) -> Option<()> { acc.add(AssistId("generate_function", AssistKind::Generate), label, text_range, |edit| { @@ -177,8 +178,14 @@ fn add_func_to_accumulator( let target = function_builder.target.clone(); let func = function_builder.render(ctx.config.snippet_cap, edit); - if let Some(name) = adt_name { - let name = make::ty_path(make::ext::ident_path(&format!("{}", name.display(ctx.db())))); + if let Some(adt) = + adt_info + .and_then(|adt_info| if adt_info.impl_exists { None } else { Some(adt_info.adt) }) + { + let name = make::ty_path(make::ext::ident_path(&format!( + "{}", + adt.name(ctx.db()).display(ctx.db()) + ))); // FIXME: adt may have generic params. let impl_ = make::impl_(None, None, name, None, None).clone_for_update(); @@ -210,6 +217,7 @@ struct FunctionBuilder { generic_param_list: Option, where_clause: Option, params: ast::ParamList, + fn_body: BlockExpr, ret_type: Option, should_focus_return_type: bool, visibility: Visibility, @@ -225,6 +233,7 @@ impl FunctionBuilder { fn_name: &str, target_module: Option, target: GeneratedFunctionTarget, + adt_info: &Option, ) -> Option { let target_module = target_module.or_else(|| ctx.sema.scope(target.syntax()).map(|it| it.module()))?; @@ -243,9 +252,27 @@ impl FunctionBuilder { let await_expr = call.syntax().parent().and_then(ast::AwaitExpr::cast); let is_async = await_expr.is_some(); - let expr_for_ret_ty = await_expr.map_or_else(|| call.clone().into(), |it| it.into()); - let (ret_type, should_focus_return_type) = - make_return_type(ctx, &expr_for_ret_ty, target_module, &mut necessary_generic_params); + let ret_type; + let should_focus_return_type; + let fn_body; + + // If generated function has the name "new" and is an associated function, we generate fn body + // as a constructor and assume a "Self" return type. + if let Some(body) = make_fn_body_as_new_function(ctx, &fn_name.text(), adt_info) { + ret_type = Some(make::ret_type(make::ty_path(make::ext::ident_path("Self")))); + should_focus_return_type = false; + fn_body = body; + } else { + let expr_for_ret_ty = await_expr.map_or_else(|| call.clone().into(), |it| it.into()); + (ret_type, should_focus_return_type) = make_return_type( + ctx, + &expr_for_ret_ty, + target_module, + &mut necessary_generic_params, + ); + let placeholder_expr = make::ext::expr_todo(); + fn_body = make::block_expr(vec![], Some(placeholder_expr)); + }; let (generic_param_list, where_clause) = fn_generic_params(ctx, necessary_generic_params, &target)?; @@ -256,6 +283,7 @@ impl FunctionBuilder { generic_param_list, where_clause, params, + fn_body, ret_type, should_focus_return_type, visibility, @@ -294,12 +322,16 @@ impl FunctionBuilder { let (generic_param_list, where_clause) = fn_generic_params(ctx, necessary_generic_params, &target)?; + let placeholder_expr = make::ext::expr_todo(); + let fn_body = make::block_expr(vec![], Some(placeholder_expr)); + Some(Self { target, fn_name, generic_param_list, where_clause, params, + fn_body, ret_type, should_focus_return_type, visibility, @@ -308,8 +340,6 @@ impl FunctionBuilder { } fn render(self, cap: Option, edit: &mut SourceChangeBuilder) -> ast::Fn { - let placeholder_expr = make::ext::expr_todo(); - let fn_body = make::block_expr(vec![], Some(placeholder_expr)); let visibility = match self.visibility { Visibility::None => None, Visibility::Crate => Some(make::visibility_pub_crate()), @@ -321,7 +351,7 @@ impl FunctionBuilder { self.generic_param_list, self.where_clause, self.params, - fn_body, + self.fn_body, self.ret_type, self.is_async, false, // FIXME : const and unsafe are not handled yet. @@ -391,6 +421,53 @@ fn make_return_type( (ret_type, should_focus_return_type) } +fn make_fn_body_as_new_function( + ctx: &AssistContext<'_>, + fn_name: &str, + adt_info: &Option, +) -> Option { + if fn_name != "new" { + return None; + }; + let adt_info = adt_info.as_ref()?; + + let path_self = make::ext::ident_path("Self"); + let placeholder_expr = make::ext::expr_todo(); + let tail_expr = if let Some(strukt) = adt_info.adt.as_struct() { + match strukt.kind(ctx.db()) { + StructKind::Record => { + let fields = strukt + .fields(ctx.db()) + .iter() + .map(|field| { + make::record_expr_field( + make::name_ref(&format!("{}", field.name(ctx.db()).display(ctx.db()))), + Some(placeholder_expr.clone()), + ) + }) + .collect::>(); + + make::record_expr(path_self, make::record_expr_field_list(fields)).into() + } + StructKind::Tuple => { + let args = strukt + .fields(ctx.db()) + .iter() + .map(|_| placeholder_expr.clone()) + .collect::>(); + + make::expr_call(make::expr_path(path_self), make::arg_list(args)) + } + StructKind::Unit => make::expr_path(path_self), + } + } else { + placeholder_expr + }; + + let fn_body = make::block_expr(vec![], Some(tail_expr)); + Some(fn_body) +} + fn get_fn_target_info( ctx: &AssistContext<'_>, target_module: Option, @@ -443,8 +520,8 @@ fn assoc_fn_target_info( } let (impl_, file) = get_adt_source(ctx, &adt, fn_name)?; let target = get_method_target(ctx, &impl_, &adt)?; - let adt_name = if impl_.is_none() { Some(adt.name(ctx.sema.db)) } else { None }; - Some(TargetInfo::new(target_module, adt_name, target, file)) + let adt_info = AdtInfo::new(adt, impl_.is_some()); + Some(TargetInfo::new(target_module, Some(adt_info), target, file)) } #[derive(Clone)] @@ -560,6 +637,17 @@ impl GeneratedFunctionTarget { } } +struct AdtInfo { + adt: hir::Adt, + impl_exists: bool, +} + +impl AdtInfo { + fn new(adt: Adt, impl_exists: bool) -> Self { + Self { adt, impl_exists } + } +} + /// Computes parameter list for the generated function. fn fn_args( ctx: &AssistContext<'_>, @@ -2758,18 +2846,18 @@ fn main() { r" enum Foo {} fn main() { - Foo::new$0(); + Foo::bar$0(); } ", r" enum Foo {} impl Foo { - fn new() ${0:-> _} { + fn bar() ${0:-> _} { todo!() } } fn main() { - Foo::new(); + Foo::bar(); } ", ) @@ -2849,4 +2937,152 @@ fn main() { ", ); } + + #[test] + fn new_function_assume_self_type() { + check_assist( + generate_function, + r" +pub struct Foo { + field_1: usize, + field_2: String, +} + +fn main() { + let foo = Foo::new$0(); +} + ", + r" +pub struct Foo { + field_1: usize, + field_2: String, +} +impl Foo { + fn new() -> Self { + ${0:Self { field_1: todo!(), field_2: todo!() }} + } +} + +fn main() { + let foo = Foo::new(); +} + ", + ) + } + + #[test] + fn new_function_assume_self_type_for_tuple_struct() { + check_assist( + generate_function, + r" +pub struct Foo (usize, String); + +fn main() { + let foo = Foo::new$0(); +} + ", + r" +pub struct Foo (usize, String); +impl Foo { + fn new() -> Self { + ${0:Self(todo!(), todo!())} + } +} + +fn main() { + let foo = Foo::new(); +} + ", + ) + } + + #[test] + fn new_function_assume_self_type_for_unit_struct() { + check_assist( + generate_function, + r" +pub struct Foo; + +fn main() { + let foo = Foo::new$0(); +} + ", + r" +pub struct Foo; +impl Foo { + fn new() -> Self { + ${0:Self} + } +} + +fn main() { + let foo = Foo::new(); +} + ", + ) + } + + #[test] + fn new_function_assume_self_type_for_enum() { + check_assist( + generate_function, + r" +pub enum Foo {} + +fn main() { + let foo = Foo::new$0(); +} + ", + r" +pub enum Foo {} +impl Foo { + fn new() -> Self { + ${0:todo!()} + } +} + +fn main() { + let foo = Foo::new(); +} + ", + ) + } + + #[test] + fn new_function_assume_self_type_with_args() { + check_assist( + generate_function, + r#" +pub struct Foo { + field_1: usize, + field_2: String, +} + +struct Baz; +fn baz() -> Baz { Baz } + +fn main() { + let foo = Foo::new$0(baz(), baz(), "foo", "bar"); +} + "#, + r#" +pub struct Foo { + field_1: usize, + field_2: String, +} +impl Foo { + fn new(baz_1: Baz, baz_2: Baz, arg_1: &str, arg_2: &str) -> Self { + ${0:Self { field_1: todo!(), field_2: todo!() }} + } +} + +struct Baz; +fn baz() -> Baz { Baz } + +fn main() { + let foo = Foo::new(baz(), baz(), "foo", "bar"); +} + "#, + ) + } } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_module_to_file.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_module_to_file.rs index 048906d9d9f5b..9af8411f4cb63 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_module_to_file.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_module_to_file.rs @@ -1,6 +1,7 @@ use std::iter; use ast::edit::IndentLevel; +use hir::HasAttrs; use ide_db::base_db::AnchoredPathBuf; use itertools::Itertools; use stdx::format_to; @@ -50,9 +51,17 @@ pub(crate) fn move_module_to_file(acc: &mut Assists, ctx: &AssistContext<'_>) -> |builder| { let path = { let mut buf = String::from("./"); - match parent_module.name(ctx.db()) { - Some(name) if !parent_module.is_mod_rs(ctx.db()) => { - format_to!(buf, "{}/", name.display(ctx.db())) + let db = ctx.db(); + match parent_module.name(db) { + Some(name) + if !parent_module.is_mod_rs(db) + && parent_module + .attrs(db) + .by_key("path") + .string_value_unescape() + .is_none() => + { + format_to!(buf, "{}/", name.display(db)) } _ => (), } @@ -107,6 +116,72 @@ mod tests { use super::*; + #[test] + fn extract_with_specified_path_attr() { + check_assist( + move_module_to_file, + r#" +//- /main.rs +#[path="parser/__mod.rs"] +mod parser; +//- /parser/__mod.rs +fn test() {} +mod $0expr { + struct A {} +} +"#, + r#" +//- /parser/__mod.rs +fn test() {} +mod expr; +//- /parser/expr.rs +struct A {} +"#, + ); + + check_assist( + move_module_to_file, + r#" +//- /main.rs +#[path="parser/a/__mod.rs"] +mod parser; +//- /parser/a/__mod.rs +fn test() {} +mod $0expr { + struct A {} +} +"#, + r#" +//- /parser/a/__mod.rs +fn test() {} +mod expr; +//- /parser/a/expr.rs +struct A {} +"#, + ); + + check_assist( + move_module_to_file, + r#" +//- /main.rs +#[path="a.rs"] +mod parser; +//- /a.rs +fn test() {} +mod $0expr { + struct A {} +} +"#, + r#" +//- /a.rs +fn test() {} +mod expr; +//- /expr.rs +struct A {} +"#, + ); + } + #[test] fn extract_from_root() { check_assist( diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/raw_string.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/raw_string.rs index 63db60633611f..5a197f23d0e3a 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/raw_string.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/raw_string.rs @@ -25,7 +25,7 @@ pub(crate) fn make_raw_string(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt if token.is_raw() { return None; } - let value = token.value()?; + let value = token.value().ok()?; let target = token.syntax().text_range(); acc.add( AssistId("make_raw_string", AssistKind::RefactorRewrite), @@ -64,7 +64,7 @@ pub(crate) fn make_usual_string(acc: &mut Assists, ctx: &AssistContext<'_>) -> O if !token.is_raw() { return None; } - let value = token.value()?; + let value = token.value().ok()?; let target = token.syntax().text_range(); acc.add( AssistId("make_usual_string", AssistKind::RefactorRewrite), @@ -398,12 +398,12 @@ string"###; } #[test] - fn remove_hash_doesnt_work() { + fn remove_hash_does_not_work() { check_assist_not_applicable(remove_hash, r#"fn f() { let s = $0"random string"; }"#); } #[test] - fn remove_hash_no_hash_doesnt_work() { + fn remove_hash_no_hash_does_not_work() { check_assist_not_applicable(remove_hash, r#"fn f() { let s = $0r"random string"; }"#); } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_impl_items.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_impl_items.rs index 66669662316fe..cf135f83e7b6a 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_impl_items.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_impl_items.rs @@ -77,7 +77,7 @@ pub(crate) fn reorder_impl_items(acc: &mut Assists, ctx: &AssistContext<'_>) -> ast::AssocItem::MacroCall(_) => None, }; - name.and_then(|n| ranks.get(&n.to_string()).copied()).unwrap_or(usize::max_value()) + name.and_then(|n| ranks.get(&n.to_string()).copied()).unwrap_or(usize::MAX) }) .collect(); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_string_with_char.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_string_with_char.rs index 6310981ccce7a..a48b20acbcac8 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_string_with_char.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_string_with_char.rs @@ -25,7 +25,7 @@ use crate::{AssistContext, AssistId, AssistKind, Assists}; // ``` pub(crate) fn replace_string_with_char(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { let token = ctx.find_token_syntax_at_offset(STRING).and_then(ast::String::cast)?; - let value = token.value()?; + let value = token.value().ok()?; let target = token.syntax().text_range(); if value.chars().take(2).count() != 1 { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/term_search.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/term_search.rs index 0f4a8e3aecb24..d0c6ae21988d8 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/term_search.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/term_search.rs @@ -1,5 +1,5 @@ //! Term search assist -use hir::term_search::TermSearchCtx; +use hir::term_search::{TermSearchConfig, TermSearchCtx}; use ide_db::{ assists::{AssistId, AssistKind, GroupLabel}, famous_defs::FamousDefs, @@ -34,7 +34,7 @@ pub(crate) fn term_search(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option< sema: &ctx.sema, scope: &scope, goal: target_ty, - config: Default::default(), + config: TermSearchConfig { fuel: ctx.config.term_search_fuel, ..Default::default() }, }; let paths = hir::term_search::term_search(&term_search_ctx); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs b/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs index 32d6984102081..3b6c951251174 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs @@ -31,6 +31,7 @@ pub(crate) const TEST_CONFIG: AssistConfig = AssistConfig { prefer_no_std: false, prefer_prelude: true, assist_emit_must_use: false, + term_search_fuel: 400, }; pub(crate) const TEST_CONFIG_NO_SNIPPET_CAP: AssistConfig = AssistConfig { @@ -46,6 +47,7 @@ pub(crate) const TEST_CONFIG_NO_SNIPPET_CAP: AssistConfig = AssistConfig { prefer_no_std: false, prefer_prelude: true, assist_emit_must_use: false, + term_search_fuel: 400, }; pub(crate) const TEST_CONFIG_IMPORT_ONE: AssistConfig = AssistConfig { @@ -61,6 +63,7 @@ pub(crate) const TEST_CONFIG_IMPORT_ONE: AssistConfig = AssistConfig { prefer_no_std: false, prefer_prelude: true, assist_emit_must_use: false, + term_search_fuel: 400, }; pub(crate) fn with_single_file(text: &str) -> (RootDatabase, FileId) { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs index 802e9bc3a8077..1e31d65fddfe6 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs @@ -353,7 +353,7 @@ pub(crate) fn complete_expr(acc: &mut Completions, ctx: &CompletionContext<'_>) config: hir::term_search::TermSearchConfig { enable_borrowcheck: false, many_alternatives_threshold: 1, - depth: 6, + fuel: 200, }, }; let exprs = hir::term_search::term_search(&term_search_ctx); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs index 3bc329ecd748f..bf6747d71b7a5 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs @@ -296,7 +296,7 @@ fn import_on_the_fly_pat_( position: SyntaxNode, potential_import_name: String, ) -> Option<()> { - let _p = tracing::span!(tracing::Level::INFO, "import_on_the_fly_pat", ?potential_import_name) + let _p = tracing::span!(tracing::Level::INFO, "import_on_the_fly_pat_", ?potential_import_name) .entered(); ImportScope::find_insert_use_container(&position, &ctx.sema)?; 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 04563fb0f469b..809c305ed82d1 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/config.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/config.rs @@ -15,6 +15,7 @@ pub struct CompletionConfig { pub enable_self_on_the_fly: bool, pub enable_private_editable: bool, pub enable_term_search: bool, + pub term_search_fuel: u64, pub full_function_signatures: bool, pub callable: Option, pub snippet_cap: Option, 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 8b435f419c713..db34beadc0fa5 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs @@ -466,7 +466,7 @@ impl CompletionContext<'_> { cov_mark::hit!(completes_if_lifetime_without_idents); TextRange::at(self.original_token.text_range().start(), TextSize::from(1)) } - IDENT | LIFETIME_IDENT | UNDERSCORE => self.original_token.text_range(), + IDENT | LIFETIME_IDENT | UNDERSCORE | INT_NUMBER => self.original_token.text_range(), _ if kind.is_keyword() => self.original_token.text_range(), _ => TextRange::empty(self.position.offset), } 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 ca0424809edeb..7fa31e2757db3 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs @@ -368,7 +368,7 @@ fn render_resolution_pat( import_to_add: Option, resolution: ScopeDef, ) -> Builder { - let _p = tracing::span!(tracing::Level::INFO, "render_resolution").entered(); + let _p = tracing::span!(tracing::Level::INFO, "render_resolution_pat").entered(); use hir::ModuleDef::*; if let ScopeDef::ModuleDef(Macro(mac)) = resolution { @@ -386,7 +386,7 @@ fn render_resolution_path( import_to_add: Option, resolution: ScopeDef, ) -> Builder { - let _p = tracing::span!(tracing::Level::INFO, "render_resolution").entered(); + let _p = tracing::span!(tracing::Level::INFO, "render_resolution_path").entered(); use hir::ModuleDef::*; match resolution { @@ -494,7 +494,7 @@ fn render_resolution_simple_( import_to_add: Option, resolution: ScopeDef, ) -> Builder { - let _p = tracing::span!(tracing::Level::INFO, "render_resolution").entered(); + let _p = tracing::span!(tracing::Level::INFO, "render_resolution_simple_").entered(); let db = ctx.db(); let ctx = ctx.import_to_add(import_to_add); @@ -1730,6 +1730,51 @@ fn foo(a: A) { B { bar: a.$0 }; } ) } + #[test] + fn tuple_field_detail() { + check( + r#" +struct S(i32); + +fn f() -> i32 { + let s = S(0); + s.0$0 +} +"#, + SymbolKind::Field, + expect![[r#" + [ + CompletionItem { + label: "0", + source_range: 56..57, + delete: 56..57, + insert: "0", + kind: SymbolKind( + Field, + ), + detail: "i32", + relevance: CompletionRelevance { + exact_name_match: false, + type_match: Some( + Exact, + ), + is_local: false, + is_item_from_trait: false, + is_item_from_notable_trait: 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, + }, + }, + ] + "#]], + ); + } + #[test] fn record_field_and_call_relevances() { check_relevance( @@ -1808,8 +1853,7 @@ fn f() { A { bar: b$0 }; } fn baz() [type] ex baz() [type] ex bar() [type] - ex A { bar: baz() }.bar [type] - ex A { bar: bar() }.bar [type] + ex A { bar: ... }.bar [type] st A [] fn f() [] "#]], @@ -1947,8 +1991,8 @@ fn main() { } "#, expect![[r#" - ex core::ops::Deref::deref(&T(S)) (use core::ops::Deref) [type_could_unify] ex core::ops::Deref::deref(&t) (use core::ops::Deref) [type_could_unify] + ex core::ops::Deref::deref(&T(S)) (use core::ops::Deref) [type_could_unify] lc m [local] lc t [local] lc &t [type+local] @@ -1997,8 +2041,8 @@ fn main() { } "#, expect![[r#" - ex core::ops::DerefMut::deref_mut(&mut T(S)) (use core::ops::DerefMut) [type_could_unify] ex core::ops::DerefMut::deref_mut(&mut t) (use core::ops::DerefMut) [type_could_unify] + ex core::ops::DerefMut::deref_mut(&mut T(S)) (use core::ops::DerefMut) [type_could_unify] lc m [local] lc t [local] lc &mut t [type+local] diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs index f52a5f7625557..9c5cb1e37d967 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs @@ -27,7 +27,7 @@ pub(crate) fn render_variant_lit( variant: hir::Variant, path: Option, ) -> Option { - let _p = tracing::span!(tracing::Level::INFO, "render_enum_variant").entered(); + let _p = tracing::span!(tracing::Level::INFO, "render_variant_lit").entered(); let db = ctx.db(); let name = local_name.unwrap_or_else(|| variant.name(db)); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs index 540cfd03d6061..8b81a95abbeef 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs @@ -27,7 +27,7 @@ pub(crate) fn render_macro_pat( name: hir::Name, macro_: hir::Macro, ) -> Builder { - let _p = tracing::span!(tracing::Level::INFO, "render_macro").entered(); + let _p = tracing::span!(tracing::Level::INFO, "render_macro_pat").entered(); render(ctx, false, false, false, name, macro_) } 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 1f032c7df480d..70e0aa4e9a919 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs @@ -80,6 +80,7 @@ pub(crate) const TEST_CONFIG: CompletionConfig = CompletionConfig { }, snippets: Vec::new(), limit: None, + term_search_fuel: 200, }; pub(crate) fn completion_list(ra_fixture: &str) -> String { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/predicate.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/predicate.rs index 64a32dee3d733..62eb642b3bc33 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/predicate.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/predicate.rs @@ -19,7 +19,7 @@ struct Foo<'lt, T, const C: usize> where $0 {} en Enum Enum ma makro!(…) macro_rules! makro md module - st Foo<…> Foo<'{error}, {unknown}, _> + st Foo<…> Foo<{unknown}, _> st Record Record st Tuple Tuple st Unit Unit @@ -92,7 +92,7 @@ struct Foo<'lt, T, const C: usize> where for<'a> $0 {} en Enum Enum ma makro!(…) macro_rules! makro md module - st Foo<…> Foo<'{error}, {unknown}, _> + st Foo<…> Foo<{unknown}, _> st Record Record st Tuple Tuple st Unit Unit diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs index 66f1bff7c1c1a..ff38c16108791 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs @@ -20,8 +20,8 @@ struct Foo<'lt, T, const C: usize> { en Enum Enum ma makro!(…) macro_rules! makro md module - sp Self Foo<'{error}, {unknown}, _> - st Foo<…> Foo<'{error}, {unknown}, _> + sp Self Foo<{unknown}, _> + st Foo<…> Foo<{unknown}, _> st Record Record st Tuple Tuple st Unit Unit @@ -45,8 +45,8 @@ struct Foo<'lt, T, const C: usize>(f$0); en Enum Enum ma makro!(…) macro_rules! makro md module - sp Self Foo<'{error}, {unknown}, _> - st Foo<…> Foo<'{error}, {unknown}, _> + sp Self Foo<{unknown}, _> + st Foo<…> Foo<{unknown}, _> st Record Record st Tuple Tuple st Unit Unit 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 c0f0faba35cdf..634277e869868 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs @@ -11,8 +11,8 @@ 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, ToolModule, Trait, TraitAlias, TupleField, - TypeAlias, Variant, VariantDef, Visibility, + ModuleDef, Name, PathResolution, Semantics, Static, StaticLifetime, ToolModule, Trait, + TraitAlias, TupleField, TypeAlias, Variant, VariantDef, Visibility, }; use stdx::{format_to, impl_from}; use syntax::{ @@ -39,12 +39,13 @@ pub enum Definition { Trait(Trait), TraitAlias(TraitAlias), TypeAlias(TypeAlias), - BuiltinType(BuiltinType), SelfType(Impl), GenericParam(GenericParam), Local(Local), Label(Label), DeriveHelper(DeriveHelper), + BuiltinType(BuiltinType), + BuiltinLifetime(StaticLifetime), BuiltinAttr(BuiltinAttr), ToolModule(ToolModule), ExternCrateDecl(ExternCrateDecl), @@ -83,6 +84,7 @@ impl Definition { Definition::DeriveHelper(it) => it.derive().module(db), Definition::BuiltinAttr(_) | Definition::BuiltinType(_) + | Definition::BuiltinLifetime(_) | Definition::TupleField(_) | Definition::ToolModule(_) => return None, }; @@ -112,6 +114,7 @@ impl Definition { Definition::BuiltinType(_) | Definition::TupleField(_) => Visibility::Public, Definition::Macro(_) => return None, Definition::BuiltinAttr(_) + | Definition::BuiltinLifetime(_) | Definition::ToolModule(_) | Definition::SelfType(_) | Definition::Local(_) @@ -141,6 +144,7 @@ impl Definition { Definition::Local(it) => it.name(db), Definition::GenericParam(it) => it.name(db), Definition::Label(it) => it.name(db), + Definition::BuiltinLifetime(StaticLifetime) => hir::known::STATIC_LIFETIME, Definition::BuiltinAttr(_) => return None, // FIXME Definition::ToolModule(_) => return None, // FIXME Definition::DeriveHelper(it) => it.name(db), @@ -174,6 +178,7 @@ impl Definition { doc_owner.docs(fd.0.db) }) } + Definition::BuiltinLifetime(StaticLifetime) => None, Definition::Local(_) => None, Definition::SelfType(impl_def) => { impl_def.self_ty(db).as_adt().map(|adt| adt.docs(db))? @@ -228,6 +233,7 @@ impl Definition { Definition::TraitAlias(it) => it.display(db).to_string(), Definition::TypeAlias(it) => it.display(db).to_string(), Definition::BuiltinType(it) => it.name().display(db).to_string(), + Definition::BuiltinLifetime(it) => it.name().display(db).to_string(), Definition::Local(it) => { let ty = it.ty(db); let ty_display = ty.display_truncated(db, None); @@ -693,6 +699,9 @@ impl NameRefClass { ) -> Option { let _p = tracing::span!(tracing::Level::INFO, "NameRefClass::classify_lifetime", ?lifetime) .entered(); + if lifetime.text() == "'static" { + return Some(NameRefClass::Definition(Definition::BuiltinLifetime(StaticLifetime))); + } let parent = lifetime.syntax().parent()?; match parent.kind() { SyntaxKind::BREAK_EXPR | SyntaxKind::CONTINUE_EXPR => { diff --git a/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs index c597555a3bf66..766bfcf4d0947 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs @@ -209,8 +209,7 @@ impl ImportAssets { prefer_no_std: bool, prefer_prelude: bool, ) -> impl Iterator { - let _p = - tracing::span!(tracing::Level::INFO, "import_assets::search_for_imports").entered(); + let _p = tracing::span!(tracing::Level::INFO, "ImportAssets::search_for_imports").entered(); self.search_for(sema, Some(prefix_kind), prefer_no_std, prefer_prelude) } @@ -221,7 +220,7 @@ impl ImportAssets { prefer_no_std: bool, prefer_prelude: bool, ) -> impl Iterator { - let _p = tracing::span!(tracing::Level::INFO, "import_assets::search_for_relative_paths") + let _p = tracing::span!(tracing::Level::INFO, "ImportAssets::search_for_relative_paths") .entered(); self.search_for(sema, None, prefer_no_std, prefer_prelude) } @@ -263,7 +262,7 @@ impl ImportAssets { prefer_no_std: bool, prefer_prelude: bool, ) -> impl Iterator { - let _p = tracing::span!(tracing::Level::INFO, "import_assets::search_for").entered(); + let _p = tracing::span!(tracing::Level::INFO, "ImportAssets::search_for").entered(); let scope = match sema.scope(&self.candidate_node) { Some(it) => it, @@ -308,7 +307,7 @@ impl ImportAssets { } fn scope_definitions(&self, sema: &Semantics<'_, RootDatabase>) -> FxHashSet { - let _p = tracing::span!(tracing::Level::INFO, "import_assets::scope_definitions").entered(); + let _p = tracing::span!(tracing::Level::INFO, "ImportAssets::scope_definitions").entered(); let mut scope_definitions = FxHashSet::default(); if let Some(scope) = sema.scope(&self.candidate_node) { scope.process_all_names(&mut |_, scope_def| { @@ -327,7 +326,7 @@ fn path_applicable_imports( scope_filter: impl Fn(ItemInNs) -> bool + Copy, ) -> FxHashSet { let _p = - tracing::span!(tracing::Level::INFO, "import_assets::path_applicable_imports").entered(); + tracing::span!(tracing::Level::INFO, "ImportAssets::path_applicable_imports").entered(); match &path_candidate.qualifier { None => { @@ -374,7 +373,7 @@ fn import_for_item( original_item: ItemInNs, scope_filter: impl Fn(ItemInNs) -> bool, ) -> Option { - let _p = tracing::span!(tracing::Level::INFO, "import_assets::import_for_item").entered(); + let _p = tracing::span!(tracing::Level::INFO, "ImportAssets::import_for_item").entered(); let [first_segment, ..] = unresolved_qualifier else { return None }; let item_as_assoc = item_as_assoc(db, original_item); @@ -508,8 +507,7 @@ fn trait_applicable_items( mod_path: impl Fn(ItemInNs) -> Option, scope_filter: impl Fn(hir::Trait) -> bool, ) -> FxHashSet { - let _p = - tracing::span!(tracing::Level::INFO, "import_assets::trait_applicable_items").entered(); + let _p = tracing::span!(tracing::Level::INFO, "ImportAssets::trait_applicable_items").entered(); let db = sema.db; diff --git a/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs index e97f1b86143d1..026d4e36f97bd 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs @@ -194,7 +194,7 @@ fn insert_use_with_alias_option( cfg: &InsertUseConfig, alias: Option, ) { - let _p = tracing::span!(tracing::Level::INFO, "insert_use").entered(); + let _p = tracing::span!(tracing::Level::INFO, "insert_use_with_alias_option").entered(); let mut mb = match cfg.granularity { ImportGranularity::Crate => Some(MergeBehavior::Crate), ImportGranularity::Module => Some(MergeBehavior::Module), diff --git a/src/tools/rust-analyzer/crates/ide-db/src/prime_caches.rs b/src/tools/rust-analyzer/crates/ide-db/src/prime_caches.rs index 024e8f6ae39d2..58077f636b61c 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/prime_caches.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/prime_caches.rs @@ -1,6 +1,6 @@ //! rust-analyzer is lazy and doesn't compute anything unless asked. This //! sometimes is counter productive when, for example, the first goto definition -//! request takes longer to compute. This modules implemented prepopulation of +//! request takes longer to compute. This module implements prepopulation of //! various caches, it's not really advanced at the moment. mod topologic_sort; @@ -32,7 +32,7 @@ pub fn parallel_prime_caches( num_worker_threads: u8, cb: &(dyn Fn(ParallelPrimeCachesProgress) + Sync), ) { - let _p = tracing::span!(tracing::Level::INFO, "prime_caches").entered(); + let _p = tracing::span!(tracing::Level::INFO, "parallel_prime_caches").entered(); let graph = db.crate_graph(); let mut crates_to_prime = { 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 6a7042988a9c4..288d56b534e9f 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/rename.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/rename.rs @@ -196,11 +196,12 @@ impl Definition { .and_then(syn_ctx_is_root) } } - Definition::BuiltinType(_) => return None, - Definition::SelfType(_) => return None, - Definition::BuiltinAttr(_) => return None, - Definition::ToolModule(_) => return None, - Definition::TupleField(_) => return None, + Definition::BuiltinType(_) + | Definition::BuiltinLifetime(_) + | Definition::BuiltinAttr(_) + | Definition::SelfType(_) + | Definition::ToolModule(_) + | Definition::TupleField(_) => 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 cb103313c9e7a..8f633065f3cf7 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/search.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/search.rs @@ -1,7 +1,7 @@ //! Implementation of find-usages functionality. //! //! It is based on the standard ide trick: first, we run a fast text search to -//! get a super-set of matches. Then, we we confirm each match using precise +//! get a super-set of matches. Then, we confirm each match using precise //! name resolution. use std::mem; diff --git a/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs b/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs index c65467a43249b..12085f9ebd252 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs @@ -301,8 +301,8 @@ impl SymbolIndex { } fn range_to_map_value(start: usize, end: usize) -> u64 { - debug_assert![start <= (std::u32::MAX as usize)]; - debug_assert![end <= (std::u32::MAX as usize)]; + debug_assert![start <= (u32::MAX as usize)]; + debug_assert![end <= (u32::MAX as usize)]; ((start as u64) << 32) | end as u64 } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs index 241fddbb90635..b3dde977b1c4f 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs @@ -7,7 +7,7 @@ use ide_db::{ helpers::mod_path_to_ast, imports::insert_use::{insert_use, ImportScope}, source_change::SourceChangeBuilder, - RootDatabase, + FxHashMap, RootDatabase, }; use itertools::Itertools; use stdx::{format_to, never}; @@ -22,15 +22,22 @@ use crate::{fix, Diagnostic, DiagnosticCode, DiagnosticsConfig, Severity}; #[derive(Default)] struct State { result: String, - struct_counts: usize, has_serialize: bool, has_deserialize: bool, + names: FxHashMap, } impl State { - fn generate_new_name(&mut self) -> ast::Name { - self.struct_counts += 1; - make::name(&format!("Struct{}", self.struct_counts)) + fn generate_new_name(&mut self, name: &str) -> ast::Name { + let name = stdx::to_camel_case(name); + let count = if let Some(count) = self.names.get_mut(&name) { + *count += 1; + *count + } else { + self.names.insert(name.clone(), 1); + 1 + }; + make::name(&format!("{}{}", name, count)) } fn serde_derive(&self) -> String { @@ -52,15 +59,21 @@ impl State { } } - fn build_struct(&mut self, value: &serde_json::Map) -> ast::Type { - let name = self.generate_new_name(); + fn build_struct( + &mut self, + name: &str, + value: &serde_json::Map, + ) -> ast::Type { + let name = self.generate_new_name(name); let ty = make::ty(&name.to_string()); let strukt = make::struct_( None, name, None, make::record_field_list(value.iter().sorted_unstable_by_key(|x| x.0).map( - |(name, value)| make::record_field(None, make::name(name), self.type_of(value)), + |(name, value)| { + make::record_field(None, make::name(name), self.type_of(name, value)) + }, )) .into(), ); @@ -68,7 +81,7 @@ impl State { ty } - fn type_of(&mut self, value: &serde_json::Value) -> ast::Type { + fn type_of(&mut self, name: &str, value: &serde_json::Value) -> ast::Type { match value { serde_json::Value::Null => make::ty_unit(), serde_json::Value::Bool(_) => make::ty("bool"), @@ -76,12 +89,12 @@ impl State { serde_json::Value::String(_) => make::ty("String"), serde_json::Value::Array(it) => { let ty = match it.iter().next() { - Some(x) => self.type_of(x), + Some(x) => self.type_of(name, x), None => make::ty_placeholder(), }; make::ty(&format!("Vec<{ty}>")) } - serde_json::Value::Object(x) => self.build_struct(x), + serde_json::Value::Object(x) => self.build_struct(name, x), } } } @@ -113,7 +126,7 @@ pub(crate) fn json_in_items( let serialize_resolved = scope_resolve("::serde::Serialize"); state.has_deserialize = deserialize_resolved.is_some(); state.has_serialize = serialize_resolved.is_some(); - state.build_struct(&it); + state.build_struct("Root", &it); edit.insert(range.start(), state.result); acc.push( Diagnostic::new( @@ -218,7 +231,7 @@ mod tests { } #[derive(Serialize)] - struct Struct1{ bar: f64, bay: i64, baz: (), r#box: bool, foo: String } + struct Root1{ bar: f64, bay: i64, baz: (), r#box: bool, foo: String } "#, ); @@ -237,9 +250,44 @@ mod tests { } "#, r#" - struct Struct3{ } - struct Struct2{ kind: String, value: Struct3 } - struct Struct1{ bar: Struct2, foo: String } + struct Value1{ } + struct Bar1{ kind: String, value: Value1 } + struct Root1{ bar: Bar1, foo: String } + + "#, + ); + } + + #[test] + fn naming() { + check_fix( + r#" + {$0 + "user": { + "address": { + "street": "Main St", + "house": 3 + }, + "email": "example@example.com" + }, + "another_user": { + "user": { + "address": { + "street": "Main St", + "house": 3 + }, + "email": "example@example.com" + } + } + } + "#, + r#" + struct Address1{ house: i64, street: String } + struct User1{ address: Address1, email: String } + struct AnotherUser1{ user: User1 } + struct Address2{ house: i64, street: String } + struct User2{ address: Address2, email: String } + struct Root1{ another_user: AnotherUser1, user: User2 } "#, ); @@ -276,9 +324,9 @@ mod tests { use serde::Deserialize; #[derive(Serialize, Deserialize)] - struct Struct2{ x: i64, y: i64 } + struct OfObject1{ x: i64, y: i64 } #[derive(Serialize, Deserialize)] - struct Struct1{ empty: Vec<_>, nested: Vec>>, of_object: Vec, of_string: Vec } + struct Root1{ empty: Vec<_>, nested: Vec>>, of_object: Vec, of_string: Vec } "#, ); diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs index 8d77e566edc9c..5a3206445c547 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs @@ -1,5 +1,5 @@ use either::Either; -use hir::{db::ExpandDatabase, HasSource, HirDisplay, HirFileIdExt, Semantics}; +use hir::{db::ExpandDatabase, HasSource, HirDisplay, HirFileIdExt, Semantics, VariantId}; use ide_db::{base_db::FileId, source_change::SourceChange, RootDatabase}; use syntax::{ ast::{self, edit::IndentLevel, make}, @@ -25,7 +25,10 @@ pub(crate) fn no_such_field(ctx: &DiagnosticsContext<'_>, d: &hir::NoSuchField) } else { Diagnostic::new_with_syntax_node_ptr( ctx, - DiagnosticCode::RustcHardError("E0559"), + match d.variant { + VariantId::EnumVariantId(_) => DiagnosticCode::RustcHardError("E0559"), + _ => DiagnosticCode::RustcHardError("E0560"), + }, "no such field", node, ) 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 56c8181e84ce6..656d79dc7324f 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 @@ -1,6 +1,6 @@ use hir::{ db::ExpandDatabase, - term_search::{term_search, TermSearchCtx}, + term_search::{term_search, TermSearchConfig, TermSearchCtx}, ClosureStyle, HirDisplay, }; use ide_db::{ @@ -47,7 +47,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::TypedHole) -> Option sema: &ctx.sema, scope: &scope, goal: d.expected.clone(), - config: Default::default(), + config: TermSearchConfig { fuel: ctx.config.term_search_fuel, ..Default::default() }, }; let paths = term_search(&term_search_ctx); @@ -274,7 +274,7 @@ impl Foo for Baz { } fn asd() -> Bar { let a = Baz; - Foo::foo(a) + Foo::foo(_) } ", ); @@ -363,7 +363,7 @@ impl Foo for A { } fn main() { let a = A; - let c: Bar = Foo::foo(&a); + let c: Bar = Foo::foo(_); }"#, ); } 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 c3ced36a696bc..15543a5d65538 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs @@ -232,6 +232,7 @@ pub struct DiagnosticsConfig { pub insert_use: InsertUseConfig, pub prefer_no_std: bool, pub prefer_prelude: bool, + pub term_search_fuel: u64, } impl DiagnosticsConfig { @@ -256,6 +257,7 @@ impl DiagnosticsConfig { }, prefer_no_std: false, prefer_prelude: true, + term_search_fuel: 400, } } } @@ -297,11 +299,10 @@ pub fn diagnostics( ) -> Vec { let _p = tracing::span!(tracing::Level::INFO, "diagnostics").entered(); let sema = Semantics::new(db); - let parse = db.parse(file_id); let mut res = Vec::new(); // [#34344] Only take first 128 errors to prevent slowing down editor/ide, the number 128 is chosen arbitrarily. - res.extend(parse.errors().into_iter().take(128).map(|err| { + res.extend(db.parse_errors(file_id).as_deref().into_iter().flatten().take(128).map(|err| { Diagnostic::new( DiagnosticCode::RustcHardError("syntax-error"), format!("Syntax Error: {err}"), @@ -340,7 +341,8 @@ pub fn diagnostics( AnyDiagnostic::MacroDefError(d) => handlers::macro_error::macro_def_error(&ctx, &d), AnyDiagnostic::MacroError(d) => handlers::macro_error::macro_error(&ctx, &d), AnyDiagnostic::MacroExpansionParseError(d) => { - res.extend(d.errors.iter().take(32).map(|err| { + // FIXME: Point to the correct error span here, not just the macro-call name + res.extend(d.errors.iter().take(16).map(|err| { { Diagnostic::new( DiagnosticCode::RustcHardError("syntax-error"), diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs index bb5c2b791392d..cd5e95cc1e377 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs @@ -293,6 +293,7 @@ fn minicore_smoke_test() { // This should be ignored since we conditionally remove code which creates single item use with braces config.disabled.insert("unused_braces".to_owned()); config.disabled.insert("unused_variables".to_owned()); + config.disabled.insert("remove-unnecessary-else".to_owned()); check_diagnostics_with_config(config, &source); } 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 64b4ccc5bd81c..8d765dfc91b41 100644 --- a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs +++ b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs @@ -214,8 +214,9 @@ pub(crate) fn resolve_doc_path_for_def( Definition::SelfType(it) => it.resolve_doc_path(db, link, ns), Definition::ExternCrateDecl(it) => it.resolve_doc_path(db, link, ns), Definition::BuiltinAttr(_) - | Definition::ToolModule(_) | Definition::BuiltinType(_) + | Definition::BuiltinLifetime(_) + | Definition::ToolModule(_) | Definition::TupleField(_) | Definition::Local(_) | Definition::GenericParam(_) @@ -648,6 +649,7 @@ fn filename_and_frag_for_def( | Definition::TupleField(_) | Definition::Label(_) | Definition::BuiltinAttr(_) + | Definition::BuiltinLifetime(_) | Definition::ToolModule(_) | Definition::DeriveHelper(_) => return 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 e2d629a02fca8..1ead045788ffa 100644 --- a/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs +++ b/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs @@ -76,8 +76,6 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option< return derive; } - // FIXME: Intermix attribute and bang! expansions - // currently we only recursively expand one of the two types let mut anc = tok.parent_ancestors(); let (name, expanded, kind) = loop { let node = anc.next()?; @@ -86,7 +84,7 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option< if let Some(def) = sema.resolve_attr_macro_call(&item) { break ( def.name(db).display(db).to_string(), - expand_attr_macro_recur(&sema, &item)?, + expand_macro_recur(&sema, &item)?, SyntaxKind::MACRO_ITEMS, ); } @@ -94,11 +92,9 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option< if let Some(mac) = ast::MacroCall::cast(node) { let mut name = mac.path()?.segment()?.name_ref()?.to_string(); name.push('!'); - break ( - name, - expand_macro_recur(&sema, &mac)?, - mac.syntax().parent().map(|it| it.kind()).unwrap_or(SyntaxKind::MACRO_ITEMS), - ); + 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); } }; @@ -112,31 +108,23 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option< fn expand_macro_recur( sema: &Semantics<'_, RootDatabase>, - macro_call: &ast::MacroCall, -) -> Option { - let expanded = sema.expand(macro_call)?.clone_for_update(); - expand(sema, expanded, ast::MacroCall::cast, expand_macro_recur) -} - -fn expand_attr_macro_recur( - sema: &Semantics<'_, RootDatabase>, - item: &ast::Item, + macro_call: &ast::Item, ) -> Option { - let expanded = sema.expand_attr_macro(item)?.clone_for_update(); - expand(sema, expanded, ast::Item::cast, expand_attr_macro_recur) + let expanded = match macro_call { + item @ ast::Item::MacroCall(macro_call) => { + sema.expand_attr_macro(item).or_else(|| sema.expand(macro_call))?.clone_for_update() + } + item => sema.expand_attr_macro(item)?.clone_for_update(), + }; + expand(sema, expanded) } -fn expand( - sema: &Semantics<'_, RootDatabase>, - expanded: SyntaxNode, - f: impl FnMut(SyntaxNode) -> Option, - exp: impl Fn(&Semantics<'_, RootDatabase>, &T) -> Option, -) -> Option { - let children = expanded.descendants().filter_map(f); +fn expand(sema: &Semantics<'_, RootDatabase>, expanded: SyntaxNode) -> Option { + let children = expanded.descendants().filter_map(ast::Item::cast); let mut replacements = Vec::new(); for child in children { - if let Some(new_node) = exp(sema, &child) { + if let Some(new_node) = expand_macro_recur(sema, &child) { // check if the whole original syntax is replaced if expanded == *child.syntax() { return Some(new_node); 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 ddeeca5f7b3e6..76b80fcefa47b 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs @@ -123,7 +123,7 @@ fn try_lookup_include_path( { return None; } - let path = token.value()?; + let path = token.value().ok()?; let file_id = sema.db.resolve_path(AnchoredPath { anchor: file_id, path: &path })?; let size = sema.db.file_text(file_id).len().try_into().ok()?; @@ -179,11 +179,11 @@ fn try_filter_trait_item_definition( AssocItem::Const(..) | AssocItem::TypeAlias(..) => { let trait_ = assoc.implemented_trait(db)?; let name = def.name(db)?; - let discri_value = discriminant(&assoc); + let discriminant_value = discriminant(&assoc); trait_ .items(db) .iter() - .filter(|itm| discriminant(*itm) == discri_value) + .filter(|itm| discriminant(*itm) == discriminant_value) .find_map(|itm| (itm.name(db)? == name).then(|| itm.try_to_nav(db)).flatten()) .map(|it| it.collect()) } diff --git a/src/tools/rust-analyzer/crates/ide/src/hover.rs b/src/tools/rust-analyzer/crates/ide/src/hover.rs index 95de3c88c8aca..bdb56081c3331 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover.rs @@ -15,7 +15,7 @@ use ide_db::{ FxIndexSet, RootDatabase, }; use itertools::{multizip, Itertools}; -use syntax::{ast, match_ast, AstNode, AstToken, SyntaxKind::*, SyntaxNode, T}; +use syntax::{ast, AstNode, SyntaxKind::*, SyntaxNode, T}; use crate::{ doc_links::token_as_doc_comment, @@ -33,7 +33,8 @@ pub struct HoverConfig { pub keywords: bool, pub format: HoverDocFormat, pub max_trait_assoc_items_count: Option, - pub max_struct_field_count: Option, + pub max_fields_count: Option, + pub max_enum_variants_count: Option, } #[derive(Copy, Clone, Debug, PartialEq, Eq)] @@ -297,61 +298,8 @@ fn hover_simple( }) // tokens .or_else(|| { - let mut res = HoverResult::default(); - match_ast! { - match original_token { - ast::String(string) => { - res.markup = Markup::fenced_block_text(format_args!("{}", string.value()?)); - }, - ast::ByteString(string) => { - res.markup = Markup::fenced_block_text(format_args!("{:?}", string.value()?)); - }, - ast::CString(string) => { - let val = string.value()?; - res.markup = Markup::fenced_block_text(format_args!("{}", std::str::from_utf8(val.as_ref()).ok()?)); - }, - ast::Char(char) => { - let mut res = HoverResult::default(); - res.markup = Markup::fenced_block_text(format_args!("{}", char.value()?)); - }, - ast::Byte(byte) => { - res.markup = Markup::fenced_block_text(format_args!("0x{:X}", byte.value()?)); - }, - ast::FloatNumber(num) => { - res.markup = if num.suffix() == Some("f32") { - match num.value_f32() { - Ok(num) => { - Markup::fenced_block_text(format_args!("{num} (bits: 0x{:X})", num.to_bits())) - }, - Err(e) => { - Markup::fenced_block_text(format_args!("{e}")) - }, - } - } else { - match num.value() { - Ok(num) => { - Markup::fenced_block_text(format_args!("{num} (bits: 0x{:X})", num.to_bits())) - }, - Err(e) => { - Markup::fenced_block_text(format_args!("{e}")) - }, - } - }; - }, - ast::IntNumber(num) => { - res.markup = match num.value() { - Ok(num) => { - Markup::fenced_block_text(format_args!("{num} (0x{num:X}|0b{num:b})")) - }, - Err(e) => { - Markup::fenced_block_text(format_args!("{e}")) - }, - }; - }, - _ => return None - } - } - Some(res) + render::literal(sema, original_token.clone()) + .map(|markup| HoverResult { markup, actions: vec![] }) }); result.map(|mut res: HoverResult| { 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 3f0fc851344ef..3bc17f95e70d1 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs @@ -17,11 +17,7 @@ use ide_db::{ }; use itertools::Itertools; use stdx::format_to; -use syntax::{ - algo, - ast::{self, RecordPat}, - match_ast, AstNode, Direction, SyntaxToken, T, -}; +use syntax::{algo, ast, match_ast, AstNode, AstToken, Direction, SyntaxToken, T}; use crate::{ doc_links::{remove_links, rewrite_links}, @@ -276,7 +272,7 @@ pub(super) fn keyword( pub(super) fn struct_rest_pat( sema: &Semantics<'_, RootDatabase>, _config: &HoverConfig, - pattern: &RecordPat, + pattern: &ast::RecordPat, ) -> HoverResult { let missing_fields = sema.record_pattern_missing_fields(pattern); @@ -411,8 +407,21 @@ pub(super) fn definition( Definition::Trait(trait_) => { trait_.display_limited(db, config.max_trait_assoc_items_count).to_string() } - Definition::Adt(Adt::Struct(struct_)) => { - struct_.display_limited(db, config.max_struct_field_count).to_string() + Definition::Adt(adt @ (Adt::Struct(_) | Adt::Union(_))) => { + adt.display_limited(db, config.max_fields_count).to_string() + } + Definition::Variant(variant) => { + variant.display_limited(db, config.max_fields_count).to_string() + } + Definition::Adt(adt @ Adt::Enum(_)) => { + adt.display_limited(db, config.max_enum_variants_count).to_string() + } + Definition::SelfType(impl_def) => { + let self_ty = &impl_def.self_ty(db); + match self_ty.as_adt() { + Some(adt) => adt.display_limited(db, config.max_fields_count).to_string(), + None => self_ty.display(db).to_string(), + } } Definition::Macro(it) => { let mut label = it.display(db).to_string(); @@ -513,6 +522,60 @@ pub(super) fn definition( markup(docs.map(Into::into), desc, mod_path) } +pub(super) fn literal(sema: &Semantics<'_, RootDatabase>, token: SyntaxToken) -> Option { + let lit = token.parent().and_then(ast::Literal::cast)?; + let ty = if let Some(p) = lit.syntax().parent().and_then(ast::Pat::cast) { + sema.type_of_pat(&p)? + } else { + sema.type_of_expr(&ast::Expr::Literal(lit))? + } + .original; + + let value = match_ast! { + match token { + ast::String(string) => string.value().as_ref().map_err(|e| format!("{e:?}")).map(ToString::to_string), + ast::ByteString(string) => string.value().as_ref().map_err(|e| format!("{e:?}")).map(|it| format!("{it:?}")), + ast::CString(string) => string.value().as_ref().map_err(|e| format!("{e:?}")).map(|it| std::str::from_utf8(it).map_or_else(|e| format!("{e:?}"), ToOwned::to_owned)), + ast::Char(char) => char .value().as_ref().map_err(|e| format!("{e:?}")).map(ToString::to_string), + ast::Byte(byte) => byte .value().as_ref().map_err(|e| format!("{e:?}")).map(|it| format!("0x{it:X}")), + ast::FloatNumber(num) => { + let (text, _) = num.split_into_parts(); + let text = text.replace('_', ""); + if ty.as_builtin().map(|it| it.is_f32()).unwrap_or(false) { + match text.parse::() { + Ok(num) => Ok(format!("{num} (bits: 0x{:X})", num.to_bits())), + Err(e) => Err(e.to_string()), + } + } else { + match text.parse::() { + Ok(num) => Ok(format!("{num} (bits: 0x{:X})", num.to_bits())), + Err(e) => Err(e.to_string()), + } + } + }, + ast::IntNumber(num) => match num.value() { + Ok(num) => Ok(format!("{num} (0x{num:X}|0b{num:b})")), + Err(e) => Err(e.to_string()), + }, + _ => return None + } + }; + let ty = ty.display(sema.db); + + let mut s = format!("```rust\n{ty}\n```\n___\n\n"); + match value { + Ok(value) => { + if let Some(newline) = value.find('\n') { + format_to!(s, "value of literal (truncated up to newline): {}", &value[..newline]) + } else { + format_to!(s, "value of literal: {value}") + } + } + Err(error) => format_to!(s, "invalid literal: {error}"), + } + Some(s.into()) +} + fn render_notable_trait_comment( db: &RootDatabase, notable_traits: &[(Trait, Vec<(Option, Name)>)], @@ -693,15 +756,7 @@ fn closure_ty( } fn definition_mod_path(db: &RootDatabase, def: &Definition) -> Option { - if matches!( - def, - Definition::GenericParam(_) - | Definition::BuiltinType(_) - | Definition::Local(_) - | Definition::Label(_) - | Definition::BuiltinAttr(_) - | Definition::ToolModule(_) - ) { + if matches!(def, Definition::GenericParam(_) | Definition::Local(_) | Definition::Label(_)) { return None; } def.module(db).map(|module| path(db, module, definition_owner_name(db, def))) 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 6bbc8b380d6c7..20d07bf991931 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs @@ -18,7 +18,8 @@ const HOVER_BASE_CONFIG: HoverConfig = HoverConfig { format: HoverDocFormat::Markdown, keywords: true, max_trait_assoc_items_count: None, - max_struct_field_count: None, + max_fields_count: Some(5), + max_enum_variants_count: Some(5), }; fn check_hover_no_result(ra_fixture: &str) { @@ -51,13 +52,43 @@ fn check(ra_fixture: &str, expect: Expect) { } #[track_caller] -fn check_hover_struct_limit(count: usize, ra_fixture: &str, expect: Expect) { +fn check_hover_fields_limit( + fields_count: impl Into>, + ra_fixture: &str, + expect: Expect, +) { let (analysis, position) = fixture::position(ra_fixture); let hover = analysis .hover( &HoverConfig { links_in_hover: true, - max_struct_field_count: Some(count), + max_fields_count: fields_count.into(), + ..HOVER_BASE_CONFIG + }, + FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) }, + ) + .unwrap() + .unwrap(); + + let content = analysis.db.file_text(position.file_id); + let hovered_element = &content[hover.range]; + + let actual = format!("*{hovered_element}*\n{}\n", hover.info.markup); + expect.assert_eq(&actual) +} + +#[track_caller] +fn check_hover_enum_variants_limit( + variants_count: impl Into>, + ra_fixture: &str, + expect: Expect, +) { + let (analysis, position) = fixture::position(ra_fixture); + let hover = analysis + .hover( + &HoverConfig { + links_in_hover: true, + max_enum_variants_count: variants_count.into(), ..HOVER_BASE_CONFIG }, FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) }, @@ -876,7 +907,9 @@ struct Foo$0 { field: u32 } ```rust // size = 4, align = 4 - struct Foo + struct Foo { + field: u32, + } ``` "#]], ); @@ -896,6 +929,9 @@ struct Foo$0 where u32: Copy { field: u32 } struct Foo where u32: Copy, + { + field: u32, + } ``` "#]], ); @@ -903,7 +939,7 @@ struct Foo$0 where u32: Copy { field: u32 } #[test] fn hover_record_struct_limit() { - check_hover_struct_limit( + check_hover_fields_limit( 3, r#" struct Foo$0 { a: u32, b: i32, c: i32 } @@ -917,7 +953,7 @@ fn hover_record_struct_limit() { ```rust // size = 12 (0xC), align = 4 - struct Foo { + struct Foo { a: u32, b: i32, c: i32, @@ -925,7 +961,7 @@ fn hover_record_struct_limit() { ``` "#]], ); - check_hover_struct_limit( + check_hover_fields_limit( 3, r#" struct Foo$0 { a: u32 } @@ -939,13 +975,13 @@ fn hover_record_struct_limit() { ```rust // size = 4, align = 4 - struct Foo { + struct Foo { a: u32, } ``` "#]], ); - check_hover_struct_limit( + check_hover_fields_limit( 3, r#" struct Foo$0 { a: u32, b: i32, c: i32, d: u32 } @@ -959,7 +995,7 @@ fn hover_record_struct_limit() { ```rust // size = 16 (0x10), align = 4 - struct Foo { + struct Foo { a: u32, b: i32, c: i32, @@ -968,6 +1004,338 @@ fn hover_record_struct_limit() { ``` "#]], ); + check_hover_fields_limit( + None, + r#" + struct Foo$0 { a: u32, b: i32, c: i32 } + "#, + expect![[r#" + *Foo* + + ```rust + test + ``` + + ```rust + // size = 12 (0xC), align = 4 + struct Foo + ``` + "#]], + ); + check_hover_fields_limit( + 0, + r#" + struct Foo$0 { a: u32, b: i32, c: i32 } + "#, + expect![[r#" + *Foo* + + ```rust + test + ``` + + ```rust + // size = 12 (0xC), align = 4 + struct Foo { /* … */ } + ``` + "#]], + ); + + // No extra spaces within `{}` when there are no fields + check_hover_fields_limit( + 5, + r#" + struct Foo$0 {} + "#, + expect![[r#" + *Foo* + + ```rust + test + ``` + + ```rust + // size = 0, align = 1 + struct Foo {} + ``` + "#]], + ); +} + +#[test] +fn hover_record_variant_limit() { + check_hover_fields_limit( + 3, + r#" + enum Foo { A$0 { a: u32, b: i32, c: i32 } } + "#, + expect![[r#" + *A* + + ```rust + test::Foo + ``` + + ```rust + // size = 12 (0xC), align = 4 + A { a: u32, b: i32, c: i32, } + ``` + "#]], + ); + check_hover_fields_limit( + 3, + r#" + enum Foo { A$0 { a: u32 } } + "#, + expect![[r#" + *A* + + ```rust + test::Foo + ``` + + ```rust + // size = 4, align = 4 + A { a: u32, } + ``` + "#]], + ); + check_hover_fields_limit( + 3, + r#" + enum Foo { A$0 { a: u32, b: i32, c: i32, d: u32 } } + "#, + expect![[r#" + *A* + + ```rust + test::Foo + ``` + + ```rust + // size = 16 (0x10), align = 4 + A { a: u32, b: i32, c: i32, /* … */ } + ``` + "#]], + ); + check_hover_fields_limit( + None, + r#" + enum Foo { A$0 { a: u32, b: i32, c: i32 } } + "#, + expect![[r#" + *A* + + ```rust + test::Foo + ``` + + ```rust + // size = 12 (0xC), align = 4 + A + ``` + "#]], + ); + check_hover_fields_limit( + 0, + r#" + enum Foo { A$0 { a: u32, b: i32, c: i32 } } + "#, + expect![[r#" + *A* + + ```rust + test::Foo + ``` + + ```rust + // size = 12 (0xC), align = 4 + A { /* … */ } + ``` + "#]], + ); +} + +#[test] +fn hover_enum_limit() { + check_hover_enum_variants_limit( + 5, + r#"enum Foo$0 { A, B }"#, + expect![[r#" + *Foo* + + ```rust + test + ``` + + ```rust + // size = 1, align = 1, niches = 254 + enum Foo { + A, + B, + } + ``` + "#]], + ); + check_hover_enum_variants_limit( + 1, + r#"enum Foo$0 { A, B }"#, + expect![[r#" + *Foo* + + ```rust + test + ``` + + ```rust + // size = 1, align = 1, niches = 254 + enum Foo { + A, + /* … */ + } + ``` + "#]], + ); + check_hover_enum_variants_limit( + 0, + r#"enum Foo$0 { A, B }"#, + expect![[r#" + *Foo* + + ```rust + test + ``` + + ```rust + // size = 1, align = 1, niches = 254 + enum Foo { /* … */ } + ``` + "#]], + ); + check_hover_enum_variants_limit( + None, + r#"enum Foo$0 { A, B }"#, + expect![[r#" + *Foo* + + ```rust + test + ``` + + ```rust + // size = 1, align = 1, niches = 254 + enum Foo + ``` + "#]], + ); + check_hover_enum_variants_limit( + 7, + r#"enum Enum$0 { + Variant {}, + Variant2 { field: i32 }, + Variant3 { field: i32, field2: i32 }, + Variant4(), + Variant5(i32), + Variant6(i32, i32), + Variant7, + Variant8, + }"#, + expect![[r#" + *Enum* + + ```rust + test + ``` + + ```rust + // size = 12 (0xC), align = 4, niches = 4294967288 + enum Enum { + Variant {}, + Variant2 { /* … */ }, + Variant3 { /* … */ }, + Variant4(), + Variant5( /* … */ ), + Variant6( /* … */ ), + Variant7, + /* … */ + } + ``` + "#]], + ); +} + +#[test] +fn hover_union_limit() { + check_hover_fields_limit( + 5, + r#"union Foo$0 { a: u32, b: i32 }"#, + expect![[r#" + *Foo* + + ```rust + test + ``` + + ```rust + // size = 4, align = 4 + union Foo { + a: u32, + b: i32, + } + ``` + "#]], + ); + check_hover_fields_limit( + 1, + r#"union Foo$0 { a: u32, b: i32 }"#, + expect![[r#" + *Foo* + + ```rust + test + ``` + + ```rust + // size = 4, align = 4 + union Foo { + a: u32, + /* … */ + } + ``` + "#]], + ); + check_hover_fields_limit( + 0, + r#"union Foo$0 { a: u32, b: i32 }"#, + expect![[r#" + *Foo* + + ```rust + test + ``` + + ```rust + // size = 4, align = 4 + union Foo { /* … */ } + ``` + "#]], + ); + check_hover_fields_limit( + None, + r#"union Foo$0 { a: u32, b: i32 }"#, + expect![[r#" + *Foo* + + ```rust + test + ``` + + ```rust + // size = 4, align = 4 + union Foo + ``` + "#]], + ); } #[test] @@ -1431,11 +1799,14 @@ impl Thing { ``` ```rust - struct Thing + struct Thing { + x: u32, + } ``` "#]], ); - check( + check_hover_fields_limit( + None, r#" struct Thing { x: u32 } impl Thing { @@ -1456,9 +1827,9 @@ impl Thing { ); check( r#" -enum Thing { A } +struct Thing { x: u32 } impl Thing { - pub fn new() -> Self$0 { Thing::A } + fn new() -> Self$0 { Self { x: 0 } } } "#, expect![[r#" @@ -1469,8 +1840,8 @@ impl Thing { ``` ```rust - enum Thing { - A, + struct Thing { + x: u32, } ``` "#]], @@ -1478,23 +1849,44 @@ impl Thing { check( r#" enum Thing { A } +impl Thing { + pub fn new() -> Self$0 { Thing::A } +} +"#, + expect![[r#" + *Self* + + ```rust + test + ``` + + ```rust + enum Thing { + A, + } + ``` + "#]], + ); + check( + r#" +enum Thing { A } impl Thing { pub fn thing(a: Self$0) {} } "#, expect![[r#" - *Self* + *Self* - ```rust - test - ``` + ```rust + test + ``` - ```rust - enum Thing { - A, - } - ``` - "#]], + ```rust + enum Thing { + A, + } + ``` + "#]], ); check( r#" @@ -2382,8 +2774,8 @@ fn test_hover_layout_of_enum() { ```rust // size = 16 (0x10), align = 8, niches = 254 enum Foo { - Variant1(u8, u16), - Variant2(i32, u8, i64), + Variant1( /* … */ ), + Variant2( /* … */ ), } ``` "#]], @@ -4049,7 +4441,7 @@ fn foo() { ```rust 'label ``` - "#]], + "#]], ); } @@ -4063,7 +4455,17 @@ fn hover_lifetime() { ```rust 'lifetime ``` - "#]], + "#]], + ); + check( + r#"fn foo(_: &'static$0 ()) {}"#, + expect![[r#" + *'static* + + ```rust + 'static + ``` + "#]], ); } @@ -6554,7 +6956,7 @@ enum Enum { ```rust // size = 4, align = 4 - RecordV { field: u32 } + RecordV { field: u32, } ``` "#]], ); @@ -7398,9 +7800,12 @@ fn main() { "#, expect![[r#" *"🦀\u{1f980}\\\x41"* - ```text - 🦀🦀\A + ```rust + &str ``` + ___ + + value of literal: 🦀🦀\A "#]], ); check( @@ -7411,9 +7816,34 @@ fn main() { "#, expect![[r#" *r"🦀\u{1f980}\\\x41"* - ```text - 🦀\u{1f980}\\\x41 + ```rust + &str ``` + ___ + + value of literal: 🦀\u{1f980}\\\x41 + "#]], + ); + check( + r#" +fn main() { + $0r"🦀\u{1f980}\\\x41 + + +fsdghs"; +} +"#, + expect![[r#" + *r"🦀\u{1f980}\\\x41 + + + fsdghs"* + ```rust + &str + ``` + ___ + + value of literal (truncated up to newline): 🦀\u{1f980}\\\x41 "#]], ); } @@ -7428,9 +7858,12 @@ fn main() { "#, expect![[r#" *c"🦀\u{1f980}\\\x41"* - ```text - 🦀🦀\A + ```rust + &{unknown} ``` + ___ + + value of literal: 🦀🦀\A "#]], ); } @@ -7445,9 +7878,12 @@ fn main() { "#, expect![[r#" *b"\xF0\x9F\xA6\x80\\"* - ```text - [240, 159, 166, 128, 92] + ```rust + &[u8; 5] ``` + ___ + + value of literal: [240, 159, 166, 128, 92] "#]], ); check( @@ -7458,9 +7894,12 @@ fn main() { "#, expect![[r#" *br"\xF0\x9F\xA6\x80\\"* - ```text - [92, 120, 70, 48, 92, 120, 57, 70, 92, 120, 65, 54, 92, 120, 56, 48, 92, 92] + ```rust + &[u8; 18] ``` + ___ + + value of literal: [92, 120, 70, 48, 92, 120, 57, 70, 92, 120, 65, 54, 92, 120, 56, 48, 92, 92] "#]], ); } @@ -7475,9 +7914,12 @@ fn main() { "#, expect![[r#" *b'\xF0'* - ```text - 0xF0 + ```rust + u8 ``` + ___ + + value of literal: 0xF0 "#]], ); check( @@ -7488,9 +7930,12 @@ fn main() { "#, expect![[r#" *b'\\'* - ```text - 0x5C + ```rust + u8 ``` + ___ + + value of literal: 0x5C "#]], ); } @@ -7505,7 +7950,12 @@ fn main() { "#, expect![[r#" *'\x41'* + ```rust + char + ``` + ___ + value of literal: A "#]], ); check( @@ -7516,7 +7966,12 @@ fn main() { "#, expect![[r#" *'\\'* + ```rust + char + ``` + ___ + value of literal: \ "#]], ); check( @@ -7527,7 +7982,12 @@ fn main() { "#, expect![[r#" *'\u{1f980}'* + ```rust + char + ``` + ___ + value of literal: 🦀 "#]], ); } @@ -7542,9 +8002,12 @@ fn main() { "#, expect![[r#" *1.0* - ```text - 1 (bits: 0x3FF0000000000000) + ```rust + f64 ``` + ___ + + value of literal: 1 (bits: 0x3FF0000000000000) "#]], ); check( @@ -7555,9 +8018,12 @@ fn main() { "#, expect![[r#" *1.0f32* - ```text - 1 (bits: 0x3F800000) + ```rust + f32 ``` + ___ + + value of literal: 1 (bits: 0x3F800000) "#]], ); check( @@ -7568,9 +8034,12 @@ fn main() { "#, expect![[r#" *134e12* - ```text - 134000000000000 (bits: 0x42DE77D399980000) + ```rust + f64 ``` + ___ + + value of literal: 134000000000000 (bits: 0x42DE77D399980000) "#]], ); check( @@ -7581,9 +8050,12 @@ fn main() { "#, expect![[r#" *1523527134274733643531312.0* - ```text - 1523527134274733600000000 (bits: 0x44F429E9249F629B) + ```rust + f64 ``` + ___ + + value of literal: 1523527134274733600000000 (bits: 0x44F429E9249F629B) "#]], ); check( @@ -7594,9 +8066,12 @@ fn main() { "#, expect![[r#" *0.1ea123* - ```text - invalid float literal + ```rust + f64 ``` + ___ + + invalid literal: invalid float literal "#]], ); } @@ -7611,9 +8086,12 @@ fn main() { "#, expect![[r#" *34325236457856836345234* - ```text - 34325236457856836345234 (0x744C659178614489D92|0b111010001001100011001011001000101111000011000010100010010001001110110010010) + ```rust + i32 ``` + ___ + + value of literal: 34325236457856836345234 (0x744C659178614489D92|0b111010001001100011001011001000101111000011000010100010010001001110110010010) "#]], ); check( @@ -7624,9 +8102,12 @@ fn main() { "#, expect![[r#" *134_123424_21* - ```text - 13412342421 (0x31F701A95|0b1100011111011100000001101010010101) + ```rust + i32 ``` + ___ + + value of literal: 13412342421 (0x31F701A95|0b1100011111011100000001101010010101) "#]], ); check( @@ -7637,9 +8118,12 @@ fn main() { "#, expect![[r#" *0x12423423* - ```text - 306328611 (0x12423423|0b10010010000100011010000100011) + ```rust + i32 ``` + ___ + + value of literal: 306328611 (0x12423423|0b10010010000100011010000100011) "#]], ); check( @@ -7650,9 +8134,12 @@ fn main() { "#, expect![[r#" *0b1111_1111* - ```text - 255 (0xFF|0b11111111) + ```rust + i32 ``` + ___ + + value of literal: 255 (0xFF|0b11111111) "#]], ); check( @@ -7663,9 +8150,12 @@ fn main() { "#, expect![[r#" *0o12345* - ```text - 5349 (0x14E5|0b1010011100101) + ```rust + i32 ``` + ___ + + value of literal: 5349 (0x14E5|0b1010011100101) "#]], ); check( @@ -7676,9 +8166,12 @@ fn main() { "#, expect![[r#" *0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_F* - ```text - number too large to fit in target type + ```rust + i32 ``` + ___ + + invalid literal: number too large to fit in target type "#]], ); } @@ -7864,8 +8357,8 @@ impl Iterator for S { file_id: FileId( 1, ), - full_range: 6290..6498, - focus_range: 6355..6361, + full_range: 7791..7999, + focus_range: 7856..7862, name: "Future", kind: Trait, container_name: "future", @@ -7878,8 +8371,8 @@ impl Iterator for S { file_id: FileId( 1, ), - full_range: 7128..7594, - focus_range: 7172..7180, + full_range: 8629..9095, + focus_range: 8673..8681, name: "Iterator", kind: Trait, container_name: "iterator", @@ -7936,7 +8429,9 @@ struct Pedro$0<'a> { ```rust // size = 16 (0x10), align = 8, niches = 1 - struct Pedro<'a> + struct Pedro<'a> { + hola: &str, + } ``` "#]], ) 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 15eecd1b54a01..dda5a005a7230 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs @@ -496,7 +496,7 @@ pub(crate) fn inlay_hints_resolve( config: &InlayHintsConfig, hasher: impl Fn(&InlayHint) -> u64, ) -> Option { - let _p = tracing::span!(tracing::Level::INFO, "inlay_hints").entered(); + let _p = tracing::span!(tracing::Level::INFO, "inlay_hints_resolve").entered(); let sema = Semantics::new(db); let file = sema.parse(file_id); let file = file.syntax(); 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 07b9f9cc1fff6..0cb8c485b2f27 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 @@ -331,6 +331,25 @@ fn main(a: SliceIter<'_, Container>) { ); } + #[test] + fn lt_hints() { + check_types( + r#" +struct S<'lt>; + +fn f<'a>() { + let x = S::<'static>; + //^ S<'static> + let y = S::<'_>; + //^ S + let z = S::<'a>; + //^ S<'a> + +} +"#, + ); + } + #[test] fn fn_hints() { check_types( @@ -341,7 +360,7 @@ fn foo1() -> impl Fn(f64) { loop {} } fn foo2() -> impl Fn(f64, f64) { loop {} } fn foo3() -> impl Fn(f64, f64) -> u32 { loop {} } fn foo4() -> &'static dyn Fn(f64, f64) -> u32 { loop {} } -fn foo5() -> &'static dyn Fn(&'static dyn Fn(f64, f64) -> u32, f64) -> u32 { loop {} } +fn foo5() -> &'static for<'a> dyn Fn(&'a dyn Fn(f64, f64) -> u32, f64) -> u32 { loop {} } fn foo6() -> impl Fn(f64, f64) -> u32 + Sized { loop {} } fn foo7() -> *const (impl Fn(f64, f64) -> u32 + Sized) { loop {} } diff --git a/src/tools/rust-analyzer/crates/ide/src/moniker.rs b/src/tools/rust-analyzer/crates/ide/src/moniker.rs index 08760c0d88cb8..68854c33cefa5 100644 --- a/src/tools/rust-analyzer/crates/ide/src/moniker.rs +++ b/src/tools/rust-analyzer/crates/ide/src/moniker.rs @@ -211,6 +211,7 @@ pub(crate) fn def_to_kind(db: &RootDatabase, def: Definition) -> SymbolInformati } } Definition::BuiltinType(..) => Type, + Definition::BuiltinLifetime(_) => TypeParameter, Definition::SelfType(..) => TypeAlias, Definition::GenericParam(..) => TypeParameter, Definition::Local(it) => { @@ -316,6 +317,7 @@ pub(crate) fn def_to_moniker( Definition::GenericParam(_) | Definition::Label(_) | Definition::DeriveHelper(_) + | Definition::BuiltinLifetime(_) | Definition::BuiltinAttr(_) | Definition::ToolModule(_) => return None, diff --git a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs index 2123c98605db2..fc836d55409d1 100644 --- a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs +++ b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs @@ -235,9 +235,11 @@ impl TryToNav for Definition { 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::BuiltinType(_) | Definition::TupleField(_) => None, - Definition::ToolModule(_) => None, - Definition::BuiltinAttr(_) => None, + Definition::BuiltinLifetime(_) + | Definition::BuiltinType(_) + | Definition::TupleField(_) + | Definition::ToolModule(_) + | Definition::BuiltinAttr(_) => None, // FIXME: The focus range should be set to the helper declaration Definition::DeriveHelper(it) => it.derive().try_to_nav(db), } diff --git a/src/tools/rust-analyzer/crates/ide/src/runnables.rs b/src/tools/rust-analyzer/crates/ide/src/runnables.rs index b6c6753755c87..64ffa59101765 100644 --- a/src/tools/rust-analyzer/crates/ide/src/runnables.rs +++ b/src/tools/rust-analyzer/crates/ide/src/runnables.rs @@ -15,6 +15,7 @@ use ide_db::{ FxHashMap, FxHashSet, RootDatabase, SymbolKind, }; use itertools::Itertools; +use span::TextSize; use stdx::{always, format_to}; use syntax::{ ast::{self, AstNode}, @@ -48,16 +49,15 @@ impl fmt::Display for TestId { #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub enum RunnableKind { - Test { test_id: TestId, attr: TestAttr }, TestMod { path: String }, + Test { test_id: TestId, attr: TestAttr }, Bench { test_id: TestId }, DocTest { test_id: TestId }, Bin, } -#[cfg(test)] -#[derive(Debug, Clone, Hash, PartialEq, Eq)] -enum RunnableTestKind { +#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +enum RunnableDiscKind { Test, TestMod, DocTest, @@ -65,6 +65,18 @@ enum RunnableTestKind { Bin, } +impl RunnableKind { + fn disc(&self) -> RunnableDiscKind { + match self { + RunnableKind::TestMod { .. } => RunnableDiscKind::TestMod, + RunnableKind::Test { .. } => RunnableDiscKind::Test, + RunnableKind::DocTest { .. } => RunnableDiscKind::DocTest, + RunnableKind::Bench { .. } => RunnableDiscKind::Bench, + RunnableKind::Bin => RunnableDiscKind::Bin, + } + } +} + impl Runnable { // test package::module::testname pub fn label(&self, target: Option) -> String { @@ -97,17 +109,6 @@ impl Runnable { s.push_str(suffix); s } - - #[cfg(test)] - fn test_kind(&self) -> RunnableTestKind { - match &self.kind { - RunnableKind::TestMod { .. } => RunnableTestKind::TestMod, - RunnableKind::Test { .. } => RunnableTestKind::Test, - RunnableKind::DocTest { .. } => RunnableTestKind::DocTest, - RunnableKind::Bench { .. } => RunnableTestKind::Bench, - RunnableKind::Bin => RunnableTestKind::Bin, - } - } } // Feature: Run @@ -193,6 +194,20 @@ pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec { r }) })); + res.sort_by(|Runnable { nav, kind, .. }, Runnable { nav: nav_b, kind: kind_b, .. }| { + // full_range.start < focus_range.start < name, should give us a decent unique ordering + nav.full_range + .start() + .cmp(&nav_b.full_range.start()) + .then_with(|| { + let t_0 = || TextSize::from(0); + nav.focus_range + .map_or_else(t_0, |it| it.start()) + .cmp(&nav_b.focus_range.map_or_else(t_0, |it| it.start())) + }) + .then_with(|| kind.disc().cmp(&kind_b.disc())) + .then_with(|| nav.name.cmp(&nav_b.name)) + }); res } @@ -571,13 +586,12 @@ mod tests { fn check(ra_fixture: &str, expect: Expect) { let (analysis, position) = fixture::position(ra_fixture); - let mut runnables = analysis.runnables(position.file_id).unwrap(); - runnables.sort_by_key(|it| (it.nav.full_range.start(), it.nav.name.clone())); - - let result = runnables + let result = analysis + .runnables(position.file_id) + .unwrap() .into_iter() .map(|runnable| { - let mut a = format!("({:?}, {:?}", runnable.test_kind(), runnable.nav); + let mut a = format!("({:?}, {:?}", runnable.kind.disc(), runnable.nav); if runnable.use_name_in_title { a.push_str(", true"); } @@ -609,6 +623,9 @@ fn main() {} #[export_name = "main"] fn __cortex_m_rt_main_trampoline() {} +#[unsafe(export_name = "main")] +fn __cortex_m_rt_main_trampoline_unsafe() {} + #[test] fn test_foo() {} @@ -628,13 +645,14 @@ mod not_a_root { "#, expect![[r#" [ - "(TestMod, NavigationTarget { file_id: FileId(0), full_range: 0..253, name: \"\", kind: Module })", + "(TestMod, NavigationTarget { file_id: FileId(0), full_range: 0..331, name: \"\", kind: Module })", "(Bin, NavigationTarget { file_id: FileId(0), full_range: 1..13, focus_range: 4..8, name: \"main\", kind: Function })", "(Bin, NavigationTarget { file_id: FileId(0), full_range: 15..76, focus_range: 42..71, name: \"__cortex_m_rt_main_trampoline\", kind: Function })", - "(Test, NavigationTarget { file_id: FileId(0), full_range: 78..102, focus_range: 89..97, name: \"test_foo\", kind: Function })", - "(Test, NavigationTarget { file_id: FileId(0), full_range: 104..155, focus_range: 136..150, name: \"test_full_path\", kind: Function })", - "(Test, NavigationTarget { file_id: FileId(0), full_range: 157..191, focus_range: 178..186, name: \"test_foo\", kind: Function })", - "(Bench, NavigationTarget { file_id: FileId(0), full_range: 193..215, focus_range: 205..210, name: \"bench\", kind: Function })", + "(Bin, NavigationTarget { file_id: FileId(0), full_range: 78..154, focus_range: 113..149, name: \"__cortex_m_rt_main_trampoline_unsafe\", kind: Function })", + "(Test, NavigationTarget { file_id: FileId(0), full_range: 156..180, focus_range: 167..175, name: \"test_foo\", kind: Function })", + "(Test, NavigationTarget { file_id: FileId(0), full_range: 182..233, focus_range: 214..228, name: \"test_full_path\", kind: Function })", + "(Test, NavigationTarget { file_id: FileId(0), full_range: 235..269, focus_range: 256..264, name: \"test_foo\", kind: Function })", + "(Bench, NavigationTarget { file_id: FileId(0), full_range: 271..293, focus_range: 283..288, name: \"bench\", kind: Function })", ] "#]], ); diff --git a/src/tools/rust-analyzer/crates/ide/src/static_index.rs b/src/tools/rust-analyzer/crates/ide/src/static_index.rs index ca013da70990f..0d2311b6e9844 100644 --- a/src/tools/rust-analyzer/crates/ide/src/static_index.rs +++ b/src/tools/rust-analyzer/crates/ide/src/static_index.rs @@ -167,7 +167,8 @@ impl StaticIndex<'_> { keywords: true, format: crate::HoverDocFormat::Markdown, max_trait_assoc_items_count: None, - max_struct_field_count: None, + max_fields_count: Some(5), + max_enum_variants_count: Some(5), }; let tokens = tokens.filter(|token| { matches!( diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/escape.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/escape.rs index 5913ca5e454c0..0439e509d21dd 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/escape.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/escape.rs @@ -25,7 +25,7 @@ pub(super) fn highlight_escape_string( } pub(super) fn highlight_escape_char(stack: &mut Highlights, char: &Char, start: TextSize) { - if char.value().is_none() { + if char.value().is_err() { // We do not emit invalid escapes highlighting here. The lexer would likely be in a bad // state and this token contains junks, since `'` is not a reliable delimiter (consider // lifetimes). Nonetheless, parser errors should already be emitted. @@ -48,7 +48,7 @@ pub(super) fn highlight_escape_char(stack: &mut Highlights, char: &Char, start: } pub(super) fn highlight_escape_byte(stack: &mut Highlights, byte: &Byte, start: TextSize) { - if byte.value().is_none() { + if byte.value().is_err() { // See `highlight_escape_char` for why no error highlighting here. return; } 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 a72f505eb850f..3b784ebec3bb9 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 @@ -484,6 +484,7 @@ pub(super) fn highlight_def( h } Definition::BuiltinType(_) => Highlight::new(HlTag::BuiltinType), + Definition::BuiltinLifetime(_) => Highlight::new(HlTag::Symbol(SymbolKind::LifetimeParam)), Definition::Static(s) => { let mut h = Highlight::new(HlTag::Symbol(SymbolKind::Static)); @@ -542,13 +543,14 @@ pub(super) fn highlight_def( let def_crate = def.krate(db); let is_from_other_crate = def_crate != Some(krate); let is_from_builtin_crate = def_crate.map_or(false, |def_crate| def_crate.is_builtin(db)); - let is_builtin_type = matches!(def, Definition::BuiltinType(_)); - let is_public = def.visibility(db) == Some(hir::Visibility::Public); - - match (is_from_other_crate, is_builtin_type, is_public) { - (true, false, _) => h |= HlMod::Library, - (false, _, true) => h |= HlMod::Public, - _ => {} + let is_builtin = matches!( + def, + Definition::BuiltinType(_) | Definition::BuiltinLifetime(_) | Definition::BuiltinAttr(_) + ); + match is_from_other_crate { + true if !is_builtin => h |= HlMod::Library, + false if def.visibility(db) == Some(hir::Visibility::Public) => h |= HlMod::Public, + _ => (), } if is_from_builtin_crate { 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 6bf13ffd06fc1..f9b8a22a3c090 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 @@ -30,7 +30,7 @@ pub(super) fn ra_fixture( if !active_parameter.ident().map_or(false, |name| name.text().starts_with("ra_fixture")) { return None; } - let value = literal.value()?; + let value = literal.value().ok()?; if let Some(range) = literal.open_quote_text_range() { hl.add(HlRange { range, highlight: HlTag::StringLiteral.into(), binding_hash: None }) @@ -299,6 +299,7 @@ fn module_def_to_hl_tag(def: Definition) -> HlTag { Definition::Trait(_) => SymbolKind::Trait, Definition::TraitAlias(_) => SymbolKind::TraitAlias, Definition::TypeAlias(_) => SymbolKind::TypeAlias, + Definition::BuiltinLifetime(_) => SymbolKind::LifetimeParam, Definition::BuiltinType(_) => return HlTag::BuiltinType, Definition::Macro(_) => SymbolKind::Macro, Definition::Field(_) | Definition::TupleField(_) => SymbolKind::Field, diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html index de902b5137d0b..cad5a8b593faa 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html @@ -45,7 +45,7 @@ .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } -
#[allow(dead_code)]
+
#[allow(dead_code)]
 #[rustfmt::skip]
 #[proc_macros::identity]
 #[derive(Default)]
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_default_library.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_default_library.html
index 366895ce7ec70..893e3c0675181 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_default_library.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_default_library.html
@@ -49,5 +49,5 @@
 
 fn main() {
     let foo = Some(92);
-    let nums = iter::repeat(foo.unwrap());
+    let nums = iter::repeat(foo.unwrap());
 }
\ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html index b2ca6e1ca4b66..35650bbe87f69 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html @@ -79,7 +79,7 @@ /// # Examples /// /// ``` - /// # #![allow(unused_mut)] + /// # #![allow(unused_mut)] /// let mut foo: Foo = Foo::new(); /// ``` pub const fn new() -> Foo { @@ -162,12 +162,12 @@ /// /// ``` /// loop {} -#[cfg_attr(not(feature = "false"), doc = "loop {}")] -#[doc = "loop {}"] +#[cfg_attr(not(feature = "false"), doc = "loop {}")] +#[doc = "loop {}"] /// ``` /// -#[cfg_attr(feature = "alloc", doc = "```rust")] -#[cfg_attr(not(feature = "alloc"), doc = "```ignore")] +#[cfg_attr(feature = "alloc", doc = "```rust")] +#[cfg_attr(not(feature = "alloc"), doc = "```ignore")] /// let _ = example(&alloc::vec![1, 2, 3]); /// ``` pub fn mix_and_match() {} diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html index 5234d362c26d4..413edb6d65fa5 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html @@ -49,13 +49,13 @@ mod inner {} pub mod ops { - #[lang = "fn_once"] + #[lang = "fn_once"] pub trait FnOnce<Args> {} - #[lang = "fn_mut"] + #[lang = "fn_mut"] pub trait FnMut<Args>: FnOnce<Args> {} - #[lang = "fn"] + #[lang = "fn"] pub trait Fn<Args>: FnMut<Args> {} } 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 7a07d17b2718d..22706dea1fa6c 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 @@ -158,9 +158,9 @@ println!("{ничоси}", ничоси = 92); println!("{:x?} {} ", thingy, n2); - panic!("{}", 0); + panic!("{}", 0); panic!("more {}", 1); - assert!(true, "{}", 1); + assert!(true, "{}", 1); assert!(true, "{} asdasd", 1); toho!("{}fmt", 0); let i: u64 = 3; diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html index 15d6be6334c6f..be6176894b4c9 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html @@ -69,7 +69,7 @@ unsafe fn unsafe_method(&self) {} } -#[repr(packed)] +#[repr(packed)] struct Packed { a: u16, } 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 31b0c8cdec50e..76940ab57ab79 100644 --- a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs +++ b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs @@ -17,6 +17,7 @@ use itertools::Itertools; use proc_macro_api::{MacroDylib, ProcMacroServer}; use project_model::{CargoConfig, PackageRoot, ProjectManifest, ProjectWorkspace}; use span::Span; +use tracing::{instrument, Level}; use vfs::{file_set::FileSetConfig, loader::Handle, AbsPath, AbsPathBuf, VfsPath}; pub struct LoadCargoConfig { @@ -50,6 +51,7 @@ pub fn load_workspace_at( load_workspace(workspace, &cargo_config.extra_env, load_config) } +#[instrument(skip_all)] pub fn load_workspace( ws: ProjectWorkspace, extra_env: &FxHashMap, @@ -66,9 +68,9 @@ pub fn load_workspace( let proc_macro_server = match &load_config.with_proc_macro_server { ProcMacroServerChoice::Sysroot => ws .find_sysroot_proc_macro_srv() - .and_then(|it| ProcMacroServer::spawn(it, extra_env).map_err(Into::into)), + .and_then(|it| ProcMacroServer::spawn(&it, extra_env).map_err(Into::into)), ProcMacroServerChoice::Explicit(path) => { - ProcMacroServer::spawn(path.clone(), extra_env).map_err(Into::into) + ProcMacroServer::spawn(path, extra_env).map_err(Into::into) } ProcMacroServerChoice::None => Err(anyhow::format_err!("proc macro server disabled")), }; @@ -333,9 +335,7 @@ fn load_crate_graph( vfs: &mut vfs::Vfs, receiver: &Receiver, ) -> RootDatabase { - let (ProjectWorkspace::Cargo { toolchain, target_layout, .. } - | ProjectWorkspace::Json { toolchain, target_layout, .. } - | ProjectWorkspace::DetachedFile { toolchain, target_layout, .. }) = ws; + let ProjectWorkspace { toolchain, target_layout, .. } = ws; let lru_cap = std::env::var("RA_LRU_CAP").ok().and_then(|it| it.parse::().ok()); let mut db = RootDatabase::new(lru_cap); @@ -352,6 +352,8 @@ fn load_crate_graph( } } vfs::loader::Message::Loaded { files } | vfs::loader::Message::Changed { files } => { + let _p = tracing::span!(Level::INFO, "load_cargo::load_crate_craph/LoadedChanged") + .entered(); for (path, contents) in files { vfs.set_file_contents(path.into(), contents); } @@ -359,8 +361,8 @@ fn load_crate_graph( } } let changes = vfs.take_changes(); - for file in changes { - if let vfs::Change::Create(v) | vfs::Change::Modify(v) = file.change { + for (_, file) in changes { + if let vfs::Change::Create(v, _) | vfs::Change::Modify(v, _) = file.change { if let Ok(text) = String::from_utf8(v) { analysis_change.change_file(file.file_id, Some(text)) } diff --git a/src/tools/rust-analyzer/crates/mbe/src/benchmark.rs b/src/tools/rust-analyzer/crates/mbe/src/benchmark.rs index f4bbaef7af3f1..f73e188c797ee 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/benchmark.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/benchmark.rs @@ -10,7 +10,7 @@ use test_utils::{bench, bench_fixture, skip_slow_tests}; use crate::{ parser::{MetaVarKind, Op, RepeatKind, Separator}, - syntax_node_to_token_tree, DeclarativeMacro, DummyTestSpanMap, DUMMY, + syntax_node_to_token_tree, DeclarativeMacro, DocCommentDesugarMode, DummyTestSpanMap, DUMMY, }; #[test] @@ -78,6 +78,7 @@ fn macro_rules_fixtures_tt() -> FxHashMap> { rule.token_tree().unwrap().syntax(), DummyTestSpanMap, DUMMY, + DocCommentDesugarMode::Mbe, ); (id, def_tt) }) diff --git a/src/tools/rust-analyzer/crates/mbe/src/lib.rs b/src/tools/rust-analyzer/crates/mbe/src/lib.rs index d5de56312a375..6920832dd2e2c 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/lib.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/lib.rs @@ -34,7 +34,7 @@ pub use tt::{Delimiter, DelimiterKind, Punct}; pub use crate::syntax_bridge::{ parse_exprs_with_sep, parse_to_token_tree, parse_to_token_tree_static_span, syntax_node_to_token_tree, syntax_node_to_token_tree_modified, token_tree_to_syntax_node, - SpanMapper, + DocCommentDesugarMode, SpanMapper, }; pub use crate::syntax_bridge::dummy_test_span_utils::*; diff --git a/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge.rs b/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge.rs index 3230eeb5bd881..412e492176882 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge.rs @@ -69,18 +69,28 @@ pub(crate) mod dummy_test_span_utils { } } +/// Doc comment desugaring differs between mbe and proc-macros. +#[derive(Copy, Clone, PartialEq, Eq)] +pub enum DocCommentDesugarMode { + /// Desugars doc comments as quoted raw strings + Mbe, + /// Desugars doc comments as quoted strings + ProcMacro, +} + /// Converts a syntax tree to a [`tt::Subtree`] using the provided span map to populate the /// subtree's spans. pub fn syntax_node_to_token_tree( node: &SyntaxNode, map: SpanMap, span: SpanData, + mode: DocCommentDesugarMode, ) -> tt::Subtree> where SpanData: Copy + fmt::Debug, SpanMap: SpanMapper>, { - let mut c = Converter::new(node, map, Default::default(), Default::default(), span); + let mut c = Converter::new(node, map, Default::default(), Default::default(), span, mode); convert_tokens(&mut c) } @@ -93,12 +103,13 @@ pub fn syntax_node_to_token_tree_modified( append: FxHashMap>>>, remove: FxHashSet, call_site: SpanData, + mode: DocCommentDesugarMode, ) -> tt::Subtree> where SpanMap: SpanMapper>, SpanData: Copy + fmt::Debug, { - let mut c = Converter::new(node, map, append, remove, call_site); + let mut c = Converter::new(node, map, append, remove, call_site, mode); convert_tokens(&mut c) } @@ -165,7 +176,8 @@ where if lexed.errors().next().is_some() { return None; } - let mut conv = RawConverter { lexed, anchor, pos: 0, ctx }; + let mut conv = + RawConverter { lexed, anchor, pos: 0, ctx, mode: DocCommentDesugarMode::ProcMacro }; Some(convert_tokens(&mut conv)) } @@ -178,7 +190,8 @@ where if lexed.errors().next().is_some() { return None; } - let mut conv = StaticRawConverter { lexed, pos: 0, span }; + let mut conv = + StaticRawConverter { lexed, pos: 0, span, mode: DocCommentDesugarMode::ProcMacro }; Some(convert_tokens(&mut conv)) } @@ -405,7 +418,7 @@ fn is_single_token_op(kind: SyntaxKind) -> bool { /// That is, strips leading `///` (or `/**`, etc) /// and strips the ending `*/` /// And then quote the string, which is needed to convert to `tt::Literal` -fn doc_comment_text(comment: &ast::Comment) -> SmolStr { +fn doc_comment_text(comment: &ast::Comment, mode: DocCommentDesugarMode) -> SmolStr { let prefix_len = comment.prefix().len(); let mut text = &comment.text()[prefix_len..]; @@ -414,26 +427,34 @@ fn doc_comment_text(comment: &ast::Comment) -> SmolStr { text = &text[0..text.len() - 2]; } - let mut num_of_hashes = 0; - let mut count = 0; - for ch in text.chars() { - count = match ch { - '"' => 1, - '#' if count > 0 => count + 1, - _ => 0, - }; - num_of_hashes = num_of_hashes.max(count); - } + let text = match mode { + DocCommentDesugarMode::Mbe => { + let mut num_of_hashes = 0; + let mut count = 0; + for ch in text.chars() { + count = match ch { + '"' => 1, + '#' if count > 0 => count + 1, + _ => 0, + }; + num_of_hashes = num_of_hashes.max(count); + } - // Quote raw string with delimiters - // Note that `tt::Literal` expect an escaped string - let text = format!("r{delim}\"{text}\"{delim}", delim = "#".repeat(num_of_hashes)); + // Quote raw string with delimiters + // Note that `tt::Literal` expect an escaped string + format!(r#"r{delim}"{text}"{delim}"#, delim = "#".repeat(num_of_hashes)) + } + // Quote string with delimiters + // Note that `tt::Literal` expect an escaped string + DocCommentDesugarMode::ProcMacro => format!(r#""{}""#, text.escape_debug()), + }; text.into() } fn convert_doc_comment( token: &syntax::SyntaxToken, span: S, + mode: DocCommentDesugarMode, ) -> Option>> { cov_mark::hit!(test_meta_doc_comments); let comment = ast::Comment::cast(token.clone())?; @@ -451,7 +472,7 @@ fn convert_doc_comment( }; let mk_doc_literal = |comment: &ast::Comment| { - let lit = tt::Literal { text: doc_comment_text(comment), span }; + let lit = tt::Literal { text: doc_comment_text(comment, mode), span }; tt::TokenTree::from(tt::Leaf::from(lit)) }; @@ -479,12 +500,14 @@ struct RawConverter<'a, Ctx> { pos: usize, anchor: SpanAnchor, ctx: Ctx, + mode: DocCommentDesugarMode, } /// A raw token (straight from lexer) converter that gives every token the same span. struct StaticRawConverter<'a, S> { lexed: parser::LexedStr<'a>, pos: usize, span: S, + mode: DocCommentDesugarMode, } trait SrcToken { @@ -553,7 +576,7 @@ where span: SpanData, ) -> Option>>> { let text = self.lexed.text(token); - convert_doc_comment(&doc_comment(text), span) + convert_doc_comment(&doc_comment(text), span, self.mode) } fn bump(&mut self) -> Option<(Self::Token, TextRange)> { @@ -592,7 +615,7 @@ where fn convert_doc_comment(&self, &token: &usize, span: S) -> Option>> { let text = self.lexed.text(token); - convert_doc_comment(&doc_comment(text), span) + convert_doc_comment(&doc_comment(text), span, self.mode) } fn bump(&mut self) -> Option<(Self::Token, TextRange)> { @@ -634,6 +657,7 @@ struct Converter { append: FxHashMap>>, remove: FxHashSet, call_site: S, + mode: DocCommentDesugarMode, } impl Converter { @@ -643,6 +667,7 @@ impl Converter { append: FxHashMap>>, remove: FxHashSet, call_site: S, + mode: DocCommentDesugarMode, ) -> Self { let mut this = Converter { current: None, @@ -654,6 +679,7 @@ impl Converter { remove, call_site, current_leaves: vec![], + mode, }; let first = this.next_token(); this.current = first; @@ -755,7 +781,7 @@ where { type Token = SynToken; fn convert_doc_comment(&self, token: &Self::Token, span: S) -> Option>> { - convert_doc_comment(token.token(), span) + convert_doc_comment(token.token(), span, self.mode) } fn bump(&mut self) -> Option<(Self::Token, TextRange)> { diff --git a/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge/tests.rs b/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge/tests.rs index bbfe378200d60..2988fb3cf154e 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge/tests.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge/tests.rs @@ -7,11 +7,16 @@ use tt::{ Leaf, Punct, Spacing, }; -use crate::{syntax_node_to_token_tree, DummyTestSpanMap, DUMMY}; +use crate::{syntax_node_to_token_tree, DocCommentDesugarMode, DummyTestSpanMap, DUMMY}; fn check_punct_spacing(fixture: &str) { let source_file = ast::SourceFile::parse(fixture, span::Edition::CURRENT).ok().unwrap(); - let subtree = syntax_node_to_token_tree(source_file.syntax(), DummyTestSpanMap, DUMMY); + let subtree = syntax_node_to_token_tree( + source_file.syntax(), + DummyTestSpanMap, + DUMMY, + DocCommentDesugarMode::Mbe, + ); let mut annotations: FxHashMap<_, _> = extract_annotations(fixture) .into_iter() .map(|(range, annotation)| { diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/attributes.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/attributes.rs index c13a1943792c1..82e4d66148805 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/attributes.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/attributes.rs @@ -36,8 +36,33 @@ fn attr(p: &mut Parser<'_>, inner: bool) { attr.complete(p, ATTR); } +// test metas +// #![simple_ident] +// #![simple::path] +// #![simple_ident_expr = ""] +// #![simple::path::Expr = ""] +// #![simple_ident_tt(a b c)] +// #![simple_ident_tt[a b c]] +// #![simple_ident_tt{a b c}] +// #![simple::path::tt(a b c)] +// #![simple::path::tt[a b c]] +// #![simple::path::tt{a b c}] +// #![unsafe(simple_ident)] +// #![unsafe(simple::path)] +// #![unsafe(simple_ident_expr = "")] +// #![unsafe(simple::path::Expr = "")] +// #![unsafe(simple_ident_tt(a b c))] +// #![unsafe(simple_ident_tt[a b c])] +// #![unsafe(simple_ident_tt{a b c})] +// #![unsafe(simple::path::tt(a b c))] +// #![unsafe(simple::path::tt[a b c])] +// #![unsafe(simple::path::tt{a b c})] pub(super) fn meta(p: &mut Parser<'_>) { let meta = p.start(); + let is_unsafe = p.eat(T![unsafe]); + if is_unsafe { + p.expect(T!['(']); + } paths::use_path(p); match p.current() { @@ -50,6 +75,9 @@ pub(super) fn meta(p: &mut Parser<'_>) { T!['('] | T!['['] | T!['{'] => items::token_tree(p), _ => {} } + if is_unsafe { + p.expect(T![')']); + } meta.complete(p, META); } diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0213_metas.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0213_metas.rast new file mode 100644 index 0000000000000..b1ac60b530ef1 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0213_metas.rast @@ -0,0 +1,457 @@ +SOURCE_FILE + ATTR + POUND "#" + BANG "!" + L_BRACK "[" + META + PATH + PATH_SEGMENT + NAME_REF + IDENT "simple_ident" + R_BRACK "]" + WHITESPACE "\n" + ATTR + POUND "#" + BANG "!" + L_BRACK "[" + META + PATH + PATH + PATH_SEGMENT + NAME_REF + IDENT "simple" + COLON2 "::" + PATH_SEGMENT + NAME_REF + IDENT "path" + R_BRACK "]" + WHITESPACE "\n" + ATTR + POUND "#" + BANG "!" + L_BRACK "[" + META + PATH + PATH_SEGMENT + NAME_REF + IDENT "simple_ident_expr" + WHITESPACE " " + EQ "=" + WHITESPACE " " + LITERAL + STRING "\"\"" + R_BRACK "]" + WHITESPACE "\n" + ATTR + POUND "#" + BANG "!" + L_BRACK "[" + META + PATH + PATH + PATH + PATH_SEGMENT + NAME_REF + IDENT "simple" + COLON2 "::" + PATH_SEGMENT + NAME_REF + IDENT "path" + COLON2 "::" + PATH_SEGMENT + NAME_REF + IDENT "Expr" + WHITESPACE " " + EQ "=" + WHITESPACE " " + LITERAL + STRING "\"\"" + R_BRACK "]" + WHITESPACE "\n" + ATTR + POUND "#" + BANG "!" + L_BRACK "[" + META + PATH + PATH_SEGMENT + NAME_REF + IDENT "simple_ident_tt" + TOKEN_TREE + L_PAREN "(" + IDENT "a" + WHITESPACE " " + IDENT "b" + WHITESPACE " " + IDENT "c" + R_PAREN ")" + R_BRACK "]" + WHITESPACE "\n" + ATTR + POUND "#" + BANG "!" + L_BRACK "[" + META + PATH + PATH_SEGMENT + NAME_REF + IDENT "simple_ident_tt" + TOKEN_TREE + L_BRACK "[" + IDENT "a" + WHITESPACE " " + IDENT "b" + WHITESPACE " " + IDENT "c" + R_BRACK "]" + R_BRACK "]" + WHITESPACE "\n" + ATTR + POUND "#" + BANG "!" + L_BRACK "[" + META + PATH + PATH_SEGMENT + NAME_REF + IDENT "simple_ident_tt" + TOKEN_TREE + L_CURLY "{" + IDENT "a" + WHITESPACE " " + IDENT "b" + WHITESPACE " " + IDENT "c" + R_CURLY "}" + R_BRACK "]" + WHITESPACE "\n" + ATTR + POUND "#" + BANG "!" + L_BRACK "[" + META + PATH + PATH + PATH + PATH_SEGMENT + NAME_REF + IDENT "simple" + COLON2 "::" + PATH_SEGMENT + NAME_REF + IDENT "path" + COLON2 "::" + PATH_SEGMENT + NAME_REF + IDENT "tt" + TOKEN_TREE + L_PAREN "(" + IDENT "a" + WHITESPACE " " + IDENT "b" + WHITESPACE " " + IDENT "c" + R_PAREN ")" + R_BRACK "]" + WHITESPACE "\n" + ATTR + POUND "#" + BANG "!" + L_BRACK "[" + META + PATH + PATH + PATH + PATH_SEGMENT + NAME_REF + IDENT "simple" + COLON2 "::" + PATH_SEGMENT + NAME_REF + IDENT "path" + COLON2 "::" + PATH_SEGMENT + NAME_REF + IDENT "tt" + TOKEN_TREE + L_BRACK "[" + IDENT "a" + WHITESPACE " " + IDENT "b" + WHITESPACE " " + IDENT "c" + R_BRACK "]" + R_BRACK "]" + WHITESPACE "\n" + ATTR + POUND "#" + BANG "!" + L_BRACK "[" + META + PATH + PATH + PATH + PATH_SEGMENT + NAME_REF + IDENT "simple" + COLON2 "::" + PATH_SEGMENT + NAME_REF + IDENT "path" + COLON2 "::" + PATH_SEGMENT + NAME_REF + IDENT "tt" + TOKEN_TREE + L_CURLY "{" + IDENT "a" + WHITESPACE " " + IDENT "b" + WHITESPACE " " + IDENT "c" + R_CURLY "}" + R_BRACK "]" + WHITESPACE "\n" + ATTR + POUND "#" + BANG "!" + L_BRACK "[" + META + UNSAFE_KW "unsafe" + L_PAREN "(" + PATH + PATH_SEGMENT + NAME_REF + IDENT "simple_ident" + R_PAREN ")" + R_BRACK "]" + WHITESPACE "\n" + ATTR + POUND "#" + BANG "!" + L_BRACK "[" + META + UNSAFE_KW "unsafe" + L_PAREN "(" + PATH + PATH + PATH_SEGMENT + NAME_REF + IDENT "simple" + COLON2 "::" + PATH_SEGMENT + NAME_REF + IDENT "path" + R_PAREN ")" + R_BRACK "]" + WHITESPACE "\n" + ATTR + POUND "#" + BANG "!" + L_BRACK "[" + META + UNSAFE_KW "unsafe" + L_PAREN "(" + PATH + PATH_SEGMENT + NAME_REF + IDENT "simple_ident_expr" + WHITESPACE " " + EQ "=" + WHITESPACE " " + LITERAL + STRING "\"\"" + R_PAREN ")" + R_BRACK "]" + WHITESPACE "\n" + ATTR + POUND "#" + BANG "!" + L_BRACK "[" + META + UNSAFE_KW "unsafe" + L_PAREN "(" + PATH + PATH + PATH + PATH_SEGMENT + NAME_REF + IDENT "simple" + COLON2 "::" + PATH_SEGMENT + NAME_REF + IDENT "path" + COLON2 "::" + PATH_SEGMENT + NAME_REF + IDENT "Expr" + WHITESPACE " " + EQ "=" + WHITESPACE " " + LITERAL + STRING "\"\"" + R_PAREN ")" + R_BRACK "]" + WHITESPACE "\n" + ATTR + POUND "#" + BANG "!" + L_BRACK "[" + META + UNSAFE_KW "unsafe" + L_PAREN "(" + PATH + PATH_SEGMENT + NAME_REF + IDENT "simple_ident_tt" + TOKEN_TREE + L_PAREN "(" + IDENT "a" + WHITESPACE " " + IDENT "b" + WHITESPACE " " + IDENT "c" + R_PAREN ")" + R_PAREN ")" + R_BRACK "]" + WHITESPACE "\n" + ATTR + POUND "#" + BANG "!" + L_BRACK "[" + META + UNSAFE_KW "unsafe" + L_PAREN "(" + PATH + PATH_SEGMENT + NAME_REF + IDENT "simple_ident_tt" + TOKEN_TREE + L_BRACK "[" + IDENT "a" + WHITESPACE " " + IDENT "b" + WHITESPACE " " + IDENT "c" + R_BRACK "]" + R_PAREN ")" + R_BRACK "]" + WHITESPACE "\n" + ATTR + POUND "#" + BANG "!" + L_BRACK "[" + META + UNSAFE_KW "unsafe" + L_PAREN "(" + PATH + PATH_SEGMENT + NAME_REF + IDENT "simple_ident_tt" + TOKEN_TREE + L_CURLY "{" + IDENT "a" + WHITESPACE " " + IDENT "b" + WHITESPACE " " + IDENT "c" + R_CURLY "}" + R_PAREN ")" + R_BRACK "]" + WHITESPACE "\n" + ATTR + POUND "#" + BANG "!" + L_BRACK "[" + META + UNSAFE_KW "unsafe" + L_PAREN "(" + PATH + PATH + PATH + PATH_SEGMENT + NAME_REF + IDENT "simple" + COLON2 "::" + PATH_SEGMENT + NAME_REF + IDENT "path" + COLON2 "::" + PATH_SEGMENT + NAME_REF + IDENT "tt" + TOKEN_TREE + L_PAREN "(" + IDENT "a" + WHITESPACE " " + IDENT "b" + WHITESPACE " " + IDENT "c" + R_PAREN ")" + R_PAREN ")" + R_BRACK "]" + WHITESPACE "\n" + ATTR + POUND "#" + BANG "!" + L_BRACK "[" + META + UNSAFE_KW "unsafe" + L_PAREN "(" + PATH + PATH + PATH + PATH_SEGMENT + NAME_REF + IDENT "simple" + COLON2 "::" + PATH_SEGMENT + NAME_REF + IDENT "path" + COLON2 "::" + PATH_SEGMENT + NAME_REF + IDENT "tt" + TOKEN_TREE + L_BRACK "[" + IDENT "a" + WHITESPACE " " + IDENT "b" + WHITESPACE " " + IDENT "c" + R_BRACK "]" + R_PAREN ")" + R_BRACK "]" + WHITESPACE "\n" + ATTR + POUND "#" + BANG "!" + L_BRACK "[" + META + UNSAFE_KW "unsafe" + L_PAREN "(" + PATH + PATH + PATH + PATH_SEGMENT + NAME_REF + IDENT "simple" + COLON2 "::" + PATH_SEGMENT + NAME_REF + IDENT "path" + COLON2 "::" + PATH_SEGMENT + NAME_REF + IDENT "tt" + TOKEN_TREE + L_CURLY "{" + IDENT "a" + WHITESPACE " " + IDENT "b" + WHITESPACE " " + IDENT "c" + R_CURLY "}" + R_PAREN ")" + R_BRACK "]" + WHITESPACE "\n" diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0213_metas.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0213_metas.rs new file mode 100644 index 0000000000000..57b7bb7170d35 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0213_metas.rs @@ -0,0 +1,20 @@ +#![simple_ident] +#![simple::path] +#![simple_ident_expr = ""] +#![simple::path::Expr = ""] +#![simple_ident_tt(a b c)] +#![simple_ident_tt[a b c]] +#![simple_ident_tt{a b c}] +#![simple::path::tt(a b c)] +#![simple::path::tt[a b c]] +#![simple::path::tt{a b c}] +#![unsafe(simple_ident)] +#![unsafe(simple::path)] +#![unsafe(simple_ident_expr = "")] +#![unsafe(simple::path::Expr = "")] +#![unsafe(simple_ident_tt(a b c))] +#![unsafe(simple_ident_tt[a b c])] +#![unsafe(simple_ident_tt{a b c})] +#![unsafe(simple::path::tt(a b c))] +#![unsafe(simple::path::tt[a b c])] +#![unsafe(simple::path::tt{a b c})] 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 0ab16c38c87ba..87494172907e9 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs @@ -13,7 +13,7 @@ mod version; use base_db::Env; use indexmap::IndexSet; -use paths::AbsPathBuf; +use paths::{AbsPath, AbsPathBuf}; use rustc_hash::FxHashMap; use span::Span; use std::{ @@ -54,6 +54,7 @@ pub struct ProcMacroServer { /// /// Therefore, we just wrap the `ProcMacroProcessSrv` in a mutex here. process: Arc>, + path: AbsPathBuf, } pub struct MacroDylib { @@ -113,15 +114,22 @@ pub struct MacroPanic { impl ProcMacroServer { /// Spawns an external process as the proc macro server and returns a client connected to it. pub fn spawn( - process_path: AbsPathBuf, + process_path: &AbsPath, env: &FxHashMap, ) -> io::Result { let process = ProcMacroProcessSrv::run(process_path, env)?; - Ok(ProcMacroServer { process: Arc::new(Mutex::new(process)) }) + Ok(ProcMacroServer { + process: Arc::new(Mutex::new(process)), + path: process_path.to_owned(), + }) + } + + pub fn path(&self) -> &AbsPath { + &self.path } pub fn load_dylib(&self, dylib: MacroDylib) -> Result, ServerError> { - let _p = tracing::span!(tracing::Level::INFO, "ProcMacroClient::load_dylib").entered(); + let _p = tracing::span!(tracing::Level::INFO, "ProcMacroServer::load_dylib").entered(); let macros = self.process.lock().unwrap_or_else(|e| e.into_inner()).find_proc_macros(&dylib.path)?; diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs index 35d48a155433f..dce086d4299be 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs @@ -6,7 +6,7 @@ use std::{ sync::Arc, }; -use paths::{AbsPath, AbsPathBuf}; +use paths::AbsPath; use rustc_hash::FxHashMap; use stdx::JodChild; @@ -28,11 +28,11 @@ pub(crate) struct ProcMacroProcessSrv { impl ProcMacroProcessSrv { pub(crate) fn run( - process_path: AbsPathBuf, + process_path: &AbsPath, env: &FxHashMap, ) -> io::Result { let create_srv = |null_stderr| { - let mut process = Process::run(process_path.clone(), env, null_stderr)?; + let mut process = Process::run(process_path, env, null_stderr)?; let (stdin, stdout) = process.stdio().expect("couldn't access child stdio"); io::Result::Ok(ProcMacroProcessSrv { @@ -153,11 +153,11 @@ struct Process { impl Process { fn run( - path: AbsPathBuf, + path: &AbsPath, env: &FxHashMap, null_stderr: bool, ) -> io::Result { - let child = JodChild(mk_child(&path, env, null_stderr)?); + let child = JodChild(mk_child(path, env, null_stderr)?); Ok(Process { child }) } diff --git a/src/tools/rust-analyzer/crates/profile/Cargo.toml b/src/tools/rust-analyzer/crates/profile/Cargo.toml index a87b67f5c69f3..11a8e7af56a48 100644 --- a/src/tools/rust-analyzer/crates/profile/Cargo.toml +++ b/src/tools/rust-analyzer/crates/profile/Cargo.toml @@ -24,7 +24,7 @@ jemalloc-ctl = { version = "0.5.0", package = "tikv-jemalloc-ctl", optional = tr perf-event = "=0.4.7" [target.'cfg(windows)'.dependencies] -winapi = { version = "0.3.9", features = ["processthreadsapi", "psapi"] } +windows-sys = { version = "0.52", features = ["Win32_System_Threading", "Win32_System_ProcessStatus"] } [features] cpu_profiler = [] diff --git a/src/tools/rust-analyzer/crates/profile/src/lib.rs b/src/tools/rust-analyzer/crates/profile/src/lib.rs index a3fdb72a6d1d2..2ccb1cd06ae4c 100644 --- a/src/tools/rust-analyzer/crates/profile/src/lib.rs +++ b/src/tools/rust-analyzer/crates/profile/src/lib.rs @@ -26,7 +26,7 @@ thread_local!(static IN_SCOPE: RefCell = const { RefCell::new(false) }); /// A wrapper around google_cpu_profiler. /// /// Usage: -/// 1. Install gpref_tools (), probably packaged with your Linux distro. +/// 1. Install gperf_tools (), probably packaged with your Linux distro. /// 2. Build with `cpu_profiler` feature. /// 3. Run the code, the *raw* output would be in the `./out.profile` file. /// 4. Install pprof for visualization (). diff --git a/src/tools/rust-analyzer/crates/profile/src/memory_usage.rs b/src/tools/rust-analyzer/crates/profile/src/memory_usage.rs index f089c78e0c195..660afff3dcaae 100644 --- a/src/tools/rust-analyzer/crates/profile/src/memory_usage.rs +++ b/src/tools/rust-analyzer/crates/profile/src/memory_usage.rs @@ -37,8 +37,7 @@ impl MemoryUsage { // There doesn't seem to be an API for determining heap usage, so we try to // approximate that by using the Commit Charge value. - use winapi::um::processthreadsapi::*; - use winapi::um::psapi::*; + use windows_sys::Win32::System::{Threading::*, ProcessStatus::*}; use std::mem::{MaybeUninit, size_of}; let proc = unsafe { GetCurrentProcess() }; diff --git a/src/tools/rust-analyzer/crates/project-model/src/build_scripts.rs b/src/tools/rust-analyzer/crates/project-model/src/build_scripts.rs index fbd423c9eac8a..8e1f7fdcded7c 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/build_scripts.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/build_scripts.rs @@ -24,7 +24,7 @@ use toolchain::Tool; use crate::{ cfg::CfgFlag, utf8_stdout, CargoConfig, CargoFeatures, CargoWorkspace, InvocationLocation, - InvocationStrategy, Package, Sysroot, TargetKind, + InvocationStrategy, ManifestPath, Package, Sysroot, TargetKind, }; /// Output of the build script and proc-macro building steps for a workspace. @@ -63,9 +63,11 @@ impl WorkspaceBuildScripts { fn build_command( config: &CargoConfig, allowed_features: &FxHashSet, - workspace_root: &AbsPathBuf, + manifest_path: &ManifestPath, + toolchain: Option<&Version>, sysroot: Option<&Sysroot>, ) -> io::Result { + const RUST_1_75: Version = Version::new(1, 75, 0); let mut cmd = match config.run_build_script_command.as_deref() { Some([program, args @ ..]) => { let mut cmd = Command::new(program); @@ -79,7 +81,7 @@ impl WorkspaceBuildScripts { cmd.args(&config.extra_args); cmd.arg("--manifest-path"); - cmd.arg(workspace_root.join("Cargo.toml")); + cmd.arg(manifest_path.as_ref()); if let Some(target_dir) = &config.target_dir { cmd.arg("--target-dir").arg(target_dir); @@ -116,6 +118,14 @@ impl WorkspaceBuildScripts { } } + if manifest_path.is_rust_manifest() { + cmd.arg("-Zscript"); + } + + if toolchain.map_or(false, |it| *it >= RUST_1_75) { + cmd.arg("--keep-going"); + } + cmd } }; @@ -138,11 +148,9 @@ impl WorkspaceBuildScripts { config: &CargoConfig, workspace: &CargoWorkspace, progress: &dyn Fn(String), - toolchain: &Option, + toolchain: Option<&Version>, sysroot: Option<&Sysroot>, ) -> io::Result { - const RUST_1_75: Version = Version::new(1, 75, 0); - let current_dir = match &config.invocation_location { InvocationLocation::Root(root) if config.run_build_script_command.is_some() => { root.as_path() @@ -152,37 +160,14 @@ impl WorkspaceBuildScripts { .as_ref(); let allowed_features = workspace.workspace_features(); - - match Self::run_per_ws( - Self::build_command( - config, - &allowed_features, - &workspace.workspace_root().to_path_buf(), - sysroot, - )?, - workspace, - current_dir, - progress, - ) { - Ok(WorkspaceBuildScripts { error: Some(error), .. }) - if toolchain.as_ref().map_or(false, |it| *it >= RUST_1_75) => - { - // building build scripts failed, attempt to build with --keep-going so - // that we potentially get more build data - let mut cmd = Self::build_command( - config, - &allowed_features, - &workspace.workspace_root().to_path_buf(), - sysroot, - )?; - - cmd.args(["--keep-going"]); - let mut res = Self::run_per_ws(cmd, workspace, current_dir, progress)?; - res.error = Some(error); - Ok(res) - } - res => res, - } + let cmd = Self::build_command( + config, + &allowed_features, + workspace.manifest_path(), + toolchain, + sysroot, + )?; + Self::run_per_ws(cmd, workspace, current_dir, progress) } /// Runs the build scripts by invoking the configured command *once*. @@ -204,7 +189,14 @@ impl WorkspaceBuildScripts { )) } }; - let cmd = Self::build_command(config, &Default::default(), workspace_root, None)?; + let cmd = Self::build_command( + config, + &Default::default(), + // This is not gonna be used anyways, so just construct a dummy here + &ManifestPath::try_from(workspace_root.clone()).unwrap(), + None, + None, + )?; // NB: Cargo.toml could have been modified between `cargo metadata` and // `cargo check`. We shouldn't assume that package ids we see here are // exactly those from `config`. diff --git a/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs index ff7cf144aa82d..9955f2687c917 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs @@ -32,6 +32,7 @@ pub struct CargoWorkspace { targets: Arena, workspace_root: AbsPathBuf, target_directory: AbsPathBuf, + manifest_path: ManifestPath, } impl ops::Index for CargoWorkspace { @@ -306,7 +307,7 @@ impl CargoWorkspace { ); } // The manifest is a rust file, so this means its a script manifest - if cargo_toml.extension().is_some_and(|ext| ext == "rs") { + if cargo_toml.is_rust_manifest() { // Deliberately don't set up RUSTC_BOOTSTRAP or a nightly override here, the user should // opt into it themselves. other_options.push("-Zscript".to_owned()); @@ -334,7 +335,7 @@ impl CargoWorkspace { .with_context(|| format!("Failed to run `{:?}`", meta.cargo_command())) } - pub fn new(mut meta: cargo_metadata::Metadata) -> CargoWorkspace { + pub fn new(mut meta: cargo_metadata::Metadata, manifest_path: ManifestPath) -> CargoWorkspace { let mut pkg_by_id = FxHashMap::default(); let mut packages = Arena::default(); let mut targets = Arena::default(); @@ -448,7 +449,7 @@ impl CargoWorkspace { let target_directory = AbsPathBuf::assert(meta.target_directory); - CargoWorkspace { packages, targets, workspace_root, target_directory } + CargoWorkspace { packages, targets, workspace_root, target_directory, manifest_path } } pub fn packages(&self) -> impl ExactSizeIterator + '_ { @@ -466,6 +467,10 @@ impl CargoWorkspace { &self.workspace_root } + pub fn manifest_path(&self) -> &ManifestPath { + &self.manifest_path + } + pub fn target_directory(&self) -> &AbsPath { &self.target_directory } diff --git a/src/tools/rust-analyzer/crates/project-model/src/env.rs b/src/tools/rust-analyzer/crates/project-model/src/env.rs index 762e01c917724..5520cdaff6b00 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/env.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/env.rs @@ -60,16 +60,19 @@ pub(crate) fn inject_rustc_tool_env(env: &mut Env, cargo_name: &str, kind: Targe } pub(crate) fn cargo_config_env( - cargo_toml: &ManifestPath, + manifest: &ManifestPath, extra_env: &FxHashMap, sysroot: Option<&Sysroot>, ) -> FxHashMap { let mut cargo_config = Sysroot::tool(sysroot, Tool::Cargo); cargo_config.envs(extra_env); cargo_config - .current_dir(cargo_toml.parent()) + .current_dir(manifest.parent()) .args(["-Z", "unstable-options", "config", "get", "env"]) .env("RUSTC_BOOTSTRAP", "1"); + if manifest.is_rust_manifest() { + cargo_config.arg("-Zscript"); + } // if successful we receive `env.key.value = "value" per entry tracing::debug!("Discovering cargo config env by {:?}", cargo_config); utf8_stdout(cargo_config).map(parse_output_cargo_config_env).unwrap_or_default() 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 7f3e35ca5dbca..181c07f46b27d 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/lib.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/lib.rs @@ -52,13 +52,15 @@ pub use crate::{ manifest_path::ManifestPath, project_json::{ProjectJson, ProjectJsonData}, sysroot::Sysroot, - workspace::{FileLoader, PackageRoot, ProjectWorkspace}, + workspace::{FileLoader, PackageRoot, ProjectWorkspace, ProjectWorkspaceKind}, }; +pub use cargo_metadata::Metadata; #[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)] pub enum ProjectManifest { ProjectJson(ManifestPath), CargoToml(ManifestPath), + CargoScript(ManifestPath), } impl ProjectManifest { @@ -71,7 +73,10 @@ impl ProjectManifest { if path.file_name().unwrap_or_default() == "Cargo.toml" { return Ok(ProjectManifest::CargoToml(path)); } - bail!("project root must point to Cargo.toml or rust-project.json: {path}"); + if path.extension().unwrap_or_default() == "rs" { + return Ok(ProjectManifest::CargoScript(path)); + } + bail!("project root must point to a Cargo.toml, rust-project.json or