diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 8266c03eaa72..f539b64d8c82 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -117,12 +117,12 @@ jobs:
with:
fetch-depth: 2
- # Free up disk space on Linux and Windows by removing preinstalled components that
+ # Free up disk space on Linux by removing preinstalled components that
# we do not need. We do this to enable some of the less resource
# intensive jobs to run on free runners, which however also have
# less disk space.
- name: free up disk space
- run: src/ci/scripts/free-disk-space.sh
+ run: src/ci/scripts/free-disk-space-linux.sh
if: matrix.free_disk
# If we don't need to free up disk space then just report how much space we have
@@ -223,11 +223,6 @@ jobs:
cd src/ci/citool
CARGO_INCREMENTAL=0 CARGO_TARGET_DIR=../../../build/citool cargo build
- - name: wait for Windows disk cleanup to finish
- if: ${{ matrix.free_disk && startsWith(matrix.os, 'windows-') }}
- run: |
- python3 src/ci/scripts/free-disk-space-windows-wait.py
-
- name: run the build
run: |
set +e
diff --git a/Cargo.lock b/Cargo.lock
index 85df0dda8185..af206e87c452 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -152,9 +152,9 @@ dependencies = [
[[package]]
name = "anyhow"
-version = "1.0.98"
+version = "1.0.99"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
+checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100"
[[package]]
name = "ar_archive_writer"
@@ -204,7 +204,7 @@ dependencies = [
"rustc-hash 2.1.1",
"serde",
"serde_derive",
- "syn 2.0.104",
+ "syn 2.0.106",
]
[[package]]
@@ -266,9 +266,9 @@ dependencies = [
[[package]]
name = "bitflags"
-version = "2.9.1"
+version = "2.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
+checksum = "6a65b545ab31d687cff52899d4890855fec459eb6afe0da6417b8a18da87aa29"
[[package]]
name = "blake3"
@@ -315,7 +315,7 @@ dependencies = [
"serde_json",
"sha2",
"tar",
- "toml 0.5.11",
+ "toml 0.7.8",
"xz2",
]
@@ -336,7 +336,7 @@ dependencies = [
"curl",
"indexmap",
"serde",
- "toml 0.5.11",
+ "toml 0.7.8",
]
[[package]]
@@ -421,7 +421,7 @@ dependencies = [
"serde",
"serde-untagged",
"serde-value",
- "thiserror 2.0.12",
+ "thiserror 2.0.15",
"toml 0.8.23",
"unicode-xid",
"url",
@@ -453,7 +453,7 @@ dependencies = [
"semver",
"serde",
"serde_json",
- "thiserror 2.0.12",
+ "thiserror 2.0.15",
]
[[package]]
@@ -518,9 +518,9 @@ dependencies = [
[[package]]
name = "clap"
-version = "4.5.43"
+version = "4.5.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "50fd97c9dc2399518aa331917ac6f274280ec5eb34e555dd291899745c48ec6f"
+checksum = "1fc0e74a703892159f5ae7d3aac52c8e6c392f5ae5f359c70b5881d60aaac318"
dependencies = [
"clap_builder",
"clap_derive",
@@ -538,9 +538,9 @@ dependencies = [
[[package]]
name = "clap_builder"
-version = "4.5.43"
+version = "4.5.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c35b5830294e1fa0462034af85cc95225a4cb07092c088c55bda3147cfcd8f65"
+checksum = "b3e7f4214277f3c7aa526a59dd3fbe306a370daee1f8b7b8c987069cd8e888a8"
dependencies = [
"anstream",
"anstyle",
@@ -550,14 +550,14 @@ dependencies = [
[[package]]
name = "clap_derive"
-version = "4.5.41"
+version = "4.5.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ef4f52386a59ca4c860f7393bcf8abd8dfd91ecccc0f774635ff68e92eeef491"
+checksum = "14cb31bb0a7d536caef2639baa7fad459e15c3144efefa6dbd1c84562c4739f6"
dependencies = [
"heck 0.5.0",
"proc-macro2",
"quote",
- "syn 2.0.104",
+ "syn 2.0.106",
]
[[package]]
@@ -720,7 +720,7 @@ dependencies = [
"nom",
"proc-macro2",
"quote",
- "syn 2.0.104",
+ "syn 2.0.106",
]
[[package]]
@@ -907,9 +907,9 @@ dependencies = [
[[package]]
name = "curl"
-version = "0.4.48"
+version = "0.4.49"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9e2d5c8f48d9c0c23250e52b55e82a6ab4fdba6650c931f5a0a57a43abda812b"
+checksum = "79fc3b6dd0b87ba36e565715bf9a2ced221311db47bd18011676f24a6066edbc"
dependencies = [
"curl-sys",
"libc",
@@ -922,9 +922,9 @@ dependencies = [
[[package]]
name = "curl-sys"
-version = "0.4.82+curl-8.14.1"
+version = "0.4.83+curl-8.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c4d63638b5ec65f1a4ae945287b3fd035be4554bbaf211901159c9a2a74fb5be"
+checksum = "5830daf304027db10c82632a464879d46a3f7c4ba17a31592657ad16c719b483"
dependencies = [
"cc",
"libc",
@@ -937,9 +937,9 @@ dependencies = [
[[package]]
name = "cxx"
-version = "1.0.166"
+version = "1.0.168"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b5287274dfdf7e7eaa3d97d460eb2a94922539e6af214bda423f292105011ee2"
+checksum = "7aa144b12f11741f0dab5b4182896afad46faa0598b6a061f7b9d17a21837ba7"
dependencies = [
"cc",
"cxxbridge-cmd",
@@ -951,9 +951,9 @@ dependencies = [
[[package]]
name = "cxx-build"
-version = "1.0.166"
+version = "1.0.168"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "65f3ce027a744135db10a1ebffa0863dab685aeef48f40a02c201f5e70c667d3"
+checksum = "12d3cbb84fb003242941c231b45ca9417e786e66e94baa39584bd99df3a270b6"
dependencies = [
"cc",
"codespan-reporting",
@@ -961,40 +961,40 @@ dependencies = [
"proc-macro2",
"quote",
"scratch",
- "syn 2.0.104",
+ "syn 2.0.106",
]
[[package]]
name = "cxxbridge-cmd"
-version = "1.0.166"
+version = "1.0.168"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a07dc23f2eea4774297f4c9a17ae4065fecb63127da556e6c9fadb0216d93595"
+checksum = "3fa36b7b249d43f67a3f54bd65788e35e7afe64bbc671396387a48b3e8aaea94"
dependencies = [
"clap",
"codespan-reporting",
"indexmap",
"proc-macro2",
"quote",
- "syn 2.0.104",
+ "syn 2.0.106",
]
[[package]]
name = "cxxbridge-flags"
-version = "1.0.166"
+version = "1.0.168"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f7a4dbad6171f763c4066c83dcd27546b6e93c5c5ae2229f9813bda7233f571d"
+checksum = "77707c70f6563edc5429618ca34a07241b75ebab35bd01d46697c75d58f8ddfe"
[[package]]
name = "cxxbridge-macro"
-version = "1.0.166"
+version = "1.0.168"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a9be4b527950fc42db06163705e78e73eedc8fd723708e942afe3572a9a2c366"
+checksum = "ede6c0fb7e318f0a11799b86ee29dcf17b9be2960bd379a6c38e1a96a6010fff"
dependencies = [
"indexmap",
"proc-macro2",
"quote",
"rustversion",
- "syn 2.0.104",
+ "syn 2.0.106",
]
[[package]]
@@ -1018,7 +1018,7 @@ dependencies = [
"proc-macro2",
"quote",
"strsim",
- "syn 2.0.104",
+ "syn 2.0.106",
]
[[package]]
@@ -1029,7 +1029,7 @@ checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead"
dependencies = [
"darling_core",
"quote",
- "syn 2.0.104",
+ "syn 2.0.106",
]
[[package]]
@@ -1061,7 +1061,7 @@ checksum = "ef941ded77d15ca19b40374869ac6000af1c9f2a4c0f3d4c70926287e6364a8f"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.104",
+ "syn 2.0.106",
]
[[package]]
@@ -1082,7 +1082,7 @@ dependencies = [
"darling",
"proc-macro2",
"quote",
- "syn 2.0.104",
+ "syn 2.0.106",
]
[[package]]
@@ -1092,7 +1092,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c"
dependencies = [
"derive_builder_core",
- "syn 2.0.104",
+ "syn 2.0.106",
]
[[package]]
@@ -1104,7 +1104,7 @@ dependencies = [
"darling",
"proc-macro2",
"quote",
- "syn 2.0.104",
+ "syn 2.0.106",
]
[[package]]
@@ -1129,16 +1129,16 @@ version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16f5094c54661b38d03bd7e50df373292118db60b585c08a411c6d840017fe7d"
dependencies = [
- "dirs-sys 0.5.0",
+ "dirs-sys",
]
[[package]]
name = "dirs"
-version = "5.0.1"
+version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225"
+checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e"
dependencies = [
- "dirs-sys 0.4.1",
+ "dirs-sys",
]
[[package]]
@@ -1151,18 +1151,6 @@ dependencies = [
"dirs-sys-next",
]
-[[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 0.4.6",
- "windows-sys 0.48.0",
-]
-
[[package]]
name = "dirs-sys"
version = "0.5.0"
@@ -1194,7 +1182,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.104",
+ "syn 2.0.106",
]
[[package]]
@@ -1379,7 +1367,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54f0d287c53ffd184d04d8677f590f4ac5379785529e5e08b1c8083acdd5c198"
dependencies = [
"memchr",
- "thiserror 2.0.12",
+ "thiserror 2.0.15",
]
[[package]]
@@ -1539,9 +1527,9 @@ dependencies = [
[[package]]
name = "glob"
-version = "0.3.2"
+version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2"
+checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
[[package]]
name = "globset"
@@ -1855,7 +1843,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.104",
+ "syn 2.0.106",
]
[[package]]
@@ -2044,7 +2032,7 @@ checksum = "03343451ff899767262ec32146f6d559dd759fdadf42ff0e227c7c48f72594b4"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.104",
+ "syn 2.0.106",
]
[[package]]
@@ -2102,7 +2090,7 @@ dependencies = [
"pest_derive",
"regex",
"serde_json",
- "thiserror 2.0.12",
+ "thiserror 2.0.15",
]
[[package]]
@@ -2337,7 +2325,7 @@ checksum = "88a9689d8d44bf9964484516275f5cd4c9b59457a6940c1d5d0ecbb94510a36b"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.104",
+ "syn 2.0.106",
]
[[package]]
@@ -2643,9 +2631,9 @@ dependencies = [
[[package]]
name = "object"
-version = "0.37.2"
+version = "0.37.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b3e3d0a7419f081f4a808147e845310313a39f322d7ae1f996b7f001d6cbed04"
+checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe"
dependencies = [
"crc32fast",
"flate2",
@@ -2653,7 +2641,7 @@ dependencies = [
"indexmap",
"memchr",
"ruzstd 0.8.1",
- "wasmparser 0.236.0",
+ "wasmparser 0.236.1",
]
[[package]]
@@ -2834,7 +2822,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1db05f56d34358a8b1066f67cbb203ee3e7ed2ba674a6263a1d5ec6db2204323"
dependencies = [
"memchr",
- "thiserror 2.0.12",
+ "thiserror 2.0.15",
"ucd-trie",
]
@@ -2858,7 +2846,7 @@ dependencies = [
"pest_meta",
"proc-macro2",
"quote",
- "syn 2.0.104",
+ "syn 2.0.106",
]
[[package]]
@@ -3007,9 +2995,9 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068"
[[package]]
name = "proc-macro2"
-version = "1.0.95"
+version = "1.0.101"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
+checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de"
dependencies = [
"unicode-ident",
]
@@ -3147,9 +3135,9 @@ dependencies = [
[[package]]
name = "rayon"
-version = "1.10.0"
+version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
+checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f"
dependencies = [
"either",
"rayon-core",
@@ -3157,9 +3145,9 @@ dependencies = [
[[package]]
name = "rayon-core"
-version = "1.12.1"
+version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
+checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91"
dependencies = [
"crossbeam-deque",
"crossbeam-utils",
@@ -3193,7 +3181,7 @@ checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac"
dependencies = [
"getrandom 0.2.16",
"libredox",
- "thiserror 2.0.12",
+ "thiserror 2.0.15",
]
[[package]]
@@ -3279,11 +3267,11 @@ dependencies = [
"build_helper",
"gimli 0.32.0",
"libc",
- "object 0.37.2",
+ "object 0.37.3",
"regex",
"serde_json",
"similar",
- "wasmparser 0.236.0",
+ "wasmparser 0.236.1",
]
[[package]]
@@ -3367,6 +3355,7 @@ dependencies = [
"rand 0.9.2",
"rand_xoshiro",
"rustc_data_structures",
+ "rustc_error_messages",
"rustc_hashes",
"rustc_index",
"rustc_macros",
@@ -3458,7 +3447,6 @@ dependencies = [
"rustc_feature",
"rustc_fluent_macro",
"rustc_macros",
- "rustc_parse",
"rustc_session",
"rustc_span",
"rustc_target",
@@ -3480,7 +3468,6 @@ dependencies = [
name = "rustc_attr_parsing"
version = "0.0.0"
dependencies = [
- "itertools",
"rustc_abi",
"rustc_ast",
"rustc_ast_pretty",
@@ -3490,6 +3477,7 @@ dependencies = [
"rustc_hir",
"rustc_lexer",
"rustc_macros",
+ "rustc_parse",
"rustc_session",
"rustc_span",
"thin-vec",
@@ -3570,7 +3558,7 @@ dependencies = [
"itertools",
"libc",
"measureme",
- "object 0.37.2",
+ "object 0.37.3",
"rustc-demangle",
"rustc_abi",
"rustc_ast",
@@ -3608,7 +3596,7 @@ dependencies = [
"cc",
"itertools",
"libc",
- "object 0.37.2",
+ "object 0.37.3",
"pathdiff",
"regex",
"rustc_abi",
@@ -3779,6 +3767,8 @@ dependencies = [
"icu_locid",
"icu_provider_adapters",
"intl-memoizer",
+ "rustc_ast",
+ "rustc_ast_pretty",
"rustc_baked_icu_data",
"rustc_data_structures",
"rustc_macros",
@@ -3796,21 +3786,17 @@ dependencies = [
"derive_setters",
"rustc_abi",
"rustc_ast",
- "rustc_ast_pretty",
"rustc_data_structures",
"rustc_error_codes",
"rustc_error_messages",
"rustc_fluent_macro",
"rustc_hashes",
- "rustc_hir",
"rustc_index",
"rustc_lexer",
"rustc_lint_defs",
"rustc_macros",
"rustc_serialize",
"rustc_span",
- "rustc_target",
- "rustc_type_ir",
"serde",
"serde_json",
"termcolor",
@@ -3865,7 +3851,7 @@ dependencies = [
"fluent-syntax",
"proc-macro2",
"quote",
- "syn 2.0.104",
+ "syn 2.0.106",
"unic-langid",
]
@@ -3898,7 +3884,9 @@ dependencies = [
"rustc_ast",
"rustc_ast_pretty",
"rustc_data_structures",
+ "rustc_error_messages",
"rustc_hashes",
+ "rustc_hir_id",
"rustc_index",
"rustc_macros",
"rustc_serialize",
@@ -3936,6 +3924,17 @@ dependencies = [
"tracing",
]
+[[package]]
+name = "rustc_hir_id"
+version = "0.0.0"
+dependencies = [
+ "rustc_data_structures",
+ "rustc_index",
+ "rustc_macros",
+ "rustc_serialize",
+ "rustc_span",
+]
+
[[package]]
name = "rustc_hir_pretty"
version = "0.0.0"
@@ -4012,7 +4011,7 @@ version = "0.0.0"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.104",
+ "syn 2.0.106",
]
[[package]]
@@ -4123,11 +4122,10 @@ dependencies = [
name = "rustc_lint_defs"
version = "0.0.0"
dependencies = [
- "rustc_abi",
"rustc_ast",
"rustc_data_structures",
"rustc_error_messages",
- "rustc_hir",
+ "rustc_hir_id",
"rustc_macros",
"rustc_serialize",
"rustc_span",
@@ -4158,7 +4156,7 @@ version = "0.0.0"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.104",
+ "syn 2.0.106",
"synstructure",
]
@@ -4268,7 +4266,6 @@ dependencies = [
"rustc_errors",
"rustc_fluent_macro",
"rustc_graphviz",
- "rustc_hir",
"rustc_index",
"rustc_macros",
"rustc_middle",
@@ -4345,7 +4342,6 @@ dependencies = [
"rustc-literal-escaper",
"rustc_ast",
"rustc_ast_pretty",
- "rustc_attr_parsing",
"rustc_data_structures",
"rustc_errors",
"rustc_feature",
@@ -4644,9 +4640,10 @@ name = "rustc_target"
version = "0.0.0"
dependencies = [
"bitflags",
- "object 0.37.2",
+ "object 0.37.3",
"rustc_abi",
"rustc_data_structures",
+ "rustc_error_messages",
"rustc_fs_util",
"rustc_macros",
"rustc_serialize",
@@ -4710,7 +4707,6 @@ name = "rustc_traits"
version = "0.0.0"
dependencies = [
"rustc_data_structures",
- "rustc_hir",
"rustc_infer",
"rustc_middle",
"rustc_span",
@@ -4765,6 +4761,7 @@ dependencies = [
"rustc-hash 2.1.1",
"rustc_ast_ir",
"rustc_data_structures",
+ "rustc_error_messages",
"rustc_index",
"rustc_macros",
"rustc_serialize",
@@ -4781,7 +4778,7 @@ version = "0.0.0"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.104",
+ "syn 2.0.106",
"synstructure",
]
@@ -4812,6 +4809,7 @@ dependencies = [
"serde_json",
"sha2",
"smallvec",
+ "stringdex",
"tempfile",
"threadpool",
"tracing",
@@ -4872,7 +4870,7 @@ dependencies = [
"proc-macro2",
"quote",
"serde",
- "syn 2.0.104",
+ "syn 2.0.106",
]
[[package]]
@@ -5009,9 +5007,9 @@ dependencies = [
[[package]]
name = "serde-untagged"
-version = "0.1.7"
+version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "299d9c19d7d466db4ab10addd5703e4c615dec2a5a16dbbafe191045e87ee66e"
+checksum = "34836a629bcbc6f1afdf0907a744870039b1e14c0561cb26094fa683b158eff3"
dependencies = [
"erased-serde",
"serde",
@@ -5036,7 +5034,7 @@ checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.104",
+ "syn 2.0.106",
]
[[package]]
@@ -5127,12 +5125,12 @@ checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
[[package]]
name = "socket2"
-version = "0.5.10"
+version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678"
+checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807"
dependencies = [
"libc",
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
]
[[package]]
@@ -5225,6 +5223,15 @@ dependencies = [
"quote",
]
+[[package]]
+name = "stringdex"
+version = "0.0.1-alpha4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2841fd43df5b1ff1b042e167068a1fe9b163dc93041eae56ab2296859013a9a0"
+dependencies = [
+ "stacker",
+]
+
[[package]]
name = "strsim"
version = "0.11.1"
@@ -5263,9 +5270,9 @@ dependencies = [
[[package]]
name = "syn"
-version = "2.0.104"
+version = "2.0.106"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40"
+checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6"
dependencies = [
"proc-macro2",
"quote",
@@ -5280,7 +5287,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.104",
+ "syn 2.0.106",
]
[[package]]
@@ -5398,11 +5405,11 @@ dependencies = [
[[package]]
name = "thiserror"
-version = "2.0.12"
+version = "2.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
+checksum = "80d76d3f064b981389ecb4b6b7f45a0bf9fdac1d5b9204c7bd6714fecc302850"
dependencies = [
- "thiserror-impl 2.0.12",
+ "thiserror-impl 2.0.15",
]
[[package]]
@@ -5413,18 +5420,18 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.104",
+ "syn 2.0.106",
]
[[package]]
name = "thiserror-impl"
-version = "2.0.12"
+version = "2.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
+checksum = "44d29feb33e986b6ea906bd9c3559a856983f92371b3eaa5e83782a351623de0"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.104",
+ "syn 2.0.106",
]
[[package]]
@@ -5525,15 +5532,6 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
-[[package]]
-name = "toml"
-version = "0.5.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234"
-dependencies = [
- "serde",
-]
-
[[package]]
name = "toml"
version = "0.7.8"
@@ -5621,7 +5619,7 @@ checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.104",
+ "syn 2.0.106",
]
[[package]]
@@ -5804,7 +5802,7 @@ checksum = "a1249a628de3ad34b821ecb1001355bca3940bcb2f88558f1a8bd82e977f75b5"
dependencies = [
"proc-macro-hack",
"quote",
- "syn 2.0.104",
+ "syn 2.0.106",
"unic-langid-impl",
]
@@ -5936,9 +5934,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
name = "uuid"
-version = "1.17.0"
+version = "1.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3cf4199d1e5d15ddd86a694e4d0dffa9c323ce759fea589f00fef9d81cc1931d"
+checksum = "f33196643e165781c20a5ead5582283a7dacbb87855d867fbc2df3f81eddc1be"
dependencies = [
"getrandom 0.3.3",
"js-sys",
@@ -6016,7 +6014,7 @@ dependencies = [
"log",
"proc-macro2",
"quote",
- "syn 2.0.104",
+ "syn 2.0.106",
"wasm-bindgen-shared",
]
@@ -6038,7 +6036,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.104",
+ "syn 2.0.106",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
@@ -6101,12 +6099,12 @@ dependencies = [
[[package]]
name = "wasm-encoder"
-version = "0.236.0"
+version = "0.236.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3108979166ab0d3c7262d2e16a2190ffe784b2a5beb963edef154b5e8e07680b"
+checksum = "724fccfd4f3c24b7e589d333fc0429c68042897a7e8a5f8694f31792471841e7"
dependencies = [
"leb128fmt",
- "wasmparser 0.236.0",
+ "wasmparser 0.236.1",
]
[[package]]
@@ -6146,9 +6144,9 @@ dependencies = [
[[package]]
name = "wasmparser"
-version = "0.236.0"
+version = "0.236.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "16d1eee846a705f6f3cb9d7b9f79b54583810f1fb57a1e3aea76d1742db2e3d2"
+checksum = "a9b1e81f3eb254cf7404a82cee6926a4a3ccc5aad80cc3d43608a070c67aa1d7"
dependencies = [
"bitflags",
"indexmap",
@@ -6157,22 +6155,22 @@ dependencies = [
[[package]]
name = "wast"
-version = "236.0.0"
+version = "236.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "11d6b6faeab519ba6fbf9b26add41617ca6f5553f99ebc33d876e591d2f4f3c6"
+checksum = "d3bec4b4db9c6808d394632fd4b0cd4654c32c540bd3237f55ee6a40fff6e51f"
dependencies = [
"bumpalo",
"leb128fmt",
"memchr",
"unicode-width 0.2.1",
- "wasm-encoder 0.236.0",
+ "wasm-encoder 0.236.1",
]
[[package]]
name = "wat"
-version = "1.236.0"
+version = "1.236.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cc31704322400f461f7f31a5f9190d5488aaeafb63ae69ad2b5888d2704dcb08"
+checksum = "64475e2f77d6071ce90624098fc236285ddafa8c3ea1fb386f2c4154b6c2bbdb"
dependencies = [
"wast",
]
@@ -6306,7 +6304,7 @@ checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.104",
+ "syn 2.0.106",
]
[[package]]
@@ -6317,7 +6315,7 @@ checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.104",
+ "syn 2.0.106",
]
[[package]]
@@ -6328,7 +6326,7 @@ checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.104",
+ "syn 2.0.106",
]
[[package]]
@@ -6339,7 +6337,7 @@ checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.104",
+ "syn 2.0.106",
]
[[package]]
@@ -6772,7 +6770,7 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.104",
+ "syn 2.0.106",
"synstructure",
]
@@ -6784,7 +6782,7 @@ checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.104",
+ "syn 2.0.106",
"synstructure",
]
@@ -6805,7 +6803,7 @@ checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.104",
+ "syn 2.0.106",
]
[[package]]
@@ -6825,7 +6823,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.104",
+ "syn 2.0.106",
"synstructure",
]
@@ -6870,7 +6868,7 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.104",
+ "syn 2.0.106",
]
[[package]]
@@ -6881,5 +6879,5 @@ checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.104",
+ "syn 2.0.106",
]
diff --git a/RELEASES.md b/RELEASES.md
index b6dc06286467..33abe45ce462 100644
--- a/RELEASES.md
+++ b/RELEASES.md
@@ -1778,7 +1778,7 @@ Language
- [Undeprecate lint `unstable_features` and make use of it in the compiler.](https://github.com/rust-lang/rust/pull/118639/)
- [Make inductive cycles in coherence ambiguous always.](https://github.com/rust-lang/rust/pull/118649/)
- [Get rid of type-driven traversal in const-eval interning](https://github.com/rust-lang/rust/pull/119044/),
- only as a [future compatiblity lint](https://github.com/rust-lang/rust/pull/122204) for now.
+ only as a [future compatibility lint](https://github.com/rust-lang/rust/pull/122204) for now.
- [Deny braced macro invocations in let-else.](https://github.com/rust-lang/rust/pull/119062/)
diff --git a/compiler/rustc_abi/Cargo.toml b/compiler/rustc_abi/Cargo.toml
index 5f9afc46a1ac..83d96d8d04da 100644
--- a/compiler/rustc_abi/Cargo.toml
+++ b/compiler/rustc_abi/Cargo.toml
@@ -9,6 +9,7 @@ bitflags = "2.4.1"
rand = { version = "0.9.0", default-features = false, optional = true }
rand_xoshiro = { version = "0.7.0", optional = true }
rustc_data_structures = { path = "../rustc_data_structures", optional = true }
+rustc_error_messages = { path = "../rustc_error_messages", optional = true }
rustc_hashes = { path = "../rustc_hashes" }
rustc_index = { path = "../rustc_index", default-features = false }
rustc_macros = { path = "../rustc_macros", optional = true }
@@ -24,6 +25,7 @@ default = ["nightly", "randomize"]
# without depending on rustc_data_structures, rustc_macros and rustc_serialize
nightly = [
"dep:rustc_data_structures",
+ "dep:rustc_error_messages",
"dep:rustc_macros",
"dep:rustc_serialize",
"dep:rustc_span",
diff --git a/compiler/rustc_abi/src/extern_abi.rs b/compiler/rustc_abi/src/extern_abi.rs
index 29a3678abf3f..41d744e1946a 100644
--- a/compiler/rustc_abi/src/extern_abi.rs
+++ b/compiler/rustc_abi/src/extern_abi.rs
@@ -223,6 +223,9 @@ impl StableOrd for ExternAbi {
const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = ();
}
+#[cfg(feature = "nightly")]
+rustc_error_messages::into_diag_arg_using_display!(ExternAbi);
+
impl ExternAbi {
/// An ABI "like Rust"
///
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index 87c9c797ea5b..de3e0e0c87f5 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -3137,7 +3137,7 @@ impl FnRetTy {
#[derive(Clone, Copy, PartialEq, Encodable, Decodable, Debug, Walkable)]
pub enum Inline {
Yes,
- No,
+ No { had_parse_error: Result<(), ErrorGuaranteed> },
}
/// Module item kind.
@@ -3147,7 +3147,7 @@ pub enum ModKind {
/// or with definition outlined to a separate file `mod foo;` and already loaded from it.
/// The inner span is from the first token past `{` to the last token until `}`,
/// or from the first to the last token in the loaded file.
- Loaded(ThinVec>, Inline, ModSpans, Result<(), ErrorGuaranteed>),
+ Loaded(ThinVec>, Inline, ModSpans),
/// Module with definition outlined to a separate file `mod foo;` but not yet loaded from it.
Unloaded,
}
diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs
index fc816f2cb792..ea98bebd3055 100644
--- a/compiler/rustc_ast/src/token.rs
+++ b/compiler/rustc_ast/src/token.rs
@@ -7,6 +7,7 @@ pub use NtPatKind::*;
pub use TokenKind::*;
use rustc_macros::{Decodable, Encodable, HashStable_Generic};
use rustc_span::edition::Edition;
+use rustc_span::symbol::IdentPrintMode;
use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, kw, sym};
#[allow(clippy::useless_attribute)] // FIXME: following use of `hidden_glob_reexports` incorrectly triggers `useless_attribute` lint.
#[allow(hidden_glob_reexports)]
@@ -344,15 +345,24 @@ pub enum IdentIsRaw {
Yes,
}
-impl From for IdentIsRaw {
- fn from(b: bool) -> Self {
- if b { Self::Yes } else { Self::No }
+impl IdentIsRaw {
+ pub fn to_print_mode_ident(self) -> IdentPrintMode {
+ match self {
+ IdentIsRaw::No => IdentPrintMode::Normal,
+ IdentIsRaw::Yes => IdentPrintMode::RawIdent,
+ }
+ }
+ pub fn to_print_mode_lifetime(self) -> IdentPrintMode {
+ match self {
+ IdentIsRaw::No => IdentPrintMode::Normal,
+ IdentIsRaw::Yes => IdentPrintMode::RawLifetime,
+ }
}
}
-impl From for bool {
- fn from(is_raw: IdentIsRaw) -> bool {
- matches!(is_raw, IdentIsRaw::Yes)
+impl From for IdentIsRaw {
+ fn from(b: bool) -> Self {
+ if b { Self::Yes } else { Self::No }
}
}
diff --git a/compiler/rustc_ast/src/tokenstream.rs b/compiler/rustc_ast/src/tokenstream.rs
index e55399adfb85..f4f35a4d2ee0 100644
--- a/compiler/rustc_ast/src/tokenstream.rs
+++ b/compiler/rustc_ast/src/tokenstream.rs
@@ -907,6 +907,12 @@ impl TokenTreeCursor {
pub fn bump(&mut self) {
self.index += 1;
}
+
+ // For skipping ahead in rare circumstances.
+ #[inline]
+ pub fn bump_to_end(&mut self) {
+ self.index = self.stream.len();
+ }
}
/// A `TokenStream` cursor that produces `Token`s. It's a bit odd that
diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs
index 72817a0a9a05..bb559bd89212 100644
--- a/compiler/rustc_ast_lowering/src/item.rs
+++ b/compiler/rustc_ast_lowering/src/item.rs
@@ -251,7 +251,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
ItemKind::Mod(_, ident, mod_kind) => {
let ident = self.lower_ident(*ident);
match mod_kind {
- ModKind::Loaded(items, _, spans, _) => {
+ ModKind::Loaded(items, _, spans) => {
hir::ItemKind::Mod(ident, self.lower_mod(items, spans))
}
ModKind::Unloaded => panic!("`mod` items should have been loaded by now"),
@@ -1596,7 +1596,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
let safety = self.lower_safety(h.safety, default_safety);
// Treat safe `#[target_feature]` functions as unsafe, but also remember that we did so.
- let safety = if find_attr!(attrs, AttributeKind::TargetFeature { .. })
+ let safety = if find_attr!(attrs, AttributeKind::TargetFeature { was_forced: false, .. })
&& safety.is_safe()
&& !self.tcx.sess.target.is_like_wasm
{
diff --git a/compiler/rustc_ast_passes/Cargo.toml b/compiler/rustc_ast_passes/Cargo.toml
index 1940628b44a5..3e04f8b11ec9 100644
--- a/compiler/rustc_ast_passes/Cargo.toml
+++ b/compiler/rustc_ast_passes/Cargo.toml
@@ -15,7 +15,6 @@ rustc_errors = { path = "../rustc_errors" }
rustc_feature = { path = "../rustc_feature" }
rustc_fluent_macro = { path = "../rustc_fluent_macro" }
rustc_macros = { path = "../rustc_macros" }
-rustc_parse = { path = "../rustc_parse" }
rustc_session = { path = "../rustc_session" }
rustc_span = { path = "../rustc_span" }
rustc_target = { path = "../rustc_target" }
diff --git a/compiler/rustc_ast_passes/messages.ftl b/compiler/rustc_ast_passes/messages.ftl
index 73cbcdd30ced..a95f14439684 100644
--- a/compiler/rustc_ast_passes/messages.ftl
+++ b/compiler/rustc_ast_passes/messages.ftl
@@ -20,6 +20,10 @@ ast_passes_abi_must_not_have_return_type=
.note = functions with the {$abi} ABI cannot have a return type
.help = remove the return type
+ast_passes_abi_x86_interrupt =
+ invalid signature for `extern "x86-interrupt"` function
+ .note = functions with the "x86-interrupt" ABI must be have either 1 or 2 parameters (but found {$param_count})
+
ast_passes_assoc_const_without_body =
associated constant in `impl` without body
.suggestion = provide a definition for the constant
@@ -109,6 +113,10 @@ ast_passes_extern_without_abi = `extern` declarations without an explicit ABI ar
.suggestion = specify an ABI
.help = prior to Rust 2024, a default ABI was inferred
+ast_passes_extern_without_abi_sugg = `extern` declarations without an explicit ABI are deprecated
+ .label = ABI should be specified here
+ .suggestion = explicitly specify the {$default_abi} ABI
+
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 0c72f3190074..6133cc3548b5 100644
--- a/compiler/rustc_ast_passes/src/ast_validation.rs
+++ b/compiler/rustc_ast_passes/src/ast_validation.rs
@@ -25,16 +25,16 @@ use rustc_abi::{CanonAbi, ExternAbi, InterruptKind};
use rustc_ast::visit::{AssocCtxt, BoundKind, FnCtxt, FnKind, Visitor, walk_list};
use rustc_ast::*;
use rustc_ast_pretty::pprust::{self, State};
+use rustc_attr_parsing::validate_attr;
use rustc_data_structures::fx::FxIndexMap;
-use rustc_errors::DiagCtxtHandle;
+use rustc_errors::{DiagCtxtHandle, LintBuffer};
use rustc_feature::Features;
-use rustc_parse::validate_attr;
use rustc_session::Session;
+use rustc_session::lint::BuiltinLintDiag;
use rustc_session::lint::builtin::{
DEPRECATED_WHERE_CLAUSE_LOCATION, MISSING_ABI, MISSING_UNSAFE_ON_EXTERN,
PATTERNS_IN_FNS_WITHOUT_BODY,
};
-use rustc_session::lint::{BuiltinLintDiag, LintBuffer};
use rustc_span::{Ident, Span, kw, sym};
use rustc_target::spec::{AbiMap, AbiMapping};
use thin_vec::thin_vec;
@@ -405,6 +405,17 @@ impl<'a> AstValidator<'a> {
if let InterruptKind::X86 = interrupt_kind {
// "x86-interrupt" is special because it does have arguments.
// FIXME(workingjubilee): properly lint on acceptable input types.
+ let inputs = &sig.decl.inputs;
+ let param_count = inputs.len();
+ if !matches!(param_count, 1 | 2) {
+ let mut spans: Vec =
+ inputs.iter().map(|arg| arg.span).collect();
+ if spans.is_empty() {
+ spans = vec![sig.span];
+ }
+ self.dcx().emit_err(errors::AbiX86Interrupt { spans, param_count });
+ }
+
if let FnRetTy::Ty(ref ret_ty) = sig.decl.output
&& match &ret_ty.kind {
TyKind::Never => false,
@@ -865,7 +876,7 @@ impl<'a> AstValidator<'a> {
MISSING_ABI,
id,
span,
- BuiltinLintDiag::MissingAbi(span, ExternAbi::FALLBACK),
+ errors::MissingAbiSugg { span, default_abi: ExternAbi::FALLBACK },
)
}
}
@@ -1169,7 +1180,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
self.dcx().emit_err(errors::UnsafeItem { span, kind: "module" });
}
// Ensure that `path` attributes on modules are recorded as used (cf. issue #35584).
- if !matches!(mod_kind, ModKind::Loaded(_, Inline::Yes, _, _))
+ if !matches!(mod_kind, ModKind::Loaded(_, Inline::Yes, _))
&& !attr::contains_name(&item.attrs, sym::path)
{
self.check_mod_file_item_asciionly(*ident);
diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs
index 5ecc0d21411d..ae8f056cb4e6 100644
--- a/compiler/rustc_ast_passes/src/errors.rs
+++ b/compiler/rustc_ast_passes/src/errors.rs
@@ -4,7 +4,7 @@ use rustc_abi::ExternAbi;
use rustc_ast::ParamKindOrd;
use rustc_errors::codes::*;
use rustc_errors::{Applicability, Diag, EmissionGuarantee, Subdiagnostic};
-use rustc_macros::{Diagnostic, Subdiagnostic};
+use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
use rustc_span::{Ident, Span, Symbol};
use crate::fluent_generated as fluent;
@@ -815,6 +815,14 @@ pub(crate) struct MissingAbi {
pub span: Span,
}
+#[derive(LintDiagnostic)]
+#[diag(ast_passes_extern_without_abi_sugg)]
+pub(crate) struct MissingAbiSugg {
+ #[suggestion(code = "extern {default_abi}", applicability = "machine-applicable")]
+ pub span: Span,
+ pub default_abi: ExternAbi,
+}
+
#[derive(Diagnostic)]
#[diag(ast_passes_abi_custom_safe_foreign_function)]
pub(crate) struct AbiCustomSafeForeignFunction {
@@ -891,3 +899,12 @@ pub(crate) struct AbiMustNotHaveReturnType {
pub span: Span,
pub abi: ExternAbi,
}
+
+#[derive(Diagnostic)]
+#[diag(ast_passes_abi_x86_interrupt)]
+#[note]
+pub(crate) struct AbiX86Interrupt {
+ #[primary_span]
+ pub spans: Vec,
+ pub param_count: usize,
+}
diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs
index c9344a76a7b3..e763c9d69fc2 100644
--- a/compiler/rustc_ast_passes/src/feature_gate.rs
+++ b/compiler/rustc_ast_passes/src/feature_gate.rs
@@ -524,6 +524,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
gate_all!(where_clause_attrs, "attributes in `where` clause are unstable");
gate_all!(super_let, "`super let` is experimental");
gate_all!(frontmatter, "frontmatters are experimental");
+ gate_all!(coroutines, "coroutine syntax is experimental");
if !visitor.features.never_patterns() {
if let Some(spans) = spans.get(&sym::never_patterns) {
diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs
index 85f76036df72..a056ce3e29d2 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state.rs
@@ -10,7 +10,7 @@ use std::borrow::Cow;
use std::sync::Arc;
use rustc_ast::attr::AttrIdGenerator;
-use rustc_ast::token::{self, CommentKind, Delimiter, IdentIsRaw, Token, TokenKind};
+use rustc_ast::token::{self, CommentKind, Delimiter, Token, TokenKind};
use rustc_ast::tokenstream::{Spacing, TokenStream, TokenTree};
use rustc_ast::util::classify;
use rustc_ast::util::comments::{Comment, CommentStyle};
@@ -441,7 +441,7 @@ pub trait PrintState<'a>: std::ops::Deref + std::ops::Dere
fn print_generic_args(&mut self, args: &ast::GenericArgs, colons_before_params: bool);
fn print_ident(&mut self, ident: Ident) {
- self.word(IdentPrinter::for_ast_ident(ident, ident.is_raw_guess()).to_string());
+ self.word(IdentPrinter::for_ast_ident(ident, ident.guess_print_mode()).to_string());
self.ann_post(ident)
}
@@ -1015,17 +1015,16 @@ pub trait PrintState<'a>: std::ops::Deref + std::ops::Dere
/* Name components */
token::Ident(name, is_raw) => {
- IdentPrinter::new(name, is_raw.into(), convert_dollar_crate).to_string().into()
+ IdentPrinter::new(name, is_raw.to_print_mode_ident(), convert_dollar_crate)
+ .to_string()
+ .into()
}
token::NtIdent(ident, is_raw) => {
- IdentPrinter::for_ast_ident(ident, is_raw.into()).to_string().into()
+ IdentPrinter::for_ast_ident(ident, is_raw.to_print_mode_ident()).to_string().into()
}
- token::Lifetime(name, IdentIsRaw::No)
- | token::NtLifetime(Ident { name, .. }, IdentIsRaw::No) => name.to_string().into(),
- token::Lifetime(name, IdentIsRaw::Yes)
- | token::NtLifetime(Ident { name, .. }, IdentIsRaw::Yes) => {
- format!("'r#{}", &name.as_str()[1..]).into()
+ token::Lifetime(name, is_raw) | token::NtLifetime(Ident { name, .. }, is_raw) => {
+ IdentPrinter::new(name, is_raw.to_print_mode_lifetime(), None).to_string().into()
}
/* Other */
diff --git a/compiler/rustc_attr_parsing/Cargo.toml b/compiler/rustc_attr_parsing/Cargo.toml
index bac89373b678..fd8f7ffb2ed6 100644
--- a/compiler/rustc_attr_parsing/Cargo.toml
+++ b/compiler/rustc_attr_parsing/Cargo.toml
@@ -5,7 +5,6 @@ edition = "2024"
[dependencies]
# tidy-alphabetical-start
-itertools = "0.12"
rustc_abi = { path = "../rustc_abi" }
rustc_ast = { path = "../rustc_ast" }
rustc_ast_pretty = { path = "../rustc_ast_pretty" }
@@ -15,6 +14,7 @@ rustc_fluent_macro = { path = "../rustc_fluent_macro" }
rustc_hir = { path = "../rustc_hir" }
rustc_lexer = { path = "../rustc_lexer" }
rustc_macros = { path = "../rustc_macros" }
+rustc_parse = { path = "../rustc_parse" }
rustc_session = { path = "../rustc_session" }
rustc_span = { path = "../rustc_span" }
thin-vec = "0.2.12"
diff --git a/compiler/rustc_attr_parsing/messages.ftl b/compiler/rustc_attr_parsing/messages.ftl
index 4fb66a816522..40e9d597530b 100644
--- a/compiler/rustc_attr_parsing/messages.ftl
+++ b/compiler/rustc_attr_parsing/messages.ftl
@@ -12,9 +12,11 @@ attr_parsing_empty_attribute =
attr_parsing_invalid_target = `#[{$name}]` attribute cannot be used on {$target}
.help = `#[{$name}]` can {$only}be applied to {$applied}
+ .suggestion = remove the attribute
attr_parsing_invalid_target_lint = `#[{$name}]` attribute cannot be used on {$target}
.warn = {-attr_parsing_previously_accepted}
.help = `#[{$name}]` can {$only}be applied to {$applied}
+ .suggestion = remove the attribute
attr_parsing_empty_confusables =
expected at least one confusable name
@@ -168,3 +170,22 @@ attr_parsing_unused_multiple =
-attr_parsing_previously_accepted =
this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+
+attr_parsing_meta_bad_delim = wrong meta list delimiters
+attr_parsing_meta_bad_delim_suggestion = the delimiters should be `(` and `)`
+
+attr_parsing_unsafe_attr_outside_unsafe = unsafe attribute used without unsafe
+ .label = usage of unsafe attribute
+attr_parsing_unsafe_attr_outside_unsafe_suggestion = wrap the attribute in `unsafe(...)`
+
+attr_parsing_invalid_attr_unsafe = `{$name}` is not an unsafe attribute
+ .label = this is not an unsafe attribute
+ .suggestion = remove the `unsafe(...)`
+ .note = extraneous unsafe is not allowed in attributes
+
+attr_parsing_invalid_meta_item = expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found {$descr}
+ .remove_neg_sugg = negative numbers are not literals, try removing the `-` sign
+ .quote_ident_sugg = surround the identifier with quotation marks to make it into a string literal
+
+attr_parsing_suffixed_literal_in_attribute = suffixed literals are not allowed in attributes
+ .help = instead of using a suffixed literal (`1u8`, `1.0f32`, etc.), use an unsuffixed version (`1`, `1.0`, etc.)
diff --git a/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs b/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs
index 4d995027814b..088fa73d7427 100644
--- a/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs
@@ -1,14 +1,6 @@
use std::iter;
-use rustc_feature::{AttributeTemplate, template};
-use rustc_hir::attrs::AttributeKind;
-use rustc_hir::{MethodKind, Target};
-use rustc_span::{Span, Symbol, sym};
-
-use super::{CombineAttributeParser, ConvertFn};
-use crate::context::MaybeWarn::{Allow, Warn};
-use crate::context::{AcceptContext, AllowedTargets, Stage};
-use crate::parser::ArgParser;
+use super::prelude::*;
use crate::session_diagnostics;
pub(crate) struct AllowInternalUnstableParser;
diff --git a/compiler/rustc_attr_parsing/src/attributes/body.rs b/compiler/rustc_attr_parsing/src/attributes/body.rs
index 88540384621d..a1492d761946 100644
--- a/compiler/rustc_attr_parsing/src/attributes/body.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/body.rs
@@ -1,12 +1,6 @@
//! Attributes that can be found in function body.
-use rustc_hir::Target;
-use rustc_hir::attrs::AttributeKind;
-use rustc_span::{Symbol, sym};
-
-use super::{NoArgsAttributeParser, OnDuplicate};
-use crate::context::MaybeWarn::Allow;
-use crate::context::{AllowedTargets, Stage};
+use super::prelude::*;
pub(crate) struct CoroutineParser;
diff --git a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs
index 6ea073896c2f..b884f8f38328 100644
--- a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs
@@ -1,16 +1,7 @@
-use rustc_feature::{AttributeTemplate, template};
-use rustc_hir::attrs::{AttributeKind, CoverageAttrKind, OptimizeAttr, UsedBy};
-use rustc_hir::{MethodKind, Target};
+use rustc_hir::attrs::{CoverageAttrKind, OptimizeAttr, SanitizerSet, UsedBy};
use rustc_session::parse::feature_err;
-use rustc_span::{Span, Symbol, sym};
-use super::{
- AcceptMapping, AttributeOrder, AttributeParser, CombineAttributeParser, ConvertFn,
- NoArgsAttributeParser, OnDuplicate, SingleAttributeParser,
-};
-use crate::context::MaybeWarn::{Allow, Warn};
-use crate::context::{AcceptContext, AllowedTargets, FinalizeContext, Stage};
-use crate::parser::ArgParser;
+use super::prelude::*;
use crate::session_diagnostics::{NakedFunctionIncompatibleAttribute, NullOnExport};
pub(crate) struct OptimizeParser;
@@ -44,7 +35,7 @@ impl SingleAttributeParser for OptimizeParser {
Some(sym::speed) => OptimizeAttr::Speed,
Some(sym::none) => OptimizeAttr::DoNotOptimize,
_ => {
- cx.expected_specific_argument(single.span(), vec!["size", "speed", "none"]);
+ cx.expected_specific_argument(single.span(), &[sym::size, sym::speed, sym::none]);
OptimizeAttr::Default
}
};
@@ -91,7 +82,7 @@ impl SingleAttributeParser for CoverageParser {
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option {
let Some(args) = args.list() else {
- cx.expected_specific_argument_and_list(cx.attr_span, vec!["on", "off"]);
+ cx.expected_specific_argument_and_list(cx.attr_span, &[sym::on, sym::off]);
return None;
};
@@ -100,7 +91,8 @@ impl SingleAttributeParser for CoverageParser {
return None;
};
- let fail_incorrect_argument = |span| cx.expected_specific_argument(span, vec!["on", "off"]);
+ let fail_incorrect_argument =
+ |span| cx.expected_specific_argument(span, &[sym::on, sym::off]);
let Some(arg) = arg.meta_item() else {
fail_incorrect_argument(args.span);
@@ -352,7 +344,7 @@ impl AttributeParser for UsedParser {
UsedBy::Linker
}
_ => {
- cx.expected_specific_argument(l.span(), vec!["compiler", "linker"]);
+ cx.expected_specific_argument(l.span(), &[sym::compiler, sym::linker]);
return;
}
}
@@ -385,57 +377,68 @@ impl AttributeParser for UsedParser {
}
}
+fn parse_tf_attribute<'c, S: Stage>(
+ cx: &'c mut AcceptContext<'_, '_, S>,
+ args: &'c ArgParser<'_>,
+) -> impl IntoIterator- + 'c {
+ let mut features = Vec::new();
+ let ArgParser::List(list) = args else {
+ cx.expected_list(cx.attr_span);
+ return features;
+ };
+ if list.is_empty() {
+ cx.warn_empty_attribute(cx.attr_span);
+ return features;
+ }
+ for item in list.mixed() {
+ let Some(name_value) = item.meta_item() else {
+ cx.expected_name_value(item.span(), Some(sym::enable));
+ return features;
+ };
+
+ // Validate name
+ let Some(name) = name_value.path().word_sym() else {
+ cx.expected_name_value(name_value.path().span(), Some(sym::enable));
+ return features;
+ };
+ if name != sym::enable {
+ cx.expected_name_value(name_value.path().span(), Some(sym::enable));
+ return features;
+ }
+
+ // Use value
+ let Some(name_value) = name_value.args().name_value() else {
+ cx.expected_name_value(item.span(), Some(sym::enable));
+ return features;
+ };
+ let Some(value_str) = name_value.value_as_str() else {
+ cx.expected_string_literal(name_value.value_span, Some(name_value.value_as_lit()));
+ return features;
+ };
+ for feature in value_str.as_str().split(",") {
+ features.push((Symbol::intern(feature), item.span()));
+ }
+ }
+ features
+}
+
pub(crate) struct TargetFeatureParser;
impl CombineAttributeParser
for TargetFeatureParser {
type Item = (Symbol, Span);
const PATH: &[Symbol] = &[sym::target_feature];
- const CONVERT: ConvertFn = |items, span| AttributeKind::TargetFeature(items, span);
+ const CONVERT: ConvertFn = |items, span| AttributeKind::TargetFeature {
+ features: items,
+ attr_span: span,
+ was_forced: false,
+ };
const TEMPLATE: AttributeTemplate = template!(List: &["enable = \"feat1, feat2\""]);
fn extend<'c>(
cx: &'c mut AcceptContext<'_, '_, S>,
args: &'c ArgParser<'_>,
) -> impl IntoIterator- + 'c {
- let mut features = Vec::new();
- let ArgParser::List(list) = args else {
- cx.expected_list(cx.attr_span);
- return features;
- };
- if list.is_empty() {
- cx.warn_empty_attribute(cx.attr_span);
- return features;
- }
- for item in list.mixed() {
- let Some(name_value) = item.meta_item() else {
- cx.expected_name_value(item.span(), Some(sym::enable));
- return features;
- };
-
- // Validate name
- let Some(name) = name_value.path().word_sym() else {
- cx.expected_name_value(name_value.path().span(), Some(sym::enable));
- return features;
- };
- if name != sym::enable {
- cx.expected_name_value(name_value.path().span(), Some(sym::enable));
- return features;
- }
-
- // Use value
- let Some(name_value) = name_value.args().name_value() else {
- cx.expected_name_value(item.span(), Some(sym::enable));
- return features;
- };
- let Some(value_str) = name_value.value_as_str() else {
- cx.expected_string_literal(name_value.value_span, Some(name_value.value_as_lit()));
- return features;
- };
- for feature in value_str.as_str().split(",") {
- features.push((Symbol::intern(feature), item.span()));
- }
- }
- features
+ parse_tf_attribute(cx, args)
}
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
@@ -449,3 +452,131 @@ impl CombineAttributeParser
for TargetFeatureParser {
Warn(Target::MacroDef),
]);
}
+
+pub(crate) struct ForceTargetFeatureParser;
+
+impl CombineAttributeParser for ForceTargetFeatureParser {
+ type Item = (Symbol, Span);
+ const PATH: &[Symbol] = &[sym::force_target_feature];
+ const CONVERT: ConvertFn = |items, span| AttributeKind::TargetFeature {
+ features: items,
+ attr_span: span,
+ was_forced: true,
+ };
+ const TEMPLATE: AttributeTemplate = template!(List: &["enable = \"feat1, feat2\""]);
+ const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
+ Allow(Target::Fn),
+ Allow(Target::Method(MethodKind::Inherent)),
+ Allow(Target::Method(MethodKind::Trait { body: true })),
+ Allow(Target::Method(MethodKind::TraitImpl)),
+ ]);
+
+ fn extend<'c>(
+ cx: &'c mut AcceptContext<'_, '_, S>,
+ args: &'c ArgParser<'_>,
+ ) -> impl IntoIterator- + 'c {
+ parse_tf_attribute(cx, args)
+ }
+}
+
+pub(crate) struct SanitizeParser;
+
+impl SingleAttributeParser
for SanitizeParser {
+ const PATH: &[Symbol] = &[sym::sanitize];
+
+ // FIXME: still checked in check_attrs.rs
+ const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS);
+
+ const TEMPLATE: AttributeTemplate = template!(List: &[
+ r#"address = "on|off""#,
+ r#"kernel_address = "on|off""#,
+ r#"cfi = "on|off""#,
+ r#"hwaddress = "on|off""#,
+ r#"kcfi = "on|off""#,
+ r#"memory = "on|off""#,
+ r#"memtag = "on|off""#,
+ r#"shadow_call_stack = "on|off""#,
+ r#"thread = "on|off""#
+ ]);
+
+ const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
+ const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error;
+
+ fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option {
+ let Some(list) = args.list() else {
+ cx.expected_list(cx.attr_span);
+ return None;
+ };
+
+ let mut on_set = SanitizerSet::empty();
+ let mut off_set = SanitizerSet::empty();
+
+ for item in list.mixed() {
+ let Some(item) = item.meta_item() else {
+ cx.expected_name_value(item.span(), None);
+ continue;
+ };
+
+ let path = item.path().word_sym();
+ let Some(value) = item.args().name_value() else {
+ cx.expected_name_value(item.span(), path);
+ continue;
+ };
+
+ let mut apply = |s: SanitizerSet| {
+ let is_on = match value.value_as_str() {
+ Some(sym::on) => true,
+ Some(sym::off) => false,
+ Some(_) => {
+ cx.expected_specific_argument_strings(
+ value.value_span,
+ &[sym::on, sym::off],
+ );
+ return;
+ }
+ None => {
+ cx.expected_string_literal(value.value_span, Some(value.value_as_lit()));
+ return;
+ }
+ };
+
+ if is_on {
+ on_set |= s;
+ } else {
+ off_set |= s;
+ }
+ };
+
+ match path {
+ Some(sym::address) | Some(sym::kernel_address) => {
+ apply(SanitizerSet::ADDRESS | SanitizerSet::KERNELADDRESS)
+ }
+ Some(sym::cfi) => apply(SanitizerSet::CFI),
+ Some(sym::kcfi) => apply(SanitizerSet::KCFI),
+ Some(sym::memory) => apply(SanitizerSet::MEMORY),
+ Some(sym::memtag) => apply(SanitizerSet::MEMTAG),
+ Some(sym::shadow_call_stack) => apply(SanitizerSet::SHADOWCALLSTACK),
+ Some(sym::thread) => apply(SanitizerSet::THREAD),
+ Some(sym::hwaddress) => apply(SanitizerSet::HWADDRESS),
+ _ => {
+ cx.expected_specific_argument_strings(
+ item.path().span(),
+ &[
+ sym::address,
+ sym::cfi,
+ sym::kcfi,
+ sym::memory,
+ sym::memtag,
+ sym::shadow_call_stack,
+ sym::thread,
+ sym::hwaddress,
+ ],
+ );
+ continue;
+ }
+ }
+ }
+
+ Some(AttributeKind::Sanitize { on_set, off_set, span: cx.attr_span })
+ }
+}
diff --git a/compiler/rustc_attr_parsing/src/attributes/confusables.rs b/compiler/rustc_attr_parsing/src/attributes/confusables.rs
index 00f949c82c5a..97e78dfb136b 100644
--- a/compiler/rustc_attr_parsing/src/attributes/confusables.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/confusables.rs
@@ -1,13 +1,6 @@
-use rustc_feature::template;
-use rustc_hir::attrs::AttributeKind;
-use rustc_hir::{MethodKind, Target};
-use rustc_span::{Span, Symbol, sym};
-use thin_vec::ThinVec;
+use super::prelude::*;
+use crate::session_diagnostics::EmptyConfusables;
-use super::{AcceptMapping, AttributeParser};
-use crate::context::MaybeWarn::Allow;
-use crate::context::{AllowedTargets, FinalizeContext, Stage};
-use crate::session_diagnostics;
#[derive(Default)]
pub(crate) struct ConfusablesParser {
confusables: ThinVec,
@@ -25,7 +18,7 @@ impl AttributeParser for ConfusablesParser {
};
if list.is_empty() {
- cx.emit_err(session_diagnostics::EmptyConfusables { span: cx.attr_span });
+ cx.emit_err(EmptyConfusables { span: cx.attr_span });
}
for param in list.mixed() {
diff --git a/compiler/rustc_attr_parsing/src/attributes/crate_level.rs b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs
new file mode 100644
index 000000000000..9175d7479e12
--- /dev/null
+++ b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs
@@ -0,0 +1,33 @@
+use super::prelude::*;
+
+pub(crate) struct CrateNameParser;
+
+impl SingleAttributeParser for CrateNameParser {
+ const PATH: &[Symbol] = &[sym::crate_name];
+ const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
+ const ON_DUPLICATE: OnDuplicate = OnDuplicate::WarnButFutureError;
+ const TEMPLATE: AttributeTemplate = template!(NameValueStr: "name");
+
+ // FIXME: crate name is allowed on all targets and ignored,
+ // even though it should only be valid on crates of course
+ const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS);
+
+ fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option {
+ let ArgParser::NameValue(n) = args else {
+ cx.expected_name_value(cx.attr_span, None);
+ return None;
+ };
+
+ let Some(name) = n.value_as_str() else {
+ cx.expected_string_literal(n.value_span, Some(n.value_as_lit()));
+ return None;
+ };
+
+ Some(AttributeKind::CrateName {
+ name,
+ name_span: n.value_span,
+ attr_span: cx.attr_span,
+ style: cx.attr_style,
+ })
+ }
+}
diff --git a/compiler/rustc_attr_parsing/src/attributes/deprecation.rs b/compiler/rustc_attr_parsing/src/attributes/deprecation.rs
index 8101c91460f2..31c698228ef4 100644
--- a/compiler/rustc_attr_parsing/src/attributes/deprecation.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/deprecation.rs
@@ -1,14 +1,11 @@
-use rustc_feature::{AttributeTemplate, template};
-use rustc_hir::attrs::{AttributeKind, DeprecatedSince, Deprecation};
-use rustc_hir::{MethodKind, Target};
-use rustc_span::{Span, Symbol, sym};
+use rustc_hir::attrs::{DeprecatedSince, Deprecation};
+use super::prelude::*;
use super::util::parse_version;
-use super::{AttributeOrder, OnDuplicate, SingleAttributeParser};
-use crate::context::MaybeWarn::{Allow, Error};
-use crate::context::{AcceptContext, AllowedTargets, Stage};
-use crate::parser::ArgParser;
-use crate::session_diagnostics;
+use crate::session_diagnostics::{
+ DeprecatedItemSuggestion, InvalidSince, MissingNote, MissingSince,
+};
+
pub(crate) struct DeprecationParser;
fn get(
@@ -54,6 +51,8 @@ impl SingleAttributeParser for DeprecationParser {
Allow(Target::TyAlias),
Allow(Target::Use),
Allow(Target::ForeignFn),
+ Allow(Target::ForeignStatic),
+ Allow(Target::ForeignTy),
Allow(Target::Field),
Allow(Target::Trait),
Allow(Target::AssocTy),
@@ -100,7 +99,7 @@ impl SingleAttributeParser for DeprecationParser {
}
Some(name @ sym::suggestion) => {
if !features.deprecated_suggestion() {
- cx.emit_err(session_diagnostics::DeprecatedItemSuggestion {
+ cx.emit_err(DeprecatedItemSuggestion {
span: param.span(),
is_nightly: cx.sess().is_nightly_build(),
details: (),
@@ -142,18 +141,18 @@ impl SingleAttributeParser for DeprecationParser {
} else if let Some(version) = parse_version(since) {
DeprecatedSince::RustcVersion(version)
} else {
- cx.emit_err(session_diagnostics::InvalidSince { span: cx.attr_span });
+ cx.emit_err(InvalidSince { span: cx.attr_span });
DeprecatedSince::Err
}
} else if is_rustc {
- cx.emit_err(session_diagnostics::MissingSince { span: cx.attr_span });
+ cx.emit_err(MissingSince { span: cx.attr_span });
DeprecatedSince::Err
} else {
DeprecatedSince::Unspecified
};
if is_rustc && note.is_none() {
- cx.emit_err(session_diagnostics::MissingNote { span: cx.attr_span });
+ cx.emit_err(MissingNote { span: cx.attr_span });
return None;
}
diff --git a/compiler/rustc_attr_parsing/src/attributes/dummy.rs b/compiler/rustc_attr_parsing/src/attributes/dummy.rs
index 85842b1b5c5f..7293cee842c2 100644
--- a/compiler/rustc_attr_parsing/src/attributes/dummy.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/dummy.rs
@@ -3,8 +3,10 @@ use rustc_hir::attrs::AttributeKind;
use rustc_span::{Symbol, sym};
use crate::attributes::{AttributeOrder, OnDuplicate, SingleAttributeParser};
-use crate::context::{ALL_TARGETS, AcceptContext, AllowedTargets, Stage};
+use crate::context::{AcceptContext, Stage};
use crate::parser::ArgParser;
+use crate::target_checking::{ALL_TARGETS, AllowedTargets};
+
pub(crate) struct DummyParser;
impl SingleAttributeParser for DummyParser {
const PATH: &[Symbol] = &[sym::rustc_dummy];
diff --git a/compiler/rustc_attr_parsing/src/attributes/inline.rs b/compiler/rustc_attr_parsing/src/attributes/inline.rs
index 6a659a95b856..39d5ff85d9f7 100644
--- a/compiler/rustc_attr_parsing/src/attributes/inline.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/inline.rs
@@ -2,17 +2,10 @@
// note: need to model better how duplicate attr errors work when not using
// SingleAttributeParser which is what we have two of here.
-use rustc_feature::{AttributeTemplate, template};
use rustc_hir::attrs::{AttributeKind, InlineAttr};
-use rustc_hir::lints::AttributeLintKind;
-use rustc_hir::{MethodKind, Target};
-use rustc_span::{Symbol, sym};
-use super::{AcceptContext, AttributeOrder, OnDuplicate};
-use crate::attributes::SingleAttributeParser;
-use crate::context::MaybeWarn::{Allow, Warn};
-use crate::context::{AllowedTargets, Stage};
-use crate::parser::ArgParser;
+use super::prelude::*;
+
pub(crate) struct InlineParser;
impl SingleAttributeParser for InlineParser {
@@ -56,14 +49,14 @@ impl SingleAttributeParser for InlineParser {
Some(AttributeKind::Inline(InlineAttr::Never, cx.attr_span))
}
_ => {
- cx.expected_specific_argument(l.span(), vec!["always", "never"]);
+ cx.expected_specific_argument(l.span(), &[sym::always, sym::never]);
return None;
}
}
}
ArgParser::NameValue(_) => {
- let suggestions =
- >::TEMPLATE.suggestions(false, "inline");
+ let suggestions = >::TEMPLATE
+ .suggestions(cx.attr_style, "inline");
let span = cx.attr_span;
cx.emit_lint(AttributeLintKind::IllFormedAttributeInput { suggestions }, span);
return None;
diff --git a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs
index 552b9dfabc29..66b02697c771 100644
--- a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs
@@ -1,16 +1,10 @@
-use rustc_feature::{AttributeTemplate, template};
use rustc_hir::attrs::AttributeKind::{LinkName, LinkOrdinal, LinkSection};
-use rustc_hir::attrs::{AttributeKind, Linkage};
-use rustc_hir::{MethodKind, Target};
-use rustc_span::{Span, Symbol, sym};
+use rustc_hir::attrs::Linkage;
-use crate::attributes::{
- AttributeOrder, NoArgsAttributeParser, OnDuplicate, SingleAttributeParser,
-};
-use crate::context::MaybeWarn::Allow;
-use crate::context::{ALL_TARGETS, AcceptContext, AllowedTargets, Stage, parse_single_integer};
-use crate::parser::ArgParser;
+use super::prelude::*;
+use super::util::parse_single_integer;
use crate::session_diagnostics::{LinkOrdinalOutOfRange, NullOnLinkSection};
+
pub(crate) struct LinkNameParser;
impl SingleAttributeParser for LinkNameParser {
@@ -212,16 +206,16 @@ impl SingleAttributeParser for LinkageParser {
_ => {
cx.expected_specific_argument(
name_value.value_span,
- vec![
- "available_externally",
- "common",
- "extern_weak",
- "external",
- "internal",
- "linkonce",
- "linkonce_odr",
- "weak",
- "weak_odr",
+ &[
+ sym::available_externally,
+ sym::common,
+ sym::extern_weak,
+ sym::external,
+ sym::internal,
+ sym::linkonce,
+ sym::linkonce_odr,
+ sym::weak,
+ sym::weak_odr,
],
);
return None;
diff --git a/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs b/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs
index 2b586d4003cd..63b0809d0d8c 100644
--- a/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs
@@ -1,10 +1,5 @@
-use rustc_hir::attrs::AttributeKind;
-use rustc_hir::{MethodKind, Target};
-use rustc_span::{Span, Symbol, sym};
+use super::prelude::*;
-use crate::attributes::{NoArgsAttributeParser, OnDuplicate};
-use crate::context::MaybeWarn::{Allow, Error};
-use crate::context::{AllowedTargets, Stage};
pub(crate) struct AsPtrParser;
impl NoArgsAttributeParser for AsPtrParser {
const PATH: &[Symbol] = &[sym::rustc_as_ptr];
diff --git a/compiler/rustc_attr_parsing/src/attributes/loop_match.rs b/compiler/rustc_attr_parsing/src/attributes/loop_match.rs
index 242e2f2c1bc0..528090b8673d 100644
--- a/compiler/rustc_attr_parsing/src/attributes/loop_match.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/loop_match.rs
@@ -1,10 +1,5 @@
-use rustc_hir::Target;
-use rustc_hir::attrs::AttributeKind;
-use rustc_span::{Span, Symbol, sym};
+use super::prelude::*;
-use crate::attributes::{NoArgsAttributeParser, OnDuplicate};
-use crate::context::MaybeWarn::Allow;
-use crate::context::{AllowedTargets, Stage};
pub(crate) struct LoopMatchParser;
impl NoArgsAttributeParser for LoopMatchParser {
const PATH: &[Symbol] = &[sym::loop_match];
diff --git a/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs
index c9b5dd35fa1d..180130c7be4f 100644
--- a/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs
@@ -1,15 +1,9 @@
use rustc_errors::DiagArgValue;
-use rustc_feature::{AttributeTemplate, template};
-use rustc_hir::Target;
-use rustc_hir::attrs::{AttributeKind, MacroUseArgs};
-use rustc_span::{Span, Symbol, sym};
-use thin_vec::ThinVec;
+use rustc_hir::attrs::MacroUseArgs;
+
+use super::prelude::*;
+use crate::session_diagnostics::IllFormedAttributeInputLint;
-use crate::attributes::{AcceptMapping, AttributeParser, NoArgsAttributeParser, OnDuplicate};
-use crate::context::MaybeWarn::{Allow, Error, Warn};
-use crate::context::{AcceptContext, AllowedTargets, FinalizeContext, Stage};
-use crate::parser::ArgParser;
-use crate::session_diagnostics;
pub(crate) struct MacroEscapeParser;
impl NoArgsAttributeParser for MacroEscapeParser {
const PATH: &[Symbol] = &[sym::macro_escape];
@@ -107,8 +101,8 @@ impl AttributeParser for MacroUseParser {
}
}
ArgParser::NameValue(_) => {
- let suggestions = MACRO_USE_TEMPLATE.suggestions(false, sym::macro_use);
- cx.emit_err(session_diagnostics::IllFormedAttributeInputLint {
+ let suggestions = MACRO_USE_TEMPLATE.suggestions(cx.attr_style, sym::macro_use);
+ cx.emit_err(IllFormedAttributeInputLint {
num_suggestions: suggestions.len(),
suggestions: DiagArgValue::StrListSepByAnd(
suggestions.into_iter().map(|s| format!("`{s}`").into()).collect(),
diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs
index ed5d1d92b8ca..9dad9c893f0a 100644
--- a/compiler/rustc_attr_parsing/src/attributes/mod.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs
@@ -7,9 +7,9 @@
//! Specifically, you might not care about managing the state of your [`AttributeParser`]
//! state machine yourself. In this case you can choose to implement:
//!
-//! - [`SingleAttributeParser`]: makes it easy to implement an attribute which should error if it
+//! - [`SingleAttributeParser`](crate::attributes::SingleAttributeParser): makes it easy to implement an attribute which should error if it
//! appears more than once in a list of attributes
-//! - [`CombineAttributeParser`]: makes it easy to implement an attribute which should combine the
+//! - [`CombineAttributeParser`](crate::attributes::CombineAttributeParser): makes it easy to implement an attribute which should combine the
//! contents of attributes, if an attribute appear multiple times in a list
//!
//! Attributes should be added to `crate::context::ATTRIBUTE_PARSERS` to be parsed.
@@ -21,9 +21,13 @@ use rustc_hir::attrs::AttributeKind;
use rustc_span::{Span, Symbol};
use thin_vec::ThinVec;
-use crate::context::{AcceptContext, AllowedTargets, FinalizeContext, Stage};
+use crate::context::{AcceptContext, FinalizeContext, Stage};
use crate::parser::ArgParser;
use crate::session_diagnostics::UnusedMultiple;
+use crate::target_checking::AllowedTargets;
+
+/// All the parsers require roughly the same imports, so this prelude has most of the often-needed ones.
+mod prelude;
pub(crate) mod allow_unstable;
pub(crate) mod body;
@@ -31,6 +35,7 @@ pub(crate) mod cfg;
pub(crate) mod cfg_old;
pub(crate) mod codegen_attrs;
pub(crate) mod confusables;
+pub(crate) mod crate_level;
pub(crate) mod deprecation;
pub(crate) mod dummy;
pub(crate) mod inline;
@@ -43,6 +48,7 @@ pub(crate) mod no_implicit_prelude;
pub(crate) mod non_exhaustive;
pub(crate) mod path;
pub(crate) mod proc_macro_attrs;
+pub(crate) mod prototype;
pub(crate) mod repr;
pub(crate) mod rustc_internal;
pub(crate) mod semantics;
diff --git a/compiler/rustc_attr_parsing/src/attributes/must_use.rs b/compiler/rustc_attr_parsing/src/attributes/must_use.rs
index b6cfc7805906..e6a5141d7830 100644
--- a/compiler/rustc_attr_parsing/src/attributes/must_use.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/must_use.rs
@@ -1,19 +1,29 @@
use rustc_errors::DiagArgValue;
-use rustc_feature::{AttributeTemplate, template};
-use rustc_hir::attrs::AttributeKind;
-use rustc_span::{Symbol, sym};
-use crate::attributes::{AttributeOrder, OnDuplicate, SingleAttributeParser};
-use crate::context::{ALL_TARGETS, AcceptContext, AllowedTargets, Stage};
-use crate::parser::ArgParser;
-use crate::session_diagnostics;
+use super::prelude::*;
+use crate::session_diagnostics::IllFormedAttributeInputLint;
+
pub(crate) struct MustUseParser;
impl SingleAttributeParser for MustUseParser {
const PATH: &[Symbol] = &[sym::must_use];
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
const ON_DUPLICATE: OnDuplicate = OnDuplicate::WarnButFutureError;
- const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); //FIXME Still checked fully in `check_attr.rs`
+ const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowListWarnRest(&[
+ Allow(Target::Fn),
+ Allow(Target::Enum),
+ Allow(Target::Struct),
+ Allow(Target::Union),
+ Allow(Target::Method(MethodKind::Trait { body: false })),
+ Allow(Target::Method(MethodKind::Trait { body: true })),
+ Allow(Target::Method(MethodKind::Inherent)),
+ Allow(Target::ForeignFn),
+ // `impl Trait` in return position can trip
+ // `unused_must_use` if `Trait` is marked as
+ // `#[must_use]`
+ Allow(Target::Trait),
+ Error(Target::WherePredicate),
+ ]);
const TEMPLATE: AttributeTemplate = template!(
Word, NameValueStr: "reason",
"https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute"
@@ -35,9 +45,9 @@ impl SingleAttributeParser for MustUseParser {
Some(value_str)
}
ArgParser::List(_) => {
- let suggestions =
- >::TEMPLATE.suggestions(false, "must_use");
- cx.emit_err(session_diagnostics::IllFormedAttributeInputLint {
+ let suggestions = >::TEMPLATE
+ .suggestions(cx.attr_style, "must_use");
+ cx.emit_err(IllFormedAttributeInputLint {
num_suggestions: suggestions.len(),
suggestions: DiagArgValue::StrListSepByAnd(
suggestions.into_iter().map(|s| format!("`{s}`").into()).collect(),
diff --git a/compiler/rustc_attr_parsing/src/attributes/no_implicit_prelude.rs b/compiler/rustc_attr_parsing/src/attributes/no_implicit_prelude.rs
index 589faf38f730..40073ea0f461 100644
--- a/compiler/rustc_attr_parsing/src/attributes/no_implicit_prelude.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/no_implicit_prelude.rs
@@ -1,10 +1,5 @@
-use rustc_hir::Target;
-use rustc_hir::attrs::AttributeKind;
-use rustc_span::{Span, sym};
+use super::prelude::*;
-use crate::attributes::{NoArgsAttributeParser, OnDuplicate};
-use crate::context::MaybeWarn::Allow;
-use crate::context::{AllowedTargets, Stage};
pub(crate) struct NoImplicitPreludeParser;
impl NoArgsAttributeParser for NoImplicitPreludeParser {
diff --git a/compiler/rustc_attr_parsing/src/attributes/non_exhaustive.rs b/compiler/rustc_attr_parsing/src/attributes/non_exhaustive.rs
index 41e9ca4de410..4e6aec95e66e 100644
--- a/compiler/rustc_attr_parsing/src/attributes/non_exhaustive.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/non_exhaustive.rs
@@ -3,8 +3,10 @@ use rustc_hir::attrs::AttributeKind;
use rustc_span::{Span, Symbol, sym};
use crate::attributes::{NoArgsAttributeParser, OnDuplicate};
-use crate::context::MaybeWarn::{Allow, Warn};
-use crate::context::{AllowedTargets, Stage};
+use crate::context::Stage;
+use crate::target_checking::AllowedTargets;
+use crate::target_checking::Policy::{Allow, Warn};
+
pub(crate) struct NonExhaustiveParser;
impl NoArgsAttributeParser for NonExhaustiveParser {
diff --git a/compiler/rustc_attr_parsing/src/attributes/path.rs b/compiler/rustc_attr_parsing/src/attributes/path.rs
index f9191d1abed3..e4cb806bb427 100644
--- a/compiler/rustc_attr_parsing/src/attributes/path.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/path.rs
@@ -1,12 +1,5 @@
-use rustc_feature::{AttributeTemplate, template};
-use rustc_hir::Target;
-use rustc_hir::attrs::AttributeKind;
-use rustc_span::{Symbol, sym};
+use super::prelude::*;
-use crate::attributes::{AttributeOrder, OnDuplicate, SingleAttributeParser};
-use crate::context::MaybeWarn::{Allow, Error};
-use crate::context::{AcceptContext, AllowedTargets, Stage};
-use crate::parser::ArgParser;
pub(crate) struct PathParser;
impl SingleAttributeParser for PathParser {
diff --git a/compiler/rustc_attr_parsing/src/attributes/prelude.rs b/compiler/rustc_attr_parsing/src/attributes/prelude.rs
new file mode 100644
index 000000000000..2bcdee55c756
--- /dev/null
+++ b/compiler/rustc_attr_parsing/src/attributes/prelude.rs
@@ -0,0 +1,20 @@
+// parsing
+// templates
+pub(super) use rustc_feature::{AttributeTemplate, template};
+// data structures
+pub(super) use rustc_hir::attrs::AttributeKind;
+pub(super) use rustc_hir::lints::AttributeLintKind;
+pub(super) use rustc_hir::{MethodKind, Target};
+pub(super) use rustc_span::{DUMMY_SP, Ident, Span, Symbol, sym};
+pub(super) use thin_vec::ThinVec;
+
+pub(super) use crate::attributes::{
+ AcceptMapping, AttributeOrder, AttributeParser, CombineAttributeParser, ConvertFn,
+ NoArgsAttributeParser, OnDuplicate, SingleAttributeParser,
+};
+// contexts
+pub(super) use crate::context::{AcceptContext, FinalizeContext, Stage};
+pub(super) use crate::parser::*;
+// target checking
+pub(super) use crate::target_checking::Policy::{Allow, Error, Warn};
+pub(super) use crate::target_checking::{ALL_TARGETS, AllowedTargets};
diff --git a/compiler/rustc_attr_parsing/src/attributes/proc_macro_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/proc_macro_attrs.rs
index 4624fa362875..9ac18c04e328 100644
--- a/compiler/rustc_attr_parsing/src/attributes/proc_macro_attrs.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/proc_macro_attrs.rs
@@ -1,15 +1,5 @@
-use rustc_feature::{AttributeTemplate, template};
-use rustc_hir::Target;
-use rustc_hir::attrs::AttributeKind;
-use rustc_span::{Span, Symbol, sym};
-use thin_vec::ThinVec;
+use super::prelude::*;
-use crate::attributes::{
- AttributeOrder, NoArgsAttributeParser, OnDuplicate, SingleAttributeParser,
-};
-use crate::context::MaybeWarn::{Allow, Warn};
-use crate::context::{AcceptContext, AllowedTargets, Stage};
-use crate::parser::ArgParser;
pub(crate) struct ProcMacroParser;
impl NoArgsAttributeParser for ProcMacroParser {
const PATH: &[Symbol] = &[sym::proc_macro];
@@ -110,7 +100,7 @@ fn parse_derive_like(
return None;
};
if !attr_list.path().word_is(sym::attributes) {
- cx.expected_specific_argument(attrs.span(), vec!["attributes"]);
+ cx.expected_specific_argument(attrs.span(), &[sym::attributes]);
return None;
}
let Some(attr_list) = attr_list.args().list() else {
diff --git a/compiler/rustc_attr_parsing/src/attributes/prototype.rs b/compiler/rustc_attr_parsing/src/attributes/prototype.rs
new file mode 100644
index 000000000000..80fe82bf5429
--- /dev/null
+++ b/compiler/rustc_attr_parsing/src/attributes/prototype.rs
@@ -0,0 +1,141 @@
+//! Attributes that are only used on function prototypes.
+
+use rustc_feature::{AttributeTemplate, template};
+use rustc_hir::Target;
+use rustc_hir::attrs::{AttributeKind, MirDialect, MirPhase};
+use rustc_span::{Span, Symbol, sym};
+
+use super::{AttributeOrder, OnDuplicate};
+use crate::attributes::SingleAttributeParser;
+use crate::context::{AcceptContext, Stage};
+use crate::parser::ArgParser;
+use crate::target_checking::AllowedTargets;
+use crate::target_checking::Policy::Allow;
+
+pub(crate) struct CustomMirParser;
+
+impl SingleAttributeParser for CustomMirParser {
+ const PATH: &[rustc_span::Symbol] = &[sym::custom_mir];
+
+ const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
+
+ const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error;
+
+ const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]);
+
+ const TEMPLATE: AttributeTemplate = template!(List: &[r#"dialect = "...", phase = "...""#]);
+
+ fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option {
+ let Some(list) = args.list() else {
+ cx.expected_list(cx.attr_span);
+ return None;
+ };
+
+ let mut dialect = None;
+ let mut phase = None;
+ let mut failed = false;
+
+ for item in list.mixed() {
+ let Some(meta_item) = item.meta_item() else {
+ cx.expected_name_value(item.span(), None);
+ failed = true;
+ break;
+ };
+
+ if let Some(arg) = meta_item.word_is(sym::dialect) {
+ extract_value(cx, sym::dialect, arg, meta_item.span(), &mut dialect, &mut failed);
+ } else if let Some(arg) = meta_item.word_is(sym::phase) {
+ extract_value(cx, sym::phase, arg, meta_item.span(), &mut phase, &mut failed);
+ } else if let Some(word) = meta_item.path().word() {
+ let word = word.to_string();
+ cx.unknown_key(meta_item.span(), word, &["dialect", "phase"]);
+ failed = true;
+ } else {
+ cx.expected_name_value(meta_item.span(), None);
+ failed = true;
+ };
+ }
+
+ let dialect = parse_dialect(cx, dialect, &mut failed);
+ let phase = parse_phase(cx, phase, &mut failed);
+
+ if failed {
+ return None;
+ }
+
+ Some(AttributeKind::CustomMir(dialect, phase, cx.attr_span))
+ }
+}
+
+fn extract_value(
+ cx: &mut AcceptContext<'_, '_, S>,
+ key: Symbol,
+ arg: &ArgParser<'_>,
+ span: Span,
+ out_val: &mut Option<(Symbol, Span)>,
+ failed: &mut bool,
+) {
+ if out_val.is_some() {
+ cx.duplicate_key(span, key);
+ *failed = true;
+ return;
+ }
+
+ let Some(val) = arg.name_value() else {
+ cx.expected_single_argument(arg.span().unwrap_or(span));
+ *failed = true;
+ return;
+ };
+
+ let Some(value_sym) = val.value_as_str() else {
+ cx.expected_string_literal(val.value_span, Some(val.value_as_lit()));
+ *failed = true;
+ return;
+ };
+
+ *out_val = Some((value_sym, val.value_span));
+}
+
+fn parse_dialect(
+ cx: &mut AcceptContext<'_, '_, S>,
+ dialect: Option<(Symbol, Span)>,
+ failed: &mut bool,
+) -> Option<(MirDialect, Span)> {
+ let (dialect, span) = dialect?;
+
+ let dialect = match dialect {
+ sym::analysis => MirDialect::Analysis,
+ sym::built => MirDialect::Built,
+ sym::runtime => MirDialect::Runtime,
+
+ _ => {
+ cx.expected_specific_argument(span, &[sym::analysis, sym::built, sym::runtime]);
+ *failed = true;
+ return None;
+ }
+ };
+
+ Some((dialect, span))
+}
+
+fn parse_phase(
+ cx: &mut AcceptContext<'_, '_, S>,
+ phase: Option<(Symbol, Span)>,
+ failed: &mut bool,
+) -> Option<(MirPhase, Span)> {
+ let (phase, span) = phase?;
+
+ let phase = match phase {
+ sym::initial => MirPhase::Initial,
+ sym::post_cleanup => MirPhase::PostCleanup,
+ sym::optimized => MirPhase::Optimized,
+
+ _ => {
+ cx.expected_specific_argument(span, &[sym::initial, sym::post_cleanup, sym::optimized]);
+ *failed = true;
+ return None;
+ }
+ };
+
+ Some((phase, span))
+}
diff --git a/compiler/rustc_attr_parsing/src/attributes/repr.rs b/compiler/rustc_attr_parsing/src/attributes/repr.rs
index 7ab58ed93474..23aabd155976 100644
--- a/compiler/rustc_attr_parsing/src/attributes/repr.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/repr.rs
@@ -1,16 +1,10 @@
use rustc_abi::Align;
use rustc_ast::{IntTy, LitIntType, LitKind, UintTy};
-use rustc_feature::{AttributeTemplate, template};
-use rustc_hir::attrs::{AttributeKind, IntType, ReprAttr};
-use rustc_hir::{MethodKind, Target};
-use rustc_span::{DUMMY_SP, Span, Symbol, sym};
+use rustc_hir::attrs::{IntType, ReprAttr};
+
+use super::prelude::*;
+use crate::session_diagnostics::{self, IncorrectReprFormatGenericCause};
-use super::{AcceptMapping, AttributeParser, CombineAttributeParser, ConvertFn, FinalizeContext};
-use crate::context::MaybeWarn::Allow;
-use crate::context::{ALL_TARGETS, AcceptContext, AllowedTargets, Stage};
-use crate::parser::{ArgParser, MetaItemListParser, MetaItemParser};
-use crate::session_diagnostics;
-use crate::session_diagnostics::IncorrectReprFormatGenericCause;
/// Parse #[repr(...)] forms.
///
/// Valid repr contents: any of the primitive integral type names (see
diff --git a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs
index efd7b650e441..a995549fc7c8 100644
--- a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs
@@ -1,12 +1,6 @@
-use rustc_feature::{AttributeTemplate, template};
-use rustc_hir::Target;
-use rustc_hir::attrs::AttributeKind;
-use rustc_span::{Symbol, sym};
+use super::prelude::*;
+use super::util::parse_single_integer;
-use crate::attributes::{AttributeOrder, OnDuplicate, SingleAttributeParser};
-use crate::context::MaybeWarn::Allow;
-use crate::context::{AcceptContext, AllowedTargets, Stage, parse_single_integer};
-use crate::parser::ArgParser;
pub(crate) struct RustcLayoutScalarValidRangeStart;
impl SingleAttributeParser for RustcLayoutScalarValidRangeStart {
diff --git a/compiler/rustc_attr_parsing/src/attributes/semantics.rs b/compiler/rustc_attr_parsing/src/attributes/semantics.rs
index d4ad861a3a22..d7f624832971 100644
--- a/compiler/rustc_attr_parsing/src/attributes/semantics.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/semantics.rs
@@ -1,8 +1,5 @@
-use rustc_hir::attrs::AttributeKind;
-use rustc_span::{Span, Symbol, sym};
+use super::prelude::*;
-use crate::attributes::{NoArgsAttributeParser, OnDuplicate};
-use crate::context::{ALL_TARGETS, AllowedTargets, Stage};
pub(crate) struct MayDangleParser;
impl NoArgsAttributeParser for MayDangleParser {
const PATH: &[Symbol] = &[sym::may_dangle];
diff --git a/compiler/rustc_attr_parsing/src/attributes/stability.rs b/compiler/rustc_attr_parsing/src/attributes/stability.rs
index 5a26178f84b5..b94e23477ffe 100644
--- a/compiler/rustc_attr_parsing/src/attributes/stability.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/stability.rs
@@ -1,20 +1,13 @@
use std::num::NonZero;
use rustc_errors::ErrorGuaranteed;
-use rustc_feature::template;
-use rustc_hir::attrs::AttributeKind;
use rustc_hir::{
DefaultBodyStability, MethodKind, PartialConstStability, Stability, StabilityLevel,
StableSince, Target, UnstableReason, VERSION_PLACEHOLDER,
};
-use rustc_span::{Ident, Span, Symbol, sym};
+use super::prelude::*;
use super::util::parse_version;
-use super::{AcceptMapping, AttributeParser, OnDuplicate};
-use crate::attributes::NoArgsAttributeParser;
-use crate::context::MaybeWarn::Allow;
-use crate::context::{AcceptContext, AllowedTargets, FinalizeContext, Stage};
-use crate::parser::{ArgParser, MetaItemParser};
use crate::session_diagnostics::{self, UnsupportedLiteralReason};
macro_rules! reject_outside_std {
@@ -54,6 +47,7 @@ const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
Allow(Target::Static),
Allow(Target::ForeignFn),
Allow(Target::ForeignStatic),
+ Allow(Target::ExternCrate),
]);
#[derive(Default)]
diff --git a/compiler/rustc_attr_parsing/src/attributes/test_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/test_attrs.rs
index 8b666c3868ba..510ff1ded490 100644
--- a/compiler/rustc_attr_parsing/src/attributes/test_attrs.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/test_attrs.rs
@@ -1,13 +1,5 @@
-use rustc_feature::{AttributeTemplate, template};
-use rustc_hir::Target;
-use rustc_hir::attrs::AttributeKind;
-use rustc_hir::lints::AttributeLintKind;
-use rustc_span::{Symbol, sym};
+use super::prelude::*;
-use crate::attributes::{AttributeOrder, OnDuplicate, SingleAttributeParser};
-use crate::context::MaybeWarn::{Allow, Error};
-use crate::context::{AcceptContext, AllowedTargets, Stage};
-use crate::parser::ArgParser;
pub(crate) struct IgnoreParser;
impl SingleAttributeParser for IgnoreParser {
@@ -29,7 +21,7 @@ impl SingleAttributeParser for IgnoreParser {
ArgParser::NameValue(name_value) => {
let Some(str_value) = name_value.value_as_str() else {
let suggestions = >::TEMPLATE
- .suggestions(false, "ignore");
+ .suggestions(cx.attr_style, "ignore");
let span = cx.attr_span;
cx.emit_lint(
AttributeLintKind::IllFormedAttributeInput { suggestions },
@@ -40,8 +32,8 @@ impl SingleAttributeParser for IgnoreParser {
Some(str_value)
}
ArgParser::List(_) => {
- let suggestions =
- >::TEMPLATE.suggestions(false, "ignore");
+ let suggestions = >::TEMPLATE
+ .suggestions(cx.attr_style, "ignore");
let span = cx.attr_span;
cx.emit_lint(AttributeLintKind::IllFormedAttributeInput { suggestions }, span);
return None;
@@ -89,7 +81,7 @@ impl SingleAttributeParser for ShouldPanicParser {
return None;
};
if !single.path().word_is(sym::expected) {
- cx.expected_specific_argument_strings(list.span, vec!["expected"]);
+ cx.expected_specific_argument_strings(list.span, &[sym::expected]);
return None;
}
let Some(nv) = single.args().name_value() else {
diff --git a/compiler/rustc_attr_parsing/src/attributes/traits.rs b/compiler/rustc_attr_parsing/src/attributes/traits.rs
index ee9d7ba99cdc..89ac1b07d164 100644
--- a/compiler/rustc_attr_parsing/src/attributes/traits.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/traits.rs
@@ -1,16 +1,14 @@
-use core::mem;
-
-use rustc_feature::{AttributeTemplate, template};
-use rustc_hir::attrs::AttributeKind;
-use rustc_hir::{MethodKind, Target};
-use rustc_span::{Span, Symbol, sym};
+use std::mem;
+use super::prelude::*;
use crate::attributes::{
AttributeOrder, NoArgsAttributeParser, OnDuplicate, SingleAttributeParser,
};
-use crate::context::MaybeWarn::{Allow, Warn};
-use crate::context::{ALL_TARGETS, AcceptContext, AllowedTargets, Stage};
+use crate::context::{AcceptContext, Stage};
use crate::parser::ArgParser;
+use crate::target_checking::Policy::{Allow, Warn};
+use crate::target_checking::{ALL_TARGETS, AllowedTargets};
+
pub(crate) struct SkipDuringMethodDispatchParser;
impl SingleAttributeParser for SkipDuringMethodDispatchParser {
const PATH: &[Symbol] = &[sym::rustc_skip_during_method_dispatch];
@@ -44,7 +42,7 @@ impl SingleAttributeParser for SkipDuringMethodDispatchParser {
Some(key @ sym::array) => (key, &mut array),
Some(key @ sym::boxed_slice) => (key, &mut boxed_slice),
_ => {
- cx.expected_specific_argument(path.span(), vec!["array", "boxed_slice"]);
+ cx.expected_specific_argument(path.span(), &[sym::array, sym::boxed_slice]);
continue;
}
};
diff --git a/compiler/rustc_attr_parsing/src/attributes/transparency.rs b/compiler/rustc_attr_parsing/src/attributes/transparency.rs
index 0ffcf434b521..ea1f5549c4ec 100644
--- a/compiler/rustc_attr_parsing/src/attributes/transparency.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/transparency.rs
@@ -1,13 +1,7 @@
-use rustc_feature::{AttributeTemplate, template};
-use rustc_hir::Target;
-use rustc_hir::attrs::AttributeKind;
use rustc_span::hygiene::Transparency;
-use rustc_span::{Symbol, sym};
-use super::{AttributeOrder, OnDuplicate, SingleAttributeParser};
-use crate::context::MaybeWarn::Allow;
-use crate::context::{AcceptContext, AllowedTargets, Stage};
-use crate::parser::ArgParser;
+use super::prelude::*;
+
pub(crate) struct TransparencyParser;
// FIXME(jdonszelmann): make these proper diagnostics
@@ -35,7 +29,7 @@ impl SingleAttributeParser for TransparencyParser {
Some(_) => {
cx.expected_specific_argument_strings(
nv.value_span,
- vec!["transparent", "semitransparent", "opaque"],
+ &[sym::transparent, sym::semitransparent, sym::opaque],
);
None
}
diff --git a/compiler/rustc_attr_parsing/src/attributes/util.rs b/compiler/rustc_attr_parsing/src/attributes/util.rs
index 10134915b278..77e8c32e59d7 100644
--- a/compiler/rustc_attr_parsing/src/attributes/util.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/util.rs
@@ -1,8 +1,12 @@
-use rustc_ast::attr::{AttributeExt, first_attr_value_str_by_name};
+use rustc_ast::LitKind;
+use rustc_ast::attr::AttributeExt;
use rustc_feature::is_builtin_attr_name;
use rustc_hir::RustcVersion;
use rustc_span::{Symbol, sym};
+use crate::context::{AcceptContext, Stage};
+use crate::parser::ArgParser;
+
/// Parse a rustc version number written inside string literal in an attribute,
/// like appears in `since = "1.0.0"`. Suffixes like "-dev" and "-nightly" are
/// not accepted in this position, unlike when parsing CFG_RELEASE.
@@ -23,10 +27,6 @@ pub fn is_builtin_attr(attr: &impl AttributeExt) -> bool {
attr.is_doc_comment() || attr.ident().is_some_and(|ident| is_builtin_attr_name(ident.name))
}
-pub fn find_crate_name(attrs: &[impl AttributeExt]) -> Option {
- first_attr_value_str_by_name(attrs, sym::crate_name)
-}
-
pub fn is_doc_alias_attrs_contain_symbol<'tcx, T: AttributeExt + 'tcx>(
attrs: impl Iterator- ,
symbol: Symbol,
@@ -56,3 +56,32 @@ pub fn is_doc_alias_attrs_contain_symbol<'tcx, T: AttributeExt + 'tcx>(
}
false
}
+
+/// Parse a single integer.
+///
+/// Used by attributes that take a single integer as argument, such as
+/// `#[link_ordinal]` and `#[rustc_layout_scalar_valid_range_start]`.
+/// `cx` is the context given to the attribute.
+/// `args` is the parser for the attribute arguments.
+pub(crate) fn parse_single_integer(
+ cx: &mut AcceptContext<'_, '_, S>,
+ args: &ArgParser<'_>,
+) -> Option {
+ let Some(list) = args.list() else {
+ cx.expected_list(cx.attr_span);
+ return None;
+ };
+ let Some(single) = list.single() else {
+ cx.expected_single_argument(list.span);
+ return None;
+ };
+ let Some(lit) = single.lit() else {
+ cx.expected_integer_literal(single.span());
+ return None;
+ };
+ let LitKind::Int(num, _ty) = lit.kind else {
+ cx.expected_integer_literal(single.span());
+ return None;
+ };
+ Some(num.0)
+}
diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs
index bebe3350c4e0..d4b9cfe00add 100644
--- a/compiler/rustc_attr_parsing/src/context.rs
+++ b/compiler/rustc_attr_parsing/src/context.rs
@@ -3,28 +3,28 @@ use std::collections::BTreeMap;
use std::ops::{Deref, DerefMut};
use std::sync::LazyLock;
-use itertools::Itertools;
use private::Sealed;
-use rustc_ast::{self as ast, LitKind, MetaItemLit, NodeId};
-use rustc_errors::{DiagCtxtHandle, Diagnostic};
-use rustc_feature::{AttributeTemplate, Features};
+use rustc_ast::{AttrStyle, MetaItemLit, NodeId};
+use rustc_errors::{Diag, Diagnostic, Level};
+use rustc_feature::AttributeTemplate;
use rustc_hir::attrs::AttributeKind;
use rustc_hir::lints::{AttributeLint, AttributeLintKind};
-use rustc_hir::{
- AttrArgs, AttrItem, AttrPath, Attribute, HashIgnoredAttrId, HirId, MethodKind, Target,
-};
+use rustc_hir::{AttrPath, HirId};
use rustc_session::Session;
-use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym};
+use rustc_span::{ErrorGuaranteed, Span, Symbol};
+use crate::AttributeParser;
use crate::attributes::allow_unstable::{
AllowConstFnUnstableParser, AllowInternalUnstableParser, UnstableFeatureBoundParser,
};
use crate::attributes::body::CoroutineParser;
use crate::attributes::codegen_attrs::{
- ColdParser, CoverageParser, ExportNameParser, NakedParser, NoMangleParser, OptimizeParser,
- TargetFeatureParser, TrackCallerParser, UsedParser,
+ ColdParser, CoverageParser, ExportNameParser, ForceTargetFeatureParser, NakedParser,
+ NoMangleParser, OptimizeParser, SanitizeParser, TargetFeatureParser, TrackCallerParser,
+ UsedParser,
};
use crate::attributes::confusables::ConfusablesParser;
+use crate::attributes::crate_level::CrateNameParser;
use crate::attributes::deprecation::DeprecationParser;
use crate::attributes::dummy::DummyParser;
use crate::attributes::inline::{InlineParser, RustcForceInlineParser};
@@ -46,6 +46,7 @@ use crate::attributes::path::PathParser as PathAttributeParser;
use crate::attributes::proc_macro_attrs::{
ProcMacroAttributeParser, ProcMacroDeriveParser, ProcMacroParser, RustcBuiltinMacroParser,
};
+use crate::attributes::prototype::CustomMirParser;
use crate::attributes::repr::{AlignParser, ReprParser};
use crate::attributes::rustc_internal::{
RustcLayoutScalarValidRangeEnd, RustcLayoutScalarValidRangeStart,
@@ -64,23 +65,21 @@ use crate::attributes::traits::{
};
use crate::attributes::transparency::TransparencyParser;
use crate::attributes::{AttributeParser as _, Combine, Single, WithoutArgs};
-use crate::context::MaybeWarn::{Allow, Error, Warn};
-use crate::parser::{ArgParser, MetaItemParser, PathParser};
-use crate::session_diagnostics::{
- AttributeParseError, AttributeParseErrorReason, InvalidTarget, UnknownMetaItem,
-};
+use crate::parser::{ArgParser, PathParser};
+use crate::session_diagnostics::{AttributeParseError, AttributeParseErrorReason, UnknownMetaItem};
+use crate::target_checking::AllowedTargets;
type GroupType
= LazyLock>;
-struct GroupTypeInner {
- accepters: BTreeMap<&'static [Symbol], Vec>>,
- finalizers: Vec>,
+pub(super) struct GroupTypeInner {
+ pub(super) accepters: BTreeMap<&'static [Symbol], Vec>>,
+ pub(super) finalizers: Vec>,
}
-struct GroupTypeInnerAccept {
- template: AttributeTemplate,
- accept_fn: AcceptFn,
- allowed_targets: AllowedTargets,
+pub(super) struct GroupTypeInnerAccept {
+ pub(super) template: AttributeTemplate,
+ pub(super) accept_fn: AcceptFn,
+ pub(super) allowed_targets: AllowedTargets,
}
type AcceptFn =
@@ -160,6 +159,7 @@ attribute_parsers!(
// tidy-alphabetical-start
Combine,
Combine,
+ Combine,
Combine,
Combine,
Combine,
@@ -167,6 +167,8 @@ attribute_parsers!(
// tidy-alphabetical-start
Single,
+ Single,
+ Single,
Single,
Single,
Single,
@@ -185,6 +187,7 @@ attribute_parsers!(
Single,
Single,
Single,
+ Single,
Single,
Single,
Single,
@@ -262,11 +265,7 @@ impl Stage for Early {
sess: &'sess Session,
diag: impl for<'x> Diagnostic<'x>,
) -> ErrorGuaranteed {
- if self.emit_errors.should_emit() {
- sess.dcx().emit_err(diag)
- } else {
- sess.dcx().create_err(diag).delay_as_bug()
- }
+ self.should_emit().emit_err(sess.dcx().create_err(diag))
}
fn should_emit(&self) -> ShouldEmit {
@@ -313,6 +312,9 @@ pub struct AcceptContext<'f, 'sess, S: Stage> {
/// The span of the attribute currently being parsed
pub(crate) attr_span: Span,
+ /// Whether it is an inner or outer attribute
+ pub(crate) attr_style: AttrStyle,
+
/// The expected structure of the attribute.
///
/// Used in reporting errors to give a hint to users what the attribute *should* look like.
@@ -331,7 +333,7 @@ impl<'f, 'sess: 'f, S: Stage> SharedContext<'f, 'sess, S> {
/// must be delayed until after HIR is built. This method will take care of the details of
/// that.
pub(crate) fn emit_lint(&mut self, lint: AttributeLintKind, span: Span) {
- if !self.stage.should_emit().should_emit() {
+ if matches!(self.stage.should_emit(), ShouldEmit::Nothing) {
return;
}
let id = self.target_id;
@@ -394,6 +396,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
i.kind.is_bytestr().then(|| self.sess().source_map().start_point(i.span))
}),
},
+ attr_style: self.attr_style,
})
}
@@ -404,6 +407,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
template: self.template.clone(),
attribute: self.attr_path.clone(),
reason: AttributeParseErrorReason::ExpectedIntegerLiteral,
+ attr_style: self.attr_style,
})
}
@@ -414,6 +418,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
template: self.template.clone(),
attribute: self.attr_path.clone(),
reason: AttributeParseErrorReason::ExpectedList,
+ attr_style: self.attr_style,
})
}
@@ -424,6 +429,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
template: self.template.clone(),
attribute: self.attr_path.clone(),
reason: AttributeParseErrorReason::ExpectedNoArgs,
+ attr_style: self.attr_style,
})
}
@@ -435,6 +441,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
template: self.template.clone(),
attribute: self.attr_path.clone(),
reason: AttributeParseErrorReason::ExpectedIdentifier,
+ attr_style: self.attr_style,
})
}
@@ -447,6 +454,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
template: self.template.clone(),
attribute: self.attr_path.clone(),
reason: AttributeParseErrorReason::ExpectedNameValue(name),
+ attr_style: self.attr_style,
})
}
@@ -458,6 +466,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
template: self.template.clone(),
attribute: self.attr_path.clone(),
reason: AttributeParseErrorReason::DuplicateKey(key),
+ attr_style: self.attr_style,
})
}
@@ -470,6 +479,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
template: self.template.clone(),
attribute: self.attr_path.clone(),
reason: AttributeParseErrorReason::UnexpectedLiteral,
+ attr_style: self.attr_style,
})
}
@@ -480,6 +490,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
template: self.template.clone(),
attribute: self.attr_path.clone(),
reason: AttributeParseErrorReason::ExpectedSingleArgument,
+ attr_style: self.attr_style,
})
}
@@ -490,13 +501,15 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
template: self.template.clone(),
attribute: self.attr_path.clone(),
reason: AttributeParseErrorReason::ExpectedAtLeastOneArgument,
+ attr_style: self.attr_style,
})
}
+ /// produces an error along the lines of `expected one of [foo, meow]`
pub(crate) fn expected_specific_argument(
&self,
span: Span,
- possibilities: Vec<&'static str>,
+ possibilities: &[Symbol],
) -> ErrorGuaranteed {
self.emit_err(AttributeParseError {
span,
@@ -508,13 +521,16 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
strings: false,
list: false,
},
+ attr_style: self.attr_style,
})
}
+ /// produces an error along the lines of `expected one of [foo, meow] as an argument`.
+ /// i.e. slightly different wording to [`expected_specific_argument`](Self::expected_specific_argument).
pub(crate) fn expected_specific_argument_and_list(
&self,
span: Span,
- possibilities: Vec<&'static str>,
+ possibilities: &[Symbol],
) -> ErrorGuaranteed {
self.emit_err(AttributeParseError {
span,
@@ -526,13 +542,15 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
strings: false,
list: true,
},
+ attr_style: self.attr_style,
})
}
+ /// produces an error along the lines of `expected one of ["foo", "meow"]`
pub(crate) fn expected_specific_argument_strings(
&self,
span: Span,
- possibilities: Vec<&'static str>,
+ possibilities: &[Symbol],
) -> ErrorGuaranteed {
self.emit_err(AttributeParseError {
span,
@@ -544,6 +562,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
strings: true,
list: false,
},
+ attr_style: self.attr_style,
})
}
@@ -579,7 +598,7 @@ pub struct SharedContext<'p, 'sess, S: Stage> {
/// The id ([`NodeId`] if `S` is `Early`, [`HirId`] if `S` is `Late`) of the syntactical component this attribute was applied to
pub(crate) target_id: S::Id,
- emit_lint: &'p mut dyn FnMut(AttributeLint),
+ pub(crate) emit_lint: &'p mut dyn FnMut(AttributeLint),
}
/// Context given to every attribute parser during finalization.
@@ -632,8 +651,13 @@ pub enum OmitDoc {
Skip,
}
-#[derive(Copy, Clone)]
+#[derive(Copy, Clone, Debug)]
pub enum ShouldEmit {
+ /// The operations will emit errors, and lints, and errors are fatal.
+ ///
+ /// Only relevant when early parsing, in late parsing equivalent to `ErrorsAndLints`.
+ /// Late parsing is never fatal, and instead tries to emit as many diagnostics as possible.
+ EarlyFatal,
/// The operation will emit errors and lints.
/// This is usually what you need.
ErrorsAndLints,
@@ -643,559 +667,12 @@ pub enum ShouldEmit {
}
impl ShouldEmit {
- pub fn should_emit(&self) -> bool {
+ pub(crate) fn emit_err(&self, diag: Diag<'_>) -> ErrorGuaranteed {
match self {
- ShouldEmit::ErrorsAndLints => true,
- ShouldEmit::Nothing => false,
+ ShouldEmit::EarlyFatal if diag.level() == Level::DelayedBug => diag.emit(),
+ ShouldEmit::EarlyFatal => diag.upgrade_to_fatal().emit(),
+ ShouldEmit::ErrorsAndLints => diag.emit(),
+ ShouldEmit::Nothing => diag.delay_as_bug(),
}
}
}
-
-#[derive(Debug)]
-pub(crate) enum AllowedTargets {
- AllowList(&'static [MaybeWarn]),
- AllowListWarnRest(&'static [MaybeWarn]),
-}
-
-pub(crate) enum AllowedResult {
- Allowed,
- Warn,
- Error,
-}
-
-impl AllowedTargets {
- pub(crate) fn is_allowed(&self, target: Target) -> AllowedResult {
- match self {
- AllowedTargets::AllowList(list) => {
- if list.contains(&Allow(target)) {
- AllowedResult::Allowed
- } else if list.contains(&Warn(target)) {
- AllowedResult::Warn
- } else {
- AllowedResult::Error
- }
- }
- AllowedTargets::AllowListWarnRest(list) => {
- if list.contains(&Allow(target)) {
- AllowedResult::Allowed
- } else if list.contains(&Error(target)) {
- AllowedResult::Error
- } else {
- AllowedResult::Warn
- }
- }
- }
- }
-
- pub(crate) fn allowed_targets(&self) -> Vec {
- match self {
- AllowedTargets::AllowList(list) => list,
- AllowedTargets::AllowListWarnRest(list) => list,
- }
- .iter()
- .filter_map(|target| match target {
- Allow(target) => Some(*target),
- Warn(_) => None,
- Error(_) => None,
- })
- .collect()
- }
-}
-
-#[derive(Debug, Eq, PartialEq)]
-pub(crate) enum MaybeWarn {
- Allow(Target),
- Warn(Target),
- Error(Target),
-}
-
-/// Context created once, for example as part of the ast lowering
-/// context, through which all attributes can be lowered.
-pub struct AttributeParser<'sess, S: Stage = Late> {
- pub(crate) tools: Vec,
- features: Option<&'sess Features>,
- sess: &'sess Session,
- stage: S,
-
- /// *Only* parse attributes with this symbol.
- ///
- /// Used in cases where we want the lowering infrastructure for parse just a single attribute.
- parse_only: Option,
-}
-
-impl<'sess> AttributeParser<'sess, Early> {
- /// This method allows you to parse attributes *before* you have access to features or tools.
- /// One example where this is necessary, is to parse `feature` attributes themselves for
- /// example.
- ///
- /// Try to use this as little as possible. Attributes *should* be lowered during
- /// `rustc_ast_lowering`. Some attributes require access to features to parse, which would
- /// crash if you tried to do so through [`parse_limited`](Self::parse_limited).
- ///
- /// To make sure use is limited, supply a `Symbol` you'd like to parse. Only attributes with
- /// that symbol are picked out of the list of instructions and parsed. Those are returned.
- ///
- /// No diagnostics will be emitted when parsing limited. Lints are not emitted at all, while
- /// errors will be emitted as a delayed bugs. in other words, we *expect* attributes parsed
- /// with `parse_limited` to be reparsed later during ast lowering where we *do* emit the errors
- pub fn parse_limited(
- sess: &'sess Session,
- attrs: &[ast::Attribute],
- sym: Symbol,
- target_span: Span,
- target_node_id: NodeId,
- features: Option<&'sess Features>,
- ) -> Option {
- let mut p = Self {
- features,
- tools: Vec::new(),
- parse_only: Some(sym),
- sess,
- stage: Early { emit_errors: ShouldEmit::Nothing },
- };
- let mut parsed = p.parse_attribute_list(
- attrs,
- target_span,
- target_node_id,
- Target::Crate, // Does not matter, we're not going to emit errors anyways
- OmitDoc::Skip,
- std::convert::identity,
- |_lint| {
- panic!("can't emit lints here for now (nothing uses this atm)");
- },
- );
- assert!(parsed.len() <= 1);
-
- parsed.pop()
- }
-
- pub fn parse_single(
- sess: &'sess Session,
- attr: &ast::Attribute,
- target_span: Span,
- target_node_id: NodeId,
- features: Option<&'sess Features>,
- emit_errors: ShouldEmit,
- parse_fn: fn(cx: &mut AcceptContext<'_, '_, Early>, item: &ArgParser<'_>) -> T,
- template: &AttributeTemplate,
- ) -> T {
- let mut parser = Self {
- features,
- tools: Vec::new(),
- parse_only: None,
- sess,
- stage: Early { emit_errors },
- };
- let ast::AttrKind::Normal(normal_attr) = &attr.kind else {
- panic!("parse_single called on a doc attr")
- };
- let meta_parser = MetaItemParser::from_attr(normal_attr, parser.dcx());
- let path = meta_parser.path();
- let args = meta_parser.args();
- let mut cx: AcceptContext<'_, 'sess, Early> = AcceptContext {
- shared: SharedContext {
- cx: &mut parser,
- target_span,
- target_id: target_node_id,
- emit_lint: &mut |_lint| {
- panic!("can't emit lints here for now (nothing uses this atm)");
- },
- },
- attr_span: attr.span,
- template,
- attr_path: path.get_attribute_path(),
- };
- parse_fn(&mut cx, args)
- }
-}
-
-impl<'sess, S: Stage> AttributeParser<'sess, S> {
- pub fn new(
- sess: &'sess Session,
- features: &'sess Features,
- tools: Vec,
- stage: S,
- ) -> Self {
- Self { features: Some(features), tools, parse_only: None, sess, stage }
- }
-
- pub(crate) fn sess(&self) -> &'sess Session {
- &self.sess
- }
-
- pub(crate) fn features(&self) -> &'sess Features {
- self.features.expect("features not available at this point in the compiler")
- }
-
- pub(crate) fn features_option(&self) -> Option<&'sess Features> {
- self.features
- }
-
- pub(crate) fn dcx(&self) -> DiagCtxtHandle<'sess> {
- self.sess().dcx()
- }
-
- /// Parse a list of attributes.
- ///
- /// `target_span` is the span of the thing this list of attributes is applied to,
- /// and when `omit_doc` is set, doc attributes are filtered out.
- pub fn parse_attribute_list(
- &mut self,
- attrs: &[ast::Attribute],
- target_span: Span,
- target_id: S::Id,
- target: Target,
- omit_doc: OmitDoc,
-
- lower_span: impl Copy + Fn(Span) -> Span,
- mut emit_lint: impl FnMut(AttributeLint),
- ) -> Vec {
- let mut attributes = Vec::new();
- let mut attr_paths = Vec::new();
-
- for attr in attrs {
- // If we're only looking for a single attribute, skip all the ones we don't care about.
- if let Some(expected) = self.parse_only {
- if !attr.has_name(expected) {
- continue;
- }
- }
-
- // Sometimes, for example for `#![doc = include_str!("readme.md")]`,
- // doc still contains a non-literal. You might say, when we're lowering attributes
- // that's expanded right? But no, sometimes, when parsing attributes on macros,
- // we already use the lowering logic and these are still there. So, when `omit_doc`
- // is set we *also* want to ignore these.
- if omit_doc == OmitDoc::Skip && attr.has_name(sym::doc) {
- continue;
- }
-
- match &attr.kind {
- ast::AttrKind::DocComment(comment_kind, symbol) => {
- if omit_doc == OmitDoc::Skip {
- continue;
- }
-
- attributes.push(Attribute::Parsed(AttributeKind::DocComment {
- style: attr.style,
- kind: *comment_kind,
- span: lower_span(attr.span),
- comment: *symbol,
- }))
- }
- // // FIXME: make doc attributes go through a proper attribute parser
- // ast::AttrKind::Normal(n) if n.has_name(sym::doc) => {
- // let p = GenericMetaItemParser::from_attr(&n, self.dcx());
- //
- // attributes.push(Attribute::Parsed(AttributeKind::DocComment {
- // style: attr.style,
- // kind: CommentKind::Line,
- // span: attr.span,
- // comment: p.args().name_value(),
- // }))
- // }
- ast::AttrKind::Normal(n) => {
- attr_paths.push(PathParser::Ast(&n.item.path));
-
- let parser = MetaItemParser::from_attr(n, self.dcx());
- let path = parser.path();
- let args = parser.args();
- let parts = path.segments().map(|i| i.name).collect::>();
-
- if let Some(accepts) = S::parsers().accepters.get(parts.as_slice()) {
- for accept in accepts {
- let mut cx: AcceptContext<'_, 'sess, S> = AcceptContext {
- shared: SharedContext {
- cx: self,
- target_span,
- target_id,
- emit_lint: &mut emit_lint,
- },
- attr_span: lower_span(attr.span),
- template: &accept.template,
- attr_path: path.get_attribute_path(),
- };
-
- (accept.accept_fn)(&mut cx, args);
-
- if self.stage.should_emit().should_emit() {
- match accept.allowed_targets.is_allowed(target) {
- AllowedResult::Allowed => {}
- AllowedResult::Warn => {
- let allowed_targets =
- accept.allowed_targets.allowed_targets();
- let (applied, only) = allowed_targets_applied(
- allowed_targets,
- target,
- self.features,
- );
- emit_lint(AttributeLint {
- id: target_id,
- span: attr.span,
- kind: AttributeLintKind::InvalidTarget {
- name: parts[0],
- target,
- only: if only { "only " } else { "" },
- applied,
- },
- });
- }
- AllowedResult::Error => {
- let allowed_targets =
- accept.allowed_targets.allowed_targets();
- let (applied, only) = allowed_targets_applied(
- allowed_targets,
- target,
- self.features,
- );
- self.dcx().emit_err(InvalidTarget {
- span: attr.span,
- name: parts[0],
- target: target.plural_name(),
- only: if only { "only " } else { "" },
- applied,
- });
- }
- }
- }
- }
- } else {
- // If we're here, we must be compiling a tool attribute... Or someone
- // forgot to parse their fancy new attribute. Let's warn them in any case.
- // If you are that person, and you really think your attribute should
- // remain unparsed, carefully read the documentation in this module and if
- // you still think so you can add an exception to this assertion.
-
- // FIXME(jdonszelmann): convert other attributes, and check with this that
- // we caught em all
- // const FIXME_TEMPORARY_ATTR_ALLOWLIST: &[Symbol] = &[sym::cfg];
- // assert!(
- // self.tools.contains(&parts[0]) || true,
- // // || FIXME_TEMPORARY_ATTR_ALLOWLIST.contains(&parts[0]),
- // "attribute {path} wasn't parsed and isn't a know tool attribute",
- // );
-
- attributes.push(Attribute::Unparsed(Box::new(AttrItem {
- path: AttrPath::from_ast(&n.item.path),
- args: self.lower_attr_args(&n.item.args, lower_span),
- id: HashIgnoredAttrId { attr_id: attr.id },
- style: attr.style,
- span: lower_span(attr.span),
- })));
- }
- }
- }
- }
-
- let mut parsed_attributes = Vec::new();
- for f in &S::parsers().finalizers {
- if let Some(attr) = f(&mut FinalizeContext {
- shared: SharedContext {
- cx: self,
- target_span,
- target_id,
- emit_lint: &mut emit_lint,
- },
- all_attrs: &attr_paths,
- }) {
- parsed_attributes.push(Attribute::Parsed(attr));
- }
- }
-
- attributes.extend(parsed_attributes);
-
- attributes
- }
-
- /// Returns whether there is a parser for an attribute with this name
- pub fn is_parsed_attribute(path: &[Symbol]) -> bool {
- Late::parsers().accepters.contains_key(path)
- }
-
- fn lower_attr_args(&self, args: &ast::AttrArgs, lower_span: impl Fn(Span) -> Span) -> AttrArgs {
- match args {
- ast::AttrArgs::Empty => AttrArgs::Empty,
- ast::AttrArgs::Delimited(args) => AttrArgs::Delimited(args.clone()),
- // This is an inert key-value attribute - it will never be visible to macros
- // after it gets lowered to HIR. Therefore, we can extract literals to handle
- // nonterminals in `#[doc]` (e.g. `#[doc = $e]`).
- ast::AttrArgs::Eq { eq_span, expr } => {
- // In valid code the value always ends up as a single literal. Otherwise, a dummy
- // literal suffices because the error is handled elsewhere.
- let lit = if let ast::ExprKind::Lit(token_lit) = expr.kind
- && let Ok(lit) =
- ast::MetaItemLit::from_token_lit(token_lit, lower_span(expr.span))
- {
- lit
- } else {
- let guar = self.dcx().span_delayed_bug(
- args.span().unwrap_or(DUMMY_SP),
- "expr in place where literal is expected (builtin attr parsing)",
- );
- ast::MetaItemLit {
- symbol: sym::dummy,
- suffix: None,
- kind: ast::LitKind::Err(guar),
- span: DUMMY_SP,
- }
- };
- AttrArgs::Eq { eq_span: lower_span(*eq_span), expr: lit }
- }
- }
- }
-}
-
-/// Takes a list of `allowed_targets` for an attribute, and the `target` the attribute was applied to.
-/// Does some heuristic-based filtering to remove uninteresting targets, and formats the targets into a string
-pub(crate) fn allowed_targets_applied(
- mut allowed_targets: Vec,
- target: Target,
- features: Option<&Features>,
-) -> (String, bool) {
- // Remove unstable targets from `allowed_targets` if their features are not enabled
- if let Some(features) = features {
- if !features.fn_delegation() {
- allowed_targets.retain(|t| !matches!(t, Target::Delegation { .. }));
- }
- if !features.stmt_expr_attributes() {
- allowed_targets.retain(|t| !matches!(t, Target::Expression | Target::Statement));
- }
- }
-
- // We define groups of "similar" targets.
- // If at least two of the targets are allowed, and the `target` is not in the group,
- // we collapse the entire group to a single entry to simplify the target list
- const FUNCTION_LIKE: &[Target] = &[
- Target::Fn,
- Target::Closure,
- Target::ForeignFn,
- Target::Method(MethodKind::Inherent),
- Target::Method(MethodKind::Trait { body: false }),
- Target::Method(MethodKind::Trait { body: true }),
- Target::Method(MethodKind::TraitImpl),
- ];
- const METHOD_LIKE: &[Target] = &[
- Target::Method(MethodKind::Inherent),
- Target::Method(MethodKind::Trait { body: false }),
- Target::Method(MethodKind::Trait { body: true }),
- Target::Method(MethodKind::TraitImpl),
- ];
- const IMPL_LIKE: &[Target] =
- &[Target::Impl { of_trait: false }, Target::Impl { of_trait: true }];
- const ADT_LIKE: &[Target] = &[Target::Struct, Target::Enum];
-
- let mut added_fake_targets = Vec::new();
- filter_targets(
- &mut allowed_targets,
- FUNCTION_LIKE,
- "functions",
- target,
- &mut added_fake_targets,
- );
- filter_targets(&mut allowed_targets, METHOD_LIKE, "methods", target, &mut added_fake_targets);
- filter_targets(&mut allowed_targets, IMPL_LIKE, "impl blocks", target, &mut added_fake_targets);
- filter_targets(&mut allowed_targets, ADT_LIKE, "data types", target, &mut added_fake_targets);
-
- // If there is now only 1 target left, show that as the only possible target
- (
- added_fake_targets
- .iter()
- .copied()
- .chain(allowed_targets.iter().map(|t| t.plural_name()))
- .join(", "),
- allowed_targets.len() + added_fake_targets.len() == 1,
- )
-}
-
-fn filter_targets(
- allowed_targets: &mut Vec,
- target_group: &'static [Target],
- target_group_name: &'static str,
- target: Target,
- added_fake_targets: &mut Vec<&'static str>,
-) {
- if target_group.contains(&target) {
- return;
- }
- if allowed_targets.iter().filter(|at| target_group.contains(at)).count() < 2 {
- return;
- }
- allowed_targets.retain(|t| !target_group.contains(t));
- added_fake_targets.push(target_group_name);
-}
-
-/// This is the list of all targets to which a attribute can be applied
-/// This is used for:
-/// - `rustc_dummy`, which can be applied to all targets
-/// - Attributes that are not parted to the new target system yet can use this list as a placeholder
-pub(crate) const ALL_TARGETS: &'static [MaybeWarn] = &[
- Allow(Target::ExternCrate),
- Allow(Target::Use),
- Allow(Target::Static),
- Allow(Target::Const),
- Allow(Target::Fn),
- Allow(Target::Closure),
- Allow(Target::Mod),
- Allow(Target::ForeignMod),
- Allow(Target::GlobalAsm),
- Allow(Target::TyAlias),
- Allow(Target::Enum),
- Allow(Target::Variant),
- Allow(Target::Struct),
- Allow(Target::Field),
- Allow(Target::Union),
- Allow(Target::Trait),
- Allow(Target::TraitAlias),
- Allow(Target::Impl { of_trait: false }),
- Allow(Target::Impl { of_trait: true }),
- Allow(Target::Expression),
- Allow(Target::Statement),
- Allow(Target::Arm),
- Allow(Target::AssocConst),
- Allow(Target::Method(MethodKind::Inherent)),
- Allow(Target::Method(MethodKind::Trait { body: false })),
- Allow(Target::Method(MethodKind::Trait { body: true })),
- Allow(Target::Method(MethodKind::TraitImpl)),
- Allow(Target::AssocTy),
- Allow(Target::ForeignFn),
- Allow(Target::ForeignStatic),
- Allow(Target::ForeignTy),
- Allow(Target::MacroDef),
- Allow(Target::Param),
- Allow(Target::PatField),
- Allow(Target::ExprField),
- Allow(Target::WherePredicate),
- Allow(Target::MacroCall),
- Allow(Target::Crate),
- Allow(Target::Delegation { mac: false }),
- Allow(Target::Delegation { mac: true }),
-];
-
-/// Parse a single integer.
-///
-/// Used by attributes that take a single integer as argument, such as
-/// `#[link_ordinal]` and `#[rustc_layout_scalar_valid_range_start]`.
-/// `cx` is the context given to the attribute.
-/// `args` is the parser for the attribute arguments.
-pub(crate) fn parse_single_integer(
- cx: &mut AcceptContext<'_, '_, S>,
- args: &ArgParser<'_>,
-) -> Option {
- let Some(list) = args.list() else {
- cx.expected_list(cx.attr_span);
- return None;
- };
- let Some(single) = list.single() else {
- cx.expected_single_argument(list.span);
- return None;
- };
- let Some(lit) = single.lit() else {
- cx.expected_integer_literal(single.span());
- return None;
- };
- let LitKind::Int(num, _ty) = lit.kind else {
- cx.expected_integer_literal(single.span());
- return None;
- };
- Some(num.0)
-}
diff --git a/compiler/rustc_attr_parsing/src/interface.rs b/compiler/rustc_attr_parsing/src/interface.rs
new file mode 100644
index 000000000000..60523c2877c0
--- /dev/null
+++ b/compiler/rustc_attr_parsing/src/interface.rs
@@ -0,0 +1,370 @@
+use std::borrow::Cow;
+
+use rustc_ast as ast;
+use rustc_ast::NodeId;
+use rustc_errors::DiagCtxtHandle;
+use rustc_feature::{AttributeTemplate, Features};
+use rustc_hir::attrs::AttributeKind;
+use rustc_hir::lints::AttributeLint;
+use rustc_hir::{AttrArgs, AttrItem, AttrPath, Attribute, HashIgnoredAttrId, Target};
+use rustc_session::Session;
+use rustc_span::{DUMMY_SP, Span, Symbol, sym};
+
+use crate::context::{AcceptContext, FinalizeContext, SharedContext, Stage};
+use crate::parser::{ArgParser, MetaItemParser, PathParser};
+use crate::{Early, Late, OmitDoc, ShouldEmit};
+
+/// Context created once, for example as part of the ast lowering
+/// context, through which all attributes can be lowered.
+pub struct AttributeParser<'sess, S: Stage = Late> {
+ pub(crate) tools: Vec,
+ pub(crate) features: Option<&'sess Features>,
+ pub(crate) sess: &'sess Session,
+ pub(crate) stage: S,
+
+ /// *Only* parse attributes with this symbol.
+ ///
+ /// Used in cases where we want the lowering infrastructure for parse just a single attribute.
+ parse_only: Option,
+}
+
+impl<'sess> AttributeParser<'sess, Early> {
+ /// This method allows you to parse attributes *before* you have access to features or tools.
+ /// One example where this is necessary, is to parse `feature` attributes themselves for
+ /// example.
+ ///
+ /// Try to use this as little as possible. Attributes *should* be lowered during
+ /// `rustc_ast_lowering`. Some attributes require access to features to parse, which would
+ /// crash if you tried to do so through [`parse_limited`](Self::parse_limited).
+ ///
+ /// To make sure use is limited, supply a `Symbol` you'd like to parse. Only attributes with
+ /// that symbol are picked out of the list of instructions and parsed. Those are returned.
+ ///
+ /// No diagnostics will be emitted when parsing limited. Lints are not emitted at all, while
+ /// errors will be emitted as a delayed bugs. in other words, we *expect* attributes parsed
+ /// with `parse_limited` to be reparsed later during ast lowering where we *do* emit the errors
+ pub fn parse_limited(
+ sess: &'sess Session,
+ attrs: &[ast::Attribute],
+ sym: Symbol,
+ target_span: Span,
+ target_node_id: NodeId,
+ features: Option<&'sess Features>,
+ ) -> Option {
+ Self::parse_limited_should_emit(
+ sess,
+ attrs,
+ sym,
+ target_span,
+ target_node_id,
+ features,
+ ShouldEmit::Nothing,
+ )
+ }
+
+ /// Usually you want `parse_limited`, which defaults to no errors.
+ pub fn parse_limited_should_emit(
+ sess: &'sess Session,
+ attrs: &[ast::Attribute],
+ sym: Symbol,
+ target_span: Span,
+ target_node_id: NodeId,
+ features: Option<&'sess Features>,
+ should_emit: ShouldEmit,
+ ) -> Option {
+ let mut parsed = Self::parse_limited_all(
+ sess,
+ attrs,
+ Some(sym),
+ Target::Crate, // Does not matter, we're not going to emit errors anyways
+ target_span,
+ target_node_id,
+ features,
+ should_emit,
+ );
+ assert!(parsed.len() <= 1);
+ parsed.pop()
+ }
+
+ pub fn parse_limited_all(
+ sess: &'sess Session,
+ attrs: &[ast::Attribute],
+ parse_only: Option,
+ target: Target,
+ target_span: Span,
+ target_node_id: NodeId,
+ features: Option<&'sess Features>,
+ emit_errors: ShouldEmit,
+ ) -> Vec {
+ let mut p =
+ Self { features, tools: Vec::new(), parse_only, sess, stage: Early { emit_errors } };
+ p.parse_attribute_list(
+ attrs,
+ target_span,
+ target_node_id,
+ target,
+ OmitDoc::Skip,
+ std::convert::identity,
+ |lint| {
+ crate::lints::emit_attribute_lint(&lint, sess);
+ },
+ )
+ }
+
+ pub fn parse_single(
+ sess: &'sess Session,
+ attr: &ast::Attribute,
+ target_span: Span,
+ target_node_id: NodeId,
+ features: Option<&'sess Features>,
+ emit_errors: ShouldEmit,
+ parse_fn: fn(cx: &mut AcceptContext<'_, '_, Early>, item: &ArgParser<'_>) -> Option,
+ template: &AttributeTemplate,
+ ) -> Option {
+ let mut parser = Self {
+ features,
+ tools: Vec::new(),
+ parse_only: None,
+ sess,
+ stage: Early { emit_errors },
+ };
+ let ast::AttrKind::Normal(normal_attr) = &attr.kind else {
+ panic!("parse_single called on a doc attr")
+ };
+ let parts =
+ normal_attr.item.path.segments.iter().map(|seg| seg.ident.name).collect::>();
+ let meta_parser = MetaItemParser::from_attr(normal_attr, &parts, &sess.psess, emit_errors)?;
+ let path = meta_parser.path();
+ let args = meta_parser.args();
+ let mut cx: AcceptContext<'_, 'sess, Early> = AcceptContext {
+ shared: SharedContext {
+ cx: &mut parser,
+ target_span,
+ target_id: target_node_id,
+ emit_lint: &mut |lint| {
+ crate::lints::emit_attribute_lint(&lint, sess);
+ },
+ },
+ attr_span: attr.span,
+ attr_style: attr.style,
+ template,
+ attr_path: path.get_attribute_path(),
+ };
+ parse_fn(&mut cx, args)
+ }
+}
+
+impl<'sess, S: Stage> AttributeParser<'sess, S> {
+ pub fn new(
+ sess: &'sess Session,
+ features: &'sess Features,
+ tools: Vec,
+ stage: S,
+ ) -> Self {
+ Self { features: Some(features), tools, parse_only: None, sess, stage }
+ }
+
+ pub(crate) fn sess(&self) -> &'sess Session {
+ &self.sess
+ }
+
+ pub(crate) fn features(&self) -> &'sess Features {
+ self.features.expect("features not available at this point in the compiler")
+ }
+
+ pub(crate) fn features_option(&self) -> Option<&'sess Features> {
+ self.features
+ }
+
+ pub(crate) fn dcx(&self) -> DiagCtxtHandle<'sess> {
+ self.sess().dcx()
+ }
+
+ /// Parse a list of attributes.
+ ///
+ /// `target_span` is the span of the thing this list of attributes is applied to,
+ /// and when `omit_doc` is set, doc attributes are filtered out.
+ pub fn parse_attribute_list(
+ &mut self,
+ attrs: &[ast::Attribute],
+ target_span: Span,
+ target_id: S::Id,
+ target: Target,
+ omit_doc: OmitDoc,
+
+ lower_span: impl Copy + Fn(Span) -> Span,
+ mut emit_lint: impl FnMut(AttributeLint),
+ ) -> Vec {
+ let mut attributes = Vec::new();
+ let mut attr_paths = Vec::new();
+
+ for attr in attrs {
+ // If we're only looking for a single attribute, skip all the ones we don't care about.
+ if let Some(expected) = self.parse_only {
+ if !attr.has_name(expected) {
+ continue;
+ }
+ }
+
+ // Sometimes, for example for `#![doc = include_str!("readme.md")]`,
+ // doc still contains a non-literal. You might say, when we're lowering attributes
+ // that's expanded right? But no, sometimes, when parsing attributes on macros,
+ // we already use the lowering logic and these are still there. So, when `omit_doc`
+ // is set we *also* want to ignore these.
+ if omit_doc == OmitDoc::Skip && attr.has_name(sym::doc) {
+ continue;
+ }
+
+ match &attr.kind {
+ ast::AttrKind::DocComment(comment_kind, symbol) => {
+ if omit_doc == OmitDoc::Skip {
+ continue;
+ }
+
+ attributes.push(Attribute::Parsed(AttributeKind::DocComment {
+ style: attr.style,
+ kind: *comment_kind,
+ span: lower_span(attr.span),
+ comment: *symbol,
+ }))
+ }
+ // // FIXME: make doc attributes go through a proper attribute parser
+ // ast::AttrKind::Normal(n) if n.has_name(sym::doc) => {
+ // let p = GenericMetaItemParser::from_attr(&n, self.dcx());
+ //
+ // attributes.push(Attribute::Parsed(AttributeKind::DocComment {
+ // style: attr.style,
+ // kind: CommentKind::Line,
+ // span: attr.span,
+ // comment: p.args().name_value(),
+ // }))
+ // }
+ ast::AttrKind::Normal(n) => {
+ attr_paths.push(PathParser(Cow::Borrowed(&n.item.path)));
+
+ let parts =
+ n.item.path.segments.iter().map(|seg| seg.ident.name).collect::>();
+
+ if let Some(accepts) = S::parsers().accepters.get(parts.as_slice()) {
+ let Some(parser) = MetaItemParser::from_attr(
+ n,
+ &parts,
+ &self.sess.psess,
+ self.stage.should_emit(),
+ ) else {
+ continue;
+ };
+ let path = parser.path();
+ let args = parser.args();
+ for accept in accepts {
+ let mut cx: AcceptContext<'_, 'sess, S> = AcceptContext {
+ shared: SharedContext {
+ cx: self,
+ target_span,
+ target_id,
+ emit_lint: &mut emit_lint,
+ },
+ attr_span: lower_span(attr.span),
+ attr_style: attr.style,
+ template: &accept.template,
+ attr_path: path.get_attribute_path(),
+ };
+
+ (accept.accept_fn)(&mut cx, args);
+
+ if !matches!(self.stage.should_emit(), ShouldEmit::Nothing) {
+ self.check_target(
+ path.get_attribute_path(),
+ attr.span,
+ &accept.allowed_targets,
+ target,
+ target_id,
+ &mut emit_lint,
+ );
+ }
+ }
+ } else {
+ // If we're here, we must be compiling a tool attribute... Or someone
+ // forgot to parse their fancy new attribute. Let's warn them in any case.
+ // If you are that person, and you really think your attribute should
+ // remain unparsed, carefully read the documentation in this module and if
+ // you still think so you can add an exception to this assertion.
+
+ // FIXME(jdonszelmann): convert other attributes, and check with this that
+ // we caught em all
+ // const FIXME_TEMPORARY_ATTR_ALLOWLIST: &[Symbol] = &[sym::cfg];
+ // assert!(
+ // self.tools.contains(&parts[0]) || true,
+ // // || FIXME_TEMPORARY_ATTR_ALLOWLIST.contains(&parts[0]),
+ // "attribute {path} wasn't parsed and isn't a know tool attribute",
+ // );
+
+ attributes.push(Attribute::Unparsed(Box::new(AttrItem {
+ path: AttrPath::from_ast(&n.item.path),
+ args: self.lower_attr_args(&n.item.args, lower_span),
+ id: HashIgnoredAttrId { attr_id: attr.id },
+ style: attr.style,
+ span: lower_span(attr.span),
+ })));
+ }
+ }
+ }
+ }
+
+ let mut parsed_attributes = Vec::new();
+ for f in &S::parsers().finalizers {
+ if let Some(attr) = f(&mut FinalizeContext {
+ shared: SharedContext {
+ cx: self,
+ target_span,
+ target_id,
+ emit_lint: &mut emit_lint,
+ },
+ all_attrs: &attr_paths,
+ }) {
+ parsed_attributes.push(Attribute::Parsed(attr));
+ }
+ }
+
+ attributes.extend(parsed_attributes);
+
+ attributes
+ }
+
+ /// Returns whether there is a parser for an attribute with this name
+ pub fn is_parsed_attribute(path: &[Symbol]) -> bool {
+ Late::parsers().accepters.contains_key(path)
+ }
+
+ fn lower_attr_args(&self, args: &ast::AttrArgs, lower_span: impl Fn(Span) -> Span) -> AttrArgs {
+ match args {
+ ast::AttrArgs::Empty => AttrArgs::Empty,
+ ast::AttrArgs::Delimited(args) => AttrArgs::Delimited(args.clone()),
+ // This is an inert key-value attribute - it will never be visible to macros
+ // after it gets lowered to HIR. Therefore, we can extract literals to handle
+ // nonterminals in `#[doc]` (e.g. `#[doc = $e]`).
+ ast::AttrArgs::Eq { eq_span, expr } => {
+ // In valid code the value always ends up as a single literal. Otherwise, a dummy
+ // literal suffices because the error is handled elsewhere.
+ let lit = if let ast::ExprKind::Lit(token_lit) = expr.kind
+ && let Ok(lit) =
+ ast::MetaItemLit::from_token_lit(token_lit, lower_span(expr.span))
+ {
+ lit
+ } else {
+ let guar = self.dcx().span_delayed_bug(
+ args.span().unwrap_or(DUMMY_SP),
+ "expr in place where literal is expected (builtin attr parsing)",
+ );
+ ast::MetaItemLit {
+ symbol: sym::dummy,
+ suffix: None,
+ kind: ast::LitKind::Err(guar),
+ span: DUMMY_SP,
+ }
+ };
+ AttrArgs::Eq { eq_span: lower_span(*eq_span), expr: lit }
+ }
+ }
+ }
+}
diff --git a/compiler/rustc_attr_parsing/src/lib.rs b/compiler/rustc_attr_parsing/src/lib.rs
index fc1377e53143..4dd908cdc40b 100644
--- a/compiler/rustc_attr_parsing/src/lib.rs
+++ b/compiler/rustc_attr_parsing/src/lib.rs
@@ -84,18 +84,31 @@
// tidy-alphabetical-end
#[macro_use]
+/// All the individual attribute parsers for each of rustc's built-in attributes.
mod attributes;
+
+/// All the important types given to attribute parsers when parsing
pub(crate) mod context;
-mod lints;
+
+/// Code that other crates interact with, to actually parse a list (or sometimes single)
+/// attribute.
+mod interface;
+
+/// Despite this entire module called attribute parsing and the term being a little overloaded,
+/// in this module the code lives that actually breaks up tokenstreams into semantic pieces of attributes,
+/// like lists or name-value pairs.
pub mod parser;
+
+mod lints;
mod session_diagnostics;
+mod target_checking;
+pub mod validate_attr;
pub use attributes::cfg::{CFG_TEMPLATE, EvalConfigResult, eval_config_entry, parse_cfg_attr};
pub use attributes::cfg_old::*;
-pub use attributes::util::{
- find_crate_name, is_builtin_attr, is_doc_alias_attrs_contain_symbol, parse_version,
-};
-pub use context::{AttributeParser, Early, Late, OmitDoc, ShouldEmit};
+pub use attributes::util::{is_builtin_attr, is_doc_alias_attrs_contain_symbol, parse_version};
+pub use context::{Early, Late, OmitDoc, ShouldEmit};
+pub use interface::AttributeParser;
pub use lints::emit_attribute_lint;
rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
diff --git a/compiler/rustc_attr_parsing/src/lints.rs b/compiler/rustc_attr_parsing/src/lints.rs
index 733225bab598..069478e7f0c8 100644
--- a/compiler/rustc_attr_parsing/src/lints.rs
+++ b/compiler/rustc_attr_parsing/src/lints.rs
@@ -1,11 +1,13 @@
+use std::borrow::Cow;
+
use rustc_errors::{DiagArgValue, LintEmitter};
+use rustc_hir::Target;
use rustc_hir::lints::{AttributeLint, AttributeLintKind};
-use rustc_hir::{HirId, Target};
use rustc_span::sym;
use crate::session_diagnostics;
-pub fn emit_attribute_lint(lint: &AttributeLint, lint_emitter: L) {
+pub fn emit_attribute_lint(lint: &AttributeLint, lint_emitter: L) {
let AttributeLint { id, span, kind } = lint;
match kind {
@@ -35,12 +37,12 @@ pub fn emit_attribute_lint(lint: &AttributeLint, lint_emi
*first_span,
session_diagnostics::EmptyAttributeList { attr_span: *first_span },
),
- &AttributeLintKind::InvalidTarget { name, target, ref applied, only } => lint_emitter
+ AttributeLintKind::InvalidTarget { name, target, applied, only } => lint_emitter
.emit_node_span_lint(
// This check is here because `deprecated` had its own lint group and removing this would be a breaking change
- if name == sym::deprecated
+ if name.segments[0].name == sym::deprecated
&& ![Target::Closure, Target::Expression, Target::Statement, Target::Arm]
- .contains(&target)
+ .contains(target)
{
rustc_session::lint::builtin::USELESS_DEPRECATED
} else {
@@ -49,10 +51,13 @@ pub fn emit_attribute_lint(lint: &AttributeLint, lint_emi
*id,
*span,
session_diagnostics::InvalidTargetLint {
- name,
+ name: name.clone(),
target: target.plural_name(),
- applied: applied.clone(),
+ applied: DiagArgValue::StrListSepByAnd(
+ applied.into_iter().map(|i| Cow::Owned(i.to_string())).collect(),
+ ),
only,
+ attr_span: *span,
},
),
}
diff --git a/compiler/rustc_attr_parsing/src/parser.rs b/compiler/rustc_attr_parsing/src/parser.rs
index aecaae947c99..6d3cf684296b 100644
--- a/compiler/rustc_attr_parsing/src/parser.rs
+++ b/compiler/rustc_attr_parsing/src/parser.rs
@@ -3,45 +3,30 @@
//!
//! FIXME(jdonszelmann): delete `rustc_ast/attr/mod.rs`
+use std::borrow::Cow;
use std::fmt::{Debug, Display};
-use std::iter::Peekable;
-use rustc_ast::token::{self, Delimiter, Token};
-use rustc_ast::tokenstream::{TokenStreamIter, TokenTree};
+use rustc_ast::token::{self, Delimiter, MetaVarKind};
+use rustc_ast::tokenstream::TokenStream;
use rustc_ast::{AttrArgs, DelimArgs, Expr, ExprKind, LitKind, MetaItemLit, NormalAttr, Path};
use rustc_ast_pretty::pprust;
-use rustc_errors::DiagCtxtHandle;
+use rustc_errors::PResult;
use rustc_hir::{self as hir, AttrPath};
-use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol, kw, sym};
+use rustc_parse::exp;
+use rustc_parse::parser::{Parser, PathStyle, token_descr};
+use rustc_session::errors::report_lit_error;
+use rustc_session::parse::ParseSess;
+use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol, sym};
+use thin_vec::ThinVec;
-pub struct SegmentIterator<'a> {
- offset: usize,
- path: &'a PathParser<'a>,
-}
-
-impl<'a> Iterator for SegmentIterator<'a> {
- type Item = &'a Ident;
-
- fn next(&mut self) -> Option {
- if self.offset >= self.path.len() {
- return None;
- }
-
- let res = match self.path {
- PathParser::Ast(ast_path) => &ast_path.segments[self.offset].ident,
- PathParser::Attr(attr_path) => &attr_path.segments[self.offset],
- };
-
- self.offset += 1;
- Some(res)
- }
-}
+use crate::ShouldEmit;
+use crate::session_diagnostics::{
+ InvalidMetaItem, InvalidMetaItemQuoteIdentSugg, InvalidMetaItemRemoveNegSugg, MetaBadDelim,
+ MetaBadDelimSugg, SuffixedLiteralInAttribute,
+};
#[derive(Clone, Debug)]
-pub enum PathParser<'a> {
- Ast(&'a Path),
- Attr(AttrPath),
-}
+pub struct PathParser<'a>(pub Cow<'a, Path>);
impl<'a> PathParser<'a> {
pub fn get_attribute_path(&self) -> hir::AttrPath {
@@ -52,21 +37,15 @@ impl<'a> PathParser<'a> {
}
pub fn segments(&'a self) -> impl Iterator- {
- SegmentIterator { offset: 0, path: self }
+ self.0.segments.iter().map(|seg| &seg.ident)
}
pub fn span(&self) -> Span {
- match self {
- PathParser::Ast(path) => path.span,
- PathParser::Attr(attr_path) => attr_path.span,
- }
+ self.0.span
}
pub fn len(&self) -> usize {
- match self {
- PathParser::Ast(path) => path.segments.len(),
- PathParser::Attr(attr_path) => attr_path.segments.len(),
- }
+ self.0.segments.len()
}
pub fn segments_is(&self, segments: &[Symbol]) -> bool {
@@ -99,10 +78,7 @@ impl<'a> PathParser<'a> {
impl Display for PathParser<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- match self {
- PathParser::Ast(path) => write!(f, "{}", pprust::path_to_string(path)),
- PathParser::Attr(attr_path) => write!(f, "{attr_path}"),
- }
+ write!(f, "{}", pprust::path_to_string(&self.0))
}
}
@@ -123,21 +99,39 @@ impl<'a> ArgParser<'a> {
}
}
- pub fn from_attr_args<'sess>(value: &'a AttrArgs, dcx: DiagCtxtHandle<'sess>) -> Self {
- match value {
+ pub fn from_attr_args<'sess>(
+ value: &'a AttrArgs,
+ parts: &[Symbol],
+ psess: &'sess ParseSess,
+ should_emit: ShouldEmit,
+ ) -> Option {
+ Some(match value {
AttrArgs::Empty => Self::NoArgs,
- AttrArgs::Delimited(args) if args.delim == Delimiter::Parenthesis => {
- Self::List(MetaItemListParser::new(args, dcx))
- }
AttrArgs::Delimited(args) => {
- Self::List(MetaItemListParser { sub_parsers: vec![], span: args.dspan.entire() })
+ // The arguments of rustc_dummy are not validated if the arguments are delimited
+ if parts == &[sym::rustc_dummy] {
+ return Some(ArgParser::List(MetaItemListParser {
+ sub_parsers: ThinVec::new(),
+ span: args.dspan.entire(),
+ }));
+ }
+
+ if args.delim != Delimiter::Parenthesis {
+ psess.dcx().emit_err(MetaBadDelim {
+ span: args.dspan.entire(),
+ sugg: MetaBadDelimSugg { open: args.dspan.open, close: args.dspan.close },
+ });
+ return None;
+ }
+
+ Self::List(MetaItemListParser::new(args, psess, should_emit)?)
}
AttrArgs::Eq { eq_span, expr } => Self::NameValue(NameValueParser {
eq_span: *eq_span,
- value: expr_to_lit(dcx, &expr, *eq_span),
+ value: expr_to_lit(psess, &expr, expr.span, should_emit)?,
value_span: expr.span,
}),
- }
+ })
}
/// Asserts that this MetaItem is a list
@@ -249,11 +243,16 @@ impl<'a> Debug for MetaItemParser<'a> {
impl<'a> MetaItemParser<'a> {
/// Create a new parser from a [`NormalAttr`], which is stored inside of any
/// [`ast::Attribute`](rustc_ast::Attribute)
- pub fn from_attr<'sess>(attr: &'a NormalAttr, dcx: DiagCtxtHandle<'sess>) -> Self {
- Self {
- path: PathParser::Ast(&attr.item.path),
- args: ArgParser::from_attr_args(&attr.item.args, dcx),
- }
+ pub fn from_attr<'sess>(
+ attr: &'a NormalAttr,
+ parts: &[Symbol],
+ psess: &'sess ParseSess,
+ should_emit: ShouldEmit,
+ ) -> Option {
+ Some(Self {
+ path: PathParser(Cow::Borrowed(&attr.item.path)),
+ args: ArgParser::from_attr_args(&attr.item.args, parts, psess, should_emit)?,
+ })
}
}
@@ -318,215 +317,235 @@ impl NameValueParser {
}
}
-fn expr_to_lit(dcx: DiagCtxtHandle<'_>, expr: &Expr, span: Span) -> MetaItemLit {
- // In valid code the value always ends up as a single literal. Otherwise, a dummy
- // literal suffices because the error is handled elsewhere.
- if let ExprKind::Lit(token_lit) = expr.kind
- && let Ok(lit) = MetaItemLit::from_token_lit(token_lit, expr.span)
- {
- lit
+fn expr_to_lit(
+ psess: &ParseSess,
+ expr: &Expr,
+ span: Span,
+ should_emit: ShouldEmit,
+) -> Option {
+ if let ExprKind::Lit(token_lit) = expr.kind {
+ let res = MetaItemLit::from_token_lit(token_lit, expr.span);
+ match res {
+ Ok(lit) => {
+ if token_lit.suffix.is_some() {
+ should_emit.emit_err(
+ psess.dcx().create_err(SuffixedLiteralInAttribute { span: lit.span }),
+ );
+ None
+ } else {
+ if !lit.kind.is_unsuffixed() {
+ // Emit error and continue, we can still parse the attribute as if the suffix isn't there
+ should_emit.emit_err(
+ psess.dcx().create_err(SuffixedLiteralInAttribute { span: lit.span }),
+ );
+ }
+
+ Some(lit)
+ }
+ }
+ Err(err) => {
+ let guar = report_lit_error(psess, err, token_lit, expr.span);
+ let lit = MetaItemLit {
+ symbol: token_lit.symbol,
+ suffix: token_lit.suffix,
+ kind: LitKind::Err(guar),
+ span: expr.span,
+ };
+ Some(lit)
+ }
+ }
} else {
- let guar = dcx.span_delayed_bug(
- span,
- "expr in place where literal is expected (builtin attr parsing)",
- );
- MetaItemLit { symbol: sym::dummy, suffix: None, kind: LitKind::Err(guar), span }
+ if matches!(should_emit, ShouldEmit::Nothing) {
+ return None;
+ }
+
+ // Example cases:
+ // - `#[foo = 1+1]`: results in `ast::ExprKind::BinOp`.
+ // - `#[foo = include_str!("nonexistent-file.rs")]`:
+ // results in `ast::ExprKind::Err`. In that case we delay
+ // the error because an earlier error will have already
+ // been reported.
+ let msg = "attribute value must be a literal";
+ let err = psess.dcx().struct_span_err(span, msg);
+ should_emit.emit_err(err);
+ None
}
}
struct MetaItemListParserContext<'a, 'sess> {
- // the tokens inside the delimiters, so `#[some::attr(a b c)]` would have `a b c` inside
- inside_delimiters: Peekable>,
- dcx: DiagCtxtHandle<'sess>,
+ parser: &'a mut Parser<'sess>,
+ should_emit: ShouldEmit,
}
impl<'a, 'sess> MetaItemListParserContext<'a, 'sess> {
- fn done(&mut self) -> bool {
- self.inside_delimiters.peek().is_none()
- }
+ fn parse_unsuffixed_meta_item_lit(&mut self) -> PResult<'sess, MetaItemLit> {
+ let uninterpolated_span = self.parser.token_uninterpolated_span();
+ let Some(token_lit) = self.parser.eat_token_lit() else {
+ return self.parser.handle_missing_lit(Parser::mk_meta_item_lit_char);
+ };
- fn next_path(&mut self) -> Option {
- // FIXME: Share code with `parse_path`.
- let tt = self.inside_delimiters.next().map(|tt| TokenTree::uninterpolate(tt));
-
- match tt.as_deref()? {
- &TokenTree::Token(
- Token { kind: ref kind @ (token::Ident(..) | token::PathSep), span },
- _,
- ) => {
- // here we have either an ident or pathsep `::`.
-
- let mut segments = if let &token::Ident(name, _) = kind {
- // when we lookahead another pathsep, more path's coming
- if let Some(TokenTree::Token(Token { kind: token::PathSep, .. }, _)) =
- self.inside_delimiters.peek()
- {
- self.inside_delimiters.next();
- vec![Ident::new(name, span)]
- } else {
- // else we have a single identifier path, that's all
- return Some(AttrPath {
- segments: vec![Ident::new(name, span)].into_boxed_slice(),
- span,
- });
- }
- } else {
- // if `::` is all we get, we just got a path root
- vec![Ident::new(kw::PathRoot, span)]
- };
-
- // one segment accepted. accept n more
- loop {
- // another ident?
- if let Some(&TokenTree::Token(Token { kind: token::Ident(name, _), span }, _)) =
- self.inside_delimiters
- .next()
- .map(|tt| TokenTree::uninterpolate(tt))
- .as_deref()
- {
- segments.push(Ident::new(name, span));
- } else {
- return None;
- }
- // stop unless we see another `::`
- if let Some(TokenTree::Token(Token { kind: token::PathSep, .. }, _)) =
- self.inside_delimiters.peek()
- {
- self.inside_delimiters.next();
- } else {
- break;
- }
- }
- let span = span.with_hi(segments.last().unwrap().span.hi());
- Some(AttrPath { segments: segments.into_boxed_slice(), span })
+ let lit = match MetaItemLit::from_token_lit(token_lit, self.parser.prev_token.span) {
+ Ok(lit) => lit,
+ Err(err) => {
+ let guar =
+ report_lit_error(&self.parser.psess, err, token_lit, uninterpolated_span);
+ // Pack possible quotes and prefixes from the original literal into
+ // the error literal's symbol so they can be pretty-printed faithfully.
+ let suffixless_lit = token::Lit::new(token_lit.kind, token_lit.symbol, None);
+ let symbol = Symbol::intern(&suffixless_lit.to_string());
+ let token_lit = token::Lit::new(token::Err(guar), symbol, token_lit.suffix);
+ MetaItemLit::from_token_lit(token_lit, uninterpolated_span).unwrap()
}
- TokenTree::Token(Token { kind, .. }, _) if kind.is_delim() => None,
- _ => {
- // malformed attributes can get here. We can't crash, but somewhere else should've
- // already warned for this.
- None
- }
- }
- }
+ };
- fn value(&mut self) -> Option {
- match self.inside_delimiters.next() {
- Some(TokenTree::Delimited(.., Delimiter::Invisible(_), inner_tokens)) => {
- MetaItemListParserContext {
- inside_delimiters: inner_tokens.iter().peekable(),
- dcx: self.dcx,
- }
- .value()
- }
- Some(TokenTree::Token(token, _)) => MetaItemLit::from_token(token),
- _ => None,
- }
- }
-
- /// parses one element on the inside of a list attribute like `#[my_attr( )]`
- ///
- /// parses a path followed be either:
- /// 1. nothing (a word attr)
- /// 2. a parenthesized list
- /// 3. an equals sign and a literal (name-value)
- ///
- /// Can also parse *just* a literal. This is for cases like as `#[my_attr("literal")]`
- /// where no path is given before the literal
- ///
- /// Some exceptions too for interpolated attributes which are already pre-processed
- fn next(&mut self) -> Option> {
- // a list element is either a literal
- if let Some(TokenTree::Token(token, _)) = self.inside_delimiters.peek()
- && let Some(lit) = MetaItemLit::from_token(token)
- {
- self.inside_delimiters.next();
- return Some(MetaItemOrLitParser::Lit(lit));
- } else if let Some(TokenTree::Delimited(.., Delimiter::Invisible(_), inner_tokens)) =
- self.inside_delimiters.peek()
- {
- self.inside_delimiters.next();
- return MetaItemListParserContext {
- inside_delimiters: inner_tokens.iter().peekable(),
- dcx: self.dcx,
- }
- .next();
+ if !lit.kind.is_unsuffixed() {
+ // Emit error and continue, we can still parse the attribute as if the suffix isn't there
+ self.should_emit.emit_err(
+ self.parser.dcx().create_err(SuffixedLiteralInAttribute { span: lit.span }),
+ );
}
- // or a path.
- let path = self.next_path()?;
-
- // Paths can be followed by:
- // - `(more meta items)` (another list)
- // - `= lit` (a name-value)
- // - nothing
- Some(MetaItemOrLitParser::MetaItemParser(match self.inside_delimiters.peek() {
- Some(TokenTree::Delimited(dspan, _, Delimiter::Parenthesis, inner_tokens)) => {
- self.inside_delimiters.next();
-
- MetaItemParser {
- path: PathParser::Attr(path),
- args: ArgParser::List(MetaItemListParser::new_tts(
- inner_tokens.iter(),
- dspan.entire(),
- self.dcx,
- )),
- }
- }
- Some(TokenTree::Delimited(_, ..)) => {
- self.inside_delimiters.next();
- // self.dcx.span_delayed_bug(span.entire(), "wrong delimiters");
- return None;
- }
- Some(TokenTree::Token(Token { kind: token::Eq, span }, _)) => {
- self.inside_delimiters.next();
- let value = self.value()?;
- MetaItemParser {
- path: PathParser::Attr(path),
- args: ArgParser::NameValue(NameValueParser {
- eq_span: *span,
- value_span: value.span,
- value,
- }),
- }
- }
- _ => MetaItemParser { path: PathParser::Attr(path), args: ArgParser::NoArgs },
- }))
+ Ok(lit)
}
- fn parse(mut self, span: Span) -> MetaItemListParser<'a> {
- let mut sub_parsers = Vec::new();
-
- while !self.done() {
- let Some(n) = self.next() else {
- continue;
+ fn parse_attr_item(&mut self) -> PResult<'sess, MetaItemParser<'static>> {
+ if let Some(MetaVarKind::Meta { has_meta_form }) = self.parser.token.is_metavar_seq() {
+ return if has_meta_form {
+ let attr_item = self
+ .parser
+ .eat_metavar_seq(MetaVarKind::Meta { has_meta_form: true }, |this| {
+ MetaItemListParserContext { parser: this, should_emit: self.should_emit }
+ .parse_attr_item()
+ })
+ .unwrap();
+ Ok(attr_item)
+ } else {
+ self.parser.unexpected_any()
};
- sub_parsers.push(n);
+ }
- match self.inside_delimiters.peek() {
- None | Some(TokenTree::Token(Token { kind: token::Comma, .. }, _)) => {
- self.inside_delimiters.next();
- }
- Some(_) => {}
+ let path = self.parser.parse_path(PathStyle::Mod)?;
+
+ // Check style of arguments that this meta item has
+ let args = if self.parser.check(exp!(OpenParen)) {
+ let start = self.parser.token.span;
+ let (sub_parsers, _) = self.parser.parse_paren_comma_seq(|parser| {
+ MetaItemListParserContext { parser, should_emit: self.should_emit }
+ .parse_meta_item_inner()
+ })?;
+ let end = self.parser.prev_token.span;
+ ArgParser::List(MetaItemListParser { sub_parsers, span: start.with_hi(end.hi()) })
+ } else if self.parser.eat(exp!(Eq)) {
+ let eq_span = self.parser.prev_token.span;
+ let value = self.parse_unsuffixed_meta_item_lit()?;
+
+ ArgParser::NameValue(NameValueParser { eq_span, value, value_span: value.span })
+ } else {
+ ArgParser::NoArgs
+ };
+
+ Ok(MetaItemParser { path: PathParser(Cow::Owned(path)), args })
+ }
+
+ fn parse_meta_item_inner(&mut self) -> PResult<'sess, MetaItemOrLitParser<'static>> {
+ match self.parse_unsuffixed_meta_item_lit() {
+ Ok(lit) => return Ok(MetaItemOrLitParser::Lit(lit)),
+ Err(err) => err.cancel(), // we provide a better error below
+ }
+
+ match self.parse_attr_item() {
+ Ok(mi) => return Ok(MetaItemOrLitParser::MetaItemParser(mi)),
+ Err(err) => err.cancel(), // we provide a better error below
+ }
+
+ let mut err = InvalidMetaItem {
+ span: self.parser.token.span,
+ descr: token_descr(&self.parser.token),
+ quote_ident_sugg: None,
+ remove_neg_sugg: None,
+ };
+
+ // Suggest quoting idents, e.g. in `#[cfg(key = value)]`. We don't use `Token::ident` and
+ // don't `uninterpolate` the token to avoid suggesting anything butchered or questionable
+ // when macro metavariables are involved.
+ if self.parser.prev_token == token::Eq
+ && let token::Ident(..) = self.parser.token.kind
+ {
+ let before = self.parser.token.span.shrink_to_lo();
+ while let token::Ident(..) = self.parser.token.kind {
+ self.parser.bump();
+ }
+ err.quote_ident_sugg = Some(InvalidMetaItemQuoteIdentSugg {
+ before,
+ after: self.parser.prev_token.span.shrink_to_hi(),
+ });
+ }
+
+ if self.parser.token == token::Minus
+ && self
+ .parser
+ .look_ahead(1, |t| matches!(t.kind, rustc_ast::token::TokenKind::Literal { .. }))
+ {
+ err.remove_neg_sugg =
+ Some(InvalidMetaItemRemoveNegSugg { negative_sign: self.parser.token.span });
+ self.parser.bump();
+ self.parser.bump();
+ }
+
+ Err(self.parser.dcx().create_err(err))
+ }
+
+ fn parse(
+ tokens: TokenStream,
+ psess: &'sess ParseSess,
+ span: Span,
+ should_emit: ShouldEmit,
+ ) -> PResult<'sess, MetaItemListParser<'static>> {
+ let mut parser = Parser::new(psess, tokens, None);
+ let mut this = MetaItemListParserContext { parser: &mut parser, should_emit };
+
+ // Presumably, the majority of the time there will only be one attr.
+ let mut sub_parsers = ThinVec::with_capacity(1);
+ while this.parser.token != token::Eof {
+ sub_parsers.push(this.parse_meta_item_inner()?);
+
+ if !this.parser.eat(exp!(Comma)) {
+ break;
}
}
- MetaItemListParser { sub_parsers, span }
+ if parser.token != token::Eof {
+ parser.unexpected()?;
+ }
+
+ Ok(MetaItemListParser { sub_parsers, span })
}
}
#[derive(Debug, Clone)]
pub struct MetaItemListParser<'a> {
- sub_parsers: Vec>,
+ sub_parsers: ThinVec>,
pub span: Span,
}
impl<'a> MetaItemListParser<'a> {
- fn new<'sess>(delim: &'a DelimArgs, dcx: DiagCtxtHandle<'sess>) -> Self {
- MetaItemListParser::new_tts(delim.tokens.iter(), delim.dspan.entire(), dcx)
- }
-
- fn new_tts<'sess>(tts: TokenStreamIter<'a>, span: Span, dcx: DiagCtxtHandle<'sess>) -> Self {
- MetaItemListParserContext { inside_delimiters: tts.peekable(), dcx }.parse(span)
+ fn new<'sess>(
+ delim: &'a DelimArgs,
+ psess: &'sess ParseSess,
+ should_emit: ShouldEmit,
+ ) -> Option {
+ match MetaItemListParserContext::parse(
+ delim.tokens.clone(),
+ psess,
+ delim.dspan.entire(),
+ should_emit,
+ ) {
+ Ok(s) => Some(s),
+ Err(e) => {
+ should_emit.emit_err(e);
+ None
+ }
+ }
}
/// Lets you pick and choose as what you want to parse each element in the list
diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs
index 95e85667cd66..72bee0ddfbfe 100644
--- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs
+++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs
@@ -1,6 +1,6 @@
use std::num::IntErrorKind;
-use rustc_ast as ast;
+use rustc_ast::{self as ast, AttrStyle, Path};
use rustc_errors::codes::*;
use rustc_errors::{
Applicability, Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level,
@@ -485,10 +485,12 @@ pub(crate) struct EmptyAttributeList {
#[warning]
#[help]
pub(crate) struct InvalidTargetLint {
- pub name: Symbol,
+ pub name: AttrPath,
pub target: &'static str,
- pub applied: String,
+ pub applied: DiagArgValue,
pub only: &'static str,
+ #[suggestion(code = "", applicability = "machine-applicable", style = "tool-only")]
+ pub attr_span: Span,
}
#[derive(Diagnostic)]
@@ -496,10 +498,11 @@ pub(crate) struct InvalidTargetLint {
#[diag(attr_parsing_invalid_target)]
pub(crate) struct InvalidTarget {
#[primary_span]
+ #[suggestion(code = "", applicability = "machine-applicable", style = "tool-only")]
pub span: Span,
- pub name: Symbol,
+ pub name: AttrPath,
pub target: &'static str,
- pub applied: String,
+ pub applied: DiagArgValue,
pub only: &'static str,
}
@@ -555,7 +558,7 @@ pub(crate) struct LinkOrdinalOutOfRange {
pub ordinal: u128,
}
-pub(crate) enum AttributeParseErrorReason {
+pub(crate) enum AttributeParseErrorReason<'a> {
ExpectedNoArgs,
ExpectedStringLiteral {
byte_string: Option,
@@ -568,7 +571,7 @@ pub(crate) enum AttributeParseErrorReason {
ExpectedNameValue(Option),
DuplicateKey(Symbol),
ExpectedSpecificArgument {
- possibilities: Vec<&'static str>,
+ possibilities: &'a [Symbol],
strings: bool,
/// Should we tell the user to write a list when they didn't?
list: bool,
@@ -576,15 +579,16 @@ pub(crate) enum AttributeParseErrorReason {
ExpectedIdentifier,
}
-pub(crate) struct AttributeParseError {
+pub(crate) struct AttributeParseError<'a> {
pub(crate) span: Span,
pub(crate) attr_span: Span,
+ pub(crate) attr_style: AttrStyle,
pub(crate) template: AttributeTemplate,
pub(crate) attribute: AttrPath,
- pub(crate) reason: AttributeParseErrorReason,
+ pub(crate) reason: AttributeParseErrorReason<'a>,
}
-impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError {
+impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError<'_> {
fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> {
let name = self.attribute.to_string();
@@ -653,7 +657,7 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError {
list: false,
} => {
let quote = if strings { '"' } else { '`' };
- match possibilities.as_slice() {
+ match possibilities {
&[] => {}
&[x] => {
diag.span_label(
@@ -683,7 +687,7 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError {
list: true,
} => {
let quote = if strings { '"' } else { '`' };
- match possibilities.as_slice() {
+ match possibilities {
&[] => {}
&[x] => {
diag.span_label(
@@ -717,7 +721,8 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError {
if let Some(link) = self.template.docs {
diag.note(format!("for more information, visit <{link}>"));
}
- let suggestions = self.template.suggestions(false, &name);
+ let suggestions = self.template.suggestions(self.attr_style, &name);
+
diag.span_suggestions(
self.attr_span,
if suggestions.len() == 1 {
@@ -732,3 +737,92 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError {
diag
}
}
+
+#[derive(Diagnostic)]
+#[diag(attr_parsing_invalid_attr_unsafe)]
+#[note]
+pub(crate) struct InvalidAttrUnsafe {
+ #[primary_span]
+ #[label]
+ pub span: Span,
+ pub name: Path,
+}
+
+#[derive(Diagnostic)]
+#[diag(attr_parsing_unsafe_attr_outside_unsafe)]
+pub(crate) struct UnsafeAttrOutsideUnsafe {
+ #[primary_span]
+ #[label]
+ pub span: Span,
+ #[subdiagnostic]
+ pub suggestion: UnsafeAttrOutsideUnsafeSuggestion,
+}
+
+#[derive(Subdiagnostic)]
+#[multipart_suggestion(
+ attr_parsing_unsafe_attr_outside_unsafe_suggestion,
+ applicability = "machine-applicable"
+)]
+pub(crate) struct UnsafeAttrOutsideUnsafeSuggestion {
+ #[suggestion_part(code = "unsafe(")]
+ pub left: Span,
+ #[suggestion_part(code = ")")]
+ pub right: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(attr_parsing_meta_bad_delim)]
+pub(crate) struct MetaBadDelim {
+ #[primary_span]
+ pub span: Span,
+ #[subdiagnostic]
+ pub sugg: MetaBadDelimSugg,
+}
+
+#[derive(Subdiagnostic)]
+#[multipart_suggestion(
+ attr_parsing_meta_bad_delim_suggestion,
+ applicability = "machine-applicable"
+)]
+pub(crate) struct MetaBadDelimSugg {
+ #[suggestion_part(code = "(")]
+ pub open: Span,
+ #[suggestion_part(code = ")")]
+ pub close: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(attr_parsing_invalid_meta_item)]
+pub(crate) struct InvalidMetaItem {
+ #[primary_span]
+ pub span: Span,
+ pub descr: String,
+ #[subdiagnostic]
+ pub quote_ident_sugg: Option,
+ #[subdiagnostic]
+ pub remove_neg_sugg: Option,
+}
+
+#[derive(Subdiagnostic)]
+#[multipart_suggestion(attr_parsing_quote_ident_sugg, applicability = "machine-applicable")]
+pub(crate) struct InvalidMetaItemQuoteIdentSugg {
+ #[suggestion_part(code = "\"")]
+ pub before: Span,
+ #[suggestion_part(code = "\"")]
+ pub after: Span,
+}
+
+#[derive(Subdiagnostic)]
+#[multipart_suggestion(attr_parsing_remove_neg_sugg, applicability = "machine-applicable")]
+pub(crate) struct InvalidMetaItemRemoveNegSugg {
+ #[suggestion_part(code = "")]
+ pub negative_sign: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(attr_parsing_suffixed_literal_in_attribute)]
+#[help]
+pub(crate) struct SuffixedLiteralInAttribute {
+ #[primary_span]
+ pub span: Span,
+}
diff --git a/compiler/rustc_attr_parsing/src/target_checking.rs b/compiler/rustc_attr_parsing/src/target_checking.rs
new file mode 100644
index 000000000000..9568b791b3f8
--- /dev/null
+++ b/compiler/rustc_attr_parsing/src/target_checking.rs
@@ -0,0 +1,247 @@
+use std::borrow::Cow;
+
+use rustc_errors::DiagArgValue;
+use rustc_feature::Features;
+use rustc_hir::lints::{AttributeLint, AttributeLintKind};
+use rustc_hir::{AttrPath, MethodKind, Target};
+use rustc_span::Span;
+
+use crate::AttributeParser;
+use crate::context::Stage;
+use crate::session_diagnostics::InvalidTarget;
+
+#[derive(Debug)]
+pub(crate) enum AllowedTargets {
+ AllowList(&'static [Policy]),
+ AllowListWarnRest(&'static [Policy]),
+}
+
+pub(crate) enum AllowedResult {
+ Allowed,
+ Warn,
+ Error,
+}
+
+impl AllowedTargets {
+ pub(crate) fn is_allowed(&self, target: Target) -> AllowedResult {
+ match self {
+ AllowedTargets::AllowList(list) => {
+ if list.contains(&Policy::Allow(target)) {
+ AllowedResult::Allowed
+ } else if list.contains(&Policy::Warn(target)) {
+ AllowedResult::Warn
+ } else {
+ AllowedResult::Error
+ }
+ }
+ AllowedTargets::AllowListWarnRest(list) => {
+ if list.contains(&Policy::Allow(target)) {
+ AllowedResult::Allowed
+ } else if list.contains(&Policy::Error(target)) {
+ AllowedResult::Error
+ } else {
+ AllowedResult::Warn
+ }
+ }
+ }
+ }
+
+ pub(crate) fn allowed_targets(&self) -> Vec {
+ match self {
+ AllowedTargets::AllowList(list) => list,
+ AllowedTargets::AllowListWarnRest(list) => list,
+ }
+ .iter()
+ .filter_map(|target| match target {
+ Policy::Allow(target) => Some(*target),
+ Policy::Warn(_) => None,
+ Policy::Error(_) => None,
+ })
+ .collect()
+ }
+}
+
+#[derive(Debug, Eq, PartialEq)]
+pub(crate) enum Policy {
+ Allow(Target),
+ Warn(Target),
+ Error(Target),
+}
+
+impl AttributeParser<'_, S> {
+ pub(crate) fn check_target(
+ &self,
+ attr_name: AttrPath,
+ attr_span: Span,
+ allowed_targets: &AllowedTargets,
+ target: Target,
+ target_id: S::Id,
+ mut emit_lint: impl FnMut(AttributeLint),
+ ) {
+ match allowed_targets.is_allowed(target) {
+ AllowedResult::Allowed => {}
+ AllowedResult::Warn => {
+ let allowed_targets = allowed_targets.allowed_targets();
+ let (applied, only) =
+ allowed_targets_applied(allowed_targets, target, self.features);
+ emit_lint(AttributeLint {
+ id: target_id,
+ span: attr_span,
+ kind: AttributeLintKind::InvalidTarget {
+ name: attr_name,
+ target,
+ only: if only { "only " } else { "" },
+ applied,
+ },
+ });
+ }
+ AllowedResult::Error => {
+ let allowed_targets = allowed_targets.allowed_targets();
+ let (applied, only) =
+ allowed_targets_applied(allowed_targets, target, self.features);
+ self.dcx().emit_err(InvalidTarget {
+ span: attr_span,
+ name: attr_name,
+ target: target.plural_name(),
+ only: if only { "only " } else { "" },
+ applied: DiagArgValue::StrListSepByAnd(
+ applied.into_iter().map(Cow::Owned).collect(),
+ ),
+ });
+ }
+ }
+ }
+}
+
+/// Takes a list of `allowed_targets` for an attribute, and the `target` the attribute was applied to.
+/// Does some heuristic-based filtering to remove uninteresting targets, and formats the targets into a string
+pub(crate) fn allowed_targets_applied(
+ mut allowed_targets: Vec,
+ target: Target,
+ features: Option<&Features>,
+) -> (Vec, bool) {
+ // Remove unstable targets from `allowed_targets` if their features are not enabled
+ if let Some(features) = features {
+ if !features.fn_delegation() {
+ allowed_targets.retain(|t| !matches!(t, Target::Delegation { .. }));
+ }
+ if !features.stmt_expr_attributes() {
+ allowed_targets.retain(|t| !matches!(t, Target::Expression | Target::Statement));
+ }
+ if !features.extern_types() {
+ allowed_targets.retain(|t| !matches!(t, Target::ForeignTy));
+ }
+ }
+
+ // We define groups of "similar" targets.
+ // If at least two of the targets are allowed, and the `target` is not in the group,
+ // we collapse the entire group to a single entry to simplify the target list
+ const FUNCTION_LIKE: &[Target] = &[
+ Target::Fn,
+ Target::Closure,
+ Target::ForeignFn,
+ Target::Method(MethodKind::Inherent),
+ Target::Method(MethodKind::Trait { body: false }),
+ Target::Method(MethodKind::Trait { body: true }),
+ Target::Method(MethodKind::TraitImpl),
+ ];
+ const METHOD_LIKE: &[Target] = &[
+ Target::Method(MethodKind::Inherent),
+ Target::Method(MethodKind::Trait { body: false }),
+ Target::Method(MethodKind::Trait { body: true }),
+ Target::Method(MethodKind::TraitImpl),
+ ];
+ const IMPL_LIKE: &[Target] =
+ &[Target::Impl { of_trait: false }, Target::Impl { of_trait: true }];
+ const ADT_LIKE: &[Target] = &[Target::Struct, Target::Enum];
+
+ let mut added_fake_targets = Vec::new();
+ filter_targets(
+ &mut allowed_targets,
+ FUNCTION_LIKE,
+ "functions",
+ target,
+ &mut added_fake_targets,
+ );
+ filter_targets(&mut allowed_targets, METHOD_LIKE, "methods", target, &mut added_fake_targets);
+ filter_targets(&mut allowed_targets, IMPL_LIKE, "impl blocks", target, &mut added_fake_targets);
+ filter_targets(&mut allowed_targets, ADT_LIKE, "data types", target, &mut added_fake_targets);
+
+ // If there is now only 1 target left, show that as the only possible target
+ (
+ added_fake_targets
+ .iter()
+ .copied()
+ .chain(allowed_targets.iter().map(|t| t.plural_name()))
+ .map(|i| i.to_string())
+ .collect(),
+ allowed_targets.len() + added_fake_targets.len() == 1,
+ )
+}
+
+fn filter_targets(
+ allowed_targets: &mut Vec,
+ target_group: &'static [Target],
+ target_group_name: &'static str,
+ target: Target,
+ added_fake_targets: &mut Vec<&'static str>,
+) {
+ if target_group.contains(&target) {
+ return;
+ }
+ if allowed_targets.iter().filter(|at| target_group.contains(at)).count() < 2 {
+ return;
+ }
+ allowed_targets.retain(|t| !target_group.contains(t));
+ added_fake_targets.push(target_group_name);
+}
+
+/// This is the list of all targets to which a attribute can be applied
+/// This is used for:
+/// - `rustc_dummy`, which can be applied to all targets
+/// - Attributes that are not parted to the new target system yet can use this list as a placeholder
+pub(crate) const ALL_TARGETS: &'static [Policy] = {
+ use Policy::Allow;
+ &[
+ Allow(Target::ExternCrate),
+ Allow(Target::Use),
+ Allow(Target::Static),
+ Allow(Target::Const),
+ Allow(Target::Fn),
+ Allow(Target::Closure),
+ Allow(Target::Mod),
+ Allow(Target::ForeignMod),
+ Allow(Target::GlobalAsm),
+ Allow(Target::TyAlias),
+ Allow(Target::Enum),
+ Allow(Target::Variant),
+ Allow(Target::Struct),
+ Allow(Target::Field),
+ Allow(Target::Union),
+ Allow(Target::Trait),
+ Allow(Target::TraitAlias),
+ Allow(Target::Impl { of_trait: false }),
+ Allow(Target::Impl { of_trait: true }),
+ Allow(Target::Expression),
+ Allow(Target::Statement),
+ Allow(Target::Arm),
+ Allow(Target::AssocConst),
+ Allow(Target::Method(MethodKind::Inherent)),
+ Allow(Target::Method(MethodKind::Trait { body: false })),
+ Allow(Target::Method(MethodKind::Trait { body: true })),
+ Allow(Target::Method(MethodKind::TraitImpl)),
+ Allow(Target::AssocTy),
+ Allow(Target::ForeignFn),
+ Allow(Target::ForeignStatic),
+ Allow(Target::ForeignTy),
+ Allow(Target::MacroDef),
+ Allow(Target::Param),
+ Allow(Target::PatField),
+ Allow(Target::ExprField),
+ Allow(Target::WherePredicate),
+ Allow(Target::MacroCall),
+ Allow(Target::Crate),
+ Allow(Target::Delegation { mac: false }),
+ Allow(Target::Delegation { mac: true }),
+ ]
+};
diff --git a/compiler/rustc_parse/src/validate_attr.rs b/compiler/rustc_attr_parsing/src/validate_attr.rs
similarity index 95%
rename from compiler/rustc_parse/src/validate_attr.rs
rename to compiler/rustc_attr_parsing/src/validate_attr.rs
index 68ef6d6f32cb..7a7624893bd2 100644
--- a/compiler/rustc_parse/src/validate_attr.rs
+++ b/compiler/rustc_attr_parsing/src/validate_attr.rs
@@ -8,16 +8,16 @@ use rustc_ast::{
self as ast, AttrArgs, Attribute, DelimArgs, MetaItem, MetaItemInner, MetaItemKind, NodeId,
Path, Safety,
};
-use rustc_attr_parsing::{AttributeParser, Late};
use rustc_errors::{Applicability, DiagCtxtHandle, FatalError, PResult};
use rustc_feature::{AttributeSafety, AttributeTemplate, BUILTIN_ATTRIBUTE_MAP, BuiltinAttribute};
+use rustc_parse::parse_in;
use rustc_session::errors::report_lit_error;
use rustc_session::lint::BuiltinLintDiag;
use rustc_session::lint::builtin::{ILL_FORMED_ATTRIBUTE_INPUT, UNSAFE_ATTR_OUTSIDE_UNSAFE};
use rustc_session::parse::ParseSess;
use rustc_span::{Span, Symbol, sym};
-use crate::{errors, parse_in};
+use crate::{AttributeParser, Late, session_diagnostics as errors};
pub fn check_attr(psess: &ParseSess, attr: &Attribute, id: NodeId) {
if attr.is_doc_comment() || attr.has_name(sym::cfg_trace) || attr.has_name(sym::cfg_attr_trace)
@@ -33,7 +33,10 @@ pub fn check_attr(psess: &ParseSess, attr: &Attribute, id: NodeId) {
// Check input tokens for built-in and key-value attributes.
match builtin_attr_info {
// `rustc_dummy` doesn't have any restrictions specific to built-in attributes.
- Some(BuiltinAttribute { name, template, .. }) if *name != sym::rustc_dummy => {
+ Some(BuiltinAttribute { name, template, .. }) => {
+ if AttributeParser::::is_parsed_attribute(slice::from_ref(&name)) {
+ return;
+ }
match parse_meta(psess, attr) {
// Don't check safety again, we just did that
Ok(meta) => {
@@ -133,16 +136,6 @@ fn check_meta_bad_delim(psess: &ParseSess, span: DelimSpan, delim: Delimiter) {
});
}
-pub(super) fn check_cfg_attr_bad_delim(psess: &ParseSess, span: DelimSpan, delim: Delimiter) {
- if let Delimiter::Parenthesis = delim {
- return;
- }
- psess.dcx().emit_err(errors::CfgAttrBadDelim {
- span: span.entire(),
- sugg: errors::MetaBadDelimSugg { open: span.open, close: span.close },
- });
-}
-
/// Checks that the given meta-item is compatible with this `AttributeTemplate`.
fn is_attr_template_compatible(template: &AttributeTemplate, meta: &ast::MetaItemKind) -> bool {
let is_one_allowed_subword = |items: &[MetaItemInner]| match items {
@@ -269,9 +262,6 @@ pub fn check_builtin_meta_item(
) {
if !is_attr_template_compatible(&template, &meta.kind) {
// attrs with new parsers are locally validated so excluded here
- if AttributeParser::::is_parsed_attribute(slice::from_ref(&name)) {
- return;
- }
emit_malformed_attribute(psess, style, meta.span, name, template);
}
diff --git a/compiler/rustc_borrowck/messages.ftl b/compiler/rustc_borrowck/messages.ftl
index 33b80c4b03d6..f59e106c7ac3 100644
--- a/compiler/rustc_borrowck/messages.ftl
+++ b/compiler/rustc_borrowck/messages.ftl
@@ -90,7 +90,7 @@ borrowck_lifetime_constraints_error =
lifetime may not live long enough
borrowck_limitations_implies_static =
- due to current limitations in the borrow checker, this implies a `'static` lifetime
+ due to a current limitation of the type system, this implies a `'static` lifetime
borrowck_move_closure_suggestion =
consider adding 'move' keyword before the nested closure
diff --git a/compiler/rustc_borrowck/src/dataflow.rs b/compiler/rustc_borrowck/src/dataflow.rs
index 57db2e9fb574..d3f6c01ab8c3 100644
--- a/compiler/rustc_borrowck/src/dataflow.rs
+++ b/compiler/rustc_borrowck/src/dataflow.rs
@@ -1,7 +1,6 @@
use std::fmt;
use rustc_data_structures::fx::FxIndexMap;
-use rustc_data_structures::graph;
use rustc_index::bit_set::{DenseBitSet, MixedBitSet};
use rustc_middle::mir::{
self, BasicBlock, Body, CallReturnPlaces, Location, Place, TerminatorEdges,
@@ -317,9 +316,8 @@ impl<'tcx> PoloniusOutOfScopePrecomputer<'_, 'tcx> {
loans_out_of_scope_at_location: FxIndexMap::default(),
};
for (loan_idx, loan_data) in borrow_set.iter_enumerated() {
- let issuing_region = loan_data.region;
let loan_issued_at = loan_data.reserve_location;
- prec.precompute_loans_out_of_scope(loan_idx, issuing_region, loan_issued_at);
+ prec.precompute_loans_out_of_scope(loan_idx, loan_issued_at);
}
prec.loans_out_of_scope_at_location
@@ -328,45 +326,7 @@ impl<'tcx> PoloniusOutOfScopePrecomputer<'_, 'tcx> {
/// Loans are in scope while they are live: whether they are contained within any live region.
/// In the location-insensitive analysis, a loan will be contained in a region if the issuing
/// region can reach it in the subset graph. So this is a reachability problem.
- fn precompute_loans_out_of_scope(
- &mut self,
- loan_idx: BorrowIndex,
- issuing_region: RegionVid,
- loan_issued_at: Location,
- ) {
- let sccs = self.regioncx.constraint_sccs();
- let universal_regions = self.regioncx.universal_regions();
-
- // The loop below was useful for the location-insensitive analysis but shouldn't be
- // impactful in the location-sensitive case. It seems that it does, however, as without it a
- // handful of tests fail. That likely means some liveness or outlives data related to choice
- // regions is missing
- // FIXME: investigate the impact of loans traversing applied member constraints and why some
- // tests fail otherwise.
- //
- // We first handle the cases where the loan doesn't go out of scope, depending on the
- // issuing region's successors.
- for successor in graph::depth_first_search(&self.regioncx.region_graph(), issuing_region) {
- // Via applied member constraints
- //
- // The issuing region can flow into the choice regions, and they are either:
- // - placeholders or free regions themselves,
- // - or also transitively outlive a free region.
- //
- // That is to say, if there are applied member constraints here, the loan escapes the
- // function and cannot go out of scope. We could early return here.
- //
- // For additional insurance via fuzzing and crater, we verify that the constraint's min
- // choice indeed escapes the function. In the future, we could e.g. turn this check into
- // a debug assert and early return as an optimization.
- let scc = sccs.scc(successor);
- for constraint in self.regioncx.applied_member_constraints(scc) {
- if universal_regions.is_universal_region(constraint.min_choice) {
- return;
- }
- }
- }
-
+ fn precompute_loans_out_of_scope(&mut self, loan_idx: BorrowIndex, loan_issued_at: Location) {
let first_block = loan_issued_at.block;
let first_bb_data = &self.body.basic_blocks[first_block];
diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs
index b67dba3af96d..5642cdf87fde 100644
--- a/compiler/rustc_borrowck/src/diagnostics/mod.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs
@@ -6,7 +6,9 @@ use rustc_abi::{FieldIdx, VariantIdx};
use rustc_data_structures::fx::FxIndexMap;
use rustc_errors::{Applicability, Diag, EmissionGuarantee, MultiSpan, listify};
use rustc_hir::def::{CtorKind, Namespace};
-use rustc_hir::{self as hir, CoroutineKind, LangItem};
+use rustc_hir::{
+ self as hir, CoroutineKind, GenericBound, LangItem, WhereBoundPredicate, WherePredicateKind,
+};
use rustc_index::{IndexSlice, IndexVec};
use rustc_infer::infer::{BoundRegionConversionTime, NllRegionVariableOrigin};
use rustc_infer::traits::SelectionError;
@@ -658,25 +660,66 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
/// Add a note to region errors and borrow explanations when higher-ranked regions in predicates
/// implicitly introduce an "outlives `'static`" constraint.
+ ///
+ /// This is very similar to `fn suggest_static_lifetime_for_gat_from_hrtb` which handles this
+ /// note for failed type tests instead of outlives errors.
fn add_placeholder_from_predicate_note(
&self,
- err: &mut Diag<'_, G>,
+ diag: &mut Diag<'_, G>,
path: &[OutlivesConstraint<'tcx>],
) {
- let predicate_span = path.iter().find_map(|constraint| {
+ let tcx = self.infcx.tcx;
+ let Some((gat_hir_id, generics)) = path.iter().find_map(|constraint| {
let outlived = constraint.sub;
if let Some(origin) = self.regioncx.definitions.get(outlived)
- && let NllRegionVariableOrigin::Placeholder(_) = origin.origin
- && let ConstraintCategory::Predicate(span) = constraint.category
+ && let NllRegionVariableOrigin::Placeholder(placeholder) = origin.origin
+ && let Some(id) = placeholder.bound.kind.get_id()
+ && let Some(placeholder_id) = id.as_local()
+ && let gat_hir_id = tcx.local_def_id_to_hir_id(placeholder_id)
+ && let Some(generics_impl) =
+ tcx.parent_hir_node(tcx.parent_hir_id(gat_hir_id)).generics()
{
- Some(span)
+ Some((gat_hir_id, generics_impl))
} else {
None
}
- });
+ }) else {
+ return;
+ };
- if let Some(span) = predicate_span {
- err.span_note(span, "due to current limitations in the borrow checker, this implies a `'static` lifetime");
+ // Look for the where-bound which introduces the placeholder.
+ // As we're using the HIR, we need to handle both `for<'a> T: Trait<'a>`
+ // and `T: for<'a> Trait`<'a>.
+ for pred in generics.predicates {
+ let WherePredicateKind::BoundPredicate(WhereBoundPredicate {
+ bound_generic_params,
+ bounds,
+ ..
+ }) = pred.kind
+ else {
+ continue;
+ };
+ if bound_generic_params
+ .iter()
+ .rfind(|bgp| tcx.local_def_id_to_hir_id(bgp.def_id) == gat_hir_id)
+ .is_some()
+ {
+ diag.span_note(pred.span, fluent::borrowck_limitations_implies_static);
+ return;
+ }
+ for bound in bounds.iter() {
+ if let GenericBound::Trait(bound) = bound {
+ if bound
+ .bound_generic_params
+ .iter()
+ .rfind(|bgp| tcx.local_def_id_to_hir_id(bgp.def_id) == gat_hir_id)
+ .is_some()
+ {
+ diag.span_note(bound.span, fluent::borrowck_limitations_implies_static);
+ return;
+ }
+ }
+ }
}
}
diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
index c0ca35f9ff83..ea264c8064ad 100644
--- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
@@ -3,6 +3,7 @@
use core::ops::ControlFlow;
+use either::Either;
use hir::{ExprKind, Param};
use rustc_abi::FieldIdx;
use rustc_errors::{Applicability, Diag};
@@ -12,15 +13,16 @@ use rustc_middle::bug;
use rustc_middle::hir::place::PlaceBase;
use rustc_middle::mir::visit::PlaceContext;
use rustc_middle::mir::{
- self, BindingForm, Local, LocalDecl, LocalInfo, LocalKind, Location, Mutability, Place,
- PlaceRef, ProjectionElem,
+ self, BindingForm, Body, BorrowKind, Local, LocalDecl, LocalInfo, LocalKind, Location,
+ Mutability, Operand, Place, PlaceRef, ProjectionElem, RawPtrKind, Rvalue, Statement,
+ StatementKind, TerminatorKind,
};
use rustc_middle::ty::{self, InstanceKind, Ty, TyCtxt, Upcast};
use rustc_span::{BytePos, DesugaringKind, Span, Symbol, kw, sym};
use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
use rustc_trait_selection::infer::InferCtxtExt;
use rustc_trait_selection::traits;
-use tracing::debug;
+use tracing::{debug, trace};
use crate::diagnostics::BorrowedContentSource;
use crate::{MirBorrowckCtxt, session_diagnostics};
@@ -31,6 +33,33 @@ pub(crate) enum AccessKind {
Mutate,
}
+/// Finds all statements that assign directly to local (i.e., X = ...) and returns their
+/// locations.
+fn find_assignments(body: &Body<'_>, local: Local) -> Vec {
+ use rustc_middle::mir::visit::Visitor;
+
+ struct FindLocalAssignmentVisitor {
+ needle: Local,
+ locations: Vec,
+ }
+
+ impl<'tcx> Visitor<'tcx> for FindLocalAssignmentVisitor {
+ fn visit_local(&mut self, local: Local, place_context: PlaceContext, location: Location) {
+ if self.needle != local {
+ return;
+ }
+
+ if place_context.is_place_assignment() {
+ self.locations.push(location);
+ }
+ }
+ }
+
+ let mut visitor = FindLocalAssignmentVisitor { needle: local, locations: vec![] };
+ visitor.visit_body(body);
+ visitor.locations
+}
+
impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
pub(crate) fn report_mutability_error(
&mut self,
@@ -384,7 +413,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
}
}
- // Also suggest adding mut for upvars
+ // Also suggest adding mut for upvars.
PlaceRef {
local,
projection: [proj_base @ .., ProjectionElem::Field(upvar_index, _)],
@@ -438,9 +467,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
}
}
- // complete hack to approximate old AST-borrowck
- // diagnostic: if the span starts with a mutable borrow of
- // a local variable, then just suggest the user remove it.
+ // Complete hack to approximate old AST-borrowck diagnostic: if the span starts
+ // with a mutable borrow of a local variable, then just suggest the user remove it.
PlaceRef { local: _, projection: [] }
if self
.infcx
@@ -769,7 +797,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
);
}
- // point to span of upvar making closure call require mutable borrow
+ // Point to span of upvar making closure call that requires a mutable borrow
fn show_mutating_upvar(
&self,
tcx: TyCtxt<'_>,
@@ -825,7 +853,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
} else {
bug!("not an upvar")
};
- // sometimes we deliberately don't store the name of a place when coming from a macro in
+ // Sometimes we deliberately don't store the name of a place when coming from a macro in
// another crate. We generally want to limit those diagnostics a little, to hide
// implementation details (such as those from pin!() or format!()). In that case show a
// slightly different error message, or none at all if something else happened. In other
@@ -936,8 +964,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
let def_id = tcx.hir_enclosing_body_owner(fn_call_id);
let mut look_at_return = true;
- // If the HIR node is a function or method call gets the def ID
- // of the called function or method and the span and args of the call expr
+ // If the HIR node is a function or method call, get the DefId
+ // of the callee function or method, the span, and args of the call expr
let get_call_details = || {
let hir::Node::Expr(hir::Expr { hir_id, kind, .. }) = node else {
return None;
@@ -1051,7 +1079,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
let mut cur_expr = expr;
while let ExprKind::MethodCall(path_segment, recv, _, _) = cur_expr.kind {
if path_segment.ident.name == sym::iter {
- // check `_ty` has `iter_mut` method
+ // Check that the type has an `iter_mut` method.
let res = self
.infcx
.tcx
@@ -1081,38 +1109,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
}
}
- /// Finds all statements that assign directly to local (i.e., X = ...) and returns their
- /// locations.
- fn find_assignments(&self, local: Local) -> Vec {
- use rustc_middle::mir::visit::Visitor;
-
- struct FindLocalAssignmentVisitor {
- needle: Local,
- locations: Vec,
- }
-
- impl<'tcx> Visitor<'tcx> for FindLocalAssignmentVisitor {
- fn visit_local(
- &mut self,
- local: Local,
- place_context: PlaceContext,
- location: Location,
- ) {
- if self.needle != local {
- return;
- }
-
- if place_context.is_place_assignment() {
- self.locations.push(location);
- }
- }
- }
-
- let mut visitor = FindLocalAssignmentVisitor { needle: local, locations: vec![] };
- visitor.visit_body(self.body);
- visitor.locations
- }
-
fn suggest_make_local_mut(&self, err: &mut Diag<'_>, local: Local, name: Symbol) {
let local_decl = &self.body.local_decls[local];
@@ -1122,7 +1118,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
let (is_trait_sig, is_local, local_trait) = self.is_error_in_trait(local);
if is_trait_sig && !is_local {
- // Do not suggest to change the signature when the trait comes from another crate.
+ // Do not suggest changing the signature when the trait comes from another crate.
err.span_label(
local_decl.source_info.span,
format!("this is an immutable {pointer_desc}"),
@@ -1131,11 +1127,11 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
}
let decl_span = local_decl.source_info.span;
- let amp_mut_sugg = match *local_decl.local_info() {
+ let (amp_mut_sugg, local_var_ty_info) = match *local_decl.local_info() {
LocalInfo::User(mir::BindingForm::ImplicitSelf(_)) => {
let (span, suggestion) = suggest_ampmut_self(self.infcx.tcx, decl_span);
let additional = local_trait.map(|span| suggest_ampmut_self(self.infcx.tcx, span));
- Some(AmpMutSugg { has_sugg: true, span, suggestion, additional })
+ (AmpMutSugg::Type { span, suggestion, additional }, None)
}
LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm {
@@ -1143,79 +1139,54 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
opt_ty_info,
..
})) => {
- // check if the RHS is from desugaring
+ // Check if the RHS is from desugaring.
+ let first_assignment = find_assignments(&self.body, local).first().copied();
+ let first_assignment_stmt = first_assignment
+ .and_then(|loc| self.body[loc.block].statements.get(loc.statement_index));
+ trace!(?first_assignment_stmt);
let opt_assignment_rhs_span =
- self.find_assignments(local).first().map(|&location| {
- if let Some(mir::Statement {
- source_info: _,
- kind:
- mir::StatementKind::Assign(box (
- _,
- mir::Rvalue::Use(mir::Operand::Copy(place)),
- )),
- ..
- }) = self.body[location.block].statements.get(location.statement_index)
- {
- self.body.local_decls[place.local].source_info.span
- } else {
- self.body.source_info(location).span
- }
- });
- match opt_assignment_rhs_span.and_then(|s| s.desugaring_kind()) {
- // on for loops, RHS points to the iterator part
- Some(DesugaringKind::ForLoop) => {
- let span = opt_assignment_rhs_span.unwrap();
- self.suggest_similar_mut_method_for_for_loop(err, span);
+ first_assignment.map(|loc| self.body.source_info(loc).span);
+ let mut source_span = opt_assignment_rhs_span;
+ if let Some(mir::Statement {
+ source_info: _,
+ kind:
+ mir::StatementKind::Assign(box (_, mir::Rvalue::Use(mir::Operand::Copy(place)))),
+ ..
+ }) = first_assignment_stmt
+ {
+ let local_span = self.body.local_decls[place.local].source_info.span;
+ // `&self` in async functions have a `desugaring_kind`, but the local we assign
+ // it with does not, so use the local_span for our checks later.
+ source_span = Some(local_span);
+ if let Some(DesugaringKind::ForLoop) = local_span.desugaring_kind() {
+ // On for loops, RHS points to the iterator part.
+ self.suggest_similar_mut_method_for_for_loop(err, local_span);
err.span_label(
- span,
+ local_span,
format!("this iterator yields `{pointer_sigil}` {pointer_desc}s",),
);
- None
- }
- // don't create labels for compiler-generated spans
- Some(_) => None,
- // don't create labels for the span not from user's code
- None if opt_assignment_rhs_span
- .is_some_and(|span| self.infcx.tcx.sess.source_map().is_imported(span)) =>
- {
- None
- }
- None => {
- if name != kw::SelfLower {
- suggest_ampmut(
- self.infcx.tcx,
- local_decl.ty,
- decl_span,
- opt_assignment_rhs_span,
- opt_ty_info,
- )
- } else {
- match local_decl.local_info() {
- LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm {
- opt_ty_info: None,
- ..
- })) => {
- let (span, sugg) =
- suggest_ampmut_self(self.infcx.tcx, decl_span);
- Some(AmpMutSugg {
- has_sugg: true,
- span,
- suggestion: sugg,
- additional: None,
- })
- }
- // explicit self (eg `self: &'a Self`)
- _ => suggest_ampmut(
- self.infcx.tcx,
- local_decl.ty,
- decl_span,
- opt_assignment_rhs_span,
- opt_ty_info,
- ),
- }
- }
+ return;
}
}
+
+ // Don't create labels for compiler-generated spans or spans not from users' code.
+ if source_span.is_some_and(|s| {
+ s.desugaring_kind().is_some() || self.infcx.tcx.sess.source_map().is_imported(s)
+ }) {
+ return;
+ }
+
+ // This could be because we're in an `async fn`.
+ if name == kw::SelfLower && opt_ty_info.is_none() {
+ let (span, suggestion) = suggest_ampmut_self(self.infcx.tcx, decl_span);
+ (AmpMutSugg::Type { span, suggestion, additional: None }, None)
+ } else if let Some(sugg) =
+ suggest_ampmut(self.infcx, self.body(), first_assignment_stmt)
+ {
+ (sugg, opt_ty_info)
+ } else {
+ return;
+ }
}
LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm {
@@ -1223,181 +1194,238 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
..
})) => {
let pattern_span: Span = local_decl.source_info.span;
- suggest_ref_mut(self.infcx.tcx, pattern_span).map(|span| AmpMutSugg {
- has_sugg: true,
- span,
- suggestion: "mut ".to_owned(),
- additional: None,
- })
+ let Some(span) = suggest_ref_mut(self.infcx.tcx, pattern_span) else {
+ return;
+ };
+ (AmpMutSugg::Type { span, suggestion: "mut ".to_owned(), additional: None }, None)
}
_ => unreachable!(),
};
- match amp_mut_sugg {
- Some(AmpMutSugg {
- has_sugg: true,
- span: err_help_span,
- suggestion: suggested_code,
- additional,
- }) => {
- let mut sugg = vec![(err_help_span, suggested_code)];
- if let Some(s) = additional {
- sugg.push(s);
- }
+ let mut suggest = |suggs: Vec<_>, applicability, extra| {
+ if suggs.iter().any(|(span, _)| self.infcx.tcx.sess.source_map().is_imported(*span)) {
+ return;
+ }
- if sugg.iter().all(|(span, _)| !self.infcx.tcx.sess.source_map().is_imported(*span))
- {
- err.multipart_suggestion_verbose(
- format!(
- "consider changing this to be a mutable {pointer_desc}{}",
- if is_trait_sig {
- " in the `impl` method and the `trait` definition"
- } else {
- ""
- }
- ),
- sugg,
- Applicability::MachineApplicable,
+ err.multipart_suggestion_verbose(
+ format!(
+ "consider changing this to be a mutable {pointer_desc}{}{extra}",
+ if is_trait_sig {
+ " in the `impl` method and the `trait` definition"
+ } else {
+ ""
+ }
+ ),
+ suggs,
+ applicability,
+ );
+ };
+
+ let (mut sugg, add_type_annotation_if_not_exists) = match amp_mut_sugg {
+ AmpMutSugg::Type { span, suggestion, additional } => {
+ let mut sugg = vec![(span, suggestion)];
+ sugg.extend(additional);
+ suggest(sugg, Applicability::MachineApplicable, "");
+ return;
+ }
+ AmpMutSugg::MapGetMut { span, suggestion } => {
+ if self.infcx.tcx.sess.source_map().is_imported(span) {
+ return;
+ }
+ err.multipart_suggestion_verbose(
+ "consider using `get_mut`",
+ vec![(span, suggestion)],
+ Applicability::MaybeIncorrect,
+ );
+ return;
+ }
+ AmpMutSugg::Expr { span, suggestion } => {
+ // `Expr` suggestions should change type annotations if they already exist (probably immut),
+ // but do not add new type annotations.
+ (vec![(span, suggestion)], false)
+ }
+ AmpMutSugg::ChangeBinding => (vec![], true),
+ };
+
+ // Find a binding's type to make mutable.
+ let (binding_exists, span) = match local_var_ty_info {
+ // If this is a variable binding with an explicit type,
+ // then we will suggest changing it to be mutable.
+ // This is `Applicability::MachineApplicable`.
+ Some(ty_span) => (true, ty_span),
+
+ // Otherwise, we'll suggest *adding* an annotated type, we'll suggest
+ // the RHS's type for that.
+ // This is `Applicability::HasPlaceholders`.
+ None => (false, decl_span),
+ };
+
+ if !binding_exists && !add_type_annotation_if_not_exists {
+ suggest(sugg, Applicability::MachineApplicable, "");
+ return;
+ }
+
+ // If the binding already exists and is a reference with an explicit
+ // lifetime, then we can suggest adding ` mut`. This is special-cased from
+ // the path without an explicit lifetime.
+ let (sugg_span, sugg_str, suggest_now) = if let Ok(src) = self.infcx.tcx.sess.source_map().span_to_snippet(span)
+ && src.starts_with("&'")
+ // Note that `&' a T` is invalid so this is correct.
+ && let Some(ws_pos) = src.find(char::is_whitespace)
+ {
+ let span = span.with_lo(span.lo() + BytePos(ws_pos as u32)).shrink_to_lo();
+ (span, " mut".to_owned(), true)
+ // If there is already a binding, we modify it to be `mut`.
+ } else if binding_exists {
+ // Shrink the span to just after the `&` in `&variable`.
+ let span = span.with_lo(span.lo() + BytePos(1)).shrink_to_lo();
+ (span, "mut ".to_owned(), true)
+ } else {
+ // Otherwise, suggest that the user annotates the binding; We provide the
+ // type of the local.
+ let ty = local_decl.ty.builtin_deref(true).unwrap();
+
+ (span, format!("{}mut {}", if local_decl.ty.is_ref() { "&" } else { "*" }, ty), false)
+ };
+
+ if suggest_now {
+ // Suggest changing `&x` to `&mut x` and changing `&T` to `&mut T` at the same time.
+ let has_change = !sugg.is_empty();
+ sugg.push((sugg_span, sugg_str));
+ suggest(
+ sugg,
+ Applicability::MachineApplicable,
+ // FIXME(fee1-dead) this somehow doesn't fire
+ if has_change { " and changing the binding's type" } else { "" },
+ );
+ return;
+ } else if !sugg.is_empty() {
+ suggest(sugg, Applicability::MachineApplicable, "");
+ return;
+ }
+
+ let def_id = self.body.source.def_id();
+ let hir_id = if let Some(local_def_id) = def_id.as_local()
+ && let Some(body) = self.infcx.tcx.hir_maybe_body_owned_by(local_def_id)
+ {
+ BindingFinder { span: sugg_span }.visit_body(&body).break_value()
+ } else {
+ None
+ };
+ let node = hir_id.map(|hir_id| self.infcx.tcx.hir_node(hir_id));
+
+ let Some(hir::Node::LetStmt(local)) = node else {
+ err.span_label(
+ sugg_span,
+ format!("consider changing this binding's type to be: `{sugg_str}`"),
+ );
+ return;
+ };
+
+ let tables = self.infcx.tcx.typeck(def_id.as_local().unwrap());
+ if let Some(clone_trait) = self.infcx.tcx.lang_items().clone_trait()
+ && let Some(expr) = local.init
+ && let ty = tables.node_type_opt(expr.hir_id)
+ && let Some(ty) = ty
+ && let ty::Ref(..) = ty.kind()
+ {
+ match self
+ .infcx
+ .type_implements_trait_shallow(clone_trait, ty.peel_refs(), self.infcx.param_env)
+ .as_deref()
+ {
+ Some([]) => {
+ // FIXME: This error message isn't useful, since we're just
+ // vaguely suggesting to clone a value that already
+ // implements `Clone`.
+ //
+ // A correct suggestion here would take into account the fact
+ // that inference may be affected by missing types on bindings,
+ // etc., to improve "tests/ui/borrowck/issue-91206.stderr", for
+ // example.
+ }
+ None => {
+ if let hir::ExprKind::MethodCall(segment, _rcvr, [], span) = expr.kind
+ && segment.ident.name == sym::clone
+ {
+ err.span_help(
+ span,
+ format!(
+ "`{}` doesn't implement `Clone`, so this call clones \
+ the reference `{ty}`",
+ ty.peel_refs(),
+ ),
+ );
+ }
+ // The type doesn't implement Clone.
+ let trait_ref = ty::Binder::dummy(ty::TraitRef::new(
+ self.infcx.tcx,
+ clone_trait,
+ [ty.peel_refs()],
+ ));
+ let obligation = traits::Obligation::new(
+ self.infcx.tcx,
+ traits::ObligationCause::dummy(),
+ self.infcx.param_env,
+ trait_ref,
+ );
+ self.infcx.err_ctxt().suggest_derive(
+ &obligation,
+ err,
+ trait_ref.upcast(self.infcx.tcx),
);
}
- }
- Some(AmpMutSugg {
- has_sugg: false, span: err_label_span, suggestion: message, ..
- }) => {
- let def_id = self.body.source.def_id();
- let hir_id = if let Some(local_def_id) = def_id.as_local()
- && let Some(body) = self.infcx.tcx.hir_maybe_body_owned_by(local_def_id)
- {
- BindingFinder { span: err_label_span }.visit_body(&body).break_value()
- } else {
- None
- };
-
- if let Some(hir_id) = hir_id
- && let hir::Node::LetStmt(local) = self.infcx.tcx.hir_node(hir_id)
- {
- let tables = self.infcx.tcx.typeck(def_id.as_local().unwrap());
- if let Some(clone_trait) = self.infcx.tcx.lang_items().clone_trait()
- && let Some(expr) = local.init
- && let ty = tables.node_type_opt(expr.hir_id)
- && let Some(ty) = ty
- && let ty::Ref(..) = ty.kind()
+ Some(errors) => {
+ if let hir::ExprKind::MethodCall(segment, _rcvr, [], span) = expr.kind
+ && segment.ident.name == sym::clone
{
- match self
- .infcx
- .type_implements_trait_shallow(
- clone_trait,
- ty.peel_refs(),
- self.infcx.param_env,
- )
- .as_deref()
- {
- Some([]) => {
- // FIXME: This error message isn't useful, since we're just
- // vaguely suggesting to clone a value that already
- // implements `Clone`.
- //
- // A correct suggestion here would take into account the fact
- // that inference may be affected by missing types on bindings,
- // etc., to improve "tests/ui/borrowck/issue-91206.stderr", for
- // example.
- }
- None => {
- if let hir::ExprKind::MethodCall(segment, _rcvr, [], span) =
- expr.kind
- && segment.ident.name == sym::clone
- {
- err.span_help(
- span,
- format!(
- "`{}` doesn't implement `Clone`, so this call clones \
- the reference `{ty}`",
- ty.peel_refs(),
- ),
- );
- }
- // The type doesn't implement Clone.
- let trait_ref = ty::Binder::dummy(ty::TraitRef::new(
- self.infcx.tcx,
- clone_trait,
- [ty.peel_refs()],
- ));
- let obligation = traits::Obligation::new(
- self.infcx.tcx,
- traits::ObligationCause::dummy(),
- self.infcx.param_env,
- trait_ref,
- );
- self.infcx.err_ctxt().suggest_derive(
- &obligation,
- err,
- trait_ref.upcast(self.infcx.tcx),
- );
- }
- Some(errors) => {
- if let hir::ExprKind::MethodCall(segment, _rcvr, [], span) =
- expr.kind
- && segment.ident.name == sym::clone
- {
- err.span_help(
- span,
- format!(
- "`{}` doesn't implement `Clone` because its \
+ err.span_help(
+ span,
+ format!(
+ "`{}` doesn't implement `Clone` because its \
implementations trait bounds could not be met, so \
this call clones the reference `{ty}`",
- ty.peel_refs(),
- ),
- );
- err.note(format!(
- "the following trait bounds weren't met: {}",
- errors
- .iter()
- .map(|e| e.obligation.predicate.to_string())
- .collect::>()
- .join("\n"),
- ));
- }
- // The type doesn't implement Clone because of unmet obligations.
- for error in errors {
- if let traits::FulfillmentErrorCode::Select(
- traits::SelectionError::Unimplemented,
- ) = error.code
- && let ty::PredicateKind::Clause(ty::ClauseKind::Trait(
- pred,
- )) = error.obligation.predicate.kind().skip_binder()
- {
- self.infcx.err_ctxt().suggest_derive(
- &error.obligation,
- err,
- error.obligation.predicate.kind().rebind(pred),
- );
- }
- }
- }
+ ty.peel_refs(),
+ ),
+ );
+ err.note(format!(
+ "the following trait bounds weren't met: {}",
+ errors
+ .iter()
+ .map(|e| e.obligation.predicate.to_string())
+ .collect::>()
+ .join("\n"),
+ ));
+ }
+ // The type doesn't implement Clone because of unmet obligations.
+ for error in errors {
+ if let traits::FulfillmentErrorCode::Select(
+ traits::SelectionError::Unimplemented,
+ ) = error.code
+ && let ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) =
+ error.obligation.predicate.kind().skip_binder()
+ {
+ self.infcx.err_ctxt().suggest_derive(
+ &error.obligation,
+ err,
+ error.obligation.predicate.kind().rebind(pred),
+ );
}
}
- let (changing, span, sugg) = match local.ty {
- Some(ty) => ("changing", ty.span, message),
- None => {
- ("specifying", local.pat.span.shrink_to_hi(), format!(": {message}"))
- }
- };
- err.span_suggestion_verbose(
- span,
- format!("consider {changing} this binding's type"),
- sugg,
- Applicability::HasPlaceholders,
- );
- } else {
- err.span_label(
- err_label_span,
- format!("consider changing this binding's type to be: `{message}`"),
- );
}
}
- None => {}
}
+ let (changing, span, sugg) = match local.ty {
+ Some(ty) => ("changing", ty.span, sugg_str),
+ None => ("specifying", local.pat.span.shrink_to_hi(), format!(": {sugg_str}")),
+ };
+ err.span_suggestion_verbose(
+ span,
+ format!("consider {changing} this binding's type"),
+ sugg,
+ Applicability::HasPlaceholders,
+ );
}
}
@@ -1464,11 +1492,25 @@ fn suggest_ampmut_self(tcx: TyCtxt<'_>, span: Span) -> (Span, String) {
}
}
-struct AmpMutSugg {
- has_sugg: bool,
- span: Span,
- suggestion: String,
- additional: Option<(Span, String)>,
+enum AmpMutSugg {
+ /// Type suggestion. Changes `&self` to `&mut self`, `x: &T` to `x: &mut T`,
+ /// `ref x` to `ref mut x`, etc.
+ Type {
+ span: Span,
+ suggestion: String,
+ additional: Option<(Span, String)>,
+ },
+ /// Suggestion for expressions, `&x` to `&mut x`, `&x[i]` to `&mut x[i]`, etc.
+ Expr {
+ span: Span,
+ suggestion: String,
+ },
+ /// Suggests `.get_mut` in the case of `&map[&key]` for Hash/BTreeMap.
+ MapGetMut {
+ span: Span,
+ suggestion: String,
+ },
+ ChangeBinding,
}
// When we want to suggest a user change a local variable to be a `&mut`, there
@@ -1487,110 +1529,111 @@ struct AmpMutSugg {
// This implementation attempts to emulate AST-borrowck prioritization
// by trying (3.), then (2.) and finally falling back on (1.).
fn suggest_ampmut<'tcx>(
- tcx: TyCtxt<'tcx>,
- decl_ty: Ty<'tcx>,
- decl_span: Span,
- opt_assignment_rhs_span: Option,
- opt_ty_info: Option,
+ infcx: &crate::BorrowckInferCtxt<'tcx>,
+ body: &Body<'tcx>,
+ opt_assignment_rhs_stmt: Option<&Statement<'tcx>>,
) -> Option {
- // if there is a RHS and it starts with a `&` from it, then check if it is
+ let tcx = infcx.tcx;
+ // If there is a RHS and it starts with a `&` from it, then check if it is
// mutable, and if not, put suggest putting `mut ` to make it mutable.
- // we don't have to worry about lifetime annotations here because they are
+ // We don't have to worry about lifetime annotations here because they are
// not valid when taking a reference. For example, the following is not valid Rust:
//
// let x: &i32 = &'a 5;
// ^^ lifetime annotation not allowed
//
- if let Some(rhs_span) = opt_assignment_rhs_span
- && let Ok(rhs_str) = tcx.sess.source_map().span_to_snippet(rhs_span)
- && let Some(rhs_str_no_amp) = rhs_str.strip_prefix('&')
+ if let Some(rhs_stmt) = opt_assignment_rhs_stmt
+ && let StatementKind::Assign(box (lhs, rvalue)) = &rhs_stmt.kind
+ && let mut rhs_span = rhs_stmt.source_info.span
+ && let Ok(mut rhs_str) = tcx.sess.source_map().span_to_snippet(rhs_span)
{
- // Suggest changing `&raw const` to `&raw mut` if applicable.
- if rhs_str_no_amp.trim_start().strip_prefix("raw const").is_some() {
- let const_idx = rhs_str.find("const").unwrap() as u32;
- let const_span = rhs_span
- .with_lo(rhs_span.lo() + BytePos(const_idx))
- .with_hi(rhs_span.lo() + BytePos(const_idx + "const".len() as u32));
+ let mut rvalue = rvalue;
- return Some(AmpMutSugg {
- has_sugg: true,
- span: const_span,
- suggestion: "mut".to_owned(),
- additional: None,
- });
- }
-
- // Figure out if rhs already is `&mut`.
- let is_mut = if let Some(rest) = rhs_str_no_amp.trim_start().strip_prefix("mut") {
- match rest.chars().next() {
- // e.g. `&mut x`
- Some(c) if c.is_whitespace() => true,
- // e.g. `&mut(x)`
- Some('(') => true,
- // e.g. `&mut{x}`
- Some('{') => true,
- // e.g. `&mutablevar`
- _ => false,
+ // Take some special care when handling `let _x = &*_y`:
+ // We want to know if this is part of an overloaded index, so `let x = &a[0]`,
+ // or whether this is a usertype ascription (`let _x: &T = y`).
+ if let Rvalue::Ref(_, BorrowKind::Shared, place) = rvalue
+ && place.projection.len() == 1
+ && place.projection[0] == ProjectionElem::Deref
+ && let Some(assign) = find_assignments(&body, place.local).first()
+ {
+ // If this is a usertype ascription (`let _x: &T = _y`) then pierce through it as either we want
+ // to suggest `&mut` on the expression (handled here) or we return `None` and let the caller
+ // suggest `&mut` on the type if the expression seems fine (e.g. `let _x: &T = &mut _y`).
+ if let Some(user_ty_projs) = body.local_decls[lhs.local].user_ty.as_ref()
+ && let [user_ty_proj] = user_ty_projs.contents.as_slice()
+ && user_ty_proj.projs.is_empty()
+ && let Either::Left(rhs_stmt_new) = body.stmt_at(*assign)
+ && let StatementKind::Assign(box (_, rvalue_new)) = &rhs_stmt_new.kind
+ && let rhs_span_new = rhs_stmt_new.source_info.span
+ && let Ok(rhs_str_new) = tcx.sess.source_map().span_to_snippet(rhs_span)
+ {
+ (rvalue, rhs_span, rhs_str) = (rvalue_new, rhs_span_new, rhs_str_new);
}
- } else {
- false
- };
- // if the reference is already mutable then there is nothing we can do
- // here.
- if !is_mut {
- // shrink the span to just after the `&` in `&variable`
- let span = rhs_span.with_lo(rhs_span.lo() + BytePos(1)).shrink_to_lo();
- // FIXME(Ezrashaw): returning is bad because we still might want to
- // update the annotated type, see #106857.
- return Some(AmpMutSugg {
- has_sugg: true,
- span,
- suggestion: "mut ".to_owned(),
- additional: None,
- });
+ if let Either::Right(call) = body.stmt_at(*assign)
+ && let TerminatorKind::Call {
+ func: Operand::Constant(box const_operand), args, ..
+ } = &call.kind
+ && let ty::FnDef(method_def_id, method_args) = *const_operand.ty().kind()
+ && let Some(trait_) = tcx.trait_of_assoc(method_def_id)
+ && tcx.is_lang_item(trait_, hir::LangItem::Index)
+ {
+ let trait_ref = ty::TraitRef::from_assoc(
+ tcx,
+ tcx.require_lang_item(hir::LangItem::IndexMut, rhs_span),
+ method_args,
+ );
+ // The type only implements `Index` but not `IndexMut`, we must not suggest `&mut`.
+ if !infcx
+ .type_implements_trait(trait_ref.def_id, trait_ref.args, infcx.param_env)
+ .must_apply_considering_regions()
+ {
+ // Suggest `get_mut` if type is a `BTreeMap` or `HashMap`.
+ if let ty::Adt(def, _) = trait_ref.self_ty().kind()
+ && [sym::BTreeMap, sym::HashMap]
+ .into_iter()
+ .any(|s| tcx.is_diagnostic_item(s, def.did()))
+ && let [map, key] = &**args
+ && let Ok(map) = tcx.sess.source_map().span_to_snippet(map.span)
+ && let Ok(key) = tcx.sess.source_map().span_to_snippet(key.span)
+ {
+ let span = rhs_span;
+ let suggestion = format!("{map}.get_mut({key}).unwrap()");
+ return Some(AmpMutSugg::MapGetMut { span, suggestion });
+ }
+ return None;
+ }
+ }
+ }
+
+ let sugg = match rvalue {
+ Rvalue::Ref(_, BorrowKind::Shared, _) if let Some(ref_idx) = rhs_str.find('&') => {
+ // Shrink the span to just after the `&` in `&variable`.
+ Some((
+ rhs_span.with_lo(rhs_span.lo() + BytePos(ref_idx as u32 + 1)).shrink_to_lo(),
+ "mut ".to_owned(),
+ ))
+ }
+ Rvalue::RawPtr(RawPtrKind::Const, _) if let Some(const_idx) = rhs_str.find("const") => {
+ // Suggest changing `&raw const` to `&raw mut` if applicable.
+ let const_idx = const_idx as u32;
+ Some((
+ rhs_span
+ .with_lo(rhs_span.lo() + BytePos(const_idx))
+ .with_hi(rhs_span.lo() + BytePos(const_idx + "const".len() as u32)),
+ "mut".to_owned(),
+ ))
+ }
+ _ => None,
+ };
+
+ if let Some((span, suggestion)) = sugg {
+ return Some(AmpMutSugg::Expr { span, suggestion });
}
}
- let (binding_exists, span) = match opt_ty_info {
- // if this is a variable binding with an explicit type,
- // then we will suggest changing it to be mutable.
- // this is `Applicability::MachineApplicable`.
- Some(ty_span) => (true, ty_span),
-
- // otherwise, we'll suggest *adding* an annotated type, we'll suggest
- // the RHS's type for that.
- // this is `Applicability::HasPlaceholders`.
- None => (false, decl_span),
- };
-
- // if the binding already exists and is a reference with an explicit
- // lifetime, then we can suggest adding ` mut`. this is special-cased from
- // the path without an explicit lifetime.
- if let Ok(src) = tcx.sess.source_map().span_to_snippet(span)
- && src.starts_with("&'")
- // note that `& 'a T` is invalid so this is correct.
- && let Some(ws_pos) = src.find(char::is_whitespace)
- {
- let span = span.with_lo(span.lo() + BytePos(ws_pos as u32)).shrink_to_lo();
- Some(AmpMutSugg { has_sugg: true, span, suggestion: " mut".to_owned(), additional: None })
- // if there is already a binding, we modify it to be `mut`
- } else if binding_exists {
- // shrink the span to just after the `&` in `&variable`
- let span = span.with_lo(span.lo() + BytePos(1)).shrink_to_lo();
- Some(AmpMutSugg { has_sugg: true, span, suggestion: "mut ".to_owned(), additional: None })
- } else {
- // otherwise, suggest that the user annotates the binding; we provide the
- // type of the local.
- let ty = decl_ty.builtin_deref(true).unwrap();
-
- Some(AmpMutSugg {
- has_sugg: false,
- span,
- suggestion: format!("{}mut {}", if decl_ty.is_ref() { "&" } else { "*" }, ty),
- additional: None,
- })
- }
+ Some(AmpMutSugg::ChangeBinding)
}
/// If the type is a `Coroutine`, `Closure`, or `CoroutineClosure`
diff --git a/compiler/rustc_borrowck/src/diagnostics/opaque_types.rs b/compiler/rustc_borrowck/src/diagnostics/opaque_types.rs
index 83fe4a9f98f4..f77f759035be 100644
--- a/compiler/rustc_borrowck/src/diagnostics/opaque_types.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/opaque_types.rs
@@ -13,6 +13,8 @@ use rustc_middle::mir::{self, ConstraintCategory, Location};
use rustc_middle::ty::{
self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor,
};
+use rustc_span::Span;
+use rustc_trait_selection::error_reporting::infer::region::unexpected_hidden_region_diagnostic;
use rustc_trait_selection::errors::impl_trait_overcapture_suggestion;
use crate::MirBorrowckCtxt;
@@ -26,13 +28,61 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
if errors.is_empty() {
return;
}
+
+ let infcx = self.infcx;
let mut guar = None;
+ let mut last_unexpected_hidden_region: Option<(Span, Ty<'_>, ty::OpaqueTypeKey<'tcx>)> =
+ None;
for error in errors {
guar = Some(match error {
- DeferredOpaqueTypeError::InvalidOpaqueTypeArgs(err) => err.report(self.infcx),
+ DeferredOpaqueTypeError::InvalidOpaqueTypeArgs(err) => err.report(infcx),
DeferredOpaqueTypeError::LifetimeMismatchOpaqueParam(err) => {
- self.infcx.dcx().emit_err(err)
+ infcx.dcx().emit_err(err)
}
+ DeferredOpaqueTypeError::UnexpectedHiddenRegion {
+ opaque_type_key,
+ hidden_type,
+ member_region,
+ } => {
+ let named_ty =
+ self.regioncx.name_regions_for_member_constraint(infcx.tcx, hidden_type.ty);
+ let named_key = self
+ .regioncx
+ .name_regions_for_member_constraint(infcx.tcx, opaque_type_key);
+ let named_region =
+ self.regioncx.name_regions_for_member_constraint(infcx.tcx, member_region);
+ let diag = unexpected_hidden_region_diagnostic(
+ infcx,
+ self.mir_def_id(),
+ hidden_type.span,
+ named_ty,
+ named_region,
+ named_key,
+ );
+ if last_unexpected_hidden_region
+ != Some((hidden_type.span, named_ty, named_key))
+ {
+ last_unexpected_hidden_region =
+ Some((hidden_type.span, named_ty, named_key));
+ diag.emit()
+ } else {
+ diag.delay_as_bug()
+ }
+ }
+ DeferredOpaqueTypeError::NonDefiningUseInDefiningScope {
+ span,
+ opaque_type_key,
+ } => infcx.dcx().span_err(
+ span,
+ format!(
+ "non-defining use of `{}` in the defining scope",
+ Ty::new_opaque(
+ infcx.tcx,
+ opaque_type_key.def_id.to_def_id(),
+ opaque_type_key.args
+ )
+ ),
+ ),
});
}
let guar = guar.unwrap();
@@ -194,7 +244,7 @@ impl<'tcx> TypeVisitor> for FindOpaqueRegion<'_, 'tcx> {
// Find a path between the borrow region and our opaque capture.
if let Some((path, _)) =
- self.regioncx.find_constraint_paths_between_regions(self.borrow_region, |r| {
+ self.regioncx.find_constraint_path_between_regions(self.borrow_region, |r| {
r == opaque_region_vid
})
{
diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
index 2b74f1a48f73..c7d2267e5f70 100644
--- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
@@ -23,7 +23,6 @@ use rustc_trait_selection::error_reporting::infer::nice_region_error::{
self, HirTraitObjectVisitor, NiceRegionError, TraitObjectVisitor, find_anon_type,
find_param_with_region, suggest_adding_lifetime_params,
};
-use rustc_trait_selection::error_reporting::infer::region::unexpected_hidden_region_diagnostic;
use rustc_trait_selection::infer::InferCtxtExt;
use rustc_trait_selection::traits::{Obligation, ObligationCtxt};
use tracing::{debug, instrument, trace};
@@ -105,18 +104,6 @@ pub(crate) enum RegionErrorKind<'tcx> {
/// A generic bound failure for a type test (`T: 'a`).
TypeTestError { type_test: TypeTest<'tcx> },
- /// An unexpected hidden region for an opaque type.
- UnexpectedHiddenRegion {
- /// The span for the member constraint.
- span: Span,
- /// The hidden type.
- hidden_ty: Ty<'tcx>,
- /// The opaque type.
- key: ty::OpaqueTypeKey<'tcx>,
- /// The unexpected region.
- member_region: ty::Region<'tcx>,
- },
-
/// Higher-ranked subtyping error.
BoundUniversalRegionError {
/// The placeholder free region.
@@ -215,7 +202,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
diag: &mut Diag<'_>,
lower_bound: RegionVid,
) {
- let mut suggestions = vec![];
let tcx = self.infcx.tcx;
// find generic associated types in the given region 'lower_bound'
@@ -237,9 +223,11 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
.collect::>();
debug!(?gat_id_and_generics);
- // find higher-ranked trait bounds bounded to the generic associated types
+ // Look for the where-bound which introduces the placeholder.
+ // As we're using the HIR, we need to handle both `for<'a> T: Trait<'a>`
+ // and `T: for<'a> Trait`<'a>.
let mut hrtb_bounds = vec![];
- gat_id_and_generics.iter().flatten().for_each(|(gat_hir_id, generics)| {
+ gat_id_and_generics.iter().flatten().for_each(|&(gat_hir_id, generics)| {
for pred in generics.predicates {
let BoundPredicate(WhereBoundPredicate { bound_generic_params, bounds, .. }) =
pred.kind
@@ -248,17 +236,32 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
};
if bound_generic_params
.iter()
- .rfind(|bgp| tcx.local_def_id_to_hir_id(bgp.def_id) == *gat_hir_id)
+ .rfind(|bgp| tcx.local_def_id_to_hir_id(bgp.def_id) == gat_hir_id)
.is_some()
{
for bound in *bounds {
hrtb_bounds.push(bound);
}
+ } else {
+ for bound in *bounds {
+ if let Trait(trait_bound) = bound {
+ if trait_bound
+ .bound_generic_params
+ .iter()
+ .rfind(|bgp| tcx.local_def_id_to_hir_id(bgp.def_id) == gat_hir_id)
+ .is_some()
+ {
+ hrtb_bounds.push(bound);
+ return;
+ }
+ }
+ }
}
}
});
debug!(?hrtb_bounds);
+ let mut suggestions = vec![];
hrtb_bounds.iter().for_each(|bound| {
let Trait(PolyTraitRef { trait_ref, span: trait_span, .. }) = bound else {
return;
@@ -307,11 +310,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
pub(crate) fn report_region_errors(&mut self, nll_errors: RegionErrors<'tcx>) {
// Iterate through all the errors, producing a diagnostic for each one. The diagnostics are
// buffered in the `MirBorrowckCtxt`.
-
let mut outlives_suggestion = OutlivesSuggestionBuilder::default();
- let mut last_unexpected_hidden_region: Option<(Span, Ty<'_>, ty::OpaqueTypeKey<'tcx>)> =
- None;
-
for (nll_error, _) in nll_errors.into_iter() {
match nll_error {
RegionErrorKind::TypeTestError { type_test } => {
@@ -362,30 +361,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
}
}
- RegionErrorKind::UnexpectedHiddenRegion { span, hidden_ty, key, member_region } => {
- let named_ty =
- self.regioncx.name_regions_for_member_constraint(self.infcx.tcx, hidden_ty);
- let named_key =
- self.regioncx.name_regions_for_member_constraint(self.infcx.tcx, key);
- let named_region = self
- .regioncx
- .name_regions_for_member_constraint(self.infcx.tcx, member_region);
- let diag = unexpected_hidden_region_diagnostic(
- self.infcx,
- self.mir_def_id(),
- span,
- named_ty,
- named_region,
- named_key,
- );
- if last_unexpected_hidden_region != Some((span, named_ty, named_key)) {
- self.buffer_error(diag);
- last_unexpected_hidden_region = Some((span, named_ty, named_key));
- } else {
- diag.delay_as_bug();
- }
- }
-
RegionErrorKind::BoundUniversalRegionError {
longer_fr,
placeholder,
diff --git a/compiler/rustc_borrowck/src/handle_placeholders.rs b/compiler/rustc_borrowck/src/handle_placeholders.rs
index 1614c112ab51..4661906fbeb5 100644
--- a/compiler/rustc_borrowck/src/handle_placeholders.rs
+++ b/compiler/rustc_borrowck/src/handle_placeholders.rs
@@ -15,7 +15,6 @@ use tracing::debug;
use crate::constraints::{ConstraintSccIndex, OutlivesConstraintSet};
use crate::consumers::OutlivesConstraint;
use crate::diagnostics::UniverseInfo;
-use crate::member_constraints::MemberConstraintSet;
use crate::region_infer::values::{LivenessValues, PlaceholderIndices};
use crate::region_infer::{ConstraintSccs, RegionDefinition, Representative, TypeTest};
use crate::ty::VarianceDiagInfo;
@@ -30,7 +29,6 @@ pub(crate) struct LoweredConstraints<'tcx> {
pub(crate) constraint_sccs: Sccs,
pub(crate) definitions: Frozen>>,
pub(crate) scc_annotations: IndexVec,
- pub(crate) member_constraints: MemberConstraintSet<'tcx, RegionVid>,
pub(crate) outlives_constraints: Frozen>,
pub(crate) type_tests: Vec>,
pub(crate) liveness_constraints: LivenessValues,
@@ -147,9 +145,10 @@ impl scc::Annotation for RegionTracker {
/// Determines if the region variable definitions contain
/// placeholders, and compute them for later use.
-fn region_definitions<'tcx>(
- universal_regions: &UniversalRegions<'tcx>,
+// FIXME: This is also used by opaque type handling. Move it to a separate file.
+pub(super) fn region_definitions<'tcx>(
infcx: &BorrowckInferCtxt<'tcx>,
+ universal_regions: &UniversalRegions<'tcx>,
) -> (Frozen>>, bool) {
let var_infos = infcx.get_region_var_infos();
// Create a RegionDefinition for each inference variable. This happens here because
@@ -213,14 +212,13 @@ pub(crate) fn compute_sccs_applying_placeholder_outlives_constraints<'tcx>(
infcx: &BorrowckInferCtxt<'tcx>,
) -> LoweredConstraints<'tcx> {
let universal_regions = &universal_region_relations.universal_regions;
- let (definitions, has_placeholders) = region_definitions(universal_regions, infcx);
+ let (definitions, has_placeholders) = region_definitions(infcx, universal_regions);
let MirTypeckRegionConstraints {
placeholder_indices,
placeholder_index_to_region: _,
liveness_constraints,
mut outlives_constraints,
- member_constraints,
universe_causes,
type_tests,
} = constraints;
@@ -246,7 +244,6 @@ pub(crate) fn compute_sccs_applying_placeholder_outlives_constraints<'tcx>(
return LoweredConstraints {
type_tests,
- member_constraints,
constraint_sccs,
scc_annotations: scc_annotations.scc_to_annotation,
definitions,
@@ -283,7 +280,6 @@ pub(crate) fn compute_sccs_applying_placeholder_outlives_constraints<'tcx>(
constraint_sccs,
definitions,
scc_annotations,
- member_constraints,
outlives_constraints: Frozen::freeze(outlives_constraints),
type_tests,
liveness_constraints,
diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs
index d76d6a04e6e0..ce78ae203a4d 100644
--- a/compiler/rustc_borrowck/src/lib.rs
+++ b/compiler/rustc_borrowck/src/lib.rs
@@ -78,7 +78,6 @@ mod dataflow;
mod def_use;
mod diagnostics;
mod handle_placeholders;
-mod member_constraints;
mod nll;
mod path_utils;
mod place_ext;
@@ -301,7 +300,7 @@ fn do_mir_borrowck<'tcx>(
def: LocalDefId,
) -> PropagatedBorrowCheckResults<'tcx> {
let tcx = root_cx.tcx;
- let infcx = BorrowckInferCtxt::new(tcx, def);
+ let infcx = BorrowckInferCtxt::new(tcx, def, root_cx.root_def_id());
let (input_body, promoted) = tcx.mir_promoted(def);
let input_body: &Body<'_> = &input_body.borrow();
let input_promoted: &IndexSlice<_, _> = &promoted.borrow();
@@ -335,9 +334,10 @@ fn do_mir_borrowck<'tcx>(
// Run the MIR type-checker.
let MirTypeckResults {
- constraints,
+ mut constraints,
universal_region_relations,
- opaque_type_values,
+ region_bound_pairs,
+ known_type_outlives_obligations,
polonius_context,
} = type_check::type_check(
root_cx,
@@ -352,6 +352,17 @@ fn do_mir_borrowck<'tcx>(
Rc::clone(&location_map),
);
+ let opaque_type_errors = region_infer::opaque_types::handle_opaque_type_uses(
+ root_cx,
+ &infcx,
+ &body,
+ &universal_region_relations,
+ ®ion_bound_pairs,
+ &known_type_outlives_obligations,
+ &location_map,
+ &mut constraints,
+ );
+
// Compute non-lexical lifetimes using the constraints computed
// by typechecking the MIR body.
let nll::NllOutput {
@@ -375,8 +386,6 @@ fn do_mir_borrowck<'tcx>(
polonius_context,
);
- let opaque_type_errors = regioncx.infer_opaque_types(root_cx, &infcx, opaque_type_values);
-
// Dump MIR results into a file, if that is enabled. This lets us
// write unit-tests, as well as helping with debugging.
nll::dump_nll_mir(&infcx, body, ®ioncx, &opt_closure_req, &borrow_set);
@@ -581,12 +590,13 @@ fn get_flow_results<'a, 'tcx>(
pub(crate) struct BorrowckInferCtxt<'tcx> {
pub(crate) infcx: InferCtxt<'tcx>,
- pub(crate) reg_var_to_origin: RefCell>,
+ pub(crate) root_def_id: LocalDefId,
pub(crate) param_env: ParamEnv<'tcx>,
+ pub(crate) reg_var_to_origin: RefCell>,
}
impl<'tcx> BorrowckInferCtxt<'tcx> {
- pub(crate) fn new(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Self {
+ pub(crate) fn new(tcx: TyCtxt<'tcx>, def_id: LocalDefId, root_def_id: LocalDefId) -> Self {
let typing_mode = if tcx.use_typing_mode_borrowck() {
TypingMode::borrowck(tcx, def_id)
} else {
@@ -594,7 +604,12 @@ impl<'tcx> BorrowckInferCtxt<'tcx> {
};
let infcx = tcx.infer_ctxt().build(typing_mode);
let param_env = tcx.param_env(def_id);
- BorrowckInferCtxt { infcx, reg_var_to_origin: RefCell::new(Default::default()), param_env }
+ BorrowckInferCtxt {
+ infcx,
+ root_def_id,
+ reg_var_to_origin: RefCell::new(Default::default()),
+ param_env,
+ }
}
pub(crate) fn next_region_var(
diff --git a/compiler/rustc_borrowck/src/member_constraints.rs b/compiler/rustc_borrowck/src/member_constraints.rs
deleted file mode 100644
index bdd0f6fe11e0..000000000000
--- a/compiler/rustc_borrowck/src/member_constraints.rs
+++ /dev/null
@@ -1,226 +0,0 @@
-use std::hash::Hash;
-use std::ops::Index;
-
-use rustc_data_structures::fx::FxIndexMap;
-use rustc_index::{IndexSlice, IndexVec};
-use rustc_middle::ty::{self, Ty};
-use rustc_span::Span;
-use tracing::instrument;
-
-/// Compactly stores a set of `R0 member of [R1...Rn]` constraints,
-/// indexed by the region `R0`.
-#[derive(Debug)]
-pub(crate) struct MemberConstraintSet<'tcx, R>
-where
- R: Copy + Eq,
-{
- /// Stores the first "member" constraint for a given `R0`. This is an
- /// index into the `constraints` vector below.
- first_constraints: FxIndexMap,
-
- /// Stores the data about each `R0 member of [R1..Rn]` constraint.
- /// These are organized into a linked list, so each constraint
- /// contains the index of the next constraint with the same `R0`.
- constraints: IndexVec>,
-
- /// Stores the `R1..Rn` regions for *all* sets. For any given
- /// constraint, we keep two indices so that we can pull out a
- /// slice.
- choice_regions: Vec,
-}
-
-/// Represents a `R0 member of [R1..Rn]` constraint
-#[derive(Debug)]
-pub(crate) struct MemberConstraint<'tcx> {
- next_constraint: Option,
-
- /// The span where the hidden type was instantiated.
- pub(crate) definition_span: Span,
-
- /// The hidden type in which `R0` appears. (Used in error reporting.)
- pub(crate) hidden_ty: Ty<'tcx>,
-
- pub(crate) key: ty::OpaqueTypeKey<'tcx>,
-
- /// The region `R0`.
- pub(crate) member_region_vid: ty::RegionVid,
-
- /// Index of `R1` in `choice_regions` vector from `MemberConstraintSet`.
- start_index: usize,
-
- /// Index of `Rn` in `choice_regions` vector from `MemberConstraintSet`.
- end_index: usize,
-}
-
-rustc_index::newtype_index! {
- #[debug_format = "MemberConstraintIndex({})"]
- pub(crate) struct NllMemberConstraintIndex {}
-}
-
-impl Default for MemberConstraintSet<'_, ty::RegionVid> {
- fn default() -> Self {
- Self {
- first_constraints: Default::default(),
- constraints: Default::default(),
- choice_regions: Default::default(),
- }
- }
-}
-
-impl<'tcx> MemberConstraintSet<'tcx, ty::RegionVid> {
- pub(crate) fn is_empty(&self) -> bool {
- self.constraints.is_empty()
- }
-
- /// Pushes a member constraint into the set.
- #[instrument(level = "debug", skip(self))]
- pub(crate) fn add_member_constraint(
- &mut self,
- key: ty::OpaqueTypeKey<'tcx>,
- hidden_ty: Ty<'tcx>,
- definition_span: Span,
- member_region_vid: ty::RegionVid,
- choice_regions: &[ty::RegionVid],
- ) {
- let next_constraint = self.first_constraints.get(&member_region_vid).cloned();
- let start_index = self.choice_regions.len();
- self.choice_regions.extend(choice_regions);
- let end_index = self.choice_regions.len();
- let constraint_index = self.constraints.push(MemberConstraint {
- next_constraint,
- member_region_vid,
- definition_span,
- hidden_ty,
- key,
- start_index,
- end_index,
- });
- self.first_constraints.insert(member_region_vid, constraint_index);
- }
-}
-
-impl<'tcx, R1> MemberConstraintSet<'tcx, R1>
-where
- R1: Copy + Hash + Eq,
-{
- /// Remap the "member region" key using `map_fn`, producing a new
- /// member constraint set. This is used in the NLL code to map from
- /// the original `RegionVid` to an scc index. In some cases, we
- /// may have multiple `R1` values mapping to the same `R2` key -- that
- /// is ok, the two sets will be merged.
- pub(crate) fn into_mapped(
- self,
- mut map_fn: impl FnMut(R1) -> R2,
- ) -> MemberConstraintSet<'tcx, R2>
- where
- R2: Copy + Hash + Eq,
- {
- // We can re-use most of the original data, just tweaking the
- // linked list links a bit.
- //
- // For example if we had two keys `Ra` and `Rb` that both now
- // wind up mapped to the same key `S`, we would append the
- // linked list for `Ra` onto the end of the linked list for
- // `Rb` (or vice versa) -- this basically just requires
- // rewriting the final link from one list to point at the other
- // other (see `append_list`).
-
- let MemberConstraintSet { first_constraints, mut constraints, choice_regions } = self;
-
- let mut first_constraints2 = FxIndexMap::default();
- first_constraints2.reserve(first_constraints.len());
-
- for (r1, start1) in first_constraints {
- let r2 = map_fn(r1);
- if let Some(&start2) = first_constraints2.get(&r2) {
- append_list(&mut constraints, start1, start2);
- }
- first_constraints2.insert(r2, start1);
- }
-
- MemberConstraintSet { first_constraints: first_constraints2, constraints, choice_regions }
- }
-}
-
-impl<'tcx, R> MemberConstraintSet<'tcx, R>
-where
- R: Copy + Hash + Eq,
-{
- pub(crate) fn all_indices(&self) -> impl Iterator
- {
- self.constraints.indices()
- }
-
- /// Iterate down the constraint indices associated with a given
- /// peek-region. You can then use `choice_regions` and other
- /// methods to access data.
- pub(crate) fn indices(
- &self,
- member_region_vid: R,
- ) -> impl Iterator
- {
- let mut next = self.first_constraints.get(&member_region_vid).cloned();
- std::iter::from_fn(move || -> Option {
- if let Some(current) = next {
- next = self.constraints[current].next_constraint;
- Some(current)
- } else {
- None
- }
- })
- }
-
- /// Returns the "choice regions" for a given member
- /// constraint. This is the `R1..Rn` from a constraint like:
- ///
- /// ```text
- /// R0 member of [R1..Rn]
- /// ```
- pub(crate) fn choice_regions(&self, pci: NllMemberConstraintIndex) -> &[ty::RegionVid] {
- let MemberConstraint { start_index, end_index, .. } = &self.constraints[pci];
- &self.choice_regions[*start_index..*end_index]
- }
-}
-
-impl<'tcx, R> Index for MemberConstraintSet<'tcx, R>
-where
- R: Copy + Eq,
-{
- type Output = MemberConstraint<'tcx>;
-
- fn index(&self, i: NllMemberConstraintIndex) -> &MemberConstraint<'tcx> {
- &self.constraints[i]
- }
-}
-
-/// Given a linked list starting at `source_list` and another linked
-/// list starting at `target_list`, modify `target_list` so that it is
-/// followed by `source_list`.
-///
-/// Before:
-///
-/// ```text
-/// target_list: A -> B -> C -> (None)
-/// source_list: D -> E -> F -> (None)
-/// ```
-///
-/// After:
-///
-/// ```text
-/// target_list: A -> B -> C -> D -> E -> F -> (None)
-/// ```
-fn append_list(
- constraints: &mut IndexSlice>,
- target_list: NllMemberConstraintIndex,
- source_list: NllMemberConstraintIndex,
-) {
- let mut p = target_list;
- loop {
- let r = &mut constraints[p];
- match r.next_constraint {
- Some(q) => p = q,
- None => {
- r.next_constraint = Some(source_list);
- return;
- }
- }
- }
-}
diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs
index c76c5c17431a..3d95eb4663ac 100644
--- a/compiler/rustc_borrowck/src/region_infer/mod.rs
+++ b/compiler/rustc_borrowck/src/region_infer/mod.rs
@@ -1,8 +1,6 @@
-use std::cell::OnceCell;
use std::collections::VecDeque;
use std::rc::Rc;
-use rustc_data_structures::binary_search_util;
use rustc_data_structures::frozen::Frozen;
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
use rustc_data_structures::graph::scc::{self, Sccs};
@@ -24,15 +22,13 @@ use rustc_span::hygiene::DesugaringKind;
use rustc_span::{DUMMY_SP, Span};
use tracing::{Level, debug, enabled, instrument, trace};
-use crate::constraints::graph::{self, NormalConstraintGraph, RegionGraph};
+use crate::constraints::graph::NormalConstraintGraph;
use crate::constraints::{ConstraintSccIndex, OutlivesConstraint, OutlivesConstraintSet};
use crate::dataflow::BorrowIndex;
use crate::diagnostics::{RegionErrorKind, RegionErrors, UniverseInfo};
use crate::handle_placeholders::{LoweredConstraints, RegionTracker};
-use crate::member_constraints::{MemberConstraintSet, NllMemberConstraintIndex};
use crate::polonius::LiveLoans;
use crate::polonius::legacy::PoloniusOutput;
-use crate::region_infer::reverse_sccs::ReverseSccGraph;
use crate::region_infer::values::{LivenessValues, RegionElement, RegionValues, ToElementIndex};
use crate::type_check::Locations;
use crate::type_check::free_region_relations::UniversalRegionRelations;
@@ -120,20 +116,6 @@ pub struct RegionInferenceContext<'tcx> {
scc_annotations: IndexVec,
- /// Reverse of the SCC constraint graph -- i.e., an edge `A -> B` exists if
- /// `B: A`. This is used to compute the universal regions that are required
- /// to outlive a given SCC.
- rev_scc_graph: OnceCell,
-
- /// The "R0 member of [R1..Rn]" constraints, indexed by SCC.
- member_constraints: Rc>,
-
- /// Records the member constraints that we applied to each scc.
- /// This is useful for error reporting. Once constraint
- /// propagation is done, this vector is sorted according to
- /// `member_region_scc`.
- member_constraints_applied: Vec,
-
/// Map universe indexes to information on why we created it.
universe_causes: FxIndexMap>,
@@ -150,32 +132,6 @@ pub struct RegionInferenceContext<'tcx> {
universal_region_relations: Frozen>,
}
-/// Each time that `apply_member_constraint` is successful, it appends
-/// one of these structs to the `member_constraints_applied` field.
-/// This is used in error reporting to trace out what happened.
-///
-/// The way that `apply_member_constraint` works is that it effectively
-/// adds a new lower bound to the SCC it is analyzing: so you wind up
-/// with `'R: 'O` where `'R` is the pick-region and `'O` is the
-/// minimal viable option.
-#[derive(Debug)]
-pub(crate) struct AppliedMemberConstraint {
- /// The SCC that was affected. (The "member region".)
- ///
- /// The vector if `AppliedMemberConstraint` elements is kept sorted
- /// by this field.
- pub(crate) member_region_scc: ConstraintSccIndex,
-
- /// The "best option" that `apply_member_constraint` found -- this was
- /// added as an "ad-hoc" lower-bound to `member_region_scc`.
- pub(crate) min_choice: ty::RegionVid,
-
- /// The "member constraint index" -- we can find out details about
- /// the constraint from
- /// `set.member_constraints[member_constraint_index]`.
- pub(crate) member_constraint_index: NllMemberConstraintIndex,
-}
-
#[derive(Debug)]
pub(crate) struct RegionDefinition<'tcx> {
/// What kind of variable is this -- a free region? existential
@@ -268,7 +224,6 @@ enum Trace<'a, 'tcx> {
StartRegion,
FromGraph(&'a OutlivesConstraint<'tcx>),
FromStatic(RegionVid),
- FromMember(RegionVid, RegionVid, Span),
NotVisited,
}
@@ -363,7 +318,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
liveness_constraints,
universe_causes,
placeholder_indices,
- member_constraints,
} = lowered_constraints;
debug!("universal_regions: {:#?}", universal_region_relations.universal_regions);
@@ -385,9 +339,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
scc_values.merge_liveness(scc, region, &liveness_constraints);
}
- let member_constraints =
- Rc::new(member_constraints.into_mapped(|r| constraint_sccs.scc(r)));
-
let mut result = Self {
definitions,
liveness_constraints,
@@ -395,9 +346,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
constraint_graph,
constraint_sccs,
scc_annotations,
- rev_scc_graph: OnceCell::new(),
- member_constraints,
- member_constraints_applied: Vec::new(),
universe_causes,
scc_values,
type_tests,
@@ -550,19 +498,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
self.scc_values.placeholders_contained_in(scc)
}
- /// Once region solving has completed, this function will return the member constraints that
- /// were applied to the value of a given SCC `scc`. See `AppliedMemberConstraint`.
- pub(crate) fn applied_member_constraints(
- &self,
- scc: ConstraintSccIndex,
- ) -> &[AppliedMemberConstraint] {
- binary_search_util::binary_search_slice(
- &self.member_constraints_applied,
- |applied| applied.member_region_scc,
- &scc,
- )
- }
-
/// Performs region inference and report errors if we see any
/// unsatisfiable constraints. If this is a closure, returns the
/// region requirements to propagate to our creator, if any.
@@ -607,12 +542,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
debug!(?errors_buffer);
- if errors_buffer.is_empty() {
- self.check_member_constraints(infcx, &mut errors_buffer);
- }
-
- debug!(?errors_buffer);
-
let outlives_requirements = outlives_requirements.unwrap_or_default();
if outlives_requirements.is_empty() {
@@ -642,146 +571,15 @@ impl<'tcx> RegionInferenceContext<'tcx> {
});
// To propagate constraints, we walk the DAG induced by the
- // SCC. For each SCC, we visit its successors and compute
+ // SCC. For each SCC `A`, we visit its successors and compute
// their values, then we union all those values to get our
// own.
- for scc in self.constraint_sccs.all_sccs() {
- self.compute_value_for_scc(scc);
- }
-
- // Sort the applied member constraints so we can binary search
- // through them later.
- self.member_constraints_applied.sort_by_key(|applied| applied.member_region_scc);
- }
-
- /// Computes the value of the SCC `scc_a`, which has not yet been
- /// computed, by unioning the values of its successors.
- /// Assumes that all successors have been computed already
- /// (which is assured by iterating over SCCs in dependency order).
- #[instrument(skip(self), level = "debug")]
- fn compute_value_for_scc(&mut self, scc_a: ConstraintSccIndex) {
- // Walk each SCC `B` such that `A: B`...
- for &scc_b in self.constraint_sccs.successors(scc_a) {
- debug!(?scc_b);
- self.scc_values.add_region(scc_a, scc_b);
- }
-
- // Now take member constraints into account.
- let member_constraints = Rc::clone(&self.member_constraints);
- for m_c_i in member_constraints.indices(scc_a) {
- self.apply_member_constraint(scc_a, m_c_i, member_constraints.choice_regions(m_c_i));
- }
-
- debug!(value = ?self.scc_values.region_value_str(scc_a));
- }
-
- /// Invoked for each `R0 member of [R1..Rn]` constraint.
- ///
- /// `scc` is the SCC containing R0, and `choice_regions` are the
- /// `R1..Rn` regions -- they are always known to be universal
- /// regions (and if that's not true, we just don't attempt to
- /// enforce the constraint).
- ///
- /// The current value of `scc` at the time the method is invoked
- /// is considered a *lower bound*. If possible, we will modify
- /// the constraint to set it equal to one of the option regions.
- /// If we make any changes, returns true, else false.
- ///
- /// This function only adds the member constraints to the region graph,
- /// it does not check them. They are later checked in
- /// `check_member_constraints` after the region graph has been computed.
- #[instrument(skip(self, member_constraint_index), level = "debug")]
- fn apply_member_constraint(
- &mut self,
- scc: ConstraintSccIndex,
- member_constraint_index: NllMemberConstraintIndex,
- choice_regions: &[ty::RegionVid],
- ) {
- // Create a mutable vector of the options. We'll try to winnow
- // them down.
- let mut choice_regions: Vec = choice_regions.to_vec();
-
- // Convert to the SCC representative: sometimes we have inference
- // variables in the member constraint that wind up equated with
- // universal regions. The scc representative is the minimal numbered
- // one from the corresponding scc so it will be the universal region
- // if one exists.
- for c_r in &mut choice_regions {
- let scc = self.constraint_sccs.scc(*c_r);
- *c_r = self.scc_representative(scc);
- }
-
- // If the member region lives in a higher universe, we currently choose
- // the most conservative option by leaving it unchanged.
- if !self.max_placeholder_universe_reached(scc).is_root() {
- return;
- }
-
- // The existing value for `scc` is a lower-bound. This will
- // consist of some set `{P} + {LB}` of points `{P}` and
- // lower-bound free regions `{LB}`. As each choice region `O`
- // is a free region, it will outlive the points. But we can
- // only consider the option `O` if `O: LB`.
- choice_regions.retain(|&o_r| {
- self.scc_values
- .universal_regions_outlived_by(scc)
- .all(|lb| self.universal_region_relations.outlives(o_r, lb))
- });
- debug!(?choice_regions, "after lb");
-
- // Now find all the *upper bounds* -- that is, each UB is a
- // free region that must outlive the member region `R0` (`UB:
- // R0`). Therefore, we need only keep an option `O` if `UB: O`
- // for all UB.
- let universal_region_relations = &self.universal_region_relations;
- for ub in self.reverse_scc_graph().upper_bounds(scc) {
- debug!(?ub);
- choice_regions.retain(|&o_r| universal_region_relations.outlives(ub, o_r));
- }
- debug!(?choice_regions, "after ub");
-
- // At this point we can pick any member of `choice_regions` and would like to choose
- // it to be a small as possible. To avoid potential non-determinism we will pick the
- // smallest such choice.
- //
- // Because universal regions are only partially ordered (i.e, not every two regions are
- // comparable), we will ignore any region that doesn't compare to all others when picking
- // the minimum choice.
- //
- // For example, consider `choice_regions = ['static, 'a, 'b, 'c, 'd, 'e]`, where
- // `'static: 'a, 'static: 'b, 'a: 'c, 'b: 'c, 'c: 'd, 'c: 'e`.
- // `['d, 'e]` are ignored because they do not compare - the same goes for `['a, 'b]`.
- let totally_ordered_subset = choice_regions.iter().copied().filter(|&r1| {
- choice_regions.iter().all(|&r2| {
- self.universal_region_relations.outlives(r1, r2)
- || self.universal_region_relations.outlives(r2, r1)
- })
- });
- // Now we're left with `['static, 'c]`. Pick `'c` as the minimum!
- let Some(min_choice) = totally_ordered_subset.reduce(|r1, r2| {
- let r1_outlives_r2 = self.universal_region_relations.outlives(r1, r2);
- let r2_outlives_r1 = self.universal_region_relations.outlives(r2, r1);
- match (r1_outlives_r2, r2_outlives_r1) {
- (true, true) => r1.min(r2),
- (true, false) => r2,
- (false, true) => r1,
- (false, false) => bug!("incomparable regions in total order"),
+ for scc_a in self.constraint_sccs.all_sccs() {
+ // Walk each SCC `B` such that `A: B`...
+ for &scc_b in self.constraint_sccs.successors(scc_a) {
+ debug!(?scc_b);
+ self.scc_values.add_region(scc_a, scc_b);
}
- }) else {
- debug!("no unique minimum choice");
- return;
- };
-
- // As we require `'scc: 'min_choice`, we have definitely already computed
- // its `scc_values` at this point.
- let min_choice_scc = self.constraint_sccs.scc(min_choice);
- debug!(?min_choice, ?min_choice_scc);
- if self.scc_values.add_region(scc, min_choice_scc) {
- self.member_constraints_applied.push(AppliedMemberConstraint {
- member_region_scc: scc,
- min_choice,
- member_constraint_index,
- });
}
}
@@ -1376,13 +1174,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
self.scc_annotations[scc].max_nameable_universe()
}
- pub(crate) fn max_placeholder_universe_reached(
- &self,
- scc: ConstraintSccIndex,
- ) -> UniverseIndex {
- self.scc_annotations[scc].max_placeholder_universe_reached()
- }
-
/// Checks the final value for the free region `fr` to see if it
/// grew too large. In particular, examine what `end(X)` points
/// wound up in `fr`'s final value; for each `end(X)` where `X !=
@@ -1551,43 +1342,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
}
}
- #[instrument(level = "debug", skip(self, infcx, errors_buffer))]
- fn check_member_constraints(
- &self,
- infcx: &InferCtxt<'tcx>,
- errors_buffer: &mut RegionErrors<'tcx>,
- ) {
- let member_constraints = Rc::clone(&self.member_constraints);
- for m_c_i in member_constraints.all_indices() {
- debug!(?m_c_i);
- let m_c = &member_constraints[m_c_i];
- let member_region_vid = m_c.member_region_vid;
- debug!(
- ?member_region_vid,
- value = ?self.region_value_str(member_region_vid),
- );
- let choice_regions = member_constraints.choice_regions(m_c_i);
- debug!(?choice_regions);
-
- // Did the member region wind up equal to any of the option regions?
- if let Some(o) =
- choice_regions.iter().find(|&&o_r| self.eval_equal(o_r, m_c.member_region_vid))
- {
- debug!("evaluated as equal to {:?}", o);
- continue;
- }
-
- // If not, report an error.
- let member_region = ty::Region::new_var(infcx.tcx, member_region_vid);
- errors_buffer.push(RegionErrorKind::UnexpectedHiddenRegion {
- span: m_c.definition_span,
- hidden_ty: m_c.hidden_ty,
- key: m_c.key,
- member_region,
- });
- }
- }
-
/// We have a constraint `fr1: fr2` that is not satisfied, where
/// `fr2` represents some universal region. Here, `r` is some
/// region where we know that `fr1: r` and this function has the
@@ -1651,18 +1405,39 @@ impl<'tcx> RegionInferenceContext<'tcx> {
}
/// Walks the graph of constraints (where `'a: 'b` is considered
- /// an edge `'a -> 'b`) to find all paths from `from_region` to
- /// `to_region`. The paths are accumulated into the vector
- /// `results`. The paths are stored as a series of
- /// `ConstraintIndex` values -- in other words, a list of *edges*.
+ /// an edge `'a -> 'b`) to find a path from `from_region` to
+ /// `to_region`.
///
/// Returns: a series of constraints as well as the region `R`
/// that passed the target test.
#[instrument(skip(self, target_test), ret)]
- pub(crate) fn find_constraint_paths_between_regions(
+ pub(crate) fn find_constraint_path_between_regions(
&self,
from_region: RegionVid,
target_test: impl Fn(RegionVid) -> bool,
+ ) -> Option<(Vec>, RegionVid)> {
+ self.find_constraint_path_between_regions_inner(true, from_region, &target_test).or_else(
+ || self.find_constraint_path_between_regions_inner(false, from_region, &target_test),
+ )
+ }
+
+ /// The constraints we get from equating the hidden type of each use of an opaque
+ /// with its final concrete type may end up getting preferred over other, potentially
+ /// longer constraint paths.
+ ///
+ /// Given that we compute the final concrete type by relying on this existing constraint
+ /// path, this can easily end up hiding the actual reason for why we require these regions
+ /// to be equal.
+ ///
+ /// To handle this, we first look at the path while ignoring these constraints and then
+ /// retry while considering them. This is not perfect, as the `from_region` may have already
+ /// been partially related to its argument region, so while we rely on a member constraint
+ /// to get a complete path, the most relevant step of that path already existed before then.
+ fn find_constraint_path_between_regions_inner(
+ &self,
+ ignore_opaque_type_constraints: bool,
+ from_region: RegionVid,
+ target_test: impl Fn(RegionVid) -> bool,
) -> Option<(Vec>, RegionVid)> {
let mut context = IndexVec::from_elem(Trace::NotVisited, &self.definitions);
context[from_region] = Trace::StartRegion;
@@ -1677,7 +1452,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
while let Some(r) = deque.pop_front() {
debug!(
- "find_constraint_paths_between_regions: from_region={:?} r={:?} value={}",
+ "find_constraint_path_between_regions: from_region={:?} r={:?} value={}",
from_region,
r,
self.region_value_str(r),
@@ -1711,20 +1486,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
result.push(c);
}
- Trace::FromMember(sup, sub, span) => {
- let c = OutlivesConstraint {
- sup,
- sub,
- locations: Locations::All(span),
- span,
- category: ConstraintCategory::OpaqueType,
- variance_info: ty::VarianceDiagInfo::default(),
- from_closure: false,
- };
- p = c.sup;
- result.push(c);
- }
-
Trace::StartRegion => {
result.reverse();
return Some((result, r));
@@ -1763,23 +1524,21 @@ impl<'tcx> RegionInferenceContext<'tcx> {
let edges = self.constraint_graph.outgoing_edges_from_graph(r, &self.constraints);
// This loop can be hot.
for constraint in edges {
- if matches!(constraint.category, ConstraintCategory::IllegalUniverse) {
- debug!("Ignoring illegal universe constraint: {constraint:?}");
- continue;
+ match constraint.category {
+ ConstraintCategory::IllegalUniverse => {
+ debug!("Ignoring illegal universe constraint: {constraint:?}");
+ continue;
+ }
+ ConstraintCategory::OpaqueType if ignore_opaque_type_constraints => {
+ debug!("Ignoring member constraint: {constraint:?}");
+ continue;
+ }
+ _ => {}
}
debug_assert_eq!(constraint.sup, r);
handle_trace(constraint.sub, Trace::FromGraph(constraint));
}
}
-
- // Member constraints can also give rise to `'r: 'x` edges that
- // were not part of the graph initially, so watch out for those.
- // (But they are extremely rare; this loop is very cold.)
- for constraint in self.applied_member_constraints(self.constraint_sccs.scc(r)) {
- let sub = constraint.min_choice;
- let p_c = &self.member_constraints[constraint.member_constraint_index];
- handle_trace(sub, Trace::FromMember(r, sub, p_c.definition_span));
- }
}
None
@@ -1790,7 +1549,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
pub(crate) fn find_sub_region_live_at(&self, fr1: RegionVid, location: Location) -> RegionVid {
trace!(scc = ?self.constraint_sccs.scc(fr1));
trace!(universe = ?self.max_nameable_universe(self.constraint_sccs.scc(fr1)));
- self.find_constraint_paths_between_regions(fr1, |r| {
+ self.find_constraint_path_between_regions(fr1, |r| {
// First look for some `r` such that `fr1: r` and `r` is live at `location`
trace!(?r, liveness_constraints=?self.liveness_constraints.pretty_print_live_points(r));
self.liveness_constraints.is_live_at(r, location)
@@ -1800,9 +1559,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
// `fr1: r` and `r` is a placeholder from some universe
// `fr1` cannot name. This would force `fr1` to be
// `'static`.
- self.find_constraint_paths_between_regions(fr1, |r| {
- self.cannot_name_placeholder(fr1, r)
- })
+ self.find_constraint_path_between_regions(fr1, |r| self.cannot_name_placeholder(fr1, r))
})
.or_else(|| {
// If we fail to find THAT, it may be that `fr1` is a
@@ -1815,9 +1572,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
// must be able to name the universe of R2, because R2 will
// be at least `'empty(Universe(R2))`, and `R1` must be at
// larger than that.
- self.find_constraint_paths_between_regions(fr1, |r| {
- self.cannot_name_placeholder(r, fr1)
- })
+ self.find_constraint_path_between_regions(fr1, |r| self.cannot_name_placeholder(r, fr1))
})
.map(|(_path, r)| r)
.unwrap()
@@ -1873,9 +1628,9 @@ impl<'tcx> RegionInferenceContext<'tcx> {
) -> (BlameConstraint<'tcx>, Vec>) {
// Find all paths
let (path, target_region) = self
- .find_constraint_paths_between_regions(from_region, target_test)
+ .find_constraint_path_between_regions(from_region, target_test)
.or_else(|| {
- self.find_constraint_paths_between_regions(from_region, |r| {
+ self.find_constraint_path_between_regions(from_region, |r| {
self.cannot_name_placeholder(from_region, r)
})
})
@@ -2111,11 +1866,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
&self.constraint_sccs
}
- /// Access to the region graph, built from the outlives constraints.
- pub(crate) fn region_graph(&self) -> RegionGraph<'_, 'tcx, graph::Normal> {
- self.constraint_graph.region_graph(&self.constraints, self.universal_regions().fr_static)
- }
-
/// Returns the representative `RegionVid` for a given SCC.
/// See `RegionTracker` for how a region variable ID is chosen.
///
diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs
deleted file mode 100644
index 23c4554aa155..000000000000
--- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs
+++ /dev/null
@@ -1,299 +0,0 @@
-use rustc_data_structures::fx::FxIndexMap;
-use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin};
-use rustc_macros::extension;
-use rustc_middle::ty::{
- self, DefiningScopeKind, OpaqueHiddenType, OpaqueTypeKey, Ty, TyCtxt, TypeFoldable,
- TypeVisitableExt, fold_regions,
-};
-use rustc_span::Span;
-use rustc_trait_selection::opaque_types::{
- InvalidOpaqueTypeArgs, check_opaque_type_parameter_valid,
-};
-use tracing::{debug, instrument};
-
-use super::RegionInferenceContext;
-use crate::BorrowCheckRootCtxt;
-use crate::session_diagnostics::LifetimeMismatchOpaqueParam;
-use crate::universal_regions::RegionClassification;
-
-pub(crate) enum DeferredOpaqueTypeError<'tcx> {
- InvalidOpaqueTypeArgs(InvalidOpaqueTypeArgs<'tcx>),
- LifetimeMismatchOpaqueParam(LifetimeMismatchOpaqueParam<'tcx>),
-}
-
-impl<'tcx> RegionInferenceContext<'tcx> {
- /// Resolve any opaque types that were encountered while borrow checking
- /// this item. This is then used to get the type in the `type_of` query.
- ///
- /// For example consider `fn f<'a>(x: &'a i32) -> impl Sized + 'a { x }`.
- /// This is lowered to give HIR something like
- ///
- /// type f<'a>::_Return<'_x> = impl Sized + '_x;
- /// fn f<'a>(x: &'a i32) -> f<'a>::_Return<'a> { x }
- ///
- /// When checking the return type record the type from the return and the
- /// type used in the return value. In this case they might be `_Return<'1>`
- /// and `&'2 i32` respectively.
- ///
- /// Once we to this method, we have completed region inference and want to
- /// call `infer_opaque_definition_from_instantiation` to get the inferred
- /// type of `_Return<'_x>`. `infer_opaque_definition_from_instantiation`
- /// compares lifetimes directly, so we need to map the inference variables
- /// back to concrete lifetimes: `'static`, `ReEarlyParam` or `ReLateParam`.
- ///
- /// First we map the regions in the generic parameters `_Return<'1>` to
- /// their `external_name` giving `_Return<'a>`. This step is a bit involved.
- /// See the [rustc-dev-guide chapter] for more info.
- ///
- /// Then we map all the lifetimes in the concrete type to an equal
- /// universal region that occurs in the opaque type's args, in this case
- /// this would result in `&'a i32`. We only consider regions in the args
- /// in case there is an equal region that does not. For example, this should
- /// be allowed:
- /// `fn f<'a: 'b, 'b: 'a>(x: *mut &'b i32) -> impl Sized + 'a { x }`
- ///
- /// This will then allow `infer_opaque_definition_from_instantiation` to
- /// determine that `_Return<'_x> = &'_x i32`.
- ///
- /// There's a slight complication around closures. Given
- /// `fn f<'a: 'a>() { || {} }` the closure's type is something like
- /// `f::<'a>::{{closure}}`. The region parameter from f is essentially
- /// ignored by type checking so ends up being inferred to an empty region.
- /// Calling `universal_upper_bound` for such a region gives `fr_fn_body`,
- /// which has no `external_name` in which case we use `'{erased}` as the
- /// region to pass to `infer_opaque_definition_from_instantiation`.
- ///
- /// [rustc-dev-guide chapter]:
- /// https://rustc-dev-guide.rust-lang.org/opaque-types-region-infer-restrictions.html
- #[instrument(level = "debug", skip(self, root_cx, infcx))]
- pub(crate) fn infer_opaque_types(
- &self,
- root_cx: &mut BorrowCheckRootCtxt<'tcx>,
- infcx: &InferCtxt<'tcx>,
- opaque_ty_decls: FxIndexMap, OpaqueHiddenType<'tcx>>,
- ) -> Vec