mirror of
https://github.com/rust-lang/rust.git
synced 2025-02-16 17:03:35 +00:00
Auto merge of #128490 - lnicola:sync-from-ra, r=lnicola
Subtree update of `rust-analyzer` r? `@ghost`
This commit is contained in:
commit
e60ebb2f2c
@ -15,7 +15,7 @@ env:
|
||||
CARGO_NET_RETRY: 10
|
||||
CI: 1
|
||||
RUST_BACKTRACE: short
|
||||
RUSTFLAGS: "-D warnings -W unreachable-pub -W bare-trait-objects"
|
||||
RUSTFLAGS: "-D warnings -D elided_lifetimes_in_paths -D explicit_outlives_requirements -D unsafe_op_in_unsafe_fn -D unused_extern_crates -D unused_lifetimes -D unreachable_pub"
|
||||
RUSTUP_MAX_RETRIES: 10
|
||||
|
||||
jobs:
|
||||
|
@ -14,6 +14,8 @@ extend-ignore-re = [
|
||||
"\\w*\\.{3,4}\\w*",
|
||||
'"flate2"',
|
||||
"raison d'être",
|
||||
"inout",
|
||||
"optin"
|
||||
]
|
||||
|
||||
[default.extend-words]
|
||||
|
12
src/tools/rust-analyzer/.vscode/launch.json
vendored
12
src/tools/rust-analyzer/.vscode/launch.json
vendored
@ -18,7 +18,8 @@
|
||||
"args": [
|
||||
// "--user-data-dir=${workspaceFolder}/target/code",
|
||||
"--disable-extensions",
|
||||
"--extensionDevelopmentPath=${workspaceFolder}/editors/code"
|
||||
"--extensionDevelopmentPath=${workspaceFolder}/editors/code",
|
||||
"--log rust-lang.rust-analyzer:debug"
|
||||
],
|
||||
"outFiles": [
|
||||
"${workspaceFolder}/editors/code/out/**/*.js"
|
||||
@ -36,7 +37,8 @@
|
||||
"runtimeExecutable": "${execPath}",
|
||||
"args": [
|
||||
"--disable-extensions",
|
||||
"--extensionDevelopmentPath=${workspaceFolder}/editors/code"
|
||||
"--extensionDevelopmentPath=${workspaceFolder}/editors/code",
|
||||
"--log rust-lang.rust-analyzer:debug"
|
||||
],
|
||||
"outFiles": [
|
||||
"${workspaceFolder}/editors/code/out/**/*.js"
|
||||
@ -57,7 +59,8 @@
|
||||
"runtimeExecutable": "${execPath}",
|
||||
"args": [
|
||||
"--disable-extensions",
|
||||
"--extensionDevelopmentPath=${workspaceFolder}/editors/code"
|
||||
"--extensionDevelopmentPath=${workspaceFolder}/editors/code",
|
||||
"--log rust-lang.rust-analyzer:debug"
|
||||
],
|
||||
"outFiles": [
|
||||
"${workspaceFolder}/editors/code/out/**/*.js"
|
||||
@ -79,7 +82,8 @@
|
||||
"runtimeExecutable": "${execPath}",
|
||||
"args": [
|
||||
"--disable-extension", "rust-lang.rust-analyzer",
|
||||
"--extensionDevelopmentPath=${workspaceFolder}/editors/code"
|
||||
"--extensionDevelopmentPath=${workspaceFolder}/editors/code",
|
||||
"--log rust-lang.rust-analyzer:debug"
|
||||
],
|
||||
"outFiles": [
|
||||
"${workspaceFolder}/editors/code/out/**/*.js"
|
||||
|
@ -4,9 +4,9 @@ version = 3
|
||||
|
||||
[[package]]
|
||||
name = "addr2line"
|
||||
version = "0.21.0"
|
||||
version = "0.22.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb"
|
||||
checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678"
|
||||
dependencies = [
|
||||
"gimli",
|
||||
]
|
||||
@ -28,9 +28,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.83"
|
||||
version = "1.0.86"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "25bdb32cbbdce2b519a9cd7df3a678443100e265d5e25ca763b7572a5104f5f3"
|
||||
checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
|
||||
|
||||
[[package]]
|
||||
name = "arbitrary"
|
||||
@ -52,16 +52,16 @@ checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
|
||||
|
||||
[[package]]
|
||||
name = "backtrace"
|
||||
version = "0.3.71"
|
||||
version = "0.3.72"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d"
|
||||
checksum = "17c6a35df3749d2e8bb1b7b21a976d82b15548788d2735b9d82f329268f71a11"
|
||||
dependencies = [
|
||||
"addr2line",
|
||||
"cc",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"miniz_oxide",
|
||||
"object 0.32.2",
|
||||
"object 0.35.0",
|
||||
"rustc-demangle",
|
||||
]
|
||||
|
||||
@ -70,6 +70,7 @@ name = "base-db"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"cfg",
|
||||
"intern",
|
||||
"la-arena 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lz4_flex",
|
||||
"rustc-hash",
|
||||
@ -103,9 +104,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||
|
||||
[[package]]
|
||||
name = "camino"
|
||||
version = "1.1.6"
|
||||
version = "1.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c59e92b5a388f549b863a7bea62612c09f24c8393560709a54558a9abdfb3b9c"
|
||||
checksum = "e0ec6b951b160caa93cc0c7b209e5a3bff7aae9062213451ac99493cd844c239"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
@ -135,9 +136,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.97"
|
||||
version = "1.0.98"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "099a5357d84c4c61eb35fc8eafa9a79a902c2f76911e5747ced4e032edd8d9b4"
|
||||
checksum = "41c270e7540d725e65ac7f1b212ac8ce349719624d7bcff99f8e2e488e8cf03f"
|
||||
|
||||
[[package]]
|
||||
name = "cfg"
|
||||
@ -146,6 +147,7 @@ dependencies = [
|
||||
"arbitrary",
|
||||
"derive_arbitrary",
|
||||
"expect-test",
|
||||
"intern",
|
||||
"mbe",
|
||||
"oorandom",
|
||||
"rustc-hash",
|
||||
@ -230,18 +232,18 @@ checksum = "0d48d8f76bd9331f19fe2aaf3821a9f9fb32c3963e1e3d6ce82a8c09cef7444a"
|
||||
|
||||
[[package]]
|
||||
name = "crc32fast"
|
||||
version = "1.4.0"
|
||||
version = "1.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa"
|
||||
checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-channel"
|
||||
version = "0.5.12"
|
||||
version = "0.5.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ab3db02a9c5b5121e1e42fbdb1aeb65f5e02624cc58c43f2884c6ccac0b82f95"
|
||||
checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2"
|
||||
dependencies = [
|
||||
"crossbeam-utils",
|
||||
]
|
||||
@ -267,9 +269,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.19"
|
||||
version = "0.8.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345"
|
||||
checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
|
||||
|
||||
[[package]]
|
||||
name = "ctrlc"
|
||||
@ -364,9 +366,9 @@ checksum = "9bda8e21c04aca2ae33ffc2fd8c23134f3cac46db123ba97bd9d3f3b8a4a85e1"
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.11.0"
|
||||
version = "1.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2"
|
||||
checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b"
|
||||
|
||||
[[package]]
|
||||
name = "ena"
|
||||
@ -429,6 +431,7 @@ dependencies = [
|
||||
"crossbeam-channel",
|
||||
"paths",
|
||||
"process-wrap",
|
||||
"project-model",
|
||||
"rustc-hash",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@ -474,9 +477,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "gimli"
|
||||
version = "0.28.1"
|
||||
version = "0.29.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
|
||||
checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
@ -835,6 +838,7 @@ dependencies = [
|
||||
"dashmap",
|
||||
"hashbrown",
|
||||
"rustc-hash",
|
||||
"sptr",
|
||||
"triomphe",
|
||||
]
|
||||
|
||||
@ -913,9 +917,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "libmimalloc-sys"
|
||||
version = "0.1.37"
|
||||
version = "0.1.38"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "81eb4061c0582dedea1cbc7aff2240300dd6982e0239d1c99e65c1dbf4a30ba7"
|
||||
checksum = "0e7bb23d733dfcc8af652a78b7bf232f0e967710d044732185e561e47c0336b6"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
@ -968,6 +972,7 @@ dependencies = [
|
||||
"crossbeam-channel",
|
||||
"hir-expand",
|
||||
"ide-db",
|
||||
"intern",
|
||||
"itertools",
|
||||
"paths",
|
||||
"proc-macro-api",
|
||||
@ -1044,6 +1049,7 @@ version = "0.0.0"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"cov-mark",
|
||||
"intern",
|
||||
"parser",
|
||||
"rustc-hash",
|
||||
"smallvec",
|
||||
@ -1081,18 +1087,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "mimalloc"
|
||||
version = "0.1.41"
|
||||
version = "0.1.42"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9f41a2280ded0da56c8cf898babb86e8f10651a34adcfff190ae9a1159c6908d"
|
||||
checksum = "e9186d86b79b52f4a77af65604b51225e8db1d6ee7e3f41aec1e40829c71a176"
|
||||
dependencies = [
|
||||
"libmimalloc-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.7.2"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7"
|
||||
checksum = "87dfd01fe195c66b572b37921ad8803d010623c0aca821bea2302239d155cdae"
|
||||
dependencies = [
|
||||
"adler",
|
||||
]
|
||||
@ -1157,9 +1163,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu-ansi-term"
|
||||
version = "0.49.0"
|
||||
version = "0.50.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c073d3c1930d0751774acf49e66653acecb416c3a54c6ec095a9b11caddb5a68"
|
||||
checksum = "dd2800e1520bdc966782168a627aa5d1ad92e33b984bf7c7615d31280c83ff14"
|
||||
dependencies = [
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
@ -1182,18 +1188,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.32.2"
|
||||
version = "0.33.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441"
|
||||
checksum = "d8dd6c0cdf9429bce006e1362bfce61fa1bfd8c898a643ed8d2b471934701d3d"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.33.0"
|
||||
version = "0.35.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d8dd6c0cdf9429bce006e1362bfce61fa1bfd8c898a643ed8d2b471934701d3d"
|
||||
checksum = "b8ec7ab813848ba4522158d5517a6093db1ded27575b070f4177b8d12b41db5e"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
@ -1218,9 +1224,9 @@ checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.12.2"
|
||||
version = "0.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb"
|
||||
checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
"parking_lot_core",
|
||||
@ -1323,6 +1329,7 @@ version = "0.0.0"
|
||||
dependencies = [
|
||||
"base-db",
|
||||
"indexmap",
|
||||
"intern",
|
||||
"la-arena 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"paths",
|
||||
"rustc-hash",
|
||||
@ -1341,6 +1348,7 @@ version = "0.0.0"
|
||||
dependencies = [
|
||||
"base-db",
|
||||
"expect-test",
|
||||
"intern",
|
||||
"libloading",
|
||||
"mbe",
|
||||
"memmap2",
|
||||
@ -1372,9 +1380,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.82"
|
||||
version = "1.0.85"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b"
|
||||
checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
@ -1411,6 +1419,7 @@ dependencies = [
|
||||
"cargo_metadata",
|
||||
"cfg",
|
||||
"expect-test",
|
||||
"intern",
|
||||
"itertools",
|
||||
"la-arena 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"paths",
|
||||
@ -1651,6 +1660,7 @@ dependencies = [
|
||||
"ide",
|
||||
"ide-db",
|
||||
"ide-ssr",
|
||||
"intern",
|
||||
"itertools",
|
||||
"load-cargo",
|
||||
"lsp-server 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -1791,18 +1801,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.201"
|
||||
version = "1.0.203"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "780f1cebed1629e4753a1a38a3c72d30b97ec044f0aef68cb26650a3c5cf363c"
|
||||
checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.201"
|
||||
version = "1.0.203"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c5e405930b9796f1c00bee880d03fc7e0bb4b9a11afc776885ffe84320da2865"
|
||||
checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -1834,9 +1844,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_spanned"
|
||||
version = "0.6.5"
|
||||
version = "0.6.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1"
|
||||
checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
@ -1858,9 +1868,9 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
|
||||
|
||||
[[package]]
|
||||
name = "smol_str"
|
||||
version = "0.2.1"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6845563ada680337a52d43bb0b29f396f2d911616f6573012645b9e3d048a49"
|
||||
checksum = "dd538fb6910ac1099850255cf94a94df6551fbdd602454387d0adb2d1ca6dead"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
@ -1885,6 +1895,12 @@ dependencies = [
|
||||
"vfs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sptr"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b9b39299b249ad65f3b7e96443bad61c02ca5cd3589f46cb6d610a0fd6c0d6a"
|
||||
|
||||
[[package]]
|
||||
name = "stable_deref_trait"
|
||||
version = "1.2.0"
|
||||
@ -1907,9 +1923,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.63"
|
||||
version = "2.0.66"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf5be731623ca1a1fb7d8be6f261a3be6d3e2337b8a1f97be944d020c8fcb704"
|
||||
checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -1958,6 +1974,7 @@ dependencies = [
|
||||
"base-db",
|
||||
"cfg",
|
||||
"hir-expand",
|
||||
"intern",
|
||||
"rustc-hash",
|
||||
"span",
|
||||
"stdx",
|
||||
@ -1993,18 +2010,18 @@ checksum = "f18aa187839b2bdb1ad2fa35ead8c4c2976b64e4363c386d45ac0f7ee85c9233"
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.60"
|
||||
version = "1.0.61"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "579e9083ca58dd9dcf91a9923bb9054071b9ebbd800b342194c9feb0ee89fc18"
|
||||
checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.60"
|
||||
version = "1.0.61"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2470041c06ec3ac1ab38d0356a6119054dedaea53e12fbefc0de730a1c08524"
|
||||
checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -2088,9 +2105,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.8.12"
|
||||
version = "0.8.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e9dd1545e8208b4a5af1aa9bbd0b4cf7e9ea08fabc5d0a5c67fcaafa17433aa3"
|
||||
checksum = "6f49eb2ab21d2f26bd6db7bf383edc527a7ebaee412d17af4d40fdccd442f335"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
@ -2100,18 +2117,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "0.6.5"
|
||||
version = "0.6.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1"
|
||||
checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.22.12"
|
||||
version = "0.22.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3328d4f68a705b2a4498da1d580585d39a6510f98318a2cec3018a7ec61ddef"
|
||||
checksum = "f21c7aaf97f1bd9ca9d4f9e73b0a6c74bd5afef56f2bc931943a6e1c37e04e38"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"serde",
|
||||
@ -2185,9 +2202,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tracing-tree"
|
||||
version = "0.3.0"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "65139ecd2c3f6484c3b99bc01c77afe21e95473630747c7aca525e78b0666675"
|
||||
checksum = "b56c62d2c80033cb36fae448730a2f2ef99410fe3ecbffc916681a32f6807dbe"
|
||||
dependencies = [
|
||||
"nu-ansi-term",
|
||||
"tracing-core",
|
||||
@ -2197,9 +2214,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "triomphe"
|
||||
version = "0.1.11"
|
||||
version = "0.1.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "859eb650cfee7434994602c3a68b25d77ad9e68c8a6cd491616ef86661382eb3"
|
||||
checksum = "1b2cb4fbb9995eeb36ac86fadf24031ccd58f99d6b4b2d7b911db70bddb80d90"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"stable_deref_trait",
|
||||
@ -2210,7 +2227,8 @@ name = "tt"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"smol_str",
|
||||
"intern",
|
||||
"ra-ap-rustc_lexer",
|
||||
"stdx",
|
||||
"text-size",
|
||||
]
|
||||
@ -2538,9 +2556,9 @@ checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.6.8"
|
||||
version = "0.6.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3c52e9c97a68071b23e836c9380edae937f17b9c4667bd021973efc689f618d"
|
||||
checksum = "56c52728401e1dc672a56e81e593e912aa54c78f40246869f78359a2bf24d29d"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
@ -50,7 +50,7 @@ debug = 2
|
||||
[workspace.dependencies]
|
||||
# local crates
|
||||
base-db = { path = "./crates/base-db", version = "0.0.0" }
|
||||
cfg = { path = "./crates/cfg", version = "0.0.0" }
|
||||
cfg = { path = "./crates/cfg", version = "0.0.0", features = ["tt"] }
|
||||
flycheck = { path = "./crates/flycheck", version = "0.0.0" }
|
||||
hir = { path = "./crates/hir", version = "0.0.0" }
|
||||
hir-def = { path = "./crates/hir-def", version = "0.0.0" }
|
||||
@ -163,14 +163,14 @@ xshell = "0.2.5"
|
||||
dashmap = { version = "=5.5.3", features = ["raw-api"] }
|
||||
|
||||
[workspace.lints.rust]
|
||||
bare_trait_objects = "warn"
|
||||
# remember to update RUSTFLAGS in ci.yml if you add something here
|
||||
|
||||
elided_lifetimes_in_paths = "warn"
|
||||
ellipsis_inclusive_range_patterns = "warn"
|
||||
explicit_outlives_requirements = "warn"
|
||||
unsafe_op_in_unsafe_fn = "warn"
|
||||
unused_extern_crates = "warn"
|
||||
unused_lifetimes = "warn"
|
||||
unreachable_pub = "warn"
|
||||
semicolon_in_expressions_from_macros = "warn"
|
||||
|
||||
[workspace.lints.clippy]
|
||||
# FIXME Remove the tidy test once the lint table is stable
|
||||
|
@ -27,6 +27,7 @@ stdx.workspace = true
|
||||
syntax.workspace = true
|
||||
vfs.workspace = true
|
||||
span.workspace = true
|
||||
intern.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
@ -9,16 +9,14 @@
|
||||
use std::{fmt, mem, ops};
|
||||
|
||||
use cfg::CfgOptions;
|
||||
use intern::Symbol;
|
||||
use la_arena::{Arena, Idx, RawIdx};
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use span::Edition;
|
||||
use syntax::SmolStr;
|
||||
use span::{Edition, EditionedFileId};
|
||||
use triomphe::Arc;
|
||||
use vfs::{file_set::FileSet, AbsPathBuf, AnchoredPath, FileId, VfsPath};
|
||||
|
||||
// Map from crate id to the name of the crate and path of the proc-macro. If the value is `None`,
|
||||
// then the crate for the proc-macro hasn't been build yet as the build data is missing.
|
||||
pub type ProcMacroPaths = FxHashMap<CrateId, Result<(Option<String>, AbsPathBuf), String>>;
|
||||
pub type ProcMacroPaths = FxHashMap<CrateId, Result<(String, AbsPathBuf), String>>;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub struct SourceRootId(pub u32);
|
||||
@ -99,8 +97,8 @@ impl fmt::Debug for CrateGraph {
|
||||
|
||||
pub type CrateId = Idx<CrateData>;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub struct CrateName(SmolStr);
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct CrateName(Symbol);
|
||||
|
||||
impl CrateName {
|
||||
/// Creates a crate name, checking for dashes in the string provided.
|
||||
@ -110,16 +108,16 @@ impl CrateName {
|
||||
if name.contains('-') {
|
||||
Err(name)
|
||||
} else {
|
||||
Ok(Self(SmolStr::new(name)))
|
||||
Ok(Self(Symbol::intern(name)))
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a crate name, unconditionally replacing the dashes with underscores.
|
||||
pub fn normalize_dashes(name: &str) -> CrateName {
|
||||
Self(SmolStr::new(name.replace('-', "_")))
|
||||
Self(Symbol::intern(&name.replace('-', "_")))
|
||||
}
|
||||
|
||||
pub fn as_smol_str(&self) -> &SmolStr {
|
||||
pub fn symbol(&self) -> &Symbol {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
@ -133,7 +131,7 @@ impl fmt::Display for CrateName {
|
||||
impl ops::Deref for CrateName {
|
||||
type Target = str;
|
||||
fn deref(&self) -> &str {
|
||||
&self.0
|
||||
self.0.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
@ -141,11 +139,11 @@ impl ops::Deref for CrateName {
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum CrateOrigin {
|
||||
/// Crates that are from the rustc workspace.
|
||||
Rustc { name: String },
|
||||
Rustc { name: Symbol },
|
||||
/// Crates that are workspace members.
|
||||
Local { repo: Option<String>, name: Option<String> },
|
||||
Local { repo: Option<String>, name: Option<Symbol> },
|
||||
/// Crates that are non member libraries.
|
||||
Library { repo: Option<String>, name: String },
|
||||
Library { repo: Option<String>, name: Symbol },
|
||||
/// Crates that are provided by the language, like std, core, proc-macro, ...
|
||||
Lang(LangCrateOrigin),
|
||||
}
|
||||
@ -201,16 +199,16 @@ impl fmt::Display for LangCrateOrigin {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct CrateDisplayName {
|
||||
// The name we use to display various paths (with `_`).
|
||||
crate_name: CrateName,
|
||||
// The name as specified in Cargo.toml (with `-`).
|
||||
canonical_name: String,
|
||||
canonical_name: Symbol,
|
||||
}
|
||||
|
||||
impl CrateDisplayName {
|
||||
pub fn canonical_name(&self) -> &str {
|
||||
pub fn canonical_name(&self) -> &Symbol {
|
||||
&self.canonical_name
|
||||
}
|
||||
pub fn crate_name(&self) -> &CrateName {
|
||||
@ -220,7 +218,7 @@ impl CrateDisplayName {
|
||||
|
||||
impl From<CrateName> for CrateDisplayName {
|
||||
fn from(crate_name: CrateName) -> CrateDisplayName {
|
||||
let canonical_name = crate_name.to_string();
|
||||
let canonical_name = crate_name.0.clone();
|
||||
CrateDisplayName { crate_name, canonical_name }
|
||||
}
|
||||
}
|
||||
@ -239,9 +237,9 @@ impl ops::Deref for CrateDisplayName {
|
||||
}
|
||||
|
||||
impl CrateDisplayName {
|
||||
pub fn from_canonical_name(canonical_name: String) -> CrateDisplayName {
|
||||
let crate_name = CrateName::normalize_dashes(&canonical_name);
|
||||
CrateDisplayName { crate_name, canonical_name }
|
||||
pub fn from_canonical_name(canonical_name: &str) -> CrateDisplayName {
|
||||
let crate_name = CrateName::normalize_dashes(canonical_name);
|
||||
CrateDisplayName { crate_name, canonical_name: Symbol::intern(canonical_name) }
|
||||
}
|
||||
}
|
||||
|
||||
@ -662,6 +660,10 @@ impl CrateData {
|
||||
fn add_dep(&mut self, dep: Dependency) {
|
||||
self.dependencies.push(dep)
|
||||
}
|
||||
|
||||
pub fn root_file_id(&self) -> EditionedFileId {
|
||||
EditionedFileId::new(self.root_file_id, self.edition)
|
||||
}
|
||||
}
|
||||
|
||||
impl Extend<(String, String)> for Env {
|
||||
|
@ -6,8 +6,10 @@ mod input;
|
||||
use std::panic;
|
||||
|
||||
use salsa::Durability;
|
||||
use span::EditionedFileId;
|
||||
use syntax::{ast, Parse, SourceFile, SyntaxError};
|
||||
use triomphe::Arc;
|
||||
use vfs::FileId;
|
||||
|
||||
pub use crate::{
|
||||
change::FileChange,
|
||||
@ -18,8 +20,7 @@ pub use crate::{
|
||||
},
|
||||
};
|
||||
pub use salsa::{self, Cancelled};
|
||||
pub use span::{FilePosition, FileRange};
|
||||
pub use vfs::{file_set::FileSet, AnchoredPath, AnchoredPathBuf, FileId, VfsPath};
|
||||
pub use vfs::{file_set::FileSet, AnchoredPath, AnchoredPathBuf, VfsPath};
|
||||
|
||||
pub use semver::{BuildMetadata, Prerelease, Version, VersionReq};
|
||||
|
||||
@ -41,9 +42,9 @@ pub trait Upcast<T: ?Sized> {
|
||||
fn upcast(&self) -> &T;
|
||||
}
|
||||
|
||||
pub const DEFAULT_FILE_TEXT_LRU_CAP: usize = 16;
|
||||
pub const DEFAULT_PARSE_LRU_CAP: usize = 128;
|
||||
pub const DEFAULT_BORROWCK_LRU_CAP: usize = 2024;
|
||||
pub const DEFAULT_FILE_TEXT_LRU_CAP: u16 = 16;
|
||||
pub const DEFAULT_PARSE_LRU_CAP: u16 = 128;
|
||||
pub const DEFAULT_BORROWCK_LRU_CAP: u16 = 2024;
|
||||
|
||||
pub trait FileLoader {
|
||||
/// Text of the file.
|
||||
@ -58,10 +59,11 @@ pub trait FileLoader {
|
||||
#[salsa::query_group(SourceDatabaseStorage)]
|
||||
pub trait SourceDatabase: FileLoader + std::fmt::Debug {
|
||||
/// Parses the file into the syntax tree.
|
||||
fn parse(&self, file_id: FileId) -> Parse<ast::SourceFile>;
|
||||
#[salsa::lru]
|
||||
fn parse(&self, file_id: EditionedFileId) -> Parse<ast::SourceFile>;
|
||||
|
||||
/// Returns the set of errors obtained from parsing the file including validation errors.
|
||||
fn parse_errors(&self, file_id: FileId) -> Option<Arc<[SyntaxError]>>;
|
||||
fn parse_errors(&self, file_id: EditionedFileId) -> Option<Arc<[SyntaxError]>>;
|
||||
|
||||
/// The crate graph.
|
||||
#[salsa::input]
|
||||
@ -82,14 +84,14 @@ fn toolchain_channel(db: &dyn SourceDatabase, krate: CrateId) -> Option<ReleaseC
|
||||
db.toolchain(krate).as_ref().and_then(|v| ReleaseChannel::from_str(&v.pre))
|
||||
}
|
||||
|
||||
fn parse(db: &dyn SourceDatabase, file_id: FileId) -> Parse<ast::SourceFile> {
|
||||
fn parse(db: &dyn SourceDatabase, file_id: EditionedFileId) -> Parse<ast::SourceFile> {
|
||||
let _p = tracing::info_span!("parse", ?file_id).entered();
|
||||
let (file_id, edition) = file_id.unpack();
|
||||
let text = db.file_text(file_id);
|
||||
// FIXME: Edition based parsing
|
||||
SourceFile::parse(&text, span::Edition::CURRENT)
|
||||
SourceFile::parse(&text, edition)
|
||||
}
|
||||
|
||||
fn parse_errors(db: &dyn SourceDatabase, file_id: FileId) -> Option<Arc<[SyntaxError]>> {
|
||||
fn parse_errors(db: &dyn SourceDatabase, file_id: EditionedFileId) -> Option<Arc<[SyntaxError]>> {
|
||||
let errors = db.parse(file_id).errors();
|
||||
match &*errors {
|
||||
[] => None,
|
||||
@ -104,6 +106,7 @@ pub trait SourceDatabaseExt: SourceDatabase {
|
||||
#[salsa::input]
|
||||
fn compressed_file_text(&self, file_id: FileId) -> Arc<[u8]>;
|
||||
|
||||
#[salsa::lru]
|
||||
fn file_text(&self, file_id: FileId) -> Arc<str>;
|
||||
|
||||
/// Path to a file, relative to the root of its source root.
|
||||
|
@ -15,7 +15,8 @@ doctest = false
|
||||
rustc-hash.workspace = true
|
||||
|
||||
# locals deps
|
||||
tt.workspace = true
|
||||
tt = { workspace = true, optional = true }
|
||||
intern.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
expect-test = "1.4.1"
|
||||
|
@ -2,20 +2,20 @@
|
||||
//!
|
||||
//! See: <https://doc.rust-lang.org/reference/conditional-compilation.html#conditional-compilation>
|
||||
|
||||
use std::{fmt, slice::Iter as SliceIter};
|
||||
use std::fmt;
|
||||
|
||||
use tt::SmolStr;
|
||||
use intern::Symbol;
|
||||
|
||||
/// A simple configuration value passed in from the outside.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum CfgAtom {
|
||||
/// eg. `#[cfg(test)]`
|
||||
Flag(SmolStr),
|
||||
Flag(Symbol),
|
||||
/// eg. `#[cfg(target_os = "linux")]`
|
||||
///
|
||||
/// Note that a key can have multiple values that are all considered "active" at the same time.
|
||||
/// For example, `#[cfg(target_feature = "sse")]` and `#[cfg(target_feature = "sse2")]`.
|
||||
KeyValue { key: SmolStr, value: SmolStr },
|
||||
KeyValue { key: Symbol, value: Symbol },
|
||||
}
|
||||
|
||||
impl fmt::Display for CfgAtom {
|
||||
@ -32,8 +32,8 @@ impl fmt::Display for CfgAtom {
|
||||
pub enum CfgExpr {
|
||||
Invalid,
|
||||
Atom(CfgAtom),
|
||||
All(Vec<CfgExpr>),
|
||||
Any(Vec<CfgExpr>),
|
||||
All(Box<[CfgExpr]>),
|
||||
Any(Box<[CfgExpr]>),
|
||||
Not(Box<CfgExpr>),
|
||||
}
|
||||
|
||||
@ -44,6 +44,7 @@ impl From<CfgAtom> for CfgExpr {
|
||||
}
|
||||
|
||||
impl CfgExpr {
|
||||
#[cfg(feature = "tt")]
|
||||
pub fn parse<S>(tt: &tt::Subtree<S>) -> CfgExpr {
|
||||
next_cfg_expr(&mut tt.token_trees.iter()).unwrap_or(CfgExpr::Invalid)
|
||||
}
|
||||
@ -63,10 +64,14 @@ impl CfgExpr {
|
||||
}
|
||||
}
|
||||
}
|
||||
fn next_cfg_expr<S>(it: &mut SliceIter<'_, tt::TokenTree<S>>) -> Option<CfgExpr> {
|
||||
|
||||
#[cfg(feature = "tt")]
|
||||
fn next_cfg_expr<S>(it: &mut std::slice::Iter<'_, tt::TokenTree<S>>) -> Option<CfgExpr> {
|
||||
use intern::sym;
|
||||
|
||||
let name = match it.next() {
|
||||
None => return None,
|
||||
Some(tt::TokenTree::Leaf(tt::Leaf::Ident(ident))) => ident.text.clone(),
|
||||
Some(tt::TokenTree::Leaf(tt::Leaf::Ident(ident))) => ident.sym.clone(),
|
||||
Some(_) => return Some(CfgExpr::Invalid),
|
||||
};
|
||||
|
||||
@ -77,10 +82,7 @@ fn next_cfg_expr<S>(it: &mut SliceIter<'_, tt::TokenTree<S>>) -> Option<CfgExpr>
|
||||
Some(tt::TokenTree::Leaf(tt::Leaf::Literal(literal))) => {
|
||||
it.next();
|
||||
it.next();
|
||||
// FIXME: escape? raw string?
|
||||
let value =
|
||||
SmolStr::new(literal.text.trim_start_matches('"').trim_end_matches('"'));
|
||||
CfgAtom::KeyValue { key: name, value }.into()
|
||||
CfgAtom::KeyValue { key: name, value: literal.symbol.clone() }.into()
|
||||
}
|
||||
_ => return Some(CfgExpr::Invalid),
|
||||
}
|
||||
@ -88,11 +90,13 @@ fn next_cfg_expr<S>(it: &mut SliceIter<'_, tt::TokenTree<S>>) -> Option<CfgExpr>
|
||||
Some(tt::TokenTree::Subtree(subtree)) => {
|
||||
it.next();
|
||||
let mut sub_it = subtree.token_trees.iter();
|
||||
let mut subs = std::iter::from_fn(|| next_cfg_expr(&mut sub_it)).collect();
|
||||
match name.as_str() {
|
||||
"all" => CfgExpr::All(subs),
|
||||
"any" => CfgExpr::Any(subs),
|
||||
"not" => CfgExpr::Not(Box::new(subs.pop().unwrap_or(CfgExpr::Invalid))),
|
||||
let mut subs = std::iter::from_fn(|| next_cfg_expr(&mut sub_it));
|
||||
match name {
|
||||
s if s == sym::all => CfgExpr::All(subs.collect()),
|
||||
s if s == sym::any => CfgExpr::Any(subs.collect()),
|
||||
s if s == sym::not => {
|
||||
CfgExpr::Not(Box::new(subs.next().unwrap_or(CfgExpr::Invalid)))
|
||||
}
|
||||
_ => CfgExpr::Invalid,
|
||||
}
|
||||
}
|
||||
@ -112,11 +116,11 @@ fn next_cfg_expr<S>(it: &mut SliceIter<'_, tt::TokenTree<S>>) -> Option<CfgExpr>
|
||||
impl arbitrary::Arbitrary<'_> for CfgAtom {
|
||||
fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
|
||||
if u.arbitrary()? {
|
||||
Ok(CfgAtom::Flag(String::arbitrary(u)?.into()))
|
||||
Ok(CfgAtom::Flag(Symbol::intern(<_>::arbitrary(u)?)))
|
||||
} else {
|
||||
Ok(CfgAtom::KeyValue {
|
||||
key: String::arbitrary(u)?.into(),
|
||||
value: String::arbitrary(u)?.into(),
|
||||
key: Symbol::intern(<_>::arbitrary(u)?),
|
||||
value: Symbol::intern(<_>::arbitrary(u)?),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ struct Literal {
|
||||
}
|
||||
|
||||
impl DnfExpr {
|
||||
pub fn new(expr: CfgExpr) -> Self {
|
||||
pub fn new(expr: &CfgExpr) -> Self {
|
||||
let builder = Builder { expr: DnfExpr { conjunctions: Vec::new() } };
|
||||
|
||||
builder.lower(expr)
|
||||
@ -66,9 +66,9 @@ impl DnfExpr {
|
||||
}
|
||||
}
|
||||
|
||||
res.enabled.sort_unstable();
|
||||
res.enabled.sort_unstable_by(compare);
|
||||
res.enabled.dedup();
|
||||
res.disabled.sort_unstable();
|
||||
res.disabled.sort_unstable_by(compare);
|
||||
res.disabled.dedup();
|
||||
Some(res)
|
||||
}
|
||||
@ -114,14 +114,25 @@ impl DnfExpr {
|
||||
};
|
||||
|
||||
// Undo the FxHashMap randomization for consistent output.
|
||||
diff.enable.sort_unstable();
|
||||
diff.disable.sort_unstable();
|
||||
diff.enable.sort_unstable_by(compare);
|
||||
diff.disable.sort_unstable_by(compare);
|
||||
|
||||
Some(diff)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn compare(a: &CfgAtom, b: &CfgAtom) -> std::cmp::Ordering {
|
||||
match (a, b) {
|
||||
(CfgAtom::Flag(a), CfgAtom::Flag(b)) => a.as_str().cmp(b.as_str()),
|
||||
(CfgAtom::Flag(_), CfgAtom::KeyValue { .. }) => std::cmp::Ordering::Less,
|
||||
(CfgAtom::KeyValue { .. }, CfgAtom::Flag(_)) => std::cmp::Ordering::Greater,
|
||||
(CfgAtom::KeyValue { key, value }, CfgAtom::KeyValue { key: key2, value: value2 }) => {
|
||||
key.as_str().cmp(key2.as_str()).then(value.as_str().cmp(value2.as_str()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for DnfExpr {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
if self.conjunctions.len() != 1 {
|
||||
@ -143,9 +154,9 @@ impl fmt::Display for DnfExpr {
|
||||
}
|
||||
|
||||
impl Conjunction {
|
||||
fn new(parts: Vec<CfgExpr>) -> Self {
|
||||
fn new(parts: Box<[CfgExpr]>) -> Self {
|
||||
let mut literals = Vec::new();
|
||||
for part in parts {
|
||||
for part in parts.into_vec() {
|
||||
match part {
|
||||
CfgExpr::Invalid | CfgExpr::Atom(_) | CfgExpr::Not(_) => {
|
||||
literals.push(Literal::new(part));
|
||||
@ -221,27 +232,28 @@ struct Builder {
|
||||
}
|
||||
|
||||
impl Builder {
|
||||
fn lower(mut self, expr: CfgExpr) -> DnfExpr {
|
||||
fn lower(mut self, expr: &CfgExpr) -> DnfExpr {
|
||||
let expr = make_nnf(expr);
|
||||
let expr = make_dnf(expr);
|
||||
|
||||
match expr {
|
||||
CfgExpr::Invalid | CfgExpr::Atom(_) | CfgExpr::Not(_) => {
|
||||
self.expr.conjunctions.push(Conjunction::new(vec![expr]));
|
||||
self.expr.conjunctions.push(Conjunction::new(Box::new([expr])));
|
||||
}
|
||||
CfgExpr::All(conj) => {
|
||||
self.expr.conjunctions.push(Conjunction::new(conj));
|
||||
}
|
||||
CfgExpr::Any(mut disj) => {
|
||||
CfgExpr::Any(disj) => {
|
||||
let mut disj = disj.into_vec();
|
||||
disj.reverse();
|
||||
while let Some(conj) = disj.pop() {
|
||||
match conj {
|
||||
CfgExpr::Invalid | CfgExpr::Atom(_) | CfgExpr::All(_) | CfgExpr::Not(_) => {
|
||||
self.expr.conjunctions.push(Conjunction::new(vec![conj]));
|
||||
self.expr.conjunctions.push(Conjunction::new(Box::new([conj])));
|
||||
}
|
||||
CfgExpr::Any(inner_disj) => {
|
||||
// Flatten.
|
||||
disj.extend(inner_disj.into_iter().rev());
|
||||
disj.extend(inner_disj.into_vec().into_iter().rev());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -255,11 +267,11 @@ impl Builder {
|
||||
fn make_dnf(expr: CfgExpr) -> CfgExpr {
|
||||
match expr {
|
||||
CfgExpr::Invalid | CfgExpr::Atom(_) | CfgExpr::Not(_) => expr,
|
||||
CfgExpr::Any(e) => flatten(CfgExpr::Any(e.into_iter().map(make_dnf).collect())),
|
||||
CfgExpr::Any(e) => flatten(CfgExpr::Any(e.into_vec().into_iter().map(make_dnf).collect())),
|
||||
CfgExpr::All(e) => {
|
||||
let e = e.into_iter().map(make_dnf).collect::<Vec<_>>();
|
||||
let e = e.into_vec().into_iter().map(make_dnf).collect::<Vec<_>>();
|
||||
|
||||
flatten(CfgExpr::Any(distribute_conj(&e)))
|
||||
flatten(CfgExpr::Any(distribute_conj(&e).into_boxed_slice()))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -270,7 +282,7 @@ fn distribute_conj(conj: &[CfgExpr]) -> Vec<CfgExpr> {
|
||||
match rest {
|
||||
[head, tail @ ..] => match head {
|
||||
CfgExpr::Any(disj) => {
|
||||
for part in disj {
|
||||
for part in disj.iter() {
|
||||
with.push(part.clone());
|
||||
go(out, with, tail);
|
||||
with.pop();
|
||||
@ -284,7 +296,7 @@ fn distribute_conj(conj: &[CfgExpr]) -> Vec<CfgExpr> {
|
||||
},
|
||||
_ => {
|
||||
// Turn accumulated parts into a new conjunction.
|
||||
out.push(CfgExpr::All(with.clone()));
|
||||
out.push(CfgExpr::All(with.clone().into_boxed_slice()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -297,25 +309,27 @@ fn distribute_conj(conj: &[CfgExpr]) -> Vec<CfgExpr> {
|
||||
out
|
||||
}
|
||||
|
||||
fn make_nnf(expr: CfgExpr) -> CfgExpr {
|
||||
fn make_nnf(expr: &CfgExpr) -> CfgExpr {
|
||||
match expr {
|
||||
CfgExpr::Invalid | CfgExpr::Atom(_) => expr,
|
||||
CfgExpr::Any(expr) => CfgExpr::Any(expr.into_iter().map(make_nnf).collect()),
|
||||
CfgExpr::All(expr) => CfgExpr::All(expr.into_iter().map(make_nnf).collect()),
|
||||
CfgExpr::Not(operand) => match *operand {
|
||||
CfgExpr::Invalid | CfgExpr::Atom(_) => CfgExpr::Not(operand.clone()), // Original negated expr
|
||||
CfgExpr::Not(expr) => {
|
||||
// Remove double negation.
|
||||
make_nnf(*expr)
|
||||
}
|
||||
// Convert negated conjunction/disjunction using DeMorgan's Law.
|
||||
CfgExpr::Any(inner) => CfgExpr::All(
|
||||
inner.into_iter().map(|expr| make_nnf(CfgExpr::Not(Box::new(expr)))).collect(),
|
||||
),
|
||||
CfgExpr::All(inner) => CfgExpr::Any(
|
||||
inner.into_iter().map(|expr| make_nnf(CfgExpr::Not(Box::new(expr)))).collect(),
|
||||
),
|
||||
},
|
||||
CfgExpr::Invalid | CfgExpr::Atom(_) => expr.clone(),
|
||||
CfgExpr::Any(expr) => CfgExpr::Any(expr.iter().map(make_nnf).collect()),
|
||||
CfgExpr::All(expr) => CfgExpr::All(expr.iter().map(make_nnf).collect()),
|
||||
CfgExpr::Not(operand) => make_nnf_neg(operand),
|
||||
}
|
||||
}
|
||||
|
||||
fn make_nnf_neg(operand: &CfgExpr) -> CfgExpr {
|
||||
match operand {
|
||||
// Original negated expr
|
||||
CfgExpr::Invalid => CfgExpr::Not(Box::new(CfgExpr::Invalid)), // Original negated expr
|
||||
// Original negated expr
|
||||
CfgExpr::Atom(atom) => CfgExpr::Not(Box::new(CfgExpr::Atom(atom.clone()))),
|
||||
// Remove double negation.
|
||||
CfgExpr::Not(expr) => make_nnf(expr),
|
||||
// Convert negated conjunction/disjunction using DeMorgan's Law.
|
||||
CfgExpr::Any(inner) => CfgExpr::All(inner.iter().map(make_nnf_neg).collect()),
|
||||
// Convert negated conjunction/disjunction using DeMorgan's Law.
|
||||
CfgExpr::All(inner) => CfgExpr::Any(inner.iter().map(make_nnf_neg).collect()),
|
||||
}
|
||||
}
|
||||
|
||||
@ -324,20 +338,22 @@ fn flatten(expr: CfgExpr) -> CfgExpr {
|
||||
match expr {
|
||||
CfgExpr::All(inner) => CfgExpr::All(
|
||||
inner
|
||||
.into_iter()
|
||||
.iter()
|
||||
.flat_map(|e| match e {
|
||||
CfgExpr::All(inner) => inner,
|
||||
_ => vec![e],
|
||||
CfgExpr::All(inner) => inner.as_ref(),
|
||||
_ => std::slice::from_ref(e),
|
||||
})
|
||||
.cloned()
|
||||
.collect(),
|
||||
),
|
||||
CfgExpr::Any(inner) => CfgExpr::Any(
|
||||
inner
|
||||
.into_iter()
|
||||
.iter()
|
||||
.flat_map(|e| match e {
|
||||
CfgExpr::Any(inner) => inner,
|
||||
_ => vec![e],
|
||||
CfgExpr::Any(inner) => inner.as_ref(),
|
||||
_ => std::slice::from_ref(e),
|
||||
})
|
||||
.cloned()
|
||||
.collect(),
|
||||
),
|
||||
_ => expr,
|
||||
|
@ -8,7 +8,8 @@ mod tests;
|
||||
use std::fmt;
|
||||
|
||||
use rustc_hash::FxHashSet;
|
||||
use tt::SmolStr;
|
||||
|
||||
use intern::Symbol;
|
||||
|
||||
pub use cfg_expr::{CfgAtom, CfgExpr};
|
||||
pub use dnf::DnfExpr;
|
||||
@ -48,11 +49,11 @@ impl CfgOptions {
|
||||
cfg.fold(&|atom| self.enabled.contains(atom))
|
||||
}
|
||||
|
||||
pub fn insert_atom(&mut self, key: SmolStr) {
|
||||
pub fn insert_atom(&mut self, key: Symbol) {
|
||||
self.enabled.insert(CfgAtom::Flag(key));
|
||||
}
|
||||
|
||||
pub fn insert_key_value(&mut self, key: SmolStr, value: SmolStr) {
|
||||
pub fn insert_key_value(&mut self, key: Symbol, value: Symbol) {
|
||||
self.enabled.insert(CfgAtom::KeyValue { key, value });
|
||||
}
|
||||
|
||||
@ -66,19 +67,16 @@ impl CfgOptions {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_cfg_keys(&self) -> impl Iterator<Item = &SmolStr> {
|
||||
pub fn get_cfg_keys(&self) -> impl Iterator<Item = &Symbol> {
|
||||
self.enabled.iter().map(|it| match it {
|
||||
CfgAtom::Flag(key) => key,
|
||||
CfgAtom::KeyValue { key, .. } => key,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_cfg_values<'a>(
|
||||
&'a self,
|
||||
cfg_key: &'a str,
|
||||
) -> impl Iterator<Item = &'a SmolStr> + 'a {
|
||||
pub fn get_cfg_values<'a>(&'a self, cfg_key: &'a str) -> impl Iterator<Item = &'a Symbol> + 'a {
|
||||
self.enabled.iter().filter_map(move |it| match it {
|
||||
CfgAtom::KeyValue { key, value } if cfg_key == key => Some(value),
|
||||
CfgAtom::KeyValue { key, value } if cfg_key == key.as_str() => Some(value),
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
use arbitrary::{Arbitrary, Unstructured};
|
||||
use expect_test::{expect, Expect};
|
||||
use intern::Symbol;
|
||||
use mbe::{syntax_node_to_token_tree, DocCommentDesugarMode, DummyTestSpanMap, DUMMY};
|
||||
use syntax::{ast, AstNode, Edition};
|
||||
|
||||
@ -28,7 +29,7 @@ fn check_dnf(input: &str, expect: Expect) {
|
||||
DocCommentDesugarMode::ProcMacro,
|
||||
);
|
||||
let cfg = CfgExpr::parse(&tt);
|
||||
let actual = format!("#![cfg({})]", DnfExpr::new(cfg));
|
||||
let actual = format!("#![cfg({})]", DnfExpr::new(&cfg));
|
||||
expect.assert_eq(&actual);
|
||||
}
|
||||
|
||||
@ -42,7 +43,7 @@ fn check_why_inactive(input: &str, opts: &CfgOptions, expect: Expect) {
|
||||
DocCommentDesugarMode::ProcMacro,
|
||||
);
|
||||
let cfg = CfgExpr::parse(&tt);
|
||||
let dnf = DnfExpr::new(cfg);
|
||||
let dnf = DnfExpr::new(&cfg);
|
||||
let why_inactive = dnf.why_inactive(opts).unwrap().to_string();
|
||||
expect.assert_eq(&why_inactive);
|
||||
}
|
||||
@ -58,40 +59,51 @@ fn check_enable_hints(input: &str, opts: &CfgOptions, expected_hints: &[&str]) {
|
||||
DocCommentDesugarMode::ProcMacro,
|
||||
);
|
||||
let cfg = CfgExpr::parse(&tt);
|
||||
let dnf = DnfExpr::new(cfg);
|
||||
let dnf = DnfExpr::new(&cfg);
|
||||
let hints = dnf.compute_enable_hints(opts).map(|diff| diff.to_string()).collect::<Vec<_>>();
|
||||
assert_eq!(hints, expected_hints);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cfg_expr_parser() {
|
||||
assert_parse_result("#![cfg(foo)]", CfgAtom::Flag("foo".into()).into());
|
||||
assert_parse_result("#![cfg(foo,)]", CfgAtom::Flag("foo".into()).into());
|
||||
assert_parse_result("#![cfg(foo)]", CfgAtom::Flag(Symbol::intern("foo")).into());
|
||||
assert_parse_result("#![cfg(foo,)]", CfgAtom::Flag(Symbol::intern("foo")).into());
|
||||
assert_parse_result(
|
||||
"#![cfg(not(foo))]",
|
||||
CfgExpr::Not(Box::new(CfgAtom::Flag("foo".into()).into())),
|
||||
CfgExpr::Not(Box::new(CfgAtom::Flag(Symbol::intern("foo")).into())),
|
||||
);
|
||||
assert_parse_result("#![cfg(foo(bar))]", CfgExpr::Invalid);
|
||||
|
||||
// Only take the first
|
||||
assert_parse_result(r#"#![cfg(foo, bar = "baz")]"#, CfgAtom::Flag("foo".into()).into());
|
||||
assert_parse_result(
|
||||
r#"#![cfg(foo, bar = "baz")]"#,
|
||||
CfgAtom::Flag(Symbol::intern("foo")).into(),
|
||||
);
|
||||
|
||||
assert_parse_result(
|
||||
r#"#![cfg(all(foo, bar = "baz"))]"#,
|
||||
CfgExpr::All(vec![
|
||||
CfgAtom::Flag("foo".into()).into(),
|
||||
CfgAtom::KeyValue { key: "bar".into(), value: "baz".into() }.into(),
|
||||
]),
|
||||
CfgExpr::All(
|
||||
vec![
|
||||
CfgAtom::Flag(Symbol::intern("foo")).into(),
|
||||
CfgAtom::KeyValue { key: Symbol::intern("bar"), value: Symbol::intern("baz") }
|
||||
.into(),
|
||||
]
|
||||
.into_boxed_slice(),
|
||||
),
|
||||
);
|
||||
|
||||
assert_parse_result(
|
||||
r#"#![cfg(any(not(), all(), , bar = "baz",))]"#,
|
||||
CfgExpr::Any(vec![
|
||||
CfgExpr::Not(Box::new(CfgExpr::Invalid)),
|
||||
CfgExpr::All(vec![]),
|
||||
CfgExpr::Invalid,
|
||||
CfgAtom::KeyValue { key: "bar".into(), value: "baz".into() }.into(),
|
||||
]),
|
||||
CfgExpr::Any(
|
||||
vec![
|
||||
CfgExpr::Not(Box::new(CfgExpr::Invalid)),
|
||||
CfgExpr::All(Box::new([])),
|
||||
CfgExpr::Invalid,
|
||||
CfgAtom::KeyValue { key: Symbol::intern("bar"), value: Symbol::intern("baz") }
|
||||
.into(),
|
||||
]
|
||||
.into_boxed_slice(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@ -167,7 +179,7 @@ fn hints() {
|
||||
|
||||
check_enable_hints("#![cfg(all(a, b))]", &opts, &["enable a and b"]);
|
||||
|
||||
opts.insert_atom("test".into());
|
||||
opts.insert_atom(Symbol::intern("test"));
|
||||
|
||||
check_enable_hints("#![cfg(test)]", &opts, &[]);
|
||||
check_enable_hints("#![cfg(not(test))]", &opts, &["disable test"]);
|
||||
@ -180,7 +192,7 @@ fn hints_impossible() {
|
||||
|
||||
check_enable_hints("#![cfg(all(test, not(test)))]", &opts, &[]);
|
||||
|
||||
opts.insert_atom("test".into());
|
||||
opts.insert_atom(Symbol::intern("test"));
|
||||
|
||||
check_enable_hints("#![cfg(all(test, not(test)))]", &opts, &[]);
|
||||
}
|
||||
@ -188,8 +200,8 @@ fn hints_impossible() {
|
||||
#[test]
|
||||
fn why_inactive() {
|
||||
let mut opts = CfgOptions::default();
|
||||
opts.insert_atom("test".into());
|
||||
opts.insert_atom("test2".into());
|
||||
opts.insert_atom(Symbol::intern("test"));
|
||||
opts.insert_atom(Symbol::intern("test2"));
|
||||
|
||||
check_why_inactive("#![cfg(a)]", &opts, expect![["a is disabled"]]);
|
||||
check_why_inactive("#![cfg(not(test))]", &opts, expect![["test is enabled"]]);
|
||||
@ -231,6 +243,6 @@ fn proptest() {
|
||||
|
||||
let mut u = Unstructured::new(&buf);
|
||||
let cfg = CfgExpr::arbitrary(&mut u).unwrap();
|
||||
DnfExpr::new(cfg);
|
||||
DnfExpr::new(&cfg);
|
||||
}
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ process-wrap.workspace = true
|
||||
paths.workspace = true
|
||||
stdx.workspace = true
|
||||
toolchain.workspace = true
|
||||
project-model.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
@ -20,10 +20,11 @@ pub use cargo_metadata::diagnostic::{
|
||||
use toolchain::Tool;
|
||||
|
||||
mod command;
|
||||
pub mod project_json;
|
||||
mod test_runner;
|
||||
|
||||
use command::{CommandHandle, ParseFromLine};
|
||||
pub use test_runner::{CargoTestHandle, CargoTestMessage, TestState};
|
||||
pub use test_runner::{CargoTestHandle, CargoTestMessage, TestState, TestTarget};
|
||||
|
||||
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
|
||||
pub enum InvocationStrategy {
|
||||
@ -240,7 +241,7 @@ enum FlycheckStatus {
|
||||
Finished,
|
||||
}
|
||||
|
||||
const SAVED_FILE_PLACEHOLDER: &str = "$saved_file";
|
||||
pub const SAVED_FILE_PLACEHOLDER: &str = "$saved_file";
|
||||
|
||||
impl FlycheckActor {
|
||||
fn new(
|
||||
|
152
src/tools/rust-analyzer/crates/flycheck/src/project_json.rs
Normal file
152
src/tools/rust-analyzer/crates/flycheck/src/project_json.rs
Normal file
@ -0,0 +1,152 @@
|
||||
//! A `cargo-metadata`-equivalent for non-Cargo build systems.
|
||||
use std::{io, process::Command};
|
||||
|
||||
use crossbeam_channel::Sender;
|
||||
use paths::{AbsPathBuf, Utf8Path, Utf8PathBuf};
|
||||
use project_model::ProjectJsonData;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::command::{CommandHandle, ParseFromLine};
|
||||
|
||||
pub const ARG_PLACEHOLDER: &str = "{arg}";
|
||||
|
||||
/// A command wrapper for getting a `rust-project.json`.
|
||||
///
|
||||
/// This is analogous to `cargo-metadata`, but for non-Cargo build systems.
|
||||
pub struct Discover {
|
||||
command: Vec<String>,
|
||||
sender: Sender<DiscoverProjectMessage>,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone, Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub enum DiscoverArgument {
|
||||
Path(#[serde(serialize_with = "serialize_abs_pathbuf")] AbsPathBuf),
|
||||
Buildfile(#[serde(serialize_with = "serialize_abs_pathbuf")] AbsPathBuf),
|
||||
}
|
||||
|
||||
fn serialize_abs_pathbuf<S>(path: &AbsPathBuf, se: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
let path: &Utf8Path = path.as_ref();
|
||||
se.serialize_str(path.as_str())
|
||||
}
|
||||
|
||||
impl Discover {
|
||||
/// Create a new [Discover].
|
||||
pub fn new(sender: Sender<DiscoverProjectMessage>, command: Vec<String>) -> Self {
|
||||
Self { sender, command }
|
||||
}
|
||||
|
||||
/// Spawn the command inside [Discover] and report progress, if any.
|
||||
pub fn spawn(&self, discover_arg: DiscoverArgument) -> io::Result<DiscoverHandle> {
|
||||
let command = &self.command[0];
|
||||
let args = &self.command[1..];
|
||||
|
||||
let args: Vec<String> = args
|
||||
.iter()
|
||||
.map(|arg| {
|
||||
if arg == ARG_PLACEHOLDER {
|
||||
serde_json::to_string(&discover_arg).expect("Unable to serialize args")
|
||||
} else {
|
||||
arg.to_owned()
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
let mut cmd = Command::new(command);
|
||||
cmd.args(args);
|
||||
|
||||
Ok(DiscoverHandle { _handle: CommandHandle::spawn(cmd, self.sender.clone())? })
|
||||
}
|
||||
}
|
||||
|
||||
/// A handle to a spawned [Discover].
|
||||
#[derive(Debug)]
|
||||
pub struct DiscoverHandle {
|
||||
_handle: CommandHandle<DiscoverProjectMessage>,
|
||||
}
|
||||
|
||||
/// An enum containing either progress messages, an error,
|
||||
/// or the materialized `rust-project`.
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
#[serde(tag = "kind")]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
enum DiscoverProjectData {
|
||||
Finished { buildfile: Utf8PathBuf, project: ProjectJsonData },
|
||||
Error { error: String, source: Option<String> },
|
||||
Progress { message: String },
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum DiscoverProjectMessage {
|
||||
Finished { project: ProjectJsonData, buildfile: AbsPathBuf },
|
||||
Error { error: String, source: Option<String> },
|
||||
Progress { message: String },
|
||||
}
|
||||
|
||||
impl DiscoverProjectMessage {
|
||||
fn new(data: DiscoverProjectData) -> Self {
|
||||
match data {
|
||||
DiscoverProjectData::Finished { project, buildfile, .. } => {
|
||||
let buildfile = buildfile.try_into().expect("Unable to make path absolute");
|
||||
DiscoverProjectMessage::Finished { project, buildfile }
|
||||
}
|
||||
DiscoverProjectData::Error { error, source } => {
|
||||
DiscoverProjectMessage::Error { error, source }
|
||||
}
|
||||
DiscoverProjectData::Progress { message } => {
|
||||
DiscoverProjectMessage::Progress { message }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ParseFromLine for DiscoverProjectMessage {
|
||||
fn from_line(line: &str, _error: &mut String) -> Option<Self> {
|
||||
// can the line even be deserialized as JSON?
|
||||
let Ok(data) = serde_json::from_str::<Value>(line) else {
|
||||
let err = DiscoverProjectData::Error { error: line.to_owned(), source: None };
|
||||
return Some(DiscoverProjectMessage::new(err));
|
||||
};
|
||||
|
||||
let Ok(data) = serde_json::from_value::<DiscoverProjectData>(data) else {
|
||||
return None;
|
||||
};
|
||||
|
||||
let msg = DiscoverProjectMessage::new(data);
|
||||
Some(msg)
|
||||
}
|
||||
|
||||
fn from_eof() -> Option<Self> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_deserialization() {
|
||||
let message = r#"
|
||||
{"kind": "progress", "message":"querying build system","input":{"files":["src/main.rs"]}}
|
||||
"#;
|
||||
let message: DiscoverProjectData =
|
||||
serde_json::from_str(message).expect("Unable to deserialize message");
|
||||
assert!(matches!(message, DiscoverProjectData::Progress { .. }));
|
||||
|
||||
let message = r#"
|
||||
{"kind": "error", "error":"failed to deserialize command output","source":"command"}
|
||||
"#;
|
||||
|
||||
let message: DiscoverProjectData =
|
||||
serde_json::from_str(message).expect("Unable to deserialize message");
|
||||
assert!(matches!(message, DiscoverProjectData::Error { .. }));
|
||||
|
||||
let message = r#"
|
||||
{"kind": "finished", "project": {"sysroot": "foo", "crates": [], "runnables": []}, "buildfile":"rust-analyzer/BUILD"}
|
||||
"#;
|
||||
|
||||
let message: DiscoverProjectData =
|
||||
serde_json::from_str(message).expect("Unable to deserialize message");
|
||||
assert!(matches!(message, DiscoverProjectData::Finished { .. }));
|
||||
}
|
@ -59,19 +59,38 @@ pub struct CargoTestHandle {
|
||||
}
|
||||
|
||||
// Example of a cargo test command:
|
||||
// cargo test --workspace --no-fail-fast -- module::func -Z unstable-options --format=json
|
||||
// cargo test --workspace --no-fail-fast -- -Z unstable-options --format=json
|
||||
// or
|
||||
// cargo test --package my-package --no-fail-fast -- module::func -Z unstable-options --format=json
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum TestTarget {
|
||||
Workspace,
|
||||
Package(String),
|
||||
}
|
||||
|
||||
impl CargoTestHandle {
|
||||
pub fn new(
|
||||
path: Option<&str>,
|
||||
options: CargoOptions,
|
||||
root: &AbsPath,
|
||||
test_target: TestTarget,
|
||||
sender: Sender<CargoTestMessage>,
|
||||
) -> std::io::Result<Self> {
|
||||
let mut cmd = Command::new(Tool::Cargo.path());
|
||||
cmd.env("RUSTC_BOOTSTRAP", "1");
|
||||
cmd.arg("test");
|
||||
cmd.arg("--workspace");
|
||||
|
||||
match &test_target {
|
||||
TestTarget::Package(package) => {
|
||||
cmd.arg("--package");
|
||||
cmd.arg(package);
|
||||
}
|
||||
TestTarget::Workspace => {
|
||||
cmd.arg("--workspace");
|
||||
}
|
||||
};
|
||||
|
||||
// --no-fail-fast is needed to ensure that all requested tests will run
|
||||
cmd.arg("--no-fail-fast");
|
||||
cmd.arg("--manifest-path");
|
||||
|
@ -1,6 +1,6 @@
|
||||
//! A higher level attributes based on TokenTree, with also some shortcuts.
|
||||
|
||||
use std::{borrow::Cow, hash::Hash, ops, slice::Iter as SliceIter};
|
||||
use std::{borrow::Cow, hash::Hash, ops, slice};
|
||||
|
||||
use base_db::CrateId;
|
||||
use cfg::{CfgExpr, CfgOptions};
|
||||
@ -9,17 +9,18 @@ use hir_expand::{
|
||||
attrs::{collect_attrs, Attr, AttrId, RawAttrs},
|
||||
HirFileId, InFile,
|
||||
};
|
||||
use intern::{sym, Symbol};
|
||||
use la_arena::{ArenaMap, Idx, RawIdx};
|
||||
use mbe::DelimiterKind;
|
||||
use syntax::{
|
||||
ast::{self, HasAttrs},
|
||||
AstPtr, SmolStr,
|
||||
AstPtr,
|
||||
};
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{
|
||||
db::DefDatabase,
|
||||
item_tree::{AttrOwner, Fields, ItemTreeNode},
|
||||
item_tree::{AttrOwner, FieldParent, ItemTreeNode},
|
||||
lang_item::LangItem,
|
||||
nameres::{ModuleOrigin, ModuleSource},
|
||||
src::{HasChildSource, HasSource},
|
||||
@ -75,40 +76,36 @@ impl Attrs {
|
||||
let mut res = ArenaMap::default();
|
||||
|
||||
let crate_graph = db.crate_graph();
|
||||
let (fields, item_tree, krate) = match v {
|
||||
let item_tree;
|
||||
let (parent, fields, krate) = match v {
|
||||
VariantId::EnumVariantId(it) => {
|
||||
let loc = it.lookup(db);
|
||||
let krate = loc.parent.lookup(db).container.krate;
|
||||
let item_tree = loc.id.item_tree(db);
|
||||
item_tree = loc.id.item_tree(db);
|
||||
let variant = &item_tree[loc.id.value];
|
||||
(variant.fields.clone(), item_tree, krate)
|
||||
(FieldParent::Variant(loc.id.value), &variant.fields, krate)
|
||||
}
|
||||
VariantId::StructId(it) => {
|
||||
let loc = it.lookup(db);
|
||||
let krate = loc.container.krate;
|
||||
let item_tree = loc.id.item_tree(db);
|
||||
item_tree = loc.id.item_tree(db);
|
||||
let struct_ = &item_tree[loc.id.value];
|
||||
(struct_.fields.clone(), item_tree, krate)
|
||||
(FieldParent::Struct(loc.id.value), &struct_.fields, krate)
|
||||
}
|
||||
VariantId::UnionId(it) => {
|
||||
let loc = it.lookup(db);
|
||||
let krate = loc.container.krate;
|
||||
let item_tree = loc.id.item_tree(db);
|
||||
item_tree = loc.id.item_tree(db);
|
||||
let union_ = &item_tree[loc.id.value];
|
||||
(union_.fields.clone(), item_tree, krate)
|
||||
(FieldParent::Union(loc.id.value), &union_.fields, krate)
|
||||
}
|
||||
};
|
||||
|
||||
let fields = match fields {
|
||||
Fields::Record(fields) | Fields::Tuple(fields) => fields,
|
||||
Fields::Unit => return Arc::new(res),
|
||||
};
|
||||
|
||||
let cfg_options = &crate_graph[krate].cfg_options;
|
||||
|
||||
let mut idx = 0;
|
||||
for field in fields {
|
||||
let attrs = item_tree.attrs(db, krate, field.into());
|
||||
for (id, _field) in fields.iter().enumerate() {
|
||||
let attrs = item_tree.attrs(db, krate, AttrOwner::make_field_indexed(parent, id));
|
||||
if attrs.is_cfg_enabled(cfg_options) {
|
||||
res.insert(Idx::from_raw(RawIdx::from(idx)), attrs);
|
||||
idx += 1;
|
||||
@ -120,12 +117,12 @@ impl Attrs {
|
||||
}
|
||||
|
||||
impl Attrs {
|
||||
pub fn by_key(&self, key: &'static str) -> AttrQuery<'_> {
|
||||
pub fn by_key<'attrs>(&'attrs self, key: &'attrs Symbol) -> AttrQuery<'_> {
|
||||
AttrQuery { attrs: self, key }
|
||||
}
|
||||
|
||||
pub fn cfg(&self) -> Option<CfgExpr> {
|
||||
let mut cfgs = self.by_key("cfg").tt_values().map(CfgExpr::parse);
|
||||
let mut cfgs = self.by_key(&sym::cfg).tt_values().map(CfgExpr::parse);
|
||||
let first = cfgs.next()?;
|
||||
match cfgs.next() {
|
||||
Some(second) => {
|
||||
@ -137,7 +134,7 @@ impl Attrs {
|
||||
}
|
||||
|
||||
pub fn cfgs(&self) -> impl Iterator<Item = CfgExpr> + '_ {
|
||||
self.by_key("cfg").tt_values().map(CfgExpr::parse)
|
||||
self.by_key(&sym::cfg).tt_values().map(CfgExpr::parse)
|
||||
}
|
||||
|
||||
pub(crate) fn is_cfg_enabled(&self, cfg_options: &CfgOptions) -> bool {
|
||||
@ -147,50 +144,50 @@ impl Attrs {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lang(&self) -> Option<&str> {
|
||||
self.by_key("lang").string_value()
|
||||
pub fn lang(&self) -> Option<&Symbol> {
|
||||
self.by_key(&sym::lang).string_value()
|
||||
}
|
||||
|
||||
pub fn lang_item(&self) -> Option<LangItem> {
|
||||
self.by_key("lang").string_value().and_then(LangItem::from_str)
|
||||
self.by_key(&sym::lang).string_value().and_then(LangItem::from_symbol)
|
||||
}
|
||||
|
||||
pub fn has_doc_hidden(&self) -> bool {
|
||||
self.by_key("doc").tt_values().any(|tt| {
|
||||
self.by_key(&sym::doc).tt_values().any(|tt| {
|
||||
tt.delimiter.kind == DelimiterKind::Parenthesis &&
|
||||
matches!(&*tt.token_trees, [tt::TokenTree::Leaf(tt::Leaf::Ident(ident))] if ident.text == "hidden")
|
||||
matches!(&*tt.token_trees, [tt::TokenTree::Leaf(tt::Leaf::Ident(ident))] if ident.sym == sym::hidden)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn has_doc_notable_trait(&self) -> bool {
|
||||
self.by_key("doc").tt_values().any(|tt| {
|
||||
self.by_key(&sym::doc).tt_values().any(|tt| {
|
||||
tt.delimiter.kind == DelimiterKind::Parenthesis &&
|
||||
matches!(&*tt.token_trees, [tt::TokenTree::Leaf(tt::Leaf::Ident(ident))] if ident.text == "notable_trait")
|
||||
matches!(&*tt.token_trees, [tt::TokenTree::Leaf(tt::Leaf::Ident(ident))] if ident.sym == sym::notable_trait)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn doc_exprs(&self) -> impl Iterator<Item = DocExpr> + '_ {
|
||||
self.by_key("doc").tt_values().map(DocExpr::parse)
|
||||
self.by_key(&sym::doc).tt_values().map(DocExpr::parse)
|
||||
}
|
||||
|
||||
pub fn doc_aliases(&self) -> impl Iterator<Item = SmolStr> + '_ {
|
||||
pub fn doc_aliases(&self) -> impl Iterator<Item = Symbol> + '_ {
|
||||
self.doc_exprs().flat_map(|doc_expr| doc_expr.aliases().to_vec())
|
||||
}
|
||||
|
||||
pub fn export_name(&self) -> Option<&str> {
|
||||
self.by_key("export_name").string_value()
|
||||
pub fn export_name(&self) -> Option<&Symbol> {
|
||||
self.by_key(&sym::export_name).string_value()
|
||||
}
|
||||
|
||||
pub fn is_proc_macro(&self) -> bool {
|
||||
self.by_key("proc_macro").exists()
|
||||
self.by_key(&sym::proc_macro).exists()
|
||||
}
|
||||
|
||||
pub fn is_proc_macro_attribute(&self) -> bool {
|
||||
self.by_key("proc_macro_attribute").exists()
|
||||
self.by_key(&sym::proc_macro_attribute).exists()
|
||||
}
|
||||
|
||||
pub fn is_proc_macro_derive(&self) -> bool {
|
||||
self.by_key("proc_macro_derive").exists()
|
||||
self.by_key(&sym::proc_macro_derive).exists()
|
||||
}
|
||||
|
||||
pub fn is_test(&self) -> bool {
|
||||
@ -199,33 +196,37 @@ impl Attrs {
|
||||
.segments()
|
||||
.iter()
|
||||
.rev()
|
||||
.zip(["core", "prelude", "v1", "test"].iter().rev())
|
||||
.all(|it| it.0.as_str() == Some(it.1))
|
||||
.zip(
|
||||
[sym::core.clone(), sym::prelude.clone(), sym::v1.clone(), sym::test.clone()]
|
||||
.iter()
|
||||
.rev(),
|
||||
)
|
||||
.all(|it| it.0 == it.1)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn is_ignore(&self) -> bool {
|
||||
self.by_key("ignore").exists()
|
||||
self.by_key(&sym::ignore).exists()
|
||||
}
|
||||
|
||||
pub fn is_bench(&self) -> bool {
|
||||
self.by_key("bench").exists()
|
||||
self.by_key(&sym::bench).exists()
|
||||
}
|
||||
|
||||
pub fn is_unstable(&self) -> bool {
|
||||
self.by_key("unstable").exists()
|
||||
self.by_key(&sym::unstable).exists()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum DocAtom {
|
||||
/// eg. `#[doc(hidden)]`
|
||||
Flag(SmolStr),
|
||||
Flag(Symbol),
|
||||
/// eg. `#[doc(alias = "it")]`
|
||||
///
|
||||
/// Note that a key can have multiple values that are all considered "active" at the same time.
|
||||
/// For example, `#[doc(alias = "x")]` and `#[doc(alias = "y")]`.
|
||||
KeyValue { key: SmolStr, value: SmolStr },
|
||||
KeyValue { key: Symbol, value: Symbol },
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
@ -234,7 +235,7 @@ pub enum DocExpr {
|
||||
/// eg. `#[doc(hidden)]`, `#[doc(alias = "x")]`
|
||||
Atom(DocAtom),
|
||||
/// eg. `#[doc(alias("x", "y"))]`
|
||||
Alias(Vec<SmolStr>),
|
||||
Alias(Vec<Symbol>),
|
||||
}
|
||||
|
||||
impl From<DocAtom> for DocExpr {
|
||||
@ -248,9 +249,9 @@ impl DocExpr {
|
||||
next_doc_expr(&mut tt.token_trees.iter()).unwrap_or(DocExpr::Invalid)
|
||||
}
|
||||
|
||||
pub fn aliases(&self) -> &[SmolStr] {
|
||||
pub fn aliases(&self) -> &[Symbol] {
|
||||
match self {
|
||||
DocExpr::Atom(DocAtom::KeyValue { key, value }) if key == "alias" => {
|
||||
DocExpr::Atom(DocAtom::KeyValue { key, value }) if *key == sym::alias => {
|
||||
std::slice::from_ref(value)
|
||||
}
|
||||
DocExpr::Alias(aliases) => aliases,
|
||||
@ -259,10 +260,10 @@ impl DocExpr {
|
||||
}
|
||||
}
|
||||
|
||||
fn next_doc_expr<S>(it: &mut SliceIter<'_, tt::TokenTree<S>>) -> Option<DocExpr> {
|
||||
fn next_doc_expr<S>(it: &mut slice::Iter<'_, tt::TokenTree<S>>) -> Option<DocExpr> {
|
||||
let name = match it.next() {
|
||||
None => return None,
|
||||
Some(tt::TokenTree::Leaf(tt::Leaf::Ident(ident))) => ident.text.clone(),
|
||||
Some(tt::TokenTree::Leaf(tt::Leaf::Ident(ident))) => ident.sym.clone(),
|
||||
Some(_) => return Some(DocExpr::Invalid),
|
||||
};
|
||||
|
||||
@ -270,13 +271,14 @@ fn next_doc_expr<S>(it: &mut SliceIter<'_, tt::TokenTree<S>>) -> Option<DocExpr>
|
||||
let ret = match it.as_slice().first() {
|
||||
Some(tt::TokenTree::Leaf(tt::Leaf::Punct(punct))) if punct.char == '=' => {
|
||||
match it.as_slice().get(1) {
|
||||
Some(tt::TokenTree::Leaf(tt::Leaf::Literal(literal))) => {
|
||||
Some(tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal {
|
||||
symbol: text,
|
||||
kind: tt::LitKind::Str,
|
||||
..
|
||||
}))) => {
|
||||
it.next();
|
||||
it.next();
|
||||
// FIXME: escape? raw string?
|
||||
let value =
|
||||
SmolStr::new(literal.text.trim_start_matches('"').trim_end_matches('"'));
|
||||
DocAtom::KeyValue { key: name, value }.into()
|
||||
DocAtom::KeyValue { key: name, value: text.clone() }.into()
|
||||
}
|
||||
_ => return Some(DocExpr::Invalid),
|
||||
}
|
||||
@ -284,8 +286,8 @@ fn next_doc_expr<S>(it: &mut SliceIter<'_, tt::TokenTree<S>>) -> Option<DocExpr>
|
||||
Some(tt::TokenTree::Subtree(subtree)) => {
|
||||
it.next();
|
||||
let subs = parse_comma_sep(subtree);
|
||||
match name.as_str() {
|
||||
"alias" => DocExpr::Alias(subs),
|
||||
match &name {
|
||||
s if *s == sym::alias => DocExpr::Alias(subs),
|
||||
_ => DocExpr::Invalid,
|
||||
}
|
||||
}
|
||||
@ -301,15 +303,16 @@ fn next_doc_expr<S>(it: &mut SliceIter<'_, tt::TokenTree<S>>) -> Option<DocExpr>
|
||||
Some(ret)
|
||||
}
|
||||
|
||||
fn parse_comma_sep<S>(subtree: &tt::Subtree<S>) -> Vec<SmolStr> {
|
||||
fn parse_comma_sep<S>(subtree: &tt::Subtree<S>) -> Vec<Symbol> {
|
||||
subtree
|
||||
.token_trees
|
||||
.iter()
|
||||
.filter_map(|tt| match tt {
|
||||
tt::TokenTree::Leaf(tt::Leaf::Literal(lit)) => {
|
||||
// FIXME: escape? raw string?
|
||||
Some(SmolStr::new(lit.text.trim_start_matches('"').trim_end_matches('"')))
|
||||
}
|
||||
tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal {
|
||||
kind: tt::LitKind::Str,
|
||||
symbol,
|
||||
..
|
||||
})) => Some(symbol.clone()),
|
||||
_ => None,
|
||||
})
|
||||
.collect()
|
||||
@ -556,7 +559,7 @@ impl AttrSourceMap {
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct AttrQuery<'attr> {
|
||||
attrs: &'attr Attrs,
|
||||
key: &'static str,
|
||||
key: &'attr Symbol,
|
||||
}
|
||||
|
||||
impl<'attr> AttrQuery<'attr> {
|
||||
@ -564,10 +567,14 @@ impl<'attr> AttrQuery<'attr> {
|
||||
self.attrs().filter_map(|attr| attr.token_tree_value())
|
||||
}
|
||||
|
||||
pub fn string_value(self) -> Option<&'attr str> {
|
||||
pub fn string_value(self) -> Option<&'attr Symbol> {
|
||||
self.attrs().find_map(|attr| attr.string_value())
|
||||
}
|
||||
|
||||
pub fn string_value_with_span(self) -> Option<(&'attr Symbol, span::Span)> {
|
||||
self.attrs().find_map(|attr| attr.string_value_with_span())
|
||||
}
|
||||
|
||||
pub fn string_value_unescape(self) -> Option<Cow<'attr, str>> {
|
||||
self.attrs().find_map(|attr| attr.string_value_unescape())
|
||||
}
|
||||
@ -578,9 +585,7 @@ impl<'attr> AttrQuery<'attr> {
|
||||
|
||||
pub fn attrs(self) -> impl Iterator<Item = &'attr Attr> + Clone {
|
||||
let key = self.key;
|
||||
self.attrs
|
||||
.iter()
|
||||
.filter(move |attr| attr.path.as_ident().map_or(false, |s| s.to_smol_str() == key))
|
||||
self.attrs.iter().filter(move |attr| attr.path.as_ident().map_or(false, |s| *s == *key))
|
||||
}
|
||||
|
||||
/// Find string value for a specific key inside token tree
|
||||
@ -589,14 +594,14 @@ impl<'attr> AttrQuery<'attr> {
|
||||
/// #[doc(html_root_url = "url")]
|
||||
/// ^^^^^^^^^^^^^ key
|
||||
/// ```
|
||||
pub fn find_string_value_in_tt(self, key: &'attr str) -> Option<&SmolStr> {
|
||||
pub fn find_string_value_in_tt(self, key: &'attr Symbol) -> Option<&str> {
|
||||
self.tt_values().find_map(|tt| {
|
||||
let name = tt.token_trees.iter()
|
||||
.skip_while(|tt| !matches!(tt, tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { text, ..} )) if text == key))
|
||||
.skip_while(|tt| !matches!(tt, tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { sym, ..} )) if *sym == *key))
|
||||
.nth(2);
|
||||
|
||||
match name {
|
||||
Some(tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal{ ref text, ..}))) => Some(text),
|
||||
Some(tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal{ symbol: text, kind: tt::LitKind::Str | tt::LitKind::StrRaw(_) , ..}))) => Some(text.as_str()),
|
||||
_ => None
|
||||
}
|
||||
})
|
||||
@ -647,11 +652,13 @@ mod tests {
|
||||
//! This module contains tests for doc-expression parsing.
|
||||
//! Currently, it tests `#[doc(hidden)]` and `#[doc(alias)]`.
|
||||
|
||||
use intern::Symbol;
|
||||
use span::EditionedFileId;
|
||||
use triomphe::Arc;
|
||||
|
||||
use base_db::FileId;
|
||||
use hir_expand::span_map::{RealSpanMap, SpanMap};
|
||||
use mbe::{syntax_node_to_token_tree, DocCommentDesugarMode};
|
||||
use span::FileId;
|
||||
use syntax::{ast, AstNode, TextRange};
|
||||
|
||||
use crate::attr::{DocAtom, DocExpr};
|
||||
@ -659,7 +666,9 @@ mod tests {
|
||||
fn assert_parse_result(input: &str, expected: DocExpr) {
|
||||
let source_file = ast::SourceFile::parse(input, span::Edition::CURRENT).ok().unwrap();
|
||||
let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
|
||||
let map = SpanMap::RealSpanMap(Arc::new(RealSpanMap::absolute(FileId::from_raw(0))));
|
||||
let map = SpanMap::RealSpanMap(Arc::new(RealSpanMap::absolute(
|
||||
EditionedFileId::current_edition(FileId::from_raw(0)),
|
||||
)));
|
||||
let tt = syntax_node_to_token_tree(
|
||||
tt.syntax(),
|
||||
map.as_ref(),
|
||||
@ -672,24 +681,29 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_doc_expr_parser() {
|
||||
assert_parse_result("#![doc(hidden)]", DocAtom::Flag("hidden".into()).into());
|
||||
assert_parse_result("#![doc(hidden)]", DocAtom::Flag(Symbol::intern("hidden")).into());
|
||||
|
||||
assert_parse_result(
|
||||
r#"#![doc(alias = "foo")]"#,
|
||||
DocAtom::KeyValue { key: "alias".into(), value: "foo".into() }.into(),
|
||||
DocAtom::KeyValue { key: Symbol::intern("alias"), value: Symbol::intern("foo") }.into(),
|
||||
);
|
||||
|
||||
assert_parse_result(r#"#![doc(alias("foo"))]"#, DocExpr::Alias(["foo".into()].into()));
|
||||
assert_parse_result(
|
||||
r#"#![doc(alias("foo"))]"#,
|
||||
DocExpr::Alias([Symbol::intern("foo")].into()),
|
||||
);
|
||||
assert_parse_result(
|
||||
r#"#![doc(alias("foo", "bar", "baz"))]"#,
|
||||
DocExpr::Alias(["foo".into(), "bar".into(), "baz".into()].into()),
|
||||
DocExpr::Alias(
|
||||
[Symbol::intern("foo"), Symbol::intern("bar"), Symbol::intern("baz")].into(),
|
||||
),
|
||||
);
|
||||
|
||||
assert_parse_result(
|
||||
r#"
|
||||
#[doc(alias("Bar", "Qux"))]
|
||||
struct Foo;"#,
|
||||
DocExpr::Alias(["Bar".into(), "Qux".into()].into()),
|
||||
DocExpr::Alias([Symbol::intern("Bar"), Symbol::intern("Qux")].into()),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -6,13 +6,14 @@ pub mod scope;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
use std::ops::Index;
|
||||
use std::ops::{Deref, Index};
|
||||
|
||||
use base_db::CrateId;
|
||||
use cfg::{CfgExpr, CfgOptions};
|
||||
use hir_expand::{name::Name, InFile};
|
||||
use la_arena::{Arena, ArenaMap};
|
||||
use hir_expand::{name::Name, ExpandError, InFile};
|
||||
use la_arena::{Arena, ArenaMap, Idx, RawIdx};
|
||||
use rustc_hash::FxHashMap;
|
||||
use smallvec::SmallVec;
|
||||
use span::MacroFileId;
|
||||
use syntax::{ast, AstPtr, SyntaxNodePtr};
|
||||
use triomphe::Arc;
|
||||
@ -23,6 +24,7 @@ use crate::{
|
||||
hir::{
|
||||
dummy_expr_id, Binding, BindingId, Expr, ExprId, Label, LabelId, Pat, PatId, RecordFieldPat,
|
||||
},
|
||||
item_tree::AttrOwner,
|
||||
nameres::DefMap,
|
||||
path::{ModPath, Path},
|
||||
src::HasSource,
|
||||
@ -91,6 +93,7 @@ pub struct BodySourceMap {
|
||||
label_map_back: ArenaMap<LabelId, LabelSource>,
|
||||
|
||||
self_param: Option<InFile<AstPtr<ast::SelfParam>>>,
|
||||
binding_definitions: FxHashMap<BindingId, SmallVec<[PatId; 4]>>,
|
||||
|
||||
/// We don't create explicit nodes for record fields (`S { record_field: 92 }`).
|
||||
/// Instead, we use id of expression (`92`) to identify the field.
|
||||
@ -112,8 +115,7 @@ pub struct SyntheticSyntax;
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
pub enum BodyDiagnostic {
|
||||
InactiveCode { node: InFile<SyntaxNodePtr>, cfg: CfgExpr, opts: CfgOptions },
|
||||
MacroError { node: InFile<AstPtr<ast::MacroCall>>, message: String },
|
||||
UnresolvedProcMacro { node: InFile<AstPtr<ast::MacroCall>>, krate: CrateId },
|
||||
MacroError { node: InFile<AstPtr<ast::MacroCall>>, err: ExpandError },
|
||||
UnresolvedMacroCall { node: InFile<AstPtr<ast::MacroCall>>, path: ModPath },
|
||||
UnreachableLabel { node: InFile<AstPtr<ast::Lifetime>>, name: Name },
|
||||
UndeclaredLabel { node: InFile<AstPtr<ast::Lifetime>>, name: Name },
|
||||
@ -134,16 +136,23 @@ impl Body {
|
||||
let data = db.function_data(f);
|
||||
let f = f.lookup(db);
|
||||
let src = f.source(db);
|
||||
params = src.value.param_list().map(|param_list| {
|
||||
params = src.value.param_list().map(move |param_list| {
|
||||
let item_tree = f.id.item_tree(db);
|
||||
let func = &item_tree[f.id.value];
|
||||
let krate = f.container.module(db).krate;
|
||||
let crate_graph = db.crate_graph();
|
||||
(
|
||||
param_list,
|
||||
func.params.clone().map(move |param| {
|
||||
(0..func.params.len()).map(move |idx| {
|
||||
item_tree
|
||||
.attrs(db, krate, param.into())
|
||||
.attrs(
|
||||
db,
|
||||
krate,
|
||||
AttrOwner::Param(
|
||||
f.id.value,
|
||||
Idx::from_raw(RawIdx::from(idx as u32)),
|
||||
),
|
||||
)
|
||||
.is_cfg_enabled(&crate_graph[krate].cfg_options)
|
||||
}),
|
||||
)
|
||||
@ -377,6 +386,10 @@ impl BodySourceMap {
|
||||
self.label_map_back[label]
|
||||
}
|
||||
|
||||
pub fn patterns_for_binding(&self, binding: BindingId) -> &[PatId] {
|
||||
self.binding_definitions.get(&binding).map_or(&[], Deref::deref)
|
||||
}
|
||||
|
||||
pub fn node_label(&self, node: InFile<&ast::Label>) -> Option<LabelId> {
|
||||
let src = node.map(AstPtr::new);
|
||||
self.label_map.get(&src).cloned()
|
||||
@ -428,6 +441,7 @@ impl BodySourceMap {
|
||||
expansions,
|
||||
format_args_template_map,
|
||||
diagnostics,
|
||||
binding_definitions,
|
||||
} = self;
|
||||
format_args_template_map.shrink_to_fit();
|
||||
expr_map.shrink_to_fit();
|
||||
@ -440,5 +454,6 @@ impl BodySourceMap {
|
||||
pat_field_map_back.shrink_to_fit();
|
||||
expansions.shrink_to_fit();
|
||||
diagnostics.shrink_to_fit();
|
||||
binding_definitions.shrink_to_fit();
|
||||
}
|
||||
}
|
||||
|
@ -4,13 +4,13 @@
|
||||
use std::mem;
|
||||
|
||||
use base_db::CrateId;
|
||||
use either::Either;
|
||||
use hir_expand::{
|
||||
name::{name, AsName, Name},
|
||||
ExpandError, InFile,
|
||||
name::{AsName, Name},
|
||||
InFile,
|
||||
};
|
||||
use intern::Interned;
|
||||
use intern::{sym, Interned, Symbol};
|
||||
use rustc_hash::FxHashMap;
|
||||
use smallvec::SmallVec;
|
||||
use span::AstIdMap;
|
||||
use stdx::never;
|
||||
use syntax::{
|
||||
@ -187,8 +187,10 @@ impl ExprCollector<'_> {
|
||||
{
|
||||
let is_mutable =
|
||||
self_param.mut_token().is_some() && self_param.amp_token().is_none();
|
||||
let binding_id: la_arena::Idx<Binding> =
|
||||
self.alloc_binding(name![self], BindingAnnotation::new(is_mutable, false));
|
||||
let binding_id: la_arena::Idx<Binding> = self.alloc_binding(
|
||||
Name::new_symbol_root(sym::self_.clone()),
|
||||
BindingAnnotation::new(is_mutable, false),
|
||||
);
|
||||
self.body.self_param = Some(binding_id);
|
||||
self.source_map.self_param = Some(self.expander.in_file(AstPtr::new(&self_param)));
|
||||
}
|
||||
@ -299,7 +301,10 @@ impl ExprCollector<'_> {
|
||||
result_expr_id
|
||||
})
|
||||
}
|
||||
None => self.collect_block(e),
|
||||
// FIXME
|
||||
Some(ast::BlockModifier::AsyncGen(_)) | Some(ast::BlockModifier::Gen(_)) | None => {
|
||||
self.collect_block(e)
|
||||
}
|
||||
},
|
||||
ast::Expr::LoopExpr(e) => {
|
||||
let label = e.label().map(|label| self.collect_label(label));
|
||||
@ -987,20 +992,11 @@ impl ExprCollector<'_> {
|
||||
}
|
||||
};
|
||||
if record_diagnostics {
|
||||
match &res.err {
|
||||
Some(ExpandError::UnresolvedProcMacro(krate)) => {
|
||||
self.source_map.diagnostics.push(BodyDiagnostic::UnresolvedProcMacro {
|
||||
node: InFile::new(outer_file, syntax_ptr),
|
||||
krate: *krate,
|
||||
});
|
||||
}
|
||||
Some(err) => {
|
||||
self.source_map.diagnostics.push(BodyDiagnostic::MacroError {
|
||||
node: InFile::new(outer_file, syntax_ptr),
|
||||
message: err.to_string(),
|
||||
});
|
||||
}
|
||||
None => {}
|
||||
if let Some(err) = res.err {
|
||||
self.source_map.diagnostics.push(BodyDiagnostic::MacroError {
|
||||
node: InFile::new(outer_file, syntax_ptr),
|
||||
err,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -1431,15 +1427,14 @@ impl ExprCollector<'_> {
|
||||
args: AstChildren<ast::Pat>,
|
||||
has_leading_comma: bool,
|
||||
binding_list: &mut BindingList,
|
||||
) -> (Box<[PatId]>, Option<usize>) {
|
||||
) -> (Box<[PatId]>, Option<u32>) {
|
||||
let args: Vec<_> = args.map(|p| self.collect_pat_possibly_rest(p, binding_list)).collect();
|
||||
// Find the location of the `..`, if there is one. Note that we do not
|
||||
// consider the possibility of there being multiple `..` here.
|
||||
let ellipsis = args.clone().position(|p| matches!(p, ast::Pat::RestPat(_)));
|
||||
let ellipsis = args.iter().position(|p| p.is_right()).map(|it| it as u32);
|
||||
|
||||
// We want to skip the `..` pattern here, since we account for it above.
|
||||
let mut args: Vec<_> = args
|
||||
.filter(|p| !matches!(p, ast::Pat::RestPat(_)))
|
||||
.map(|p| self.collect_pat(p, binding_list))
|
||||
.collect();
|
||||
let mut args: Vec<_> = args.into_iter().filter_map(Either::left).collect();
|
||||
// if there is a leading comma, the user is most likely to type out a leading pattern
|
||||
// so we insert a missing pattern at the beginning for IDE features
|
||||
if has_leading_comma {
|
||||
@ -1449,6 +1444,41 @@ impl ExprCollector<'_> {
|
||||
(args.into_boxed_slice(), ellipsis)
|
||||
}
|
||||
|
||||
// `collect_pat` rejects `ast::Pat::RestPat`, but it should be handled in some cases that
|
||||
// it is the macro expansion result of an arg sub-pattern in a slice or tuple pattern.
|
||||
fn collect_pat_possibly_rest(
|
||||
&mut self,
|
||||
pat: ast::Pat,
|
||||
binding_list: &mut BindingList,
|
||||
) -> Either<PatId, ()> {
|
||||
match &pat {
|
||||
ast::Pat::RestPat(_) => Either::Right(()),
|
||||
ast::Pat::MacroPat(mac) => match mac.macro_call() {
|
||||
Some(call) => {
|
||||
let macro_ptr = AstPtr::new(&call);
|
||||
let src = self.expander.in_file(AstPtr::new(&pat));
|
||||
let pat =
|
||||
self.collect_macro_call(call, macro_ptr, true, |this, expanded_pat| {
|
||||
if let Some(expanded_pat) = expanded_pat {
|
||||
this.collect_pat_possibly_rest(expanded_pat, binding_list)
|
||||
} else {
|
||||
Either::Left(this.missing_pat())
|
||||
}
|
||||
});
|
||||
if let Some(pat) = pat.left() {
|
||||
self.source_map.pat_map.insert(src, pat);
|
||||
}
|
||||
pat
|
||||
}
|
||||
None => {
|
||||
let ptr = AstPtr::new(&pat);
|
||||
Either::Left(self.alloc_pat(Pat::Missing, ptr))
|
||||
}
|
||||
},
|
||||
_ => Either::Left(self.collect_pat(pat, binding_list)),
|
||||
}
|
||||
}
|
||||
|
||||
// endregion: patterns
|
||||
|
||||
/// Returns `None` (and emits diagnostics) when `owner` if `#[cfg]`d out, and `Some(())` when
|
||||
@ -1473,7 +1503,7 @@ impl ExprCollector<'_> {
|
||||
}
|
||||
|
||||
fn add_definition_to_binding(&mut self, binding_id: BindingId, pat_id: PatId) {
|
||||
self.body.bindings[binding_id].definitions.push(pat_id);
|
||||
self.source_map.binding_definitions.entry(binding_id).or_default().push(pat_id);
|
||||
}
|
||||
|
||||
// region: labels
|
||||
@ -1588,18 +1618,22 @@ impl ExprCollector<'_> {
|
||||
});
|
||||
let mut mappings = vec![];
|
||||
let fmt = match template.and_then(|it| self.expand_macros_to_string(it)) {
|
||||
Some((s, is_direct_literal)) => format_args::parse(
|
||||
&s,
|
||||
fmt_snippet,
|
||||
args,
|
||||
is_direct_literal,
|
||||
|name| self.alloc_expr_desugared(Expr::Path(Path::from(name))),
|
||||
|name, span| {
|
||||
if let Some(span) = span {
|
||||
mappings.push((span, name))
|
||||
}
|
||||
},
|
||||
),
|
||||
Some((s, is_direct_literal)) => {
|
||||
let call_ctx = self.expander.syntax_context();
|
||||
format_args::parse(
|
||||
&s,
|
||||
fmt_snippet,
|
||||
args,
|
||||
is_direct_literal,
|
||||
|name| self.alloc_expr_desugared(Expr::Path(Path::from(name))),
|
||||
|name, span| {
|
||||
if let Some(span) = span {
|
||||
mappings.push((span, name))
|
||||
}
|
||||
},
|
||||
call_ctx,
|
||||
)
|
||||
}
|
||||
None => FormatArgs {
|
||||
template: Default::default(),
|
||||
arguments: args.finish(),
|
||||
@ -1617,30 +1651,29 @@ impl ExprCollector<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
let lit_pieces =
|
||||
fmt.template
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter_map(|(i, piece)| {
|
||||
match piece {
|
||||
FormatArgsPiece::Literal(s) => Some(
|
||||
self.alloc_expr_desugared(Expr::Literal(Literal::String(s.clone()))),
|
||||
),
|
||||
&FormatArgsPiece::Placeholder(_) => {
|
||||
// Inject empty string before placeholders when not already preceded by a literal piece.
|
||||
if i == 0
|
||||
|| matches!(fmt.template[i - 1], FormatArgsPiece::Placeholder(_))
|
||||
{
|
||||
Some(self.alloc_expr_desugared(Expr::Literal(Literal::String(
|
||||
"".into(),
|
||||
))))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
let lit_pieces = fmt
|
||||
.template
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter_map(|(i, piece)| {
|
||||
match piece {
|
||||
FormatArgsPiece::Literal(s) => {
|
||||
Some(self.alloc_expr_desugared(Expr::Literal(Literal::String(s.clone()))))
|
||||
}
|
||||
&FormatArgsPiece::Placeholder(_) => {
|
||||
// Inject empty string before placeholders when not already preceded by a literal piece.
|
||||
if i == 0 || matches!(fmt.template[i - 1], FormatArgsPiece::Placeholder(_))
|
||||
{
|
||||
Some(self.alloc_expr_desugared(Expr::Literal(Literal::String(
|
||||
Symbol::empty(),
|
||||
))))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
let lit_pieces = self.alloc_expr_desugared(Expr::Array(Array::ElementList {
|
||||
elements: lit_pieces,
|
||||
is_assignee_expr: false,
|
||||
@ -1723,14 +1756,18 @@ impl ExprCollector<'_> {
|
||||
// unsafe { ::core::fmt::UnsafeArg::new() }
|
||||
// )
|
||||
|
||||
let Some(new_v1_formatted) =
|
||||
LangItem::FormatArguments.ty_rel_path(self.db, self.krate, name![new_v1_formatted])
|
||||
else {
|
||||
let Some(new_v1_formatted) = LangItem::FormatArguments.ty_rel_path(
|
||||
self.db,
|
||||
self.krate,
|
||||
Name::new_symbol_root(sym::new_v1_formatted.clone()),
|
||||
) else {
|
||||
return self.missing_expr();
|
||||
};
|
||||
let Some(unsafe_arg_new) =
|
||||
LangItem::FormatUnsafeArg.ty_rel_path(self.db, self.krate, name![new])
|
||||
else {
|
||||
let Some(unsafe_arg_new) = LangItem::FormatUnsafeArg.ty_rel_path(
|
||||
self.db,
|
||||
self.krate,
|
||||
Name::new_symbol_root(sym::new.clone()),
|
||||
) else {
|
||||
return self.missing_expr();
|
||||
};
|
||||
let new_v1_formatted = self.alloc_expr_desugared(Expr::Path(new_v1_formatted));
|
||||
@ -1812,10 +1849,10 @@ impl ExprCollector<'_> {
|
||||
self.db,
|
||||
self.krate,
|
||||
match alignment {
|
||||
Some(FormatAlignment::Left) => name![Left],
|
||||
Some(FormatAlignment::Right) => name![Right],
|
||||
Some(FormatAlignment::Center) => name![Center],
|
||||
None => name![Unknown],
|
||||
Some(FormatAlignment::Left) => Name::new_symbol_root(sym::Left.clone()),
|
||||
Some(FormatAlignment::Right) => Name::new_symbol_root(sym::Right.clone()),
|
||||
Some(FormatAlignment::Center) => Name::new_symbol_root(sym::Center.clone()),
|
||||
None => Name::new_symbol_root(sym::Unknown.clone()),
|
||||
},
|
||||
);
|
||||
match align {
|
||||
@ -1838,8 +1875,11 @@ impl ExprCollector<'_> {
|
||||
let width = self.make_count(width, argmap);
|
||||
|
||||
let format_placeholder_new = {
|
||||
let format_placeholder_new =
|
||||
LangItem::FormatPlaceholder.ty_rel_path(self.db, self.krate, name![new]);
|
||||
let format_placeholder_new = LangItem::FormatPlaceholder.ty_rel_path(
|
||||
self.db,
|
||||
self.krate,
|
||||
Name::new_symbol_root(sym::new.clone()),
|
||||
);
|
||||
match format_placeholder_new {
|
||||
Some(path) => self.alloc_expr_desugared(Expr::Path(path)),
|
||||
None => self.missing_expr(),
|
||||
@ -1883,11 +1923,14 @@ impl ExprCollector<'_> {
|
||||
*n as u128,
|
||||
Some(BuiltinUint::Usize),
|
||||
)));
|
||||
let count_is =
|
||||
match LangItem::FormatCount.ty_rel_path(self.db, self.krate, name![Is]) {
|
||||
Some(count_is) => self.alloc_expr_desugared(Expr::Path(count_is)),
|
||||
None => self.missing_expr(),
|
||||
};
|
||||
let count_is = match LangItem::FormatCount.ty_rel_path(
|
||||
self.db,
|
||||
self.krate,
|
||||
Name::new_symbol_root(sym::Is.clone()),
|
||||
) {
|
||||
Some(count_is) => self.alloc_expr_desugared(Expr::Path(count_is)),
|
||||
None => self.missing_expr(),
|
||||
};
|
||||
self.alloc_expr_desugared(Expr::Call {
|
||||
callee: count_is,
|
||||
args: Box::new([args]),
|
||||
@ -1905,7 +1948,7 @@ impl ExprCollector<'_> {
|
||||
let count_param = match LangItem::FormatCount.ty_rel_path(
|
||||
self.db,
|
||||
self.krate,
|
||||
name![Param],
|
||||
Name::new_symbol_root(sym::Param.clone()),
|
||||
) {
|
||||
Some(count_param) => self.alloc_expr_desugared(Expr::Path(count_param)),
|
||||
None => self.missing_expr(),
|
||||
@ -1921,7 +1964,11 @@ impl ExprCollector<'_> {
|
||||
self.missing_expr()
|
||||
}
|
||||
}
|
||||
None => match LangItem::FormatCount.ty_rel_path(self.db, self.krate, name![Implied]) {
|
||||
None => match LangItem::FormatCount.ty_rel_path(
|
||||
self.db,
|
||||
self.krate,
|
||||
Name::new_symbol_root(sym::Implied.clone()),
|
||||
) {
|
||||
Some(count_param) => self.alloc_expr_desugared(Expr::Path(count_param)),
|
||||
None => self.missing_expr(),
|
||||
},
|
||||
@ -1942,18 +1989,18 @@ impl ExprCollector<'_> {
|
||||
let new_fn = match LangItem::FormatArgument.ty_rel_path(
|
||||
self.db,
|
||||
self.krate,
|
||||
match ty {
|
||||
Format(Display) => name![new_display],
|
||||
Format(Debug) => name![new_debug],
|
||||
Format(LowerExp) => name![new_lower_exp],
|
||||
Format(UpperExp) => name![new_upper_exp],
|
||||
Format(Octal) => name![new_octal],
|
||||
Format(Pointer) => name![new_pointer],
|
||||
Format(Binary) => name![new_binary],
|
||||
Format(LowerHex) => name![new_lower_hex],
|
||||
Format(UpperHex) => name![new_upper_hex],
|
||||
Usize => name![from_usize],
|
||||
},
|
||||
Name::new_symbol_root(match ty {
|
||||
Format(Display) => sym::new_display.clone(),
|
||||
Format(Debug) => sym::new_debug.clone(),
|
||||
Format(LowerExp) => sym::new_lower_exp.clone(),
|
||||
Format(UpperExp) => sym::new_upper_exp.clone(),
|
||||
Format(Octal) => sym::new_octal.clone(),
|
||||
Format(Pointer) => sym::new_pointer.clone(),
|
||||
Format(Binary) => sym::new_binary.clone(),
|
||||
Format(LowerHex) => sym::new_lower_hex.clone(),
|
||||
Format(UpperHex) => sym::new_upper_hex.clone(),
|
||||
Usize => sym::from_usize.clone(),
|
||||
}),
|
||||
) {
|
||||
Some(new_fn) => self.alloc_expr_desugared(Expr::Path(new_fn)),
|
||||
None => self.missing_expr(),
|
||||
@ -2002,12 +2049,7 @@ impl ExprCollector<'_> {
|
||||
}
|
||||
|
||||
fn alloc_binding(&mut self, name: Name, mode: BindingAnnotation) -> BindingId {
|
||||
let binding = self.body.bindings.alloc(Binding {
|
||||
name,
|
||||
mode,
|
||||
definitions: SmallVec::new(),
|
||||
problems: None,
|
||||
});
|
||||
let binding = self.body.bindings.alloc(Binding { name, mode, problems: None });
|
||||
if let Some(owner) = self.current_binding_owner {
|
||||
self.body.binding_owners.insert(binding, owner);
|
||||
}
|
||||
|
@ -517,7 +517,7 @@ impl Printer<'_> {
|
||||
if i != 0 {
|
||||
w!(self, ", ");
|
||||
}
|
||||
if *ellipsis == Some(i) {
|
||||
if *ellipsis == Some(i as u32) {
|
||||
w!(self, ".., ");
|
||||
}
|
||||
self.print_pat(*pat);
|
||||
@ -595,7 +595,7 @@ impl Printer<'_> {
|
||||
if i != 0 {
|
||||
w!(self, ", ");
|
||||
}
|
||||
if *ellipsis == Some(i) {
|
||||
if *ellipsis == Some(i as u32) {
|
||||
w!(self, ", ..");
|
||||
}
|
||||
self.print_pat(*arg);
|
||||
|
@ -288,8 +288,9 @@ fn compute_expr_scopes(
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use base_db::{FileId, SourceDatabase};
|
||||
use base_db::SourceDatabase;
|
||||
use hir_expand::{name::AsName, InFile};
|
||||
use span::FileId;
|
||||
use syntax::{algo::find_node_at_offset, ast, AstNode};
|
||||
use test_fixture::WithFixture;
|
||||
use test_utils::{assert_eq_text, extract_offset};
|
||||
@ -325,7 +326,7 @@ mod tests {
|
||||
|
||||
let file_syntax = db.parse(file_id).syntax_node();
|
||||
let marker: ast::PathExpr = find_node_at_offset(&file_syntax, offset).unwrap();
|
||||
let function = find_function(&db, file_id);
|
||||
let function = find_function(&db, file_id.file_id());
|
||||
|
||||
let scopes = db.expr_scopes(function.into());
|
||||
let (_body, source_map) = db.body_with_source_map(function.into());
|
||||
@ -338,7 +339,7 @@ mod tests {
|
||||
let actual = scopes
|
||||
.scope_chain(scope)
|
||||
.flat_map(|scope| scopes.entries(scope))
|
||||
.map(|it| it.name().to_smol_str())
|
||||
.map(|it| it.name().as_str())
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n");
|
||||
let expected = expected.join("\n");
|
||||
@ -480,10 +481,10 @@ fn foo() {
|
||||
.expect("failed to find a name at the target offset");
|
||||
let name_ref: ast::NameRef = find_node_at_offset(file.syntax(), offset).unwrap();
|
||||
|
||||
let function = find_function(&db, file_id);
|
||||
let function = find_function(&db, file_id.file_id());
|
||||
|
||||
let scopes = db.expr_scopes(function.into());
|
||||
let (body, source_map) = db.body_with_source_map(function.into());
|
||||
let (_, source_map) = db.body_with_source_map(function.into());
|
||||
|
||||
let expr_scope = {
|
||||
let expr_ast = name_ref.syntax().ancestors().find_map(ast::Expr::cast).unwrap();
|
||||
@ -494,7 +495,7 @@ fn foo() {
|
||||
|
||||
let resolved = scopes.resolve_name_in_scope(expr_scope, &name_ref.as_name()).unwrap();
|
||||
let pat_src = source_map
|
||||
.pat_syntax(*body.bindings[resolved.binding()].definitions.first().unwrap())
|
||||
.pat_syntax(*source_map.binding_definitions[&resolved.binding()].first().unwrap())
|
||||
.unwrap();
|
||||
|
||||
let local_name = pat_src.value.syntax_node_ptr().to_node(file.syntax());
|
||||
|
@ -5,7 +5,8 @@
|
||||
|
||||
use std::fmt;
|
||||
|
||||
use hir_expand::name::{name, AsName, Name};
|
||||
use hir_expand::name::{AsName, Name};
|
||||
use intern::{sym, Symbol};
|
||||
/// Different signed int types.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub enum BuiltinInt {
|
||||
@ -48,63 +49,67 @@ pub enum BuiltinType {
|
||||
|
||||
impl BuiltinType {
|
||||
#[rustfmt::skip]
|
||||
pub const ALL: &'static [(Name, BuiltinType)] = &[
|
||||
(name![char], BuiltinType::Char),
|
||||
(name![bool], BuiltinType::Bool),
|
||||
(name![str], BuiltinType::Str),
|
||||
pub fn all_builtin_types() -> [(Name, BuiltinType); 19] {
|
||||
[
|
||||
(Name::new_symbol_root(sym::char.clone()), BuiltinType::Char),
|
||||
(Name::new_symbol_root(sym::bool.clone()), BuiltinType::Bool),
|
||||
(Name::new_symbol_root(sym::str.clone()), BuiltinType::Str),
|
||||
|
||||
(name![isize], BuiltinType::Int(BuiltinInt::Isize)),
|
||||
(name![i8], BuiltinType::Int(BuiltinInt::I8)),
|
||||
(name![i16], BuiltinType::Int(BuiltinInt::I16)),
|
||||
(name![i32], BuiltinType::Int(BuiltinInt::I32)),
|
||||
(name![i64], BuiltinType::Int(BuiltinInt::I64)),
|
||||
(name![i128], BuiltinType::Int(BuiltinInt::I128)),
|
||||
(Name::new_symbol_root(sym::isize.clone()), BuiltinType::Int(BuiltinInt::Isize)),
|
||||
(Name::new_symbol_root(sym::i8.clone()), BuiltinType::Int(BuiltinInt::I8)),
|
||||
(Name::new_symbol_root(sym::i16.clone()), BuiltinType::Int(BuiltinInt::I16)),
|
||||
(Name::new_symbol_root(sym::i32.clone()), BuiltinType::Int(BuiltinInt::I32)),
|
||||
(Name::new_symbol_root(sym::i64.clone()), BuiltinType::Int(BuiltinInt::I64)),
|
||||
(Name::new_symbol_root(sym::i128.clone()), BuiltinType::Int(BuiltinInt::I128)),
|
||||
|
||||
(name![usize], BuiltinType::Uint(BuiltinUint::Usize)),
|
||||
(name![u8], BuiltinType::Uint(BuiltinUint::U8)),
|
||||
(name![u16], BuiltinType::Uint(BuiltinUint::U16)),
|
||||
(name![u32], BuiltinType::Uint(BuiltinUint::U32)),
|
||||
(name![u64], BuiltinType::Uint(BuiltinUint::U64)),
|
||||
(name![u128], BuiltinType::Uint(BuiltinUint::U128)),
|
||||
(Name::new_symbol_root(sym::usize.clone()), BuiltinType::Uint(BuiltinUint::Usize)),
|
||||
(Name::new_symbol_root(sym::u8.clone()), BuiltinType::Uint(BuiltinUint::U8)),
|
||||
(Name::new_symbol_root(sym::u16.clone()), BuiltinType::Uint(BuiltinUint::U16)),
|
||||
(Name::new_symbol_root(sym::u32.clone()), BuiltinType::Uint(BuiltinUint::U32)),
|
||||
(Name::new_symbol_root(sym::u64.clone()), BuiltinType::Uint(BuiltinUint::U64)),
|
||||
(Name::new_symbol_root(sym::u128.clone()), BuiltinType::Uint(BuiltinUint::U128)),
|
||||
|
||||
(name![f16], BuiltinType::Float(BuiltinFloat::F16)),
|
||||
(name![f32], BuiltinType::Float(BuiltinFloat::F32)),
|
||||
(name![f64], BuiltinType::Float(BuiltinFloat::F64)),
|
||||
(name![f128], BuiltinType::Float(BuiltinFloat::F128)),
|
||||
];
|
||||
(Name::new_symbol_root(sym::f16.clone()), BuiltinType::Float(BuiltinFloat::F16)),
|
||||
(Name::new_symbol_root(sym::f32.clone()), BuiltinType::Float(BuiltinFloat::F32)),
|
||||
(Name::new_symbol_root(sym::f64.clone()), BuiltinType::Float(BuiltinFloat::F64)),
|
||||
(Name::new_symbol_root(sym::f128.clone()), BuiltinType::Float(BuiltinFloat::F128)),
|
||||
]
|
||||
}
|
||||
|
||||
pub fn by_name(name: &Name) -> Option<Self> {
|
||||
Self::ALL.iter().find_map(|(n, ty)| if n == name { Some(*ty) } else { None })
|
||||
Self::all_builtin_types()
|
||||
.iter()
|
||||
.find_map(|(n, ty)| if n == name { Some(*ty) } else { None })
|
||||
}
|
||||
}
|
||||
|
||||
impl AsName for BuiltinType {
|
||||
fn as_name(&self) -> Name {
|
||||
match self {
|
||||
BuiltinType::Char => name![char],
|
||||
BuiltinType::Bool => name![bool],
|
||||
BuiltinType::Str => name![str],
|
||||
BuiltinType::Char => Name::new_symbol_root(sym::char.clone()),
|
||||
BuiltinType::Bool => Name::new_symbol_root(sym::bool.clone()),
|
||||
BuiltinType::Str => Name::new_symbol_root(sym::str.clone()),
|
||||
BuiltinType::Int(it) => match it {
|
||||
BuiltinInt::Isize => name![isize],
|
||||
BuiltinInt::I8 => name![i8],
|
||||
BuiltinInt::I16 => name![i16],
|
||||
BuiltinInt::I32 => name![i32],
|
||||
BuiltinInt::I64 => name![i64],
|
||||
BuiltinInt::I128 => name![i128],
|
||||
BuiltinInt::Isize => Name::new_symbol_root(sym::isize.clone()),
|
||||
BuiltinInt::I8 => Name::new_symbol_root(sym::i8.clone()),
|
||||
BuiltinInt::I16 => Name::new_symbol_root(sym::i16.clone()),
|
||||
BuiltinInt::I32 => Name::new_symbol_root(sym::i32.clone()),
|
||||
BuiltinInt::I64 => Name::new_symbol_root(sym::i64.clone()),
|
||||
BuiltinInt::I128 => Name::new_symbol_root(sym::i128.clone()),
|
||||
},
|
||||
BuiltinType::Uint(it) => match it {
|
||||
BuiltinUint::Usize => name![usize],
|
||||
BuiltinUint::U8 => name![u8],
|
||||
BuiltinUint::U16 => name![u16],
|
||||
BuiltinUint::U32 => name![u32],
|
||||
BuiltinUint::U64 => name![u64],
|
||||
BuiltinUint::U128 => name![u128],
|
||||
BuiltinUint::Usize => Name::new_symbol_root(sym::usize.clone()),
|
||||
BuiltinUint::U8 => Name::new_symbol_root(sym::u8.clone()),
|
||||
BuiltinUint::U16 => Name::new_symbol_root(sym::u16.clone()),
|
||||
BuiltinUint::U32 => Name::new_symbol_root(sym::u32.clone()),
|
||||
BuiltinUint::U64 => Name::new_symbol_root(sym::u64.clone()),
|
||||
BuiltinUint::U128 => Name::new_symbol_root(sym::u128.clone()),
|
||||
},
|
||||
BuiltinType::Float(it) => match it {
|
||||
BuiltinFloat::F16 => name![f16],
|
||||
BuiltinFloat::F32 => name![f32],
|
||||
BuiltinFloat::F64 => name![f64],
|
||||
BuiltinFloat::F128 => name![f128],
|
||||
BuiltinFloat::F16 => Name::new_symbol_root(sym::f16.clone()),
|
||||
BuiltinFloat::F32 => Name::new_symbol_root(sym::f32.clone()),
|
||||
BuiltinFloat::F64 => Name::new_symbol_root(sym::f64.clone()),
|
||||
BuiltinFloat::F128 => Name::new_symbol_root(sym::f128.clone()),
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -138,6 +143,18 @@ impl BuiltinInt {
|
||||
};
|
||||
Some(res)
|
||||
}
|
||||
pub fn from_suffix_sym(suffix: &Symbol) -> Option<BuiltinInt> {
|
||||
let res = match suffix {
|
||||
s if *s == sym::isize => Self::Isize,
|
||||
s if *s == sym::i8 => Self::I8,
|
||||
s if *s == sym::i16 => Self::I16,
|
||||
s if *s == sym::i32 => Self::I32,
|
||||
s if *s == sym::i64 => Self::I64,
|
||||
s if *s == sym::i128 => Self::I128,
|
||||
_ => return None,
|
||||
};
|
||||
Some(res)
|
||||
}
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
@ -155,6 +172,19 @@ impl BuiltinUint {
|
||||
};
|
||||
Some(res)
|
||||
}
|
||||
pub fn from_suffix_sym(suffix: &Symbol) -> Option<BuiltinUint> {
|
||||
let res = match suffix {
|
||||
s if *s == sym::usize => Self::Usize,
|
||||
s if *s == sym::u8 => Self::U8,
|
||||
s if *s == sym::u16 => Self::U16,
|
||||
s if *s == sym::u32 => Self::U32,
|
||||
s if *s == sym::u64 => Self::U64,
|
||||
s if *s == sym::u128 => Self::U128,
|
||||
|
||||
_ => return None,
|
||||
};
|
||||
Some(res)
|
||||
}
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
|
@ -6,7 +6,8 @@ use base_db::CrateId;
|
||||
use hir_expand::{
|
||||
name::Name, AstId, ExpandResult, HirFileId, InFile, MacroCallId, MacroCallKind, MacroDefKind,
|
||||
};
|
||||
use intern::Interned;
|
||||
use intern::{sym, Interned, Symbol};
|
||||
use la_arena::{Idx, RawIdx};
|
||||
use smallvec::SmallVec;
|
||||
use syntax::{ast, Parse};
|
||||
use triomphe::Arc;
|
||||
@ -38,8 +39,8 @@ pub struct FunctionData {
|
||||
pub ret_type: Interned<TypeRef>,
|
||||
pub attrs: Attrs,
|
||||
pub visibility: RawVisibility,
|
||||
pub abi: Option<Interned<str>>,
|
||||
pub legacy_const_generics_indices: Box<[u32]>,
|
||||
pub abi: Option<Symbol>,
|
||||
pub legacy_const_generics_indices: Option<Box<Box<[u32]>>>,
|
||||
pub rustc_allow_incoherent_impl: bool,
|
||||
flags: FnFlags,
|
||||
}
|
||||
@ -58,52 +59,52 @@ impl FunctionData {
|
||||
|
||||
let crate_graph = db.crate_graph();
|
||||
let cfg_options = &crate_graph[krate].cfg_options;
|
||||
let enabled_params = func
|
||||
.params
|
||||
.clone()
|
||||
.filter(|¶m| item_tree.attrs(db, krate, param.into()).is_cfg_enabled(cfg_options));
|
||||
|
||||
// If last cfg-enabled param is a `...` param, it's a varargs function.
|
||||
let is_varargs = enabled_params
|
||||
.clone()
|
||||
.next_back()
|
||||
.map_or(false, |param| item_tree[param].type_ref.is_none());
|
||||
let attr_owner = |idx| {
|
||||
item_tree::AttrOwner::Param(loc.id.value, Idx::from_raw(RawIdx::from(idx as u32)))
|
||||
};
|
||||
|
||||
let mut flags = func.flags;
|
||||
if is_varargs {
|
||||
flags |= FnFlags::IS_VARARGS;
|
||||
}
|
||||
if flags.contains(FnFlags::HAS_SELF_PARAM) {
|
||||
// If there's a self param in the syntax, but it is cfg'd out, remove the flag.
|
||||
let is_cfgd_out = match func.params.clone().next() {
|
||||
Some(param) => {
|
||||
!item_tree.attrs(db, krate, param.into()).is_cfg_enabled(cfg_options)
|
||||
}
|
||||
None => {
|
||||
stdx::never!("fn HAS_SELF_PARAM but no parameters allocated");
|
||||
true
|
||||
}
|
||||
};
|
||||
let is_cfgd_out =
|
||||
!item_tree.attrs(db, krate, attr_owner(0usize)).is_cfg_enabled(cfg_options);
|
||||
if is_cfgd_out {
|
||||
cov_mark::hit!(cfgd_out_self_param);
|
||||
flags.remove(FnFlags::HAS_SELF_PARAM);
|
||||
}
|
||||
}
|
||||
if flags.contains(FnFlags::IS_VARARGS) {
|
||||
if let Some((_, param)) = func.params.iter().enumerate().rev().find(|&(idx, _)| {
|
||||
item_tree.attrs(db, krate, attr_owner(idx)).is_cfg_enabled(cfg_options)
|
||||
}) {
|
||||
if param.type_ref.is_some() {
|
||||
flags.remove(FnFlags::IS_VARARGS);
|
||||
}
|
||||
} else {
|
||||
flags.remove(FnFlags::IS_VARARGS);
|
||||
}
|
||||
}
|
||||
|
||||
let attrs = item_tree.attrs(db, krate, ModItem::from(loc.id.value).into());
|
||||
let legacy_const_generics_indices = attrs
|
||||
.by_key("rustc_legacy_const_generics")
|
||||
.by_key(&sym::rustc_legacy_const_generics)
|
||||
.tt_values()
|
||||
.next()
|
||||
.map(parse_rustc_legacy_const_generics)
|
||||
.unwrap_or_default();
|
||||
let rustc_allow_incoherent_impl = attrs.by_key("rustc_allow_incoherent_impl").exists();
|
||||
.filter(|it| !it.is_empty())
|
||||
.map(Box::new);
|
||||
let rustc_allow_incoherent_impl = attrs.by_key(&sym::rustc_allow_incoherent_impl).exists();
|
||||
|
||||
Arc::new(FunctionData {
|
||||
name: func.name.clone(),
|
||||
params: enabled_params
|
||||
.clone()
|
||||
.filter_map(|id| item_tree[id].type_ref.clone())
|
||||
params: func
|
||||
.params
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|&(idx, _)| {
|
||||
item_tree.attrs(db, krate, attr_owner(idx)).is_cfg_enabled(cfg_options)
|
||||
})
|
||||
.filter_map(|(_, param)| param.type_ref.clone())
|
||||
.collect(),
|
||||
ret_type: func.ret_type.clone(),
|
||||
attrs: item_tree.attrs(db, krate, ModItem::from(loc.id.value).into()),
|
||||
@ -150,7 +151,7 @@ fn parse_rustc_legacy_const_generics(tt: &crate::tt::Subtree) -> Box<[u32]> {
|
||||
let mut indices = Vec::new();
|
||||
for args in tt.token_trees.chunks(2) {
|
||||
match &args[0] {
|
||||
tt::TokenTree::Leaf(tt::Leaf::Literal(lit)) => match lit.text.parse() {
|
||||
tt::TokenTree::Leaf(tt::Leaf::Literal(lit)) => match lit.symbol.as_str().parse() {
|
||||
Ok(index) => indices.push(index),
|
||||
Err(_) => break,
|
||||
},
|
||||
@ -200,8 +201,8 @@ impl TypeAliasData {
|
||||
ModItem::from(loc.id.value).into(),
|
||||
);
|
||||
let rustc_has_incoherent_inherent_impls =
|
||||
attrs.by_key("rustc_has_incoherent_inherent_impls").exists();
|
||||
let rustc_allow_incoherent_impl = attrs.by_key("rustc_allow_incoherent_impl").exists();
|
||||
attrs.by_key(&sym::rustc_has_incoherent_inherent_impls).exists();
|
||||
let rustc_allow_incoherent_impl = attrs.by_key(&sym::rustc_allow_incoherent_impl).exists();
|
||||
|
||||
Arc::new(TypeAliasData {
|
||||
name: typ.name.clone(),
|
||||
@ -223,6 +224,7 @@ pub struct TraitData {
|
||||
pub is_unsafe: bool,
|
||||
pub rustc_has_incoherent_inherent_impls: bool,
|
||||
pub skip_array_during_method_dispatch: bool,
|
||||
pub skip_boxed_slice_during_method_dispatch: bool,
|
||||
pub fundamental: bool,
|
||||
pub visibility: RawVisibility,
|
||||
/// Whether the trait has `#[rust_skip_array_during_method_dispatch]`. `hir_ty` will ignore
|
||||
@ -250,11 +252,20 @@ impl TraitData {
|
||||
let is_unsafe = tr_def.is_unsafe;
|
||||
let visibility = item_tree[tr_def.visibility].clone();
|
||||
let attrs = item_tree.attrs(db, module_id.krate(), ModItem::from(tree_id.value).into());
|
||||
let skip_array_during_method_dispatch =
|
||||
attrs.by_key("rustc_skip_array_during_method_dispatch").exists();
|
||||
let mut skip_array_during_method_dispatch =
|
||||
attrs.by_key(&sym::rustc_skip_array_during_method_dispatch).exists();
|
||||
let mut skip_boxed_slice_during_method_dispatch = false;
|
||||
for tt in attrs.by_key(&sym::rustc_skip_during_method_dispatch).tt_values() {
|
||||
for tt in tt.token_trees.iter() {
|
||||
if let crate::tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) = tt {
|
||||
skip_array_during_method_dispatch |= ident.sym == sym::array;
|
||||
skip_boxed_slice_during_method_dispatch |= ident.sym == sym::boxed_slice;
|
||||
}
|
||||
}
|
||||
}
|
||||
let rustc_has_incoherent_inherent_impls =
|
||||
attrs.by_key("rustc_has_incoherent_inherent_impls").exists();
|
||||
let fundamental = attrs.by_key("fundamental").exists();
|
||||
attrs.by_key(&sym::rustc_has_incoherent_inherent_impls).exists();
|
||||
let fundamental = attrs.by_key(&sym::fundamental).exists();
|
||||
let mut collector =
|
||||
AssocItemCollector::new(db, module_id, tree_id.file_id(), ItemContainerId::TraitId(tr));
|
||||
collector.collect(&item_tree, tree_id.tree_id(), &tr_def.items);
|
||||
@ -269,6 +280,7 @@ impl TraitData {
|
||||
is_unsafe,
|
||||
visibility,
|
||||
skip_array_during_method_dispatch,
|
||||
skip_boxed_slice_during_method_dispatch,
|
||||
rustc_has_incoherent_inherent_impls,
|
||||
fundamental,
|
||||
}),
|
||||
@ -393,7 +405,7 @@ impl Macro2Data {
|
||||
|
||||
let helpers = item_tree
|
||||
.attrs(db, loc.container.krate(), ModItem::from(loc.id.value).into())
|
||||
.by_key("rustc_builtin_macro")
|
||||
.by_key(&sym::rustc_builtin_macro)
|
||||
.tt_values()
|
||||
.next()
|
||||
.and_then(|attr| parse_macro_name_and_helper_attrs(&attr.token_trees))
|
||||
@ -423,7 +435,7 @@ impl MacroRulesData {
|
||||
|
||||
let macro_export = item_tree
|
||||
.attrs(db, loc.container.krate(), ModItem::from(loc.id.value).into())
|
||||
.by_key("macro_export")
|
||||
.by_key(&sym::macro_export)
|
||||
.exists();
|
||||
|
||||
Arc::new(MacroRulesData { name: makro.name.clone(), macro_export })
|
||||
@ -485,7 +497,7 @@ impl ExternCrateDeclData {
|
||||
|
||||
let name = extern_crate.name.clone();
|
||||
let krate = loc.container.krate();
|
||||
let crate_id = if name == hir_expand::name![self] {
|
||||
let crate_id = if name == sym::self_.clone() {
|
||||
Some(krate)
|
||||
} else {
|
||||
db.crate_def_map(krate)
|
||||
@ -526,7 +538,7 @@ impl ConstData {
|
||||
|
||||
let rustc_allow_incoherent_impl = item_tree
|
||||
.attrs(db, loc.container.module(db).krate(), ModItem::from(loc.id.value).into())
|
||||
.by_key("rustc_allow_incoherent_impl")
|
||||
.by_key(&sym::rustc_allow_incoherent_impl)
|
||||
.exists();
|
||||
|
||||
Arc::new(ConstData {
|
||||
@ -618,7 +630,8 @@ impl<'a> AssocItemCollector<'a> {
|
||||
if !attrs.is_cfg_enabled(self.expander.cfg_options()) {
|
||||
self.diagnostics.push(DefDiagnostic::unconfigured_code(
|
||||
self.module_id.local_id,
|
||||
InFile::new(self.expander.current_file_id(), item.ast_id(item_tree).erase()),
|
||||
tree_id,
|
||||
ModItem::from(item).into(),
|
||||
attrs.cfg().unwrap(),
|
||||
self.expander.cfg_options().clone(),
|
||||
));
|
||||
@ -644,22 +657,18 @@ impl<'a> AssocItemCollector<'a> {
|
||||
// crate failed), skip expansion like we would if it was
|
||||
// disabled. This is analogous to the handling in
|
||||
// `DefCollector::collect_macros`.
|
||||
if exp.is_dummy() {
|
||||
self.diagnostics.push(DefDiagnostic::unresolved_proc_macro(
|
||||
if let Some(err) = exp.as_expand_error(self.module_id.krate) {
|
||||
self.diagnostics.push(DefDiagnostic::macro_error(
|
||||
self.module_id.local_id,
|
||||
loc.kind,
|
||||
loc.def.krate,
|
||||
ast_id,
|
||||
(*attr.path).clone(),
|
||||
err,
|
||||
));
|
||||
|
||||
continue 'attrs;
|
||||
}
|
||||
if exp.is_disabled() {
|
||||
continue 'attrs;
|
||||
}
|
||||
}
|
||||
|
||||
self.macro_calls.push((ast_id, call_id));
|
||||
|
||||
let res =
|
||||
self.expander.enter_expand_id::<ast::MacroItems>(self.db, call_id);
|
||||
self.collect_macro_items(res);
|
||||
|
@ -5,24 +5,20 @@ use bitflags::bitflags;
|
||||
use cfg::CfgOptions;
|
||||
use either::Either;
|
||||
|
||||
use hir_expand::{
|
||||
name::{AsName, Name},
|
||||
HirFileId, InFile,
|
||||
};
|
||||
use intern::Interned;
|
||||
use hir_expand::name::Name;
|
||||
use intern::{sym, Interned};
|
||||
use la_arena::Arena;
|
||||
use rustc_abi::{Align, Integer, IntegerType, ReprFlags, ReprOptions};
|
||||
use syntax::ast::{self, HasName, HasVisibility};
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{
|
||||
builtin_type::{BuiltinInt, BuiltinUint},
|
||||
db::DefDatabase,
|
||||
item_tree::{AttrOwner, Field, FieldAstId, Fields, ItemTree, ModItem, RawVisibilityId},
|
||||
item_tree::{
|
||||
AttrOwner, Field, FieldParent, FieldsShape, ItemTree, ModItem, RawVisibilityId, TreeId,
|
||||
},
|
||||
lang_item::LangItem,
|
||||
lower::LowerCtx,
|
||||
nameres::diagnostics::{DefDiagnostic, DefDiagnostics},
|
||||
trace::Trace,
|
||||
tt::{Delimiter, DelimiterKind, Leaf, Subtree, TokenTree},
|
||||
type_ref::TypeRef,
|
||||
visibility::RawVisibility,
|
||||
@ -95,7 +91,7 @@ fn repr_from_value(
|
||||
item_tree: &ItemTree,
|
||||
of: AttrOwner,
|
||||
) -> Option<ReprOptions> {
|
||||
item_tree.attrs(db, krate, of).by_key("repr").tt_values().find_map(parse_repr_tt)
|
||||
item_tree.attrs(db, krate, of).by_key(&sym::repr).tt_values().find_map(parse_repr_tt)
|
||||
}
|
||||
|
||||
fn parse_repr_tt(tt: &Subtree) -> Option<ReprOptions> {
|
||||
@ -112,12 +108,12 @@ fn parse_repr_tt(tt: &Subtree) -> Option<ReprOptions> {
|
||||
let mut tts = tt.token_trees.iter().peekable();
|
||||
while let Some(tt) = tts.next() {
|
||||
if let TokenTree::Leaf(Leaf::Ident(ident)) = tt {
|
||||
flags.insert(match &*ident.text {
|
||||
"packed" => {
|
||||
flags.insert(match &ident.sym {
|
||||
s if *s == sym::packed => {
|
||||
let pack = if let Some(TokenTree::Subtree(tt)) = tts.peek() {
|
||||
tts.next();
|
||||
if let Some(TokenTree::Leaf(Leaf::Literal(lit))) = tt.token_trees.first() {
|
||||
lit.text.parse().unwrap_or_default()
|
||||
lit.symbol.as_str().parse().unwrap_or_default()
|
||||
} else {
|
||||
0
|
||||
}
|
||||
@ -129,11 +125,11 @@ fn parse_repr_tt(tt: &Subtree) -> Option<ReprOptions> {
|
||||
Some(if let Some(min_pack) = min_pack { min_pack.min(pack) } else { pack });
|
||||
ReprFlags::empty()
|
||||
}
|
||||
"align" => {
|
||||
s if *s == sym::align => {
|
||||
if let Some(TokenTree::Subtree(tt)) = tts.peek() {
|
||||
tts.next();
|
||||
if let Some(TokenTree::Leaf(Leaf::Literal(lit))) = tt.token_trees.first() {
|
||||
if let Ok(align) = lit.text.parse() {
|
||||
if let Ok(align) = lit.symbol.as_str().parse() {
|
||||
let align = Align::from_bytes(align).ok();
|
||||
max_align = max_align.max(align);
|
||||
}
|
||||
@ -141,13 +137,13 @@ fn parse_repr_tt(tt: &Subtree) -> Option<ReprOptions> {
|
||||
}
|
||||
ReprFlags::empty()
|
||||
}
|
||||
"C" => ReprFlags::IS_C,
|
||||
"transparent" => ReprFlags::IS_TRANSPARENT,
|
||||
"simd" => ReprFlags::IS_SIMD,
|
||||
s if *s == sym::C => ReprFlags::IS_C,
|
||||
s if *s == sym::transparent => ReprFlags::IS_TRANSPARENT,
|
||||
s if *s == sym::simd => ReprFlags::IS_SIMD,
|
||||
repr => {
|
||||
if let Some(builtin) = BuiltinInt::from_suffix(repr)
|
||||
if let Some(builtin) = BuiltinInt::from_suffix_sym(repr)
|
||||
.map(Either::Left)
|
||||
.or_else(|| BuiltinUint::from_suffix(repr).map(Either::Right))
|
||||
.or_else(|| BuiltinUint::from_suffix_sym(repr).map(Either::Right))
|
||||
{
|
||||
int = Some(match builtin {
|
||||
Either::Left(bi) => match bi {
|
||||
@ -194,10 +190,10 @@ impl StructData {
|
||||
let attrs = item_tree.attrs(db, krate, ModItem::from(loc.id.value).into());
|
||||
|
||||
let mut flags = StructFlags::NO_FLAGS;
|
||||
if attrs.by_key("rustc_has_incoherent_inherent_impls").exists() {
|
||||
if attrs.by_key(&sym::rustc_has_incoherent_inherent_impls).exists() {
|
||||
flags |= StructFlags::IS_RUSTC_HAS_INCOHERENT_INHERENT_IMPL;
|
||||
}
|
||||
if attrs.by_key("fundamental").exists() {
|
||||
if attrs.by_key(&sym::fundamental).exists() {
|
||||
flags |= StructFlags::IS_FUNDAMENTAL;
|
||||
}
|
||||
if let Some(lang) = attrs.lang_item() {
|
||||
@ -211,20 +207,25 @@ impl StructData {
|
||||
}
|
||||
|
||||
let strukt = &item_tree[loc.id.value];
|
||||
let (variant_data, diagnostics) = lower_fields(
|
||||
let (data, diagnostics) = lower_fields(
|
||||
db,
|
||||
krate,
|
||||
loc.id.file_id(),
|
||||
loc.container.local_id,
|
||||
loc.id.tree_id(),
|
||||
&item_tree,
|
||||
&db.crate_graph()[krate].cfg_options,
|
||||
FieldParent::Struct(loc.id.value),
|
||||
&strukt.fields,
|
||||
None,
|
||||
);
|
||||
(
|
||||
Arc::new(StructData {
|
||||
name: strukt.name.clone(),
|
||||
variant_data: Arc::new(variant_data),
|
||||
variant_data: Arc::new(match strukt.shape {
|
||||
FieldsShape::Record => VariantData::Record(data),
|
||||
FieldsShape::Tuple => VariantData::Tuple(data),
|
||||
FieldsShape::Unit => VariantData::Unit,
|
||||
}),
|
||||
repr,
|
||||
visibility: item_tree[strukt.visibility].clone(),
|
||||
flags,
|
||||
@ -248,28 +249,29 @@ impl StructData {
|
||||
let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into());
|
||||
let attrs = item_tree.attrs(db, krate, ModItem::from(loc.id.value).into());
|
||||
let mut flags = StructFlags::NO_FLAGS;
|
||||
if attrs.by_key("rustc_has_incoherent_inherent_impls").exists() {
|
||||
if attrs.by_key(&sym::rustc_has_incoherent_inherent_impls).exists() {
|
||||
flags |= StructFlags::IS_RUSTC_HAS_INCOHERENT_INHERENT_IMPL;
|
||||
}
|
||||
if attrs.by_key("fundamental").exists() {
|
||||
if attrs.by_key(&sym::fundamental).exists() {
|
||||
flags |= StructFlags::IS_FUNDAMENTAL;
|
||||
}
|
||||
|
||||
let union = &item_tree[loc.id.value];
|
||||
let (variant_data, diagnostics) = lower_fields(
|
||||
let (data, diagnostics) = lower_fields(
|
||||
db,
|
||||
krate,
|
||||
loc.id.file_id(),
|
||||
loc.container.local_id,
|
||||
loc.id.tree_id(),
|
||||
&item_tree,
|
||||
&db.crate_graph()[krate].cfg_options,
|
||||
FieldParent::Union(loc.id.value),
|
||||
&union.fields,
|
||||
None,
|
||||
);
|
||||
(
|
||||
Arc::new(StructData {
|
||||
name: union.name.clone(),
|
||||
variant_data: Arc::new(variant_data),
|
||||
variant_data: Arc::new(VariantData::Record(data)),
|
||||
repr,
|
||||
visibility: item_tree[union.visibility].clone(),
|
||||
flags,
|
||||
@ -287,7 +289,7 @@ impl EnumData {
|
||||
let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into());
|
||||
let rustc_has_incoherent_inherent_impls = item_tree
|
||||
.attrs(db, loc.container.krate, ModItem::from(loc.id.value).into())
|
||||
.by_key("rustc_has_incoherent_inherent_impls")
|
||||
.by_key(&sym::rustc_has_incoherent_inherent_impls)
|
||||
.exists();
|
||||
|
||||
let enum_ = &item_tree[loc.id.value];
|
||||
@ -336,13 +338,14 @@ impl EnumVariantData {
|
||||
let item_tree = loc.id.item_tree(db);
|
||||
let variant = &item_tree[loc.id.value];
|
||||
|
||||
let (var_data, diagnostics) = lower_fields(
|
||||
let (data, diagnostics) = lower_fields(
|
||||
db,
|
||||
krate,
|
||||
loc.id.file_id(),
|
||||
container.local_id,
|
||||
loc.id.tree_id(),
|
||||
&item_tree,
|
||||
&db.crate_graph()[krate].cfg_options,
|
||||
FieldParent::Variant(loc.id.value),
|
||||
&variant.fields,
|
||||
Some(item_tree[loc.parent.lookup(db).id.value].visibility),
|
||||
);
|
||||
@ -350,7 +353,11 @@ impl EnumVariantData {
|
||||
(
|
||||
Arc::new(EnumVariantData {
|
||||
name: variant.name.clone(),
|
||||
variant_data: Arc::new(var_data),
|
||||
variant_data: Arc::new(match variant.shape {
|
||||
FieldsShape::Record => VariantData::Record(data),
|
||||
FieldsShape::Tuple => VariantData::Tuple(data),
|
||||
FieldsShape::Unit => VariantData::Unit,
|
||||
}),
|
||||
}),
|
||||
DefDiagnostics::new(diagnostics),
|
||||
)
|
||||
@ -396,123 +403,35 @@ pub enum StructKind {
|
||||
Unit,
|
||||
}
|
||||
|
||||
pub(crate) fn lower_struct(
|
||||
db: &dyn DefDatabase,
|
||||
trace: &mut Trace<FieldData, Either<ast::TupleField, ast::RecordField>>,
|
||||
ast: &InFile<ast::StructKind>,
|
||||
krate: CrateId,
|
||||
item_tree: &ItemTree,
|
||||
fields: &Fields,
|
||||
) -> StructKind {
|
||||
let ctx = LowerCtx::new(db, ast.file_id);
|
||||
|
||||
match (&ast.value, fields) {
|
||||
(ast::StructKind::Tuple(fl), Fields::Tuple(fields)) => {
|
||||
let cfg_options = &db.crate_graph()[krate].cfg_options;
|
||||
for ((i, fd), item_tree_id) in fl.fields().enumerate().zip(fields.clone()) {
|
||||
if !item_tree.attrs(db, krate, item_tree_id.into()).is_cfg_enabled(cfg_options) {
|
||||
continue;
|
||||
}
|
||||
|
||||
trace.alloc(
|
||||
|| Either::Left(fd.clone()),
|
||||
|| FieldData {
|
||||
name: Name::new_tuple_field(i),
|
||||
type_ref: Interned::new(TypeRef::from_ast_opt(&ctx, fd.ty())),
|
||||
visibility: RawVisibility::from_ast(db, fd.visibility(), &mut |range| {
|
||||
ctx.span_map().span_for_range(range).ctx
|
||||
}),
|
||||
},
|
||||
);
|
||||
}
|
||||
StructKind::Tuple
|
||||
}
|
||||
(ast::StructKind::Record(fl), Fields::Record(fields)) => {
|
||||
let cfg_options = &db.crate_graph()[krate].cfg_options;
|
||||
for (fd, item_tree_id) in fl.fields().zip(fields.clone()) {
|
||||
if !item_tree.attrs(db, krate, item_tree_id.into()).is_cfg_enabled(cfg_options) {
|
||||
continue;
|
||||
}
|
||||
|
||||
trace.alloc(
|
||||
|| Either::Right(fd.clone()),
|
||||
|| FieldData {
|
||||
name: fd.name().map(|n| n.as_name()).unwrap_or_else(Name::missing),
|
||||
type_ref: Interned::new(TypeRef::from_ast_opt(&ctx, fd.ty())),
|
||||
visibility: RawVisibility::from_ast(db, fd.visibility(), &mut |range| {
|
||||
ctx.span_map().span_for_range(range).ctx
|
||||
}),
|
||||
},
|
||||
);
|
||||
}
|
||||
StructKind::Record
|
||||
}
|
||||
_ => StructKind::Unit,
|
||||
}
|
||||
}
|
||||
|
||||
fn lower_fields(
|
||||
db: &dyn DefDatabase,
|
||||
krate: CrateId,
|
||||
current_file_id: HirFileId,
|
||||
container: LocalModuleId,
|
||||
tree_id: TreeId,
|
||||
item_tree: &ItemTree,
|
||||
cfg_options: &CfgOptions,
|
||||
fields: &Fields,
|
||||
parent: FieldParent,
|
||||
fields: &[Field],
|
||||
override_visibility: Option<RawVisibilityId>,
|
||||
) -> (VariantData, Vec<DefDiagnostic>) {
|
||||
) -> (Arena<FieldData>, Vec<DefDiagnostic>) {
|
||||
let mut diagnostics = Vec::new();
|
||||
match fields {
|
||||
Fields::Record(flds) => {
|
||||
let mut arena = Arena::new();
|
||||
for field_id in flds.clone() {
|
||||
let attrs = item_tree.attrs(db, krate, field_id.into());
|
||||
let field = &item_tree[field_id];
|
||||
if attrs.is_cfg_enabled(cfg_options) {
|
||||
arena.alloc(lower_field(item_tree, field, override_visibility));
|
||||
} else {
|
||||
diagnostics.push(DefDiagnostic::unconfigured_code(
|
||||
container,
|
||||
InFile::new(
|
||||
current_file_id,
|
||||
match field.ast_id {
|
||||
FieldAstId::Record(it) => it.erase(),
|
||||
FieldAstId::Tuple(it) => it.erase(),
|
||||
},
|
||||
),
|
||||
attrs.cfg().unwrap(),
|
||||
cfg_options.clone(),
|
||||
))
|
||||
}
|
||||
}
|
||||
(VariantData::Record(arena), diagnostics)
|
||||
let mut arena = Arena::new();
|
||||
for (idx, field) in fields.iter().enumerate() {
|
||||
let attr_owner = AttrOwner::make_field_indexed(parent, idx);
|
||||
let attrs = item_tree.attrs(db, krate, attr_owner);
|
||||
if attrs.is_cfg_enabled(cfg_options) {
|
||||
arena.alloc(lower_field(item_tree, field, override_visibility));
|
||||
} else {
|
||||
diagnostics.push(DefDiagnostic::unconfigured_code(
|
||||
container,
|
||||
tree_id,
|
||||
attr_owner,
|
||||
attrs.cfg().unwrap(),
|
||||
cfg_options.clone(),
|
||||
))
|
||||
}
|
||||
Fields::Tuple(flds) => {
|
||||
let mut arena = Arena::new();
|
||||
for field_id in flds.clone() {
|
||||
let attrs = item_tree.attrs(db, krate, field_id.into());
|
||||
let field = &item_tree[field_id];
|
||||
if attrs.is_cfg_enabled(cfg_options) {
|
||||
arena.alloc(lower_field(item_tree, field, override_visibility));
|
||||
} else {
|
||||
diagnostics.push(DefDiagnostic::unconfigured_code(
|
||||
container,
|
||||
InFile::new(
|
||||
current_file_id,
|
||||
match field.ast_id {
|
||||
FieldAstId::Record(it) => it.erase(),
|
||||
FieldAstId::Tuple(it) => it.erase(),
|
||||
},
|
||||
),
|
||||
attrs.cfg().unwrap(),
|
||||
cfg_options.clone(),
|
||||
))
|
||||
}
|
||||
}
|
||||
(VariantData::Tuple(arena), diagnostics)
|
||||
}
|
||||
Fields::Unit => (VariantData::Unit, diagnostics),
|
||||
}
|
||||
(arena, diagnostics)
|
||||
}
|
||||
|
||||
fn lower_field(
|
||||
|
@ -1,10 +1,10 @@
|
||||
//! Defines database & queries for name resolution.
|
||||
use base_db::{salsa, CrateId, FileId, SourceDatabase, Upcast};
|
||||
use base_db::{salsa, CrateId, SourceDatabase, Upcast};
|
||||
use either::Either;
|
||||
use hir_expand::{db::ExpandDatabase, HirFileId, MacroDefId};
|
||||
use intern::Interned;
|
||||
use intern::{sym, Interned};
|
||||
use la_arena::ArenaMap;
|
||||
use span::MacroCallId;
|
||||
use span::{EditionedFileId, MacroCallId};
|
||||
use syntax::{ast, AstPtr};
|
||||
use triomphe::Arc;
|
||||
|
||||
@ -177,6 +177,7 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast<dyn ExpandDataba
|
||||
// endregion:data
|
||||
|
||||
#[salsa::invoke(Body::body_with_source_map_query)]
|
||||
#[salsa::lru]
|
||||
fn body_with_source_map(&self, def: DefWithBodyId) -> (Arc<Body>, Arc<BodySourceMap>);
|
||||
|
||||
#[salsa::invoke(Body::body_query)]
|
||||
@ -239,11 +240,14 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast<dyn ExpandDataba
|
||||
|
||||
fn crate_supports_no_std(&self, crate_id: CrateId) -> bool;
|
||||
|
||||
fn include_macro_invoc(&self, crate_id: CrateId) -> Vec<(MacroCallId, FileId)>;
|
||||
fn include_macro_invoc(&self, crate_id: CrateId) -> Vec<(MacroCallId, EditionedFileId)>;
|
||||
}
|
||||
|
||||
// return: macro call id and include file id
|
||||
fn include_macro_invoc(db: &dyn DefDatabase, krate: CrateId) -> Vec<(MacroCallId, FileId)> {
|
||||
fn include_macro_invoc(
|
||||
db: &dyn DefDatabase,
|
||||
krate: CrateId,
|
||||
) -> Vec<(MacroCallId, EditionedFileId)> {
|
||||
db.crate_def_map(krate)
|
||||
.modules
|
||||
.values()
|
||||
@ -257,13 +261,13 @@ fn include_macro_invoc(db: &dyn DefDatabase, krate: CrateId) -> Vec<(MacroCallId
|
||||
}
|
||||
|
||||
fn crate_supports_no_std(db: &dyn DefDatabase, crate_id: CrateId) -> bool {
|
||||
let file = db.crate_graph()[crate_id].root_file_id;
|
||||
let file = db.crate_graph()[crate_id].root_file_id();
|
||||
let item_tree = db.file_item_tree(file.into());
|
||||
let attrs = item_tree.raw_attrs(AttrOwner::TopLevel);
|
||||
for attr in &**attrs {
|
||||
match attr.path().as_ident().and_then(|id| id.as_text()) {
|
||||
Some(ident) if ident == "no_std" => return true,
|
||||
Some(ident) if ident == "cfg_attr" => {}
|
||||
match attr.path().as_ident() {
|
||||
Some(ident) if *ident == sym::no_std.clone() => return true,
|
||||
Some(ident) if *ident == sym::cfg_attr.clone() => {}
|
||||
_ => continue,
|
||||
}
|
||||
|
||||
@ -278,7 +282,7 @@ fn crate_supports_no_std(db: &dyn DefDatabase, crate_id: CrateId) -> bool {
|
||||
tt.split(|tt| matches!(tt, tt::TokenTree::Leaf(tt::Leaf::Punct(p)) if p.char == ','));
|
||||
for output in segments.skip(1) {
|
||||
match output {
|
||||
[tt::TokenTree::Leaf(tt::Leaf::Ident(ident))] if ident.text == "no_std" => {
|
||||
[tt::TokenTree::Leaf(tt::Leaf::Ident(ident))] if ident.sym == sym::no_std => {
|
||||
return true
|
||||
}
|
||||
_ => {}
|
||||
|
@ -6,10 +6,11 @@ use base_db::CrateId;
|
||||
use cfg::CfgOptions;
|
||||
use drop_bomb::DropBomb;
|
||||
use hir_expand::{
|
||||
attrs::RawAttrs, mod_path::ModPath, span_map::SpanMap, ExpandError, ExpandResult, HirFileId,
|
||||
InFile, MacroCallId,
|
||||
attrs::RawAttrs, mod_path::ModPath, span_map::SpanMap, ExpandError, ExpandErrorKind,
|
||||
ExpandResult, HirFileId, InFile, Lookup, MacroCallId,
|
||||
};
|
||||
use limit::Limit;
|
||||
use span::SyntaxContextId;
|
||||
use syntax::{ast, Parse};
|
||||
use triomphe::Arc;
|
||||
|
||||
@ -52,6 +53,11 @@ impl Expander {
|
||||
self.module.krate
|
||||
}
|
||||
|
||||
pub fn syntax_context(&self) -> SyntaxContextId {
|
||||
// FIXME:
|
||||
SyntaxContextId::ROOT
|
||||
}
|
||||
|
||||
pub fn enter_expand<T: ast::AstNode>(
|
||||
&mut self,
|
||||
db: &dyn DefDatabase,
|
||||
@ -154,26 +160,30 @@ impl Expander {
|
||||
// so don't return overflow error here to avoid diagnostics duplication.
|
||||
cov_mark::hit!(overflow_but_not_me);
|
||||
return ExpandResult::ok(None);
|
||||
} else if self.recursion_limit.check(self.recursion_depth as usize + 1).is_err() {
|
||||
self.recursion_depth = u32::MAX;
|
||||
cov_mark::hit!(your_stack_belongs_to_me);
|
||||
return ExpandResult::only_err(ExpandError::RecursionOverflow);
|
||||
}
|
||||
|
||||
let ExpandResult { value, err } = op(self);
|
||||
let Some(call_id) = value else {
|
||||
return ExpandResult { value: None, err };
|
||||
};
|
||||
if self.recursion_limit.check(self.recursion_depth as usize + 1).is_err() {
|
||||
self.recursion_depth = u32::MAX;
|
||||
cov_mark::hit!(your_stack_belongs_to_me);
|
||||
return ExpandResult::only_err(ExpandError::new(
|
||||
db.macro_arg_considering_derives(call_id, &call_id.lookup(db.upcast()).kind).2,
|
||||
ExpandErrorKind::RecursionOverflow,
|
||||
));
|
||||
}
|
||||
|
||||
let macro_file = call_id.as_macro_file();
|
||||
let res = db.parse_macro_expansion(macro_file);
|
||||
|
||||
let err = err.or(res.err);
|
||||
ExpandResult {
|
||||
value: match err {
|
||||
value: match &err {
|
||||
// If proc-macro is disabled or unresolved, we want to expand to a missing expression
|
||||
// instead of an empty tree which might end up in an empty block.
|
||||
Some(ExpandError::UnresolvedProcMacro(_)) => None,
|
||||
Some(e) if matches!(e.kind(), ExpandErrorKind::MissingProcMacroExpander(_)) => None,
|
||||
_ => (|| {
|
||||
let parse = res.value.0.cast::<T>()?;
|
||||
|
||||
|
@ -2,10 +2,12 @@
|
||||
|
||||
use std::{cell::Cell, cmp::Ordering, iter};
|
||||
|
||||
use base_db::{CrateId, CrateOrigin, LangCrateOrigin};
|
||||
use hir_expand::{
|
||||
name::{known, AsName, Name},
|
||||
name::{AsName, Name},
|
||||
Lookup,
|
||||
};
|
||||
use intern::sym;
|
||||
use rustc_hash::FxHashSet;
|
||||
|
||||
use crate::{
|
||||
@ -36,7 +38,8 @@ pub fn find_path(
|
||||
|
||||
// within block modules, forcing a `self` or `crate` prefix will not allow using inner items, so
|
||||
// default to plain paths.
|
||||
if item.module(db).is_some_and(ModuleId::is_within_block) {
|
||||
let item_module = item.module(db)?;
|
||||
if item_module.is_within_block() {
|
||||
prefix_kind = PrefixKind::Plain;
|
||||
}
|
||||
cfg.prefer_no_std = cfg.prefer_no_std || db.crate_supports_no_std(from.krate());
|
||||
@ -53,23 +56,17 @@ pub fn find_path(
|
||||
},
|
||||
item,
|
||||
MAX_PATH_LEN,
|
||||
db.crate_graph()[item_module.krate()].origin.is_lang(),
|
||||
)
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
enum Stability {
|
||||
Unstable,
|
||||
Stable,
|
||||
}
|
||||
use Stability::*;
|
||||
|
||||
fn zip_stability(a: Stability, b: Stability) -> Stability {
|
||||
match (a, b) {
|
||||
(Stable, Stable) => Stable,
|
||||
_ => Unstable,
|
||||
}
|
||||
}
|
||||
|
||||
const MAX_PATH_LEN: usize = 15;
|
||||
const FIND_PATH_FUEL: usize = 10000;
|
||||
|
||||
@ -107,12 +104,18 @@ struct FindPathCtx<'db> {
|
||||
}
|
||||
|
||||
/// Attempts to find a path to refer to the given `item` visible from the `from` ModuleId
|
||||
fn find_path_inner(ctx: &FindPathCtx<'_>, item: ItemInNs, max_len: usize) -> Option<ModPath> {
|
||||
fn find_path_inner(
|
||||
ctx: &FindPathCtx<'_>,
|
||||
item: ItemInNs,
|
||||
max_len: usize,
|
||||
is_std_item: bool,
|
||||
) -> Option<ModPath> {
|
||||
// - if the item is a module, jump straight to module search
|
||||
if let ItemInNs::Types(ModuleDefId::ModuleId(module_id)) = item {
|
||||
let mut visited_modules = FxHashSet::default();
|
||||
return find_path_for_module(ctx, &mut visited_modules, module_id, max_len)
|
||||
.map(|(item, _)| item);
|
||||
if !is_std_item {
|
||||
if let ItemInNs::Types(ModuleDefId::ModuleId(module_id)) = item {
|
||||
return find_path_for_module(ctx, &mut FxHashSet::default(), module_id, true, max_len)
|
||||
.map(|choice| choice.path);
|
||||
}
|
||||
}
|
||||
|
||||
let may_be_in_scope = match ctx.prefix {
|
||||
@ -130,14 +133,17 @@ fn find_path_inner(ctx: &FindPathCtx<'_>, item: ItemInNs, max_len: usize) -> Opt
|
||||
|
||||
// - if the item is in the prelude, return the name from there
|
||||
if let Some(value) = find_in_prelude(ctx.db, ctx.from_def_map, item, ctx.from) {
|
||||
return Some(value);
|
||||
return Some(value.path);
|
||||
}
|
||||
|
||||
if let Some(ModuleDefId::EnumVariantId(variant)) = item.as_module_def_id() {
|
||||
// - if the item is an enum variant, refer to it via the enum
|
||||
if let Some(mut path) =
|
||||
find_path_inner(ctx, ItemInNs::Types(variant.lookup(ctx.db).parent.into()), max_len)
|
||||
{
|
||||
if let Some(mut path) = find_path_inner(
|
||||
ctx,
|
||||
ItemInNs::Types(variant.lookup(ctx.db).parent.into()),
|
||||
max_len,
|
||||
is_std_item,
|
||||
) {
|
||||
path.push_segment(ctx.db.enum_variant_data(variant).name.clone());
|
||||
return Some(path);
|
||||
}
|
||||
@ -146,22 +152,42 @@ fn find_path_inner(ctx: &FindPathCtx<'_>, item: ItemInNs, max_len: usize) -> Opt
|
||||
// variant somewhere
|
||||
}
|
||||
|
||||
let mut visited_modules = FxHashSet::default();
|
||||
if is_std_item {
|
||||
// The item we are searching for comes from the sysroot libraries, so skip prefer looking in
|
||||
// the sysroot libraries directly.
|
||||
// We do need to fallback as the item in question could be re-exported by another crate
|
||||
// while not being a transitive dependency of the current crate.
|
||||
if let Some(choice) = find_in_sysroot(ctx, &mut FxHashSet::default(), item, max_len) {
|
||||
return Some(choice.path);
|
||||
}
|
||||
}
|
||||
|
||||
calculate_best_path(ctx, &mut visited_modules, item, max_len).map(|(item, _)| item)
|
||||
let mut best_choice = None;
|
||||
calculate_best_path(ctx, &mut FxHashSet::default(), item, max_len, &mut best_choice);
|
||||
best_choice.map(|choice| choice.path)
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
fn find_path_for_module(
|
||||
ctx: &FindPathCtx<'_>,
|
||||
visited_modules: &mut FxHashSet<ModuleId>,
|
||||
visited_modules: &mut FxHashSet<(ItemInNs, ModuleId)>,
|
||||
module_id: ModuleId,
|
||||
maybe_extern: bool,
|
||||
max_len: usize,
|
||||
) -> Option<(ModPath, Stability)> {
|
||||
) -> Option<Choice> {
|
||||
if max_len == 0 {
|
||||
// recursive base case, we can't find a path of length 0
|
||||
return None;
|
||||
}
|
||||
if let Some(crate_root) = module_id.as_crate_root() {
|
||||
if crate_root == ctx.from.derive_crate_root() {
|
||||
if !maybe_extern || crate_root == ctx.from.derive_crate_root() {
|
||||
// - if the item is the crate root, return `crate`
|
||||
return Some((ModPath::from_segments(PathKind::Crate, None), Stable));
|
||||
return Some(Choice {
|
||||
path: ModPath::from_segments(PathKind::Crate, None),
|
||||
path_text_len: 5,
|
||||
stability: Stable,
|
||||
prefer_due_to_prelude: false,
|
||||
});
|
||||
}
|
||||
// - otherwise if the item is the crate root of a dependency crate, return the name from the extern prelude
|
||||
|
||||
@ -188,7 +214,7 @@ fn find_path_for_module(
|
||||
} else {
|
||||
PathKind::Plain
|
||||
};
|
||||
return Some((ModPath::from_segments(kind, iter::once(name.clone())), Stable));
|
||||
return Some(Choice::new(ctx.cfg.prefer_prelude, kind, name.clone(), Stable));
|
||||
}
|
||||
}
|
||||
|
||||
@ -206,27 +232,39 @@ fn find_path_for_module(
|
||||
);
|
||||
if let Some(scope_name) = scope_name {
|
||||
// - if the item is already in scope, return the name under which it is
|
||||
return Some((
|
||||
ModPath::from_segments(ctx.prefix.path_kind(), iter::once(scope_name)),
|
||||
return Some(Choice::new(
|
||||
ctx.cfg.prefer_prelude,
|
||||
ctx.prefix.path_kind(),
|
||||
scope_name,
|
||||
Stable,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// - if the module can be referenced as self, super or crate, do that
|
||||
if let Some(mod_path) = is_kw_kind_relative_to_from(ctx.from_def_map, module_id, ctx.from) {
|
||||
if ctx.prefix != PrefixKind::ByCrate || mod_path.kind == PathKind::Crate {
|
||||
return Some((mod_path, Stable));
|
||||
if let Some(kind) = is_kw_kind_relative_to_from(ctx.from_def_map, module_id, ctx.from) {
|
||||
if ctx.prefix != PrefixKind::ByCrate || kind == PathKind::Crate {
|
||||
return Some(Choice {
|
||||
path: ModPath::from_segments(kind, None),
|
||||
path_text_len: path_kind_len(kind),
|
||||
stability: Stable,
|
||||
prefer_due_to_prelude: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// - if the module is in the prelude, return it by that path
|
||||
if let Some(mod_path) =
|
||||
find_in_prelude(ctx.db, ctx.from_def_map, ItemInNs::Types(module_id.into()), ctx.from)
|
||||
{
|
||||
return Some((mod_path, Stable));
|
||||
let item = ItemInNs::Types(module_id.into());
|
||||
if let Some(choice) = find_in_prelude(ctx.db, ctx.from_def_map, item, ctx.from) {
|
||||
return Some(choice);
|
||||
}
|
||||
calculate_best_path(ctx, visited_modules, ItemInNs::Types(module_id.into()), max_len)
|
||||
let mut best_choice = None;
|
||||
if maybe_extern {
|
||||
calculate_best_path(ctx, visited_modules, item, max_len, &mut best_choice);
|
||||
} else {
|
||||
calculate_best_path_local(ctx, visited_modules, item, max_len, &mut best_choice);
|
||||
}
|
||||
best_choice
|
||||
}
|
||||
|
||||
fn find_in_scope(
|
||||
@ -251,7 +289,7 @@ fn find_in_prelude(
|
||||
local_def_map: &DefMap,
|
||||
item: ItemInNs,
|
||||
from: ModuleId,
|
||||
) -> Option<ModPath> {
|
||||
) -> Option<Choice> {
|
||||
let (prelude_module, _) = local_def_map.prelude()?;
|
||||
let prelude_def_map = prelude_module.def_map(db);
|
||||
let prelude_scope = &prelude_def_map[prelude_module.local_id].scope;
|
||||
@ -273,7 +311,7 @@ fn find_in_prelude(
|
||||
});
|
||||
|
||||
if found_and_same_def.unwrap_or(true) {
|
||||
Some(ModPath::from_segments(PathKind::Plain, iter::once(name.clone())))
|
||||
Some(Choice::new(false, PathKind::Plain, name.clone(), Stable))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@ -283,7 +321,7 @@ fn is_kw_kind_relative_to_from(
|
||||
def_map: &DefMap,
|
||||
item: ModuleId,
|
||||
from: ModuleId,
|
||||
) -> Option<ModPath> {
|
||||
) -> Option<PathKind> {
|
||||
if item.krate != from.krate || item.is_within_block() || from.is_within_block() {
|
||||
return None;
|
||||
}
|
||||
@ -291,14 +329,11 @@ fn is_kw_kind_relative_to_from(
|
||||
let from = from.local_id;
|
||||
if item == from {
|
||||
// - if the item is the module we're in, use `self`
|
||||
Some(ModPath::from_segments(PathKind::SELF, None))
|
||||
Some(PathKind::SELF)
|
||||
} else if let Some(parent_id) = def_map[from].parent {
|
||||
if item == parent_id {
|
||||
// - if the item is the parent module, use `super` (this is not used recursively, since `super::super` is ugly)
|
||||
Some(ModPath::from_segments(
|
||||
if parent_id == DefMap::ROOT { PathKind::Crate } else { PathKind::Super(1) },
|
||||
None,
|
||||
))
|
||||
Some(if parent_id == DefMap::ROOT { PathKind::Crate } else { PathKind::Super(1) })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@ -310,15 +345,11 @@ fn is_kw_kind_relative_to_from(
|
||||
#[tracing::instrument(skip_all)]
|
||||
fn calculate_best_path(
|
||||
ctx: &FindPathCtx<'_>,
|
||||
visited_modules: &mut FxHashSet<ModuleId>,
|
||||
visited_modules: &mut FxHashSet<(ItemInNs, ModuleId)>,
|
||||
item: ItemInNs,
|
||||
max_len: usize,
|
||||
) -> Option<(ModPath, Stability)> {
|
||||
if max_len <= 1 {
|
||||
// recursive base case, we can't find a path prefix of length 0, one segment is occupied by
|
||||
// the item's name itself.
|
||||
return None;
|
||||
}
|
||||
best_choice: &mut Option<Choice>,
|
||||
) {
|
||||
let fuel = ctx.fuel.get();
|
||||
if fuel == 0 {
|
||||
// we ran out of fuel, so we stop searching here
|
||||
@ -327,144 +358,208 @@ fn calculate_best_path(
|
||||
item.krate(ctx.db),
|
||||
ctx.from.krate()
|
||||
);
|
||||
return None;
|
||||
return;
|
||||
}
|
||||
ctx.fuel.set(fuel - 1);
|
||||
|
||||
let mut best_path = None;
|
||||
let mut best_path_len = max_len;
|
||||
let mut process = |mut path: (ModPath, Stability), name, best_path_len: &mut _| {
|
||||
path.0.push_segment(name);
|
||||
let new_path = match best_path.take() {
|
||||
Some(best_path) => select_best_path(best_path, path, ctx.cfg),
|
||||
None => path,
|
||||
};
|
||||
if new_path.1 == Stable {
|
||||
*best_path_len = new_path.0.len();
|
||||
}
|
||||
match &mut best_path {
|
||||
Some((old_path, old_stability)) => {
|
||||
*old_path = new_path.0;
|
||||
*old_stability = zip_stability(*old_stability, new_path.1);
|
||||
}
|
||||
None => best_path = Some(new_path),
|
||||
}
|
||||
};
|
||||
let db = ctx.db;
|
||||
if item.krate(db) == Some(ctx.from.krate) {
|
||||
if item.krate(ctx.db) == Some(ctx.from.krate) {
|
||||
// Item was defined in the same crate that wants to import it. It cannot be found in any
|
||||
// dependency in this case.
|
||||
// FIXME: cache the `find_local_import_locations` output?
|
||||
find_local_import_locations(db, item, ctx.from, ctx.from_def_map, |name, module_id| {
|
||||
if !visited_modules.insert(module_id) {
|
||||
return;
|
||||
}
|
||||
// we are looking for paths of length up to best_path_len, any longer will make it be
|
||||
// less optimal. The -1 is due to us pushing name onto it afterwards.
|
||||
if let Some(path) =
|
||||
find_path_for_module(ctx, visited_modules, module_id, best_path_len - 1)
|
||||
{
|
||||
process(path, name.clone(), &mut best_path_len);
|
||||
}
|
||||
})
|
||||
calculate_best_path_local(ctx, visited_modules, item, max_len, best_choice)
|
||||
} else {
|
||||
// Item was defined in some upstream crate. This means that it must be exported from one,
|
||||
// too (unless we can't name it at all). It could *also* be (re)exported by the same crate
|
||||
// that wants to import it here, but we always prefer to use the external path here.
|
||||
|
||||
for dep in &db.crate_graph()[ctx.from.krate].dependencies {
|
||||
let import_map = db.import_map(dep.crate_id);
|
||||
let Some(import_info_for) = import_map.import_info_for(item) else { continue };
|
||||
for info in import_info_for {
|
||||
if info.is_doc_hidden {
|
||||
// the item or import is `#[doc(hidden)]`, so skip it as it is in an external crate
|
||||
continue;
|
||||
}
|
||||
|
||||
// Determine best path for containing module and append last segment from `info`.
|
||||
// FIXME: we should guide this to look up the path locally, or from the same crate again?
|
||||
let path =
|
||||
find_path_for_module(ctx, visited_modules, info.container, best_path_len - 1);
|
||||
let Some((path, path_stability)) = path else {
|
||||
continue;
|
||||
};
|
||||
cov_mark::hit!(partially_imported);
|
||||
let path = (
|
||||
path,
|
||||
zip_stability(path_stability, if info.is_unstable { Unstable } else { Stable }),
|
||||
);
|
||||
|
||||
process(path, info.name.clone(), &mut best_path_len);
|
||||
}
|
||||
}
|
||||
ctx.db.crate_graph()[ctx.from.krate].dependencies.iter().for_each(|dep| {
|
||||
find_in_dep(ctx, visited_modules, item, max_len, best_choice, dep.crate_id)
|
||||
});
|
||||
}
|
||||
best_path
|
||||
}
|
||||
|
||||
/// Select the best (most relevant) path between two paths.
|
||||
/// This accounts for stability, path length whether, std should be chosen over alloc/core paths as
|
||||
/// well as ignoring prelude like paths or not.
|
||||
fn select_best_path(
|
||||
old_path @ (_, old_stability): (ModPath, Stability),
|
||||
new_path @ (_, new_stability): (ModPath, Stability),
|
||||
cfg: ImportPathConfig,
|
||||
) -> (ModPath, Stability) {
|
||||
match (old_stability, new_stability) {
|
||||
(Stable, Unstable) => return old_path,
|
||||
(Unstable, Stable) => return new_path,
|
||||
_ => {}
|
||||
}
|
||||
const STD_CRATES: [Name; 3] = [known::std, known::core, known::alloc];
|
||||
|
||||
let choose = |new: (ModPath, _), old: (ModPath, _)| {
|
||||
let (new_path, _) = &new;
|
||||
let (old_path, _) = &old;
|
||||
let new_has_prelude = new_path.segments().iter().any(|seg| seg == &known::prelude);
|
||||
let old_has_prelude = old_path.segments().iter().any(|seg| seg == &known::prelude);
|
||||
match (new_has_prelude, old_has_prelude, cfg.prefer_prelude) {
|
||||
(true, false, true) | (false, true, false) => new,
|
||||
(true, false, false) | (false, true, true) => old,
|
||||
// no prelude difference in the paths, so pick the shorter one
|
||||
(true, true, _) | (false, false, _) => {
|
||||
let new_path_is_shorter = new_path
|
||||
.len()
|
||||
.cmp(&old_path.len())
|
||||
.then_with(|| new_path.textual_len().cmp(&old_path.textual_len()))
|
||||
.is_lt();
|
||||
if new_path_is_shorter {
|
||||
new
|
||||
} else {
|
||||
old
|
||||
}
|
||||
fn find_in_sysroot(
|
||||
ctx: &FindPathCtx<'_>,
|
||||
visited_modules: &mut FxHashSet<(ItemInNs, ModuleId)>,
|
||||
item: ItemInNs,
|
||||
max_len: usize,
|
||||
) -> Option<Choice> {
|
||||
let crate_graph = ctx.db.crate_graph();
|
||||
let dependencies = &crate_graph[ctx.from.krate].dependencies;
|
||||
let mut best_choice = None;
|
||||
let mut search = |lang, best_choice: &mut _| {
|
||||
if let Some(dep) = dependencies.iter().filter(|it| it.is_sysroot()).find(|dep| {
|
||||
match crate_graph[dep.crate_id].origin {
|
||||
CrateOrigin::Lang(l) => l == lang,
|
||||
_ => false,
|
||||
}
|
||||
}) {
|
||||
find_in_dep(ctx, visited_modules, item, max_len, best_choice, dep.crate_id);
|
||||
}
|
||||
};
|
||||
if ctx.cfg.prefer_no_std {
|
||||
search(LangCrateOrigin::Core, &mut best_choice);
|
||||
if matches!(best_choice, Some(Choice { stability: Stable, .. })) {
|
||||
return best_choice;
|
||||
}
|
||||
search(LangCrateOrigin::Std, &mut best_choice);
|
||||
if matches!(best_choice, Some(Choice { stability: Stable, .. })) {
|
||||
return best_choice;
|
||||
}
|
||||
} else {
|
||||
search(LangCrateOrigin::Std, &mut best_choice);
|
||||
if matches!(best_choice, Some(Choice { stability: Stable, .. })) {
|
||||
return best_choice;
|
||||
}
|
||||
search(LangCrateOrigin::Core, &mut best_choice);
|
||||
if matches!(best_choice, Some(Choice { stability: Stable, .. })) {
|
||||
return best_choice;
|
||||
}
|
||||
}
|
||||
let mut best_choice = None;
|
||||
dependencies.iter().filter(|it| it.is_sysroot()).for_each(|dep| {
|
||||
find_in_dep(ctx, visited_modules, item, max_len, &mut best_choice, dep.crate_id);
|
||||
});
|
||||
best_choice
|
||||
}
|
||||
|
||||
match (old_path.0.segments().first(), new_path.0.segments().first()) {
|
||||
(Some(old), Some(new)) if STD_CRATES.contains(old) && STD_CRATES.contains(new) => {
|
||||
let rank = match cfg.prefer_no_std {
|
||||
false => |name: &Name| match name {
|
||||
name if name == &known::core => 0,
|
||||
name if name == &known::alloc => 1,
|
||||
name if name == &known::std => 2,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
true => |name: &Name| match name {
|
||||
name if name == &known::core => 2,
|
||||
name if name == &known::alloc => 1,
|
||||
name if name == &known::std => 0,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
};
|
||||
let nrank = rank(new);
|
||||
let orank = rank(old);
|
||||
match nrank.cmp(&orank) {
|
||||
Ordering::Less => old_path,
|
||||
Ordering::Equal => choose(new_path, old_path),
|
||||
Ordering::Greater => new_path,
|
||||
fn find_in_dep(
|
||||
ctx: &FindPathCtx<'_>,
|
||||
visited_modules: &mut FxHashSet<(ItemInNs, ModuleId)>,
|
||||
item: ItemInNs,
|
||||
max_len: usize,
|
||||
best_choice: &mut Option<Choice>,
|
||||
dep: CrateId,
|
||||
) {
|
||||
let import_map = ctx.db.import_map(dep);
|
||||
let Some(import_info_for) = import_map.import_info_for(item) else {
|
||||
return;
|
||||
};
|
||||
for info in import_info_for {
|
||||
if info.is_doc_hidden {
|
||||
// the item or import is `#[doc(hidden)]`, so skip it as it is in an external crate
|
||||
continue;
|
||||
}
|
||||
|
||||
// Determine best path for containing module and append last segment from `info`.
|
||||
// FIXME: we should guide this to look up the path locally, or from the same crate again?
|
||||
let choice = find_path_for_module(
|
||||
ctx,
|
||||
visited_modules,
|
||||
info.container,
|
||||
true,
|
||||
best_choice.as_ref().map_or(max_len, |it| it.path.len()) - 1,
|
||||
);
|
||||
let Some(mut choice) = choice else {
|
||||
continue;
|
||||
};
|
||||
cov_mark::hit!(partially_imported);
|
||||
if info.is_unstable {
|
||||
choice.stability = Unstable;
|
||||
}
|
||||
|
||||
Choice::try_select(best_choice, choice, ctx.cfg.prefer_prelude, info.name.clone());
|
||||
}
|
||||
}
|
||||
|
||||
fn calculate_best_path_local(
|
||||
ctx: &FindPathCtx<'_>,
|
||||
visited_modules: &mut FxHashSet<(ItemInNs, ModuleId)>,
|
||||
item: ItemInNs,
|
||||
max_len: usize,
|
||||
best_choice: &mut Option<Choice>,
|
||||
) {
|
||||
// FIXME: cache the `find_local_import_locations` output?
|
||||
find_local_import_locations(
|
||||
ctx.db,
|
||||
item,
|
||||
ctx.from,
|
||||
ctx.from_def_map,
|
||||
visited_modules,
|
||||
|visited_modules, name, module_id| {
|
||||
// we are looking for paths of length up to best_path_len, any longer will make it be
|
||||
// less optimal. The -1 is due to us pushing name onto it afterwards.
|
||||
if let Some(choice) = find_path_for_module(
|
||||
ctx,
|
||||
visited_modules,
|
||||
module_id,
|
||||
false,
|
||||
best_choice.as_ref().map_or(max_len, |it| it.path.len()) - 1,
|
||||
) {
|
||||
Choice::try_select(best_choice, choice, ctx.cfg.prefer_prelude, name.clone());
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
struct Choice {
|
||||
path: ModPath,
|
||||
/// The length in characters of the path
|
||||
path_text_len: usize,
|
||||
/// The stability of the path
|
||||
stability: Stability,
|
||||
/// Whether this path contains a prelude segment and preference for it has been signaled
|
||||
prefer_due_to_prelude: bool,
|
||||
}
|
||||
|
||||
impl Choice {
|
||||
fn new(prefer_prelude: bool, kind: PathKind, name: Name, stability: Stability) -> Self {
|
||||
Self {
|
||||
path_text_len: path_kind_len(kind) + name.as_str().len(),
|
||||
stability,
|
||||
prefer_due_to_prelude: prefer_prelude && name == sym::prelude,
|
||||
path: ModPath::from_segments(kind, iter::once(name)),
|
||||
}
|
||||
}
|
||||
|
||||
fn push(mut self, prefer_prelude: bool, name: Name) -> Self {
|
||||
self.path_text_len += name.as_str().len();
|
||||
self.prefer_due_to_prelude |= prefer_prelude && name == sym::prelude;
|
||||
self.path.push_segment(name);
|
||||
self
|
||||
}
|
||||
|
||||
fn try_select(
|
||||
current: &mut Option<Choice>,
|
||||
mut other: Choice,
|
||||
prefer_prelude: bool,
|
||||
name: Name,
|
||||
) {
|
||||
let Some(current) = current else {
|
||||
*current = Some(other.push(prefer_prelude, name));
|
||||
return;
|
||||
};
|
||||
match other
|
||||
.stability
|
||||
.cmp(¤t.stability)
|
||||
.then_with(|| other.prefer_due_to_prelude.cmp(¤t.prefer_due_to_prelude))
|
||||
.then_with(|| (current.path.len()).cmp(&(other.path.len() + 1)))
|
||||
{
|
||||
Ordering::Less => return,
|
||||
Ordering::Equal => {
|
||||
other.path_text_len += name.as_str().len();
|
||||
if let Ordering::Less | Ordering::Equal =
|
||||
current.path_text_len.cmp(&other.path_text_len)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
Ordering::Greater => {
|
||||
other.path_text_len += name.as_str().len();
|
||||
}
|
||||
}
|
||||
_ => choose(new_path, old_path),
|
||||
other.path.push_segment(name);
|
||||
*current = other;
|
||||
}
|
||||
}
|
||||
|
||||
fn path_kind_len(kind: PathKind) -> usize {
|
||||
match kind {
|
||||
PathKind::Plain => 0,
|
||||
PathKind::Super(0) => 4,
|
||||
PathKind::Super(s) => s as usize * 5,
|
||||
PathKind::Crate => 5,
|
||||
PathKind::Abs => 2,
|
||||
PathKind::DollarCrate(_) => 0,
|
||||
}
|
||||
}
|
||||
|
||||
@ -474,7 +569,8 @@ fn find_local_import_locations(
|
||||
item: ItemInNs,
|
||||
from: ModuleId,
|
||||
def_map: &DefMap,
|
||||
mut cb: impl FnMut(&Name, ModuleId),
|
||||
visited_modules: &mut FxHashSet<(ItemInNs, ModuleId)>,
|
||||
mut cb: impl FnMut(&mut FxHashSet<(ItemInNs, ModuleId)>, &Name, ModuleId),
|
||||
) {
|
||||
let _p = tracing::info_span!("find_local_import_locations").entered();
|
||||
|
||||
@ -487,32 +583,29 @@ fn find_local_import_locations(
|
||||
let mut worklist = def_map[from.local_id]
|
||||
.children
|
||||
.values()
|
||||
.map(|child| def_map.module_id(*child))
|
||||
// FIXME: do we need to traverse out of block expressions here?
|
||||
.map(|&child| def_map.module_id(child))
|
||||
.chain(iter::successors(from.containing_module(db), |m| m.containing_module(db)))
|
||||
.zip(iter::repeat(false))
|
||||
.collect::<Vec<_>>();
|
||||
let mut seen: FxHashSet<_> = FxHashSet::default();
|
||||
|
||||
let def_map = def_map.crate_root().def_map(db);
|
||||
let mut block_def_map;
|
||||
let mut cursor = 0;
|
||||
|
||||
while let Some(module) = worklist.pop() {
|
||||
if !seen.insert(module) {
|
||||
continue; // already processed this module
|
||||
while let Some(&mut (module, ref mut processed)) = worklist.get_mut(cursor) {
|
||||
cursor += 1;
|
||||
if !visited_modules.insert((item, module)) {
|
||||
// already processed this module
|
||||
continue;
|
||||
}
|
||||
let ext_def_map;
|
||||
let data = if module.krate == from.krate {
|
||||
if module.block.is_some() {
|
||||
// Re-query the block's DefMap
|
||||
ext_def_map = module.def_map(db);
|
||||
&ext_def_map[module.local_id]
|
||||
} else {
|
||||
// Reuse the root DefMap
|
||||
&def_map[module.local_id]
|
||||
}
|
||||
*processed = true;
|
||||
let data = if module.block.is_some() {
|
||||
// Re-query the block's DefMap
|
||||
block_def_map = module.def_map(db);
|
||||
&block_def_map[module.local_id]
|
||||
} else {
|
||||
// The crate might reexport a module defined in another crate.
|
||||
ext_def_map = module.def_map(db);
|
||||
&ext_def_map[module.local_id]
|
||||
// Reuse the root DefMap
|
||||
&def_map[module.local_id]
|
||||
};
|
||||
|
||||
if let Some((name, vis, declared)) = data.scope.name_of(item) {
|
||||
@ -535,18 +628,30 @@ fn find_local_import_locations(
|
||||
// the item and we're a submodule of it, so can we.
|
||||
// Also this keeps the cached data smaller.
|
||||
if declared || is_pub_or_explicit {
|
||||
cb(name, module);
|
||||
cb(visited_modules, name, module);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Descend into all modules visible from `from`.
|
||||
for (module, vis) in data.scope.modules_in_scope() {
|
||||
if module.krate != from.krate {
|
||||
// We don't need to look at modules from other crates as our item has to be in the
|
||||
// current crate
|
||||
continue;
|
||||
}
|
||||
if visited_modules.contains(&(item, module)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if vis.is_visible_from(db, from) {
|
||||
worklist.push(module);
|
||||
worklist.push((module, false));
|
||||
}
|
||||
}
|
||||
}
|
||||
worklist.into_iter().filter(|&(_, processed)| processed).for_each(|(module, _)| {
|
||||
visited_modules.remove(&(item, module));
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@ -1514,8 +1619,6 @@ fn main() {
|
||||
|
||||
#[test]
|
||||
fn from_inside_module() {
|
||||
// This worked correctly, but the test suite logic was broken.
|
||||
cov_mark::check!(submodule_in_testdb);
|
||||
check_found_path(
|
||||
r#"
|
||||
mod baz {
|
||||
@ -1540,6 +1643,35 @@ mod bar {
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_inside_module2() {
|
||||
check_found_path(
|
||||
r#"
|
||||
mod qux {
|
||||
pub mod baz {
|
||||
pub struct Foo {}
|
||||
}
|
||||
|
||||
mod bar {
|
||||
fn bar() {
|
||||
$0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"#,
|
||||
"crate::qux::baz::Foo",
|
||||
expect![[r#"
|
||||
Plain (imports ✔): super::baz::Foo
|
||||
Plain (imports ✖): super::baz::Foo
|
||||
ByCrate(imports ✔): crate::qux::baz::Foo
|
||||
ByCrate(imports ✖): crate::qux::baz::Foo
|
||||
BySelf (imports ✔): super::baz::Foo
|
||||
BySelf (imports ✖): super::baz::Foo
|
||||
"#]],
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_inside_module_with_inner_items() {
|
||||
check_found_path(
|
||||
@ -1915,4 +2047,34 @@ pub fn c() {}
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn prefer_long_std_over_short_extern() {
|
||||
check_found_path(
|
||||
r#"
|
||||
//- /lib.rs crate:main deps:futures_lite,std,core
|
||||
$0
|
||||
//- /futures_lite.rs crate:futures_lite deps:std,core
|
||||
pub use crate::future::Future;
|
||||
pub mod future {
|
||||
pub use core::future::Future;
|
||||
}
|
||||
//- /std.rs crate:std deps:core
|
||||
pub use core::future;
|
||||
//- /core.rs crate:core
|
||||
pub mod future {
|
||||
pub trait Future {}
|
||||
}
|
||||
"#,
|
||||
"core::future::Future",
|
||||
expect![[r#"
|
||||
Plain (imports ✔): std::future::Future
|
||||
Plain (imports ✖): std::future::Future
|
||||
ByCrate(imports ✔): std::future::Future
|
||||
ByCrate(imports ✖): std::future::Future
|
||||
BySelf (imports ✔): std::future::Future
|
||||
BySelf (imports ✖): std::future::Future
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -18,10 +18,9 @@ pub mod type_ref;
|
||||
use std::fmt;
|
||||
|
||||
use hir_expand::name::Name;
|
||||
use intern::Interned;
|
||||
use intern::{Interned, Symbol};
|
||||
use la_arena::{Idx, RawIdx};
|
||||
use rustc_apfloat::ieee::{Half as f16, Quad as f128};
|
||||
use smallvec::SmallVec;
|
||||
use syntax::ast;
|
||||
|
||||
use crate::{
|
||||
@ -60,41 +59,41 @@ pub type LabelId = Idx<Label>;
|
||||
// We leave float values as a string to avoid double rounding.
|
||||
// For PartialEq, string comparison should work, as ordering is not important
|
||||
// https://github.com/rust-lang/rust-analyzer/issues/12380#issuecomment-1137284360
|
||||
#[derive(Default, Debug, Clone, Eq, PartialEq)]
|
||||
pub struct FloatTypeWrapper(Box<str>);
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct FloatTypeWrapper(Symbol);
|
||||
|
||||
// FIXME(#17451): Use builtin types once stabilised.
|
||||
impl FloatTypeWrapper {
|
||||
pub fn new(value: String) -> Self {
|
||||
Self(value.into())
|
||||
pub fn new(sym: Symbol) -> Self {
|
||||
Self(sym)
|
||||
}
|
||||
|
||||
pub fn to_f128(&self) -> f128 {
|
||||
self.0.parse().unwrap_or_default()
|
||||
self.0.as_str().parse().unwrap_or_default()
|
||||
}
|
||||
|
||||
pub fn to_f64(&self) -> f64 {
|
||||
self.0.parse().unwrap_or_default()
|
||||
self.0.as_str().parse().unwrap_or_default()
|
||||
}
|
||||
|
||||
pub fn to_f32(&self) -> f32 {
|
||||
self.0.parse().unwrap_or_default()
|
||||
self.0.as_str().parse().unwrap_or_default()
|
||||
}
|
||||
|
||||
pub fn to_f16(&self) -> f16 {
|
||||
self.0.parse().unwrap_or_default()
|
||||
self.0.as_str().parse().unwrap_or_default()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for FloatTypeWrapper {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str(&self.0)
|
||||
f.write_str(self.0.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub enum Literal {
|
||||
String(Box<str>),
|
||||
String(Symbol),
|
||||
ByteString(Box<[u8]>),
|
||||
CString(Box<[u8]>),
|
||||
Char(char),
|
||||
@ -130,7 +129,10 @@ impl From<ast::LiteralKind> for Literal {
|
||||
match ast_lit_kind {
|
||||
LiteralKind::IntNumber(lit) => {
|
||||
if let builtin @ Some(_) = lit.suffix().and_then(BuiltinFloat::from_suffix) {
|
||||
Literal::Float(FloatTypeWrapper::new(lit.value_string()), builtin)
|
||||
Literal::Float(
|
||||
FloatTypeWrapper::new(Symbol::intern(&lit.value_string())),
|
||||
builtin,
|
||||
)
|
||||
} else if let builtin @ Some(_) = lit.suffix().and_then(BuiltinUint::from_suffix) {
|
||||
Literal::Uint(lit.value().unwrap_or(0), builtin)
|
||||
} else {
|
||||
@ -140,14 +142,14 @@ impl From<ast::LiteralKind> for Literal {
|
||||
}
|
||||
LiteralKind::FloatNumber(lit) => {
|
||||
let ty = lit.suffix().and_then(BuiltinFloat::from_suffix);
|
||||
Literal::Float(FloatTypeWrapper::new(lit.value_string()), ty)
|
||||
Literal::Float(FloatTypeWrapper::new(Symbol::intern(&lit.value_string())), ty)
|
||||
}
|
||||
LiteralKind::ByteString(bs) => {
|
||||
let text = bs.value().map_or_else(|_| Default::default(), Box::from);
|
||||
Literal::ByteString(text)
|
||||
}
|
||||
LiteralKind::String(s) => {
|
||||
let text = s.value().map_or_else(|_| Default::default(), Box::from);
|
||||
let text = s.value().map_or_else(|_| Symbol::empty(), |it| Symbol::intern(&it));
|
||||
Literal::String(text)
|
||||
}
|
||||
LiteralKind::CString(s) => {
|
||||
@ -522,7 +524,6 @@ pub enum BindingProblems {
|
||||
pub struct Binding {
|
||||
pub name: Name,
|
||||
pub mode: BindingAnnotation,
|
||||
pub definitions: SmallVec<[PatId; 1]>,
|
||||
pub problems: Option<BindingProblems>,
|
||||
}
|
||||
|
||||
@ -537,7 +538,7 @@ pub struct RecordFieldPat {
|
||||
pub enum Pat {
|
||||
Missing,
|
||||
Wild,
|
||||
Tuple { args: Box<[PatId]>, ellipsis: Option<usize> },
|
||||
Tuple { args: Box<[PatId]>, ellipsis: Option<u32> },
|
||||
Or(Box<[PatId]>),
|
||||
Record { path: Option<Box<Path>>, args: Box<[RecordFieldPat]>, ellipsis: bool },
|
||||
Range { start: Option<Box<LiteralOrConst>>, end: Option<Box<LiteralOrConst>> },
|
||||
@ -545,7 +546,7 @@ pub enum Pat {
|
||||
Path(Box<Path>),
|
||||
Lit(ExprId),
|
||||
Bind { id: BindingId, subpat: Option<PatId> },
|
||||
TupleStruct { path: Option<Box<Path>>, args: Box<[PatId]>, ellipsis: Option<usize> },
|
||||
TupleStruct { path: Option<Box<Path>>, args: Box<[PatId]>, ellipsis: Option<u32> },
|
||||
Ref { pat: PatId, mutability: Mutability },
|
||||
Box { inner: PatId },
|
||||
ConstBlock(ExprId),
|
||||
|
@ -1,12 +1,13 @@
|
||||
//! Parses `format_args` input.
|
||||
use std::mem;
|
||||
|
||||
use hir_expand::name::Name;
|
||||
use intern::Symbol;
|
||||
use rustc_parse_format as parse;
|
||||
use span::SyntaxContextId;
|
||||
use stdx::TupleExt;
|
||||
use syntax::{
|
||||
ast::{self, IsString},
|
||||
SmolStr, TextRange, TextSize,
|
||||
TextRange, TextSize,
|
||||
};
|
||||
|
||||
use crate::hir::ExprId;
|
||||
@ -28,7 +29,7 @@ pub struct FormatArguments {
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum FormatArgsPiece {
|
||||
Literal(Box<str>),
|
||||
Literal(Symbol),
|
||||
Placeholder(FormatPlaceholder),
|
||||
}
|
||||
|
||||
@ -174,6 +175,7 @@ pub(crate) fn parse(
|
||||
is_direct_literal: bool,
|
||||
mut synth: impl FnMut(Name) -> ExprId,
|
||||
mut record_usage: impl FnMut(Name, Option<TextRange>),
|
||||
call_ctx: SyntaxContextId,
|
||||
) -> FormatArgs {
|
||||
let Ok(text) = s.value() else {
|
||||
return FormatArgs {
|
||||
@ -248,7 +250,7 @@ pub(crate) fn parse(
|
||||
}
|
||||
}
|
||||
ArgRef::Name(name, span) => {
|
||||
let name = Name::new_text_dont_use(SmolStr::new(name));
|
||||
let name = Name::new(name, tt::IdentIsRaw::No, call_ctx);
|
||||
if let Some((index, _)) = args.by_name(&name) {
|
||||
record_usage(name, span);
|
||||
// Name found in `args`, so we resolve it to its index.
|
||||
@ -289,9 +291,8 @@ pub(crate) fn parse(
|
||||
parse::Piece::NextArgument(arg) => {
|
||||
let parse::Argument { position, position_span, format } = *arg;
|
||||
if !unfinished_literal.is_empty() {
|
||||
template.push(FormatArgsPiece::Literal(
|
||||
mem::take(&mut unfinished_literal).into_boxed_str(),
|
||||
));
|
||||
template.push(FormatArgsPiece::Literal(Symbol::intern(&unfinished_literal)));
|
||||
unfinished_literal.clear();
|
||||
}
|
||||
|
||||
let span = parser.arg_places.get(placeholder_index).and_then(|&s| to_span(s));
|
||||
@ -411,7 +412,7 @@ pub(crate) fn parse(
|
||||
}
|
||||
|
||||
if !unfinished_literal.is_empty() {
|
||||
template.push(FormatArgsPiece::Literal(unfinished_literal.into_boxed_str()));
|
||||
template.push(FormatArgsPiece::Literal(Symbol::intern(&unfinished_literal)));
|
||||
}
|
||||
|
||||
if !invalid_refs.is_empty() {
|
||||
|
@ -9,7 +9,7 @@ use hir_expand::{
|
||||
name::{AsName, Name},
|
||||
AstId,
|
||||
};
|
||||
use intern::Interned;
|
||||
use intern::{sym, Interned, Symbol};
|
||||
use syntax::ast::{self, HasGenericArgs, HasName, IsString};
|
||||
|
||||
use crate::{
|
||||
@ -121,10 +121,10 @@ pub enum TypeRef {
|
||||
Slice(Box<TypeRef>),
|
||||
/// A fn pointer. Last element of the vector is the return type.
|
||||
Fn(
|
||||
Vec<(Option<Name>, TypeRef)>,
|
||||
bool, /*varargs*/
|
||||
bool, /*is_unsafe*/
|
||||
Option<Interned<str>>, /* abi */
|
||||
Box<[(Option<Name>, TypeRef)]>,
|
||||
bool, /*varargs*/
|
||||
bool, /*is_unsafe*/
|
||||
Option<Symbol>, /* abi */
|
||||
),
|
||||
ImplTrait(Vec<Interned<TypeBound>>),
|
||||
DynTrait(Vec<Interned<TypeBound>>),
|
||||
@ -228,19 +228,19 @@ impl TypeRef {
|
||||
})
|
||||
.collect()
|
||||
} else {
|
||||
Vec::new()
|
||||
Vec::with_capacity(1)
|
||||
};
|
||||
fn lower_abi(abi: ast::Abi) -> Interned<str> {
|
||||
fn lower_abi(abi: ast::Abi) -> Symbol {
|
||||
match abi.abi_string() {
|
||||
Some(tok) => Interned::new_str(tok.text_without_quotes()),
|
||||
Some(tok) => Symbol::intern(tok.text_without_quotes()),
|
||||
// `extern` default to be `extern "C"`.
|
||||
_ => Interned::new_str("C"),
|
||||
_ => sym::C.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
let abi = inner.abi().map(lower_abi);
|
||||
params.push((None, ret_ty));
|
||||
TypeRef::Fn(params, is_varargs, inner.unsafe_token().is_some(), abi)
|
||||
TypeRef::Fn(params.into(), is_varargs, inner.unsafe_token().is_some(), abi)
|
||||
}
|
||||
// for types are close enough for our purposes to the inner type for now...
|
||||
ast::Type::ForType(inner) => TypeRef::from_ast_opt(ctx, inner.ty()),
|
||||
@ -379,6 +379,7 @@ impl TypeBound {
|
||||
None => TypeBound::Error,
|
||||
}
|
||||
}
|
||||
ast::TypeBoundKind::Use(_) => TypeBound::Error,
|
||||
ast::TypeBoundKind::Lifetime(lifetime) => {
|
||||
TypeBound::Lifetime(LifetimeRef::new(&lifetime))
|
||||
}
|
||||
@ -396,7 +397,7 @@ impl TypeBound {
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum ConstRef {
|
||||
Scalar(LiteralConstRef),
|
||||
Scalar(Box<LiteralConstRef>),
|
||||
Path(Name),
|
||||
Complex(AstId<ast::ConstArg>),
|
||||
}
|
||||
@ -408,7 +409,7 @@ impl ConstRef {
|
||||
return Self::from_expr(expr, Some(lower_ctx.ast_id(&arg)));
|
||||
}
|
||||
}
|
||||
Self::Scalar(LiteralConstRef::Unknown)
|
||||
Self::Scalar(Box::new(LiteralConstRef::Unknown))
|
||||
}
|
||||
|
||||
pub(crate) fn from_const_param(
|
||||
@ -452,10 +453,10 @@ impl ConstRef {
|
||||
ast::Expr::PathExpr(p) if is_path_ident(&p) => {
|
||||
match p.path().and_then(|it| it.segment()).and_then(|it| it.name_ref()) {
|
||||
Some(it) => Self::Path(it.as_name()),
|
||||
None => Self::Scalar(LiteralConstRef::Unknown),
|
||||
None => Self::Scalar(Box::new(LiteralConstRef::Unknown)),
|
||||
}
|
||||
}
|
||||
ast::Expr::Literal(literal) => Self::Scalar(match literal.kind() {
|
||||
ast::Expr::Literal(literal) => Self::Scalar(Box::new(match literal.kind() {
|
||||
ast::LiteralKind::IntNumber(num) => {
|
||||
num.value().map(LiteralConstRef::UInt).unwrap_or(LiteralConstRef::Unknown)
|
||||
}
|
||||
@ -464,12 +465,12 @@ impl ConstRef {
|
||||
}
|
||||
ast::LiteralKind::Bool(f) => LiteralConstRef::Bool(f),
|
||||
_ => LiteralConstRef::Unknown,
|
||||
}),
|
||||
})),
|
||||
_ => {
|
||||
if let Some(ast_id) = ast_id {
|
||||
Self::Complex(ast_id)
|
||||
} else {
|
||||
Self::Scalar(LiteralConstRef::Unknown)
|
||||
Self::Scalar(Box::new(LiteralConstRef::Unknown))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ use itertools::Itertools;
|
||||
use rustc_hash::FxHashSet;
|
||||
use smallvec::SmallVec;
|
||||
use stdx::{format_to, TupleExt};
|
||||
use syntax::ToSmolStr;
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{
|
||||
@ -81,9 +82,9 @@ impl ImportMap {
|
||||
.iter()
|
||||
// We've only collected items, whose name cannot be tuple field so unwrapping is fine.
|
||||
.flat_map(|(&item, (info, _))| {
|
||||
info.iter()
|
||||
.enumerate()
|
||||
.map(move |(idx, info)| (item, info.name.to_smol_str(), idx as u32))
|
||||
info.iter().enumerate().map(move |(idx, info)| {
|
||||
(item, info.name.display(db.upcast()).to_smolstr(), idx as u32)
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
importables.sort_by(|(_, l_info, _), (_, r_info, _)| {
|
||||
@ -412,7 +413,7 @@ pub fn search_dependencies(
|
||||
for map in &import_maps {
|
||||
op = op.add(map.fst.search(&automaton));
|
||||
}
|
||||
search_maps(&import_maps, op.union(), query)
|
||||
search_maps(db, &import_maps, op.union(), query)
|
||||
}
|
||||
SearchMode::Fuzzy => {
|
||||
let automaton = fst::automaton::Subsequence::new(&query.lowercased);
|
||||
@ -420,7 +421,7 @@ pub fn search_dependencies(
|
||||
for map in &import_maps {
|
||||
op = op.add(map.fst.search(&automaton));
|
||||
}
|
||||
search_maps(&import_maps, op.union(), query)
|
||||
search_maps(db, &import_maps, op.union(), query)
|
||||
}
|
||||
SearchMode::Prefix => {
|
||||
let automaton = fst::automaton::Str::new(&query.lowercased).starts_with();
|
||||
@ -428,12 +429,13 @@ pub fn search_dependencies(
|
||||
for map in &import_maps {
|
||||
op = op.add(map.fst.search(&automaton));
|
||||
}
|
||||
search_maps(&import_maps, op.union(), query)
|
||||
search_maps(db, &import_maps, op.union(), query)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn search_maps(
|
||||
db: &dyn DefDatabase,
|
||||
import_maps: &[Arc<ImportMap>],
|
||||
mut stream: fst::map::Union<'_>,
|
||||
query: &Query,
|
||||
@ -459,7 +461,7 @@ fn search_maps(
|
||||
query.search_mode.check(
|
||||
&query.query,
|
||||
query.case_sensitive,
|
||||
&info.name.to_smol_str(),
|
||||
&info.name.display(db.upcast()).to_smolstr(),
|
||||
)
|
||||
});
|
||||
res.extend(iter.map(TupleExt::head));
|
||||
|
@ -1,10 +1,9 @@
|
||||
//! Describes items defined or visible (ie, imported) in a certain scope.
|
||||
//! This is shared between modules and blocks.
|
||||
|
||||
use std::collections::hash_map::Entry;
|
||||
|
||||
use base_db::CrateId;
|
||||
use hir_expand::{attrs::AttrId, db::ExpandDatabase, name::Name, AstId, MacroCallId};
|
||||
use indexmap::map::Entry;
|
||||
use itertools::Itertools;
|
||||
use la_arena::Idx;
|
||||
use once_cell::sync::Lazy;
|
||||
@ -17,8 +16,8 @@ use crate::{
|
||||
db::DefDatabase,
|
||||
per_ns::PerNs,
|
||||
visibility::{Visibility, VisibilityExplicitness},
|
||||
AdtId, BuiltinType, ConstId, ExternCrateId, HasModule, ImplId, LocalModuleId, Lookup, MacroId,
|
||||
ModuleDefId, ModuleId, TraitId, UseId,
|
||||
AdtId, BuiltinType, ConstId, ExternCrateId, FxIndexMap, HasModule, ImplId, LocalModuleId,
|
||||
Lookup, MacroId, ModuleDefId, ModuleId, TraitId, UseId,
|
||||
};
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
@ -62,14 +61,26 @@ pub struct ImportId {
|
||||
pub idx: Idx<ast::UseTree>,
|
||||
}
|
||||
|
||||
impl PerNsGlobImports {
|
||||
pub(crate) fn contains_type(&self, module_id: LocalModuleId, name: Name) -> bool {
|
||||
self.types.contains(&(module_id, name))
|
||||
}
|
||||
pub(crate) fn contains_value(&self, module_id: LocalModuleId, name: Name) -> bool {
|
||||
self.values.contains(&(module_id, name))
|
||||
}
|
||||
pub(crate) fn contains_macro(&self, module_id: LocalModuleId, name: Name) -> bool {
|
||||
self.macros.contains(&(module_id, name))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, PartialEq, Eq)]
|
||||
pub struct ItemScope {
|
||||
/// Defs visible in this scope. This includes `declarations`, but also
|
||||
/// imports. The imports belong to this module and can be resolved by using them on
|
||||
/// the `use_imports_*` fields.
|
||||
types: FxHashMap<Name, (ModuleDefId, Visibility, Option<ImportOrExternCrate>)>,
|
||||
values: FxHashMap<Name, (ModuleDefId, Visibility, Option<ImportId>)>,
|
||||
macros: FxHashMap<Name, (MacroId, Visibility, Option<ImportId>)>,
|
||||
types: FxIndexMap<Name, (ModuleDefId, Visibility, Option<ImportOrExternCrate>)>,
|
||||
values: FxIndexMap<Name, (ModuleDefId, Visibility, Option<ImportId>)>,
|
||||
macros: FxIndexMap<Name, (MacroId, Visibility, Option<ImportId>)>,
|
||||
unresolved: FxHashSet<Name>,
|
||||
|
||||
/// The defs declared in this scope. Each def has a single scope where it is
|
||||
@ -118,8 +129,8 @@ struct DeriveMacroInvocation {
|
||||
derive_call_ids: SmallVec<[Option<MacroCallId>; 1]>,
|
||||
}
|
||||
|
||||
pub(crate) static BUILTIN_SCOPE: Lazy<FxHashMap<Name, PerNs>> = Lazy::new(|| {
|
||||
BuiltinType::ALL
|
||||
pub(crate) static BUILTIN_SCOPE: Lazy<FxIndexMap<Name, PerNs>> = Lazy::new(|| {
|
||||
BuiltinType::all_builtin_types()
|
||||
.iter()
|
||||
.map(|(name, ty)| (name.clone(), PerNs::types((*ty).into(), Visibility::Public, None)))
|
||||
.collect()
|
||||
@ -511,38 +522,48 @@ impl ItemScope {
|
||||
entry.insert(fld);
|
||||
changed = true;
|
||||
}
|
||||
Entry::Occupied(mut entry) if !matches!(import, Some(ImportType::Glob(..))) => {
|
||||
if glob_imports.types.remove(&lookup) {
|
||||
let import = match import {
|
||||
Some(ImportType::ExternCrate(extern_crate)) => {
|
||||
Some(ImportOrExternCrate::ExternCrate(extern_crate))
|
||||
}
|
||||
Some(ImportType::Import(import)) => {
|
||||
Some(ImportOrExternCrate::Import(import))
|
||||
}
|
||||
None | Some(ImportType::Glob(_)) => None,
|
||||
};
|
||||
let prev = std::mem::replace(&mut fld.2, import);
|
||||
if let Some(import) = import {
|
||||
self.use_imports_types.insert(
|
||||
import,
|
||||
match prev {
|
||||
Some(ImportOrExternCrate::Import(import)) => {
|
||||
ImportOrDef::Import(import)
|
||||
}
|
||||
Some(ImportOrExternCrate::ExternCrate(import)) => {
|
||||
ImportOrDef::ExternCrate(import)
|
||||
}
|
||||
None => ImportOrDef::Def(fld.0),
|
||||
},
|
||||
);
|
||||
Entry::Occupied(mut entry) => {
|
||||
match import {
|
||||
Some(ImportType::Glob(..)) => {
|
||||
// Multiple globs may import the same item and they may
|
||||
// override visibility from previously resolved globs. This is
|
||||
// currently handled by `DefCollector`, because we need to
|
||||
// compute the max visibility for items and we need `DefMap`
|
||||
// for that.
|
||||
}
|
||||
_ => {
|
||||
if glob_imports.types.remove(&lookup) {
|
||||
let import = match import {
|
||||
Some(ImportType::ExternCrate(extern_crate)) => {
|
||||
Some(ImportOrExternCrate::ExternCrate(extern_crate))
|
||||
}
|
||||
Some(ImportType::Import(import)) => {
|
||||
Some(ImportOrExternCrate::Import(import))
|
||||
}
|
||||
None | Some(ImportType::Glob(_)) => None,
|
||||
};
|
||||
let prev = std::mem::replace(&mut fld.2, import);
|
||||
if let Some(import) = import {
|
||||
self.use_imports_types.insert(
|
||||
import,
|
||||
match prev {
|
||||
Some(ImportOrExternCrate::Import(import)) => {
|
||||
ImportOrDef::Import(import)
|
||||
}
|
||||
Some(ImportOrExternCrate::ExternCrate(import)) => {
|
||||
ImportOrDef::ExternCrate(import)
|
||||
}
|
||||
None => ImportOrDef::Def(fld.0),
|
||||
},
|
||||
);
|
||||
}
|
||||
cov_mark::hit!(import_shadowed);
|
||||
entry.insert(fld);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
cov_mark::hit!(import_shadowed);
|
||||
entry.insert(fld);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
@ -757,6 +778,27 @@ impl ItemScope {
|
||||
}
|
||||
}
|
||||
|
||||
// These methods are a temporary measure only meant to be used by `DefCollector::push_res_and_update_glob_vis()`.
|
||||
impl ItemScope {
|
||||
pub(crate) fn update_visibility_types(&mut self, name: &Name, vis: Visibility) {
|
||||
let res =
|
||||
self.types.get_mut(name).expect("tried to update visibility of non-existent type");
|
||||
res.1 = vis;
|
||||
}
|
||||
|
||||
pub(crate) fn update_visibility_values(&mut self, name: &Name, vis: Visibility) {
|
||||
let res =
|
||||
self.values.get_mut(name).expect("tried to update visibility of non-existent value");
|
||||
res.1 = vis;
|
||||
}
|
||||
|
||||
pub(crate) fn update_visibility_macros(&mut self, name: &Name, vis: Visibility) {
|
||||
let res =
|
||||
self.macros.get_mut(name).expect("tried to update visibility of non-existent macro");
|
||||
res.1 = vis;
|
||||
}
|
||||
}
|
||||
|
||||
impl PerNs {
|
||||
pub(crate) fn from_def(
|
||||
def: ModuleDefId,
|
||||
|
@ -46,8 +46,8 @@ use ast::{AstNode, StructKind};
|
||||
use base_db::CrateId;
|
||||
use either::Either;
|
||||
use hir_expand::{attrs::RawAttrs, name::Name, ExpandTo, HirFileId, InFile};
|
||||
use intern::Interned;
|
||||
use la_arena::{Arena, Idx, IdxRange, RawIdx};
|
||||
use intern::{Interned, Symbol};
|
||||
use la_arena::{Arena, Idx, RawIdx};
|
||||
use once_cell::sync::OnceCell;
|
||||
use rustc_hash::FxHashMap;
|
||||
use smallvec::SmallVec;
|
||||
@ -218,9 +218,7 @@ impl ItemTree {
|
||||
extern_crates,
|
||||
extern_blocks,
|
||||
functions,
|
||||
params,
|
||||
structs,
|
||||
fields,
|
||||
unions,
|
||||
enums,
|
||||
variants,
|
||||
@ -241,9 +239,7 @@ impl ItemTree {
|
||||
extern_crates.shrink_to_fit();
|
||||
extern_blocks.shrink_to_fit();
|
||||
functions.shrink_to_fit();
|
||||
params.shrink_to_fit();
|
||||
structs.shrink_to_fit();
|
||||
fields.shrink_to_fit();
|
||||
unions.shrink_to_fit();
|
||||
enums.shrink_to_fit();
|
||||
variants.shrink_to_fit();
|
||||
@ -295,9 +291,7 @@ struct ItemTreeData {
|
||||
extern_crates: Arena<ExternCrate>,
|
||||
extern_blocks: Arena<ExternBlock>,
|
||||
functions: Arena<Function>,
|
||||
params: Arena<Param>,
|
||||
structs: Arena<Struct>,
|
||||
fields: Arena<Field>,
|
||||
unions: Arena<Union>,
|
||||
enums: Arena<Enum>,
|
||||
variants: Arena<Variant>,
|
||||
@ -315,7 +309,7 @@ struct ItemTreeData {
|
||||
vis: ItemVisibilities,
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, Hash)]
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||
pub enum AttrOwner {
|
||||
/// Attributes on an item.
|
||||
ModItem(ModItem),
|
||||
@ -323,12 +317,28 @@ pub enum AttrOwner {
|
||||
TopLevel,
|
||||
|
||||
Variant(FileItemTreeId<Variant>),
|
||||
Field(Idx<Field>),
|
||||
Param(Idx<Param>),
|
||||
Field(FieldParent, ItemTreeFieldId),
|
||||
Param(FileItemTreeId<Function>, ItemTreeParamId),
|
||||
TypeOrConstParamData(GenericModItem, LocalTypeOrConstParamId),
|
||||
LifetimeParamData(GenericModItem, LocalLifetimeParamId),
|
||||
}
|
||||
|
||||
impl AttrOwner {
|
||||
pub fn make_field_indexed(parent: FieldParent, idx: usize) -> Self {
|
||||
AttrOwner::Field(parent, ItemTreeFieldId::from_raw(RawIdx::from_u32(idx as u32)))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||
pub enum FieldParent {
|
||||
Struct(FileItemTreeId<Struct>),
|
||||
Union(FileItemTreeId<Union>),
|
||||
Variant(FileItemTreeId<Variant>),
|
||||
}
|
||||
|
||||
pub type ItemTreeParamId = Idx<Param>;
|
||||
pub type ItemTreeFieldId = Idx<Field>;
|
||||
|
||||
macro_rules! from_attrs {
|
||||
( $( $var:ident($t:ty) ),+ $(,)? ) => {
|
||||
$(
|
||||
@ -341,12 +351,7 @@ macro_rules! from_attrs {
|
||||
};
|
||||
}
|
||||
|
||||
from_attrs!(
|
||||
ModItem(ModItem),
|
||||
Variant(FileItemTreeId<Variant>),
|
||||
Field(Idx<Field>),
|
||||
Param(Idx<Param>),
|
||||
);
|
||||
from_attrs!(ModItem(ModItem), Variant(FileItemTreeId<Variant>));
|
||||
|
||||
/// Trait implemented by all nodes in the item tree.
|
||||
pub trait ItemTreeNode: Clone {
|
||||
@ -365,7 +370,7 @@ pub trait GenericsItemTreeNode: ItemTreeNode {
|
||||
pub struct FileItemTreeId<N>(Idx<N>);
|
||||
|
||||
impl<N> FileItemTreeId<N> {
|
||||
pub fn range_iter(range: Range<Self>) -> impl Iterator<Item = Self> {
|
||||
pub fn range_iter(range: Range<Self>) -> impl Iterator<Item = Self> + Clone {
|
||||
(range.start.index().into_raw().into_u32()..range.end.index().into_raw().into_u32())
|
||||
.map(RawIdx::from_u32)
|
||||
.map(Idx::from_raw)
|
||||
@ -417,18 +422,18 @@ impl TreeId {
|
||||
Self { file, block }
|
||||
}
|
||||
|
||||
pub(crate) fn item_tree(&self, db: &dyn DefDatabase) -> Arc<ItemTree> {
|
||||
pub fn item_tree(&self, db: &dyn DefDatabase) -> Arc<ItemTree> {
|
||||
match self.block {
|
||||
Some(block) => db.block_item_tree(block),
|
||||
None => db.file_item_tree(self.file),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn file_id(self) -> HirFileId {
|
||||
pub fn file_id(self) -> HirFileId {
|
||||
self.file
|
||||
}
|
||||
|
||||
pub(crate) fn is_block(self) -> bool {
|
||||
pub fn is_block(self) -> bool {
|
||||
self.block.is_some()
|
||||
}
|
||||
}
|
||||
@ -505,6 +510,27 @@ macro_rules! mod_items {
|
||||
)+
|
||||
}
|
||||
|
||||
impl ModItem {
|
||||
pub fn ast_id(&self, tree: &ItemTree) -> FileAstId<ast::Item> {
|
||||
match self {
|
||||
$(ModItem::$typ(it) => tree[it.index()].ast_id().upcast()),+
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl GenericModItem {
|
||||
pub fn ast_id(&self, tree: &ItemTree) -> FileAstId<ast::AnyHasGenericParams> {
|
||||
match self {
|
||||
$(
|
||||
$(
|
||||
#[cfg_attr(ignore_fragment, $generic_params)]
|
||||
GenericModItem::$typ(it) => tree[it.index()].ast_id().upcast(),
|
||||
)?
|
||||
)+
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<GenericModItem> for ModItem {
|
||||
fn from(id: GenericModItem) -> ModItem {
|
||||
match id {
|
||||
@ -596,22 +622,6 @@ mod_items! {
|
||||
Macro2 in macro_defs -> ast::MacroDef,
|
||||
}
|
||||
|
||||
macro_rules! impl_index {
|
||||
( $($fld:ident: $t:ty),+ $(,)? ) => {
|
||||
$(
|
||||
impl Index<Idx<$t>> for ItemTree {
|
||||
type Output = $t;
|
||||
|
||||
fn index(&self, index: Idx<$t>) -> &Self::Output {
|
||||
&self.data().$fld[index]
|
||||
}
|
||||
}
|
||||
)+
|
||||
};
|
||||
}
|
||||
|
||||
impl_index!(fields: Field, variants: Variant, params: Param);
|
||||
|
||||
impl Index<RawVisibilityId> for ItemTree {
|
||||
type Output = RawVisibility;
|
||||
fn index(&self, index: RawVisibilityId) -> &Self::Output {
|
||||
@ -712,7 +722,7 @@ pub struct ExternCrate {
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct ExternBlock {
|
||||
pub abi: Option<Interned<str>>,
|
||||
pub abi: Option<Symbol>,
|
||||
pub ast_id: FileAstId<ast::ExternBlock>,
|
||||
pub children: Box<[ModItem]>,
|
||||
}
|
||||
@ -722,8 +732,8 @@ pub struct Function {
|
||||
pub name: Name,
|
||||
pub visibility: RawVisibilityId,
|
||||
pub explicit_generic_params: Interned<GenericParams>,
|
||||
pub abi: Option<Interned<str>>,
|
||||
pub params: IdxRange<Param>,
|
||||
pub abi: Option<Symbol>,
|
||||
pub params: Box<[Param]>,
|
||||
pub ret_type: Interned<TypeRef>,
|
||||
pub ast_id: FileAstId<ast::Fn>,
|
||||
pub(crate) flags: FnFlags,
|
||||
@ -731,15 +741,7 @@ pub struct Function {
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Param {
|
||||
/// This is [`None`] for varargs
|
||||
pub type_ref: Option<Interned<TypeRef>>,
|
||||
pub ast_id: ParamAstId,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum ParamAstId {
|
||||
Param(FileAstId<ast::Param>),
|
||||
SelfParam(FileAstId<ast::SelfParam>),
|
||||
}
|
||||
|
||||
bitflags::bitflags! {
|
||||
@ -760,7 +762,8 @@ pub struct Struct {
|
||||
pub name: Name,
|
||||
pub visibility: RawVisibilityId,
|
||||
pub generic_params: Interned<GenericParams>,
|
||||
pub fields: Fields,
|
||||
pub fields: Box<[Field]>,
|
||||
pub shape: FieldsShape,
|
||||
pub ast_id: FileAstId<ast::Struct>,
|
||||
}
|
||||
|
||||
@ -769,7 +772,7 @@ pub struct Union {
|
||||
pub name: Name,
|
||||
pub visibility: RawVisibilityId,
|
||||
pub generic_params: Interned<GenericParams>,
|
||||
pub fields: Fields,
|
||||
pub fields: Box<[Field]>,
|
||||
pub ast_id: FileAstId<ast::Union>,
|
||||
}
|
||||
|
||||
@ -782,6 +785,29 @@ pub struct Enum {
|
||||
pub ast_id: FileAstId<ast::Enum>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Variant {
|
||||
pub name: Name,
|
||||
pub fields: Box<[Field]>,
|
||||
pub shape: FieldsShape,
|
||||
pub ast_id: FileAstId<ast::Variant>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum FieldsShape {
|
||||
Record,
|
||||
Tuple,
|
||||
Unit,
|
||||
}
|
||||
|
||||
/// A single field of an enum variant or struct
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Field {
|
||||
pub name: Name,
|
||||
pub type_ref: Interned<TypeRef>,
|
||||
pub visibility: RawVisibilityId,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct Const {
|
||||
/// `None` for `const _: () = ();`
|
||||
@ -1039,28 +1065,6 @@ impl ModItem {
|
||||
&ModItem::Function(func) => Some(AssocItem::Function(func)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ast_id(&self, tree: &ItemTree) -> FileAstId<ast::Item> {
|
||||
match self {
|
||||
ModItem::Use(it) => tree[it.index()].ast_id().upcast(),
|
||||
ModItem::ExternCrate(it) => tree[it.index()].ast_id().upcast(),
|
||||
ModItem::ExternBlock(it) => tree[it.index()].ast_id().upcast(),
|
||||
ModItem::Function(it) => tree[it.index()].ast_id().upcast(),
|
||||
ModItem::Struct(it) => tree[it.index()].ast_id().upcast(),
|
||||
ModItem::Union(it) => tree[it.index()].ast_id().upcast(),
|
||||
ModItem::Enum(it) => tree[it.index()].ast_id().upcast(),
|
||||
ModItem::Const(it) => tree[it.index()].ast_id().upcast(),
|
||||
ModItem::Static(it) => tree[it.index()].ast_id().upcast(),
|
||||
ModItem::Trait(it) => tree[it.index()].ast_id().upcast(),
|
||||
ModItem::TraitAlias(it) => tree[it.index()].ast_id().upcast(),
|
||||
ModItem::Impl(it) => tree[it.index()].ast_id().upcast(),
|
||||
ModItem::TypeAlias(it) => tree[it.index()].ast_id().upcast(),
|
||||
ModItem::Mod(it) => tree[it.index()].ast_id().upcast(),
|
||||
ModItem::MacroCall(it) => tree[it.index()].ast_id().upcast(),
|
||||
ModItem::MacroRules(it) => tree[it.index()].ast_id().upcast(),
|
||||
ModItem::Macro2(it) => tree[it.index()].ast_id().upcast(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
@ -1099,32 +1103,3 @@ impl AssocItem {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Variant {
|
||||
pub name: Name,
|
||||
pub fields: Fields,
|
||||
pub ast_id: FileAstId<ast::Variant>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum Fields {
|
||||
Record(IdxRange<Field>),
|
||||
Tuple(IdxRange<Field>),
|
||||
Unit,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum FieldAstId {
|
||||
Record(FileAstId<ast::RecordField>),
|
||||
Tuple(FileAstId<ast::TupleField>),
|
||||
}
|
||||
|
||||
/// A single field of an enum variant or struct
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Field {
|
||||
pub name: Name,
|
||||
pub type_ref: Interned<TypeRef>,
|
||||
pub visibility: RawVisibilityId,
|
||||
pub ast_id: FieldAstId,
|
||||
}
|
||||
|
@ -2,7 +2,8 @@
|
||||
|
||||
use std::collections::hash_map::Entry;
|
||||
|
||||
use hir_expand::{mod_path::path, name, name::AsName, span_map::SpanMapRef, HirFileId};
|
||||
use hir_expand::{mod_path::path, name::AsName, span_map::SpanMapRef, HirFileId};
|
||||
use intern::{sym, Symbol};
|
||||
use la_arena::Arena;
|
||||
use rustc_hash::FxHashMap;
|
||||
use span::{AstIdMap, SyntaxContextId};
|
||||
@ -16,12 +17,12 @@ use crate::{
|
||||
db::DefDatabase,
|
||||
generics::{GenericParams, GenericParamsCollector, TypeParamData, TypeParamProvenance},
|
||||
item_tree::{
|
||||
AssocItem, AttrOwner, Const, Either, Enum, ExternBlock, ExternCrate, Field, FieldAstId,
|
||||
Fields, FileItemTreeId, FnFlags, Function, GenericArgs, GenericModItem, Idx, IdxRange,
|
||||
Impl, ImportAlias, Interned, ItemTree, ItemTreeData, ItemTreeNode, Macro2, MacroCall,
|
||||
MacroRules, Mod, ModItem, ModKind, ModPath, Mutability, Name, Param, ParamAstId, Path,
|
||||
Range, RawAttrs, RawIdx, RawVisibilityId, Static, Struct, StructKind, Trait, TraitAlias,
|
||||
TypeAlias, Union, Use, UseTree, UseTreeKind, Variant,
|
||||
AssocItem, AttrOwner, Const, Either, Enum, ExternBlock, ExternCrate, Field, FieldParent,
|
||||
FieldsShape, FileItemTreeId, FnFlags, Function, GenericArgs, GenericModItem, Idx, Impl,
|
||||
ImportAlias, Interned, ItemTree, ItemTreeData, Macro2, MacroCall, MacroRules, Mod, ModItem,
|
||||
ModKind, ModPath, Mutability, Name, Param, Path, Range, RawAttrs, RawIdx, RawVisibilityId,
|
||||
Static, Struct, StructKind, Trait, TraitAlias, TypeAlias, Union, Use, UseTree, UseTreeKind,
|
||||
Variant,
|
||||
},
|
||||
path::AssociatedTypeBinding,
|
||||
type_ref::{LifetimeRef, TraitBoundModifier, TraitRef, TypeBound, TypeRef},
|
||||
@ -29,7 +30,7 @@ use crate::{
|
||||
LocalLifetimeParamId, LocalTypeOrConstParamId,
|
||||
};
|
||||
|
||||
fn id<N: ItemTreeNode>(index: Idx<N>) -> FileItemTreeId<N> {
|
||||
fn id<N>(index: Idx<N>) -> FileItemTreeId<N> {
|
||||
FileItemTreeId(index)
|
||||
}
|
||||
|
||||
@ -192,82 +193,98 @@ impl<'a> Ctx<'a> {
|
||||
let visibility = self.lower_visibility(strukt);
|
||||
let name = strukt.name()?.as_name();
|
||||
let ast_id = self.source_ast_id_map.ast_id(strukt);
|
||||
let fields = self.lower_fields(&strukt.kind());
|
||||
let (fields, kind, attrs) = self.lower_fields(&strukt.kind());
|
||||
let generic_params = self.lower_generic_params(HasImplicitSelf::No, strukt);
|
||||
let res = Struct { name, visibility, generic_params, fields, ast_id };
|
||||
let res = Struct { name, visibility, generic_params, fields, shape: kind, ast_id };
|
||||
let id = id(self.data().structs.alloc(res));
|
||||
for (idx, attr) in attrs {
|
||||
self.add_attrs(
|
||||
AttrOwner::Field(
|
||||
FieldParent::Struct(id),
|
||||
Idx::from_raw(RawIdx::from_u32(idx as u32)),
|
||||
),
|
||||
attr,
|
||||
);
|
||||
}
|
||||
self.write_generic_params_attributes(id.into());
|
||||
Some(id)
|
||||
}
|
||||
|
||||
fn lower_fields(&mut self, strukt_kind: &ast::StructKind) -> Fields {
|
||||
fn lower_fields(
|
||||
&mut self,
|
||||
strukt_kind: &ast::StructKind,
|
||||
) -> (Box<[Field]>, FieldsShape, Vec<(usize, RawAttrs)>) {
|
||||
match strukt_kind {
|
||||
ast::StructKind::Record(it) => {
|
||||
let range = self.lower_record_fields(it);
|
||||
Fields::Record(range)
|
||||
let mut fields = vec![];
|
||||
let mut attrs = vec![];
|
||||
|
||||
for (i, field) in it.fields().enumerate() {
|
||||
let data = self.lower_record_field(&field);
|
||||
fields.push(data);
|
||||
let attr = RawAttrs::new(self.db.upcast(), &field, self.span_map());
|
||||
if !attr.is_empty() {
|
||||
attrs.push((i, attr))
|
||||
}
|
||||
}
|
||||
(fields.into(), FieldsShape::Record, attrs)
|
||||
}
|
||||
ast::StructKind::Tuple(it) => {
|
||||
let range = self.lower_tuple_fields(it);
|
||||
Fields::Tuple(range)
|
||||
let mut fields = vec![];
|
||||
let mut attrs = vec![];
|
||||
|
||||
for (i, field) in it.fields().enumerate() {
|
||||
let data = self.lower_tuple_field(i, &field);
|
||||
fields.push(data);
|
||||
let attr = RawAttrs::new(self.db.upcast(), &field, self.span_map());
|
||||
if !attr.is_empty() {
|
||||
attrs.push((i, attr))
|
||||
}
|
||||
}
|
||||
(fields.into(), FieldsShape::Tuple, attrs)
|
||||
}
|
||||
ast::StructKind::Unit => Fields::Unit,
|
||||
ast::StructKind::Unit => (Box::default(), FieldsShape::Unit, Vec::default()),
|
||||
}
|
||||
}
|
||||
|
||||
fn lower_record_fields(&mut self, fields: &ast::RecordFieldList) -> IdxRange<Field> {
|
||||
let start = self.next_field_idx();
|
||||
for field in fields.fields() {
|
||||
if let Some(data) = self.lower_record_field(&field) {
|
||||
let idx = self.data().fields.alloc(data);
|
||||
self.add_attrs(
|
||||
idx.into(),
|
||||
RawAttrs::new(self.db.upcast(), &field, self.span_map()),
|
||||
);
|
||||
}
|
||||
}
|
||||
let end = self.next_field_idx();
|
||||
IdxRange::new(start..end)
|
||||
}
|
||||
|
||||
fn lower_record_field(&mut self, field: &ast::RecordField) -> Option<Field> {
|
||||
let name = field.name()?.as_name();
|
||||
fn lower_record_field(&mut self, field: &ast::RecordField) -> Field {
|
||||
let name = match field.name() {
|
||||
Some(name) => name.as_name(),
|
||||
None => Name::missing(),
|
||||
};
|
||||
let visibility = self.lower_visibility(field);
|
||||
let type_ref = self.lower_type_ref_opt(field.ty());
|
||||
let ast_id = FieldAstId::Record(self.source_ast_id_map.ast_id(field));
|
||||
let res = Field { name, type_ref, visibility, ast_id };
|
||||
Some(res)
|
||||
}
|
||||
|
||||
fn lower_tuple_fields(&mut self, fields: &ast::TupleFieldList) -> IdxRange<Field> {
|
||||
let start = self.next_field_idx();
|
||||
for (i, field) in fields.fields().enumerate() {
|
||||
let data = self.lower_tuple_field(i, &field);
|
||||
let idx = self.data().fields.alloc(data);
|
||||
self.add_attrs(idx.into(), RawAttrs::new(self.db.upcast(), &field, self.span_map()));
|
||||
}
|
||||
let end = self.next_field_idx();
|
||||
IdxRange::new(start..end)
|
||||
Field { name, type_ref, visibility }
|
||||
}
|
||||
|
||||
fn lower_tuple_field(&mut self, idx: usize, field: &ast::TupleField) -> Field {
|
||||
let name = Name::new_tuple_field(idx);
|
||||
let visibility = self.lower_visibility(field);
|
||||
let type_ref = self.lower_type_ref_opt(field.ty());
|
||||
let ast_id = FieldAstId::Tuple(self.source_ast_id_map.ast_id(field));
|
||||
Field { name, type_ref, visibility, ast_id }
|
||||
Field { name, type_ref, visibility }
|
||||
}
|
||||
|
||||
fn lower_union(&mut self, union: &ast::Union) -> Option<FileItemTreeId<Union>> {
|
||||
let visibility = self.lower_visibility(union);
|
||||
let name = union.name()?.as_name();
|
||||
let ast_id = self.source_ast_id_map.ast_id(union);
|
||||
let fields = match union.record_field_list() {
|
||||
let (fields, _, attrs) = match union.record_field_list() {
|
||||
Some(record_field_list) => self.lower_fields(&StructKind::Record(record_field_list)),
|
||||
None => Fields::Record(IdxRange::new(self.next_field_idx()..self.next_field_idx())),
|
||||
None => (Box::default(), FieldsShape::Record, Vec::default()),
|
||||
};
|
||||
let generic_params = self.lower_generic_params(HasImplicitSelf::No, union);
|
||||
let res = Union { name, visibility, generic_params, fields, ast_id };
|
||||
let id = id(self.data().unions.alloc(res));
|
||||
for (idx, attr) in attrs {
|
||||
self.add_attrs(
|
||||
AttrOwner::Field(
|
||||
FieldParent::Union(id),
|
||||
Idx::from_raw(RawIdx::from_u32(idx as u32)),
|
||||
),
|
||||
attr,
|
||||
);
|
||||
}
|
||||
self.write_generic_params_attributes(id.into());
|
||||
Some(id)
|
||||
}
|
||||
@ -292,24 +309,35 @@ impl<'a> Ctx<'a> {
|
||||
fn lower_variants(&mut self, variants: &ast::VariantList) -> Range<FileItemTreeId<Variant>> {
|
||||
let start = self.next_variant_idx();
|
||||
for variant in variants.variants() {
|
||||
if let Some(data) = self.lower_variant(&variant) {
|
||||
let idx = self.data().variants.alloc(data);
|
||||
self.add_attrs(
|
||||
id(idx).into(),
|
||||
RawAttrs::new(self.db.upcast(), &variant, self.span_map()),
|
||||
);
|
||||
}
|
||||
let idx = self.lower_variant(&variant);
|
||||
self.add_attrs(
|
||||
id(idx).into(),
|
||||
RawAttrs::new(self.db.upcast(), &variant, self.span_map()),
|
||||
);
|
||||
}
|
||||
let end = self.next_variant_idx();
|
||||
FileItemTreeId(start)..FileItemTreeId(end)
|
||||
}
|
||||
|
||||
fn lower_variant(&mut self, variant: &ast::Variant) -> Option<Variant> {
|
||||
let name = variant.name()?.as_name();
|
||||
let fields = self.lower_fields(&variant.kind());
|
||||
fn lower_variant(&mut self, variant: &ast::Variant) -> Idx<Variant> {
|
||||
let name = match variant.name() {
|
||||
Some(name) => name.as_name(),
|
||||
None => Name::missing(),
|
||||
};
|
||||
let (fields, kind, attrs) = self.lower_fields(&variant.kind());
|
||||
let ast_id = self.source_ast_id_map.ast_id(variant);
|
||||
let res = Variant { name, fields, ast_id };
|
||||
Some(res)
|
||||
let res = Variant { name, fields, shape: kind, ast_id };
|
||||
let id = self.data().variants.alloc(res);
|
||||
for (idx, attr) in attrs {
|
||||
self.add_attrs(
|
||||
AttrOwner::Field(
|
||||
FieldParent::Variant(FileItemTreeId(id)),
|
||||
Idx::from_raw(RawIdx::from_u32(idx as u32)),
|
||||
),
|
||||
attr,
|
||||
);
|
||||
}
|
||||
id
|
||||
}
|
||||
|
||||
fn lower_function(&mut self, func: &ast::Fn) -> Option<FileItemTreeId<Function>> {
|
||||
@ -317,13 +345,25 @@ impl<'a> Ctx<'a> {
|
||||
let name = func.name()?.as_name();
|
||||
|
||||
let mut has_self_param = false;
|
||||
let start_param = self.next_param_idx();
|
||||
let mut has_var_args = false;
|
||||
let mut params = vec![];
|
||||
let mut attrs = vec![];
|
||||
let mut push_attr = |idx, attr: RawAttrs| {
|
||||
if !attr.is_empty() {
|
||||
attrs.push((idx, attr))
|
||||
}
|
||||
};
|
||||
if let Some(param_list) = func.param_list() {
|
||||
if let Some(self_param) = param_list.self_param() {
|
||||
push_attr(
|
||||
params.len(),
|
||||
RawAttrs::new(self.db.upcast(), &self_param, self.span_map()),
|
||||
);
|
||||
let self_type = match self_param.ty() {
|
||||
Some(type_ref) => TypeRef::from_ast(&self.body_ctx, type_ref),
|
||||
None => {
|
||||
let self_type = TypeRef::Path(name![Self].into());
|
||||
let self_type =
|
||||
TypeRef::Path(Name::new_symbol_root(sym::Self_.clone()).into());
|
||||
match self_param.kind() {
|
||||
ast::SelfParamKind::Owned => self_type,
|
||||
ast::SelfParamKind::Ref => TypeRef::Reference(
|
||||
@ -340,40 +380,25 @@ impl<'a> Ctx<'a> {
|
||||
}
|
||||
};
|
||||
let type_ref = Interned::new(self_type);
|
||||
let ast_id = self.source_ast_id_map.ast_id(&self_param);
|
||||
let idx = self.data().params.alloc(Param {
|
||||
type_ref: Some(type_ref),
|
||||
ast_id: ParamAstId::SelfParam(ast_id),
|
||||
});
|
||||
self.add_attrs(
|
||||
idx.into(),
|
||||
RawAttrs::new(self.db.upcast(), &self_param, self.span_map()),
|
||||
);
|
||||
params.push(Param { type_ref: Some(type_ref) });
|
||||
has_self_param = true;
|
||||
}
|
||||
for param in param_list.params() {
|
||||
let ast_id = self.source_ast_id_map.ast_id(¶m);
|
||||
let idx = match param.dotdotdot_token() {
|
||||
Some(_) => self
|
||||
.data()
|
||||
.params
|
||||
.alloc(Param { type_ref: None, ast_id: ParamAstId::Param(ast_id) }),
|
||||
push_attr(params.len(), RawAttrs::new(self.db.upcast(), ¶m, self.span_map()));
|
||||
let param = match param.dotdotdot_token() {
|
||||
Some(_) => {
|
||||
has_var_args = true;
|
||||
Param { type_ref: None }
|
||||
}
|
||||
None => {
|
||||
let type_ref = TypeRef::from_ast_opt(&self.body_ctx, param.ty());
|
||||
let ty = Interned::new(type_ref);
|
||||
self.data()
|
||||
.params
|
||||
.alloc(Param { type_ref: Some(ty), ast_id: ParamAstId::Param(ast_id) })
|
||||
Param { type_ref: Some(ty) }
|
||||
}
|
||||
};
|
||||
self.add_attrs(
|
||||
idx.into(),
|
||||
RawAttrs::new(self.db.upcast(), ¶m, self.span_map()),
|
||||
);
|
||||
params.push(param);
|
||||
}
|
||||
}
|
||||
let end_param = self.next_param_idx();
|
||||
let params = IdxRange::new(start_param..end_param);
|
||||
|
||||
let ret_type = match func.ret_type() {
|
||||
Some(rt) => match rt.ty() {
|
||||
@ -415,19 +440,25 @@ impl<'a> Ctx<'a> {
|
||||
if func.unsafe_token().is_some() {
|
||||
flags |= FnFlags::HAS_UNSAFE_KW;
|
||||
}
|
||||
if has_var_args {
|
||||
flags |= FnFlags::IS_VARARGS;
|
||||
}
|
||||
|
||||
let res = Function {
|
||||
name,
|
||||
visibility,
|
||||
explicit_generic_params: self.lower_generic_params(HasImplicitSelf::No, func),
|
||||
abi,
|
||||
params,
|
||||
params: params.into_boxed_slice(),
|
||||
ret_type: Interned::new(ret_type),
|
||||
ast_id,
|
||||
flags,
|
||||
};
|
||||
|
||||
let id = id(self.data().functions.alloc(res));
|
||||
for (idx, attr) in attrs {
|
||||
self.add_attrs(AttrOwner::Param(id, Idx::from_raw(RawIdx::from_u32(idx as u32))), attr);
|
||||
}
|
||||
self.write_generic_params_attributes(id.into());
|
||||
Some(id)
|
||||
}
|
||||
@ -669,7 +700,7 @@ impl<'a> Ctx<'a> {
|
||||
// Traits and trait aliases get the Self type as an implicit first type parameter.
|
||||
generics.type_or_consts.alloc(
|
||||
TypeParamData {
|
||||
name: Some(name![Self]),
|
||||
name: Some(Name::new_symbol_root(sym::Self_.clone())),
|
||||
default: None,
|
||||
provenance: TypeParamProvenance::TraitSelf,
|
||||
}
|
||||
@ -680,7 +711,7 @@ impl<'a> Ctx<'a> {
|
||||
generics.fill_bounds(
|
||||
&self.body_ctx,
|
||||
bounds,
|
||||
Either::Left(TypeRef::Path(name![Self].into())),
|
||||
Either::Left(TypeRef::Path(Name::new_symbol_root(sym::Self_.clone()).into())),
|
||||
);
|
||||
}
|
||||
|
||||
@ -723,21 +754,11 @@ impl<'a> Ctx<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn next_field_idx(&self) -> Idx<Field> {
|
||||
Idx::from_raw(RawIdx::from(
|
||||
self.tree.data.as_ref().map_or(0, |data| data.fields.len() as u32),
|
||||
))
|
||||
}
|
||||
fn next_variant_idx(&self) -> Idx<Variant> {
|
||||
Idx::from_raw(RawIdx::from(
|
||||
self.tree.data.as_ref().map_or(0, |data| data.variants.len() as u32),
|
||||
))
|
||||
}
|
||||
fn next_param_idx(&self) -> Idx<Param> {
|
||||
Idx::from_raw(RawIdx::from(
|
||||
self.tree.data.as_ref().map_or(0, |data| data.params.len() as u32),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
fn desugar_future_path(orig: TypeRef) -> Path {
|
||||
@ -745,7 +766,7 @@ fn desugar_future_path(orig: TypeRef) -> Path {
|
||||
let mut generic_args: Vec<_> =
|
||||
std::iter::repeat(None).take(path.segments().len() - 1).collect();
|
||||
let binding = AssociatedTypeBinding {
|
||||
name: name![Output],
|
||||
name: Name::new_symbol_root(sym::Output.clone()),
|
||||
args: None,
|
||||
type_ref: Some(orig),
|
||||
bounds: Box::default(),
|
||||
@ -764,11 +785,11 @@ enum HasImplicitSelf {
|
||||
No,
|
||||
}
|
||||
|
||||
fn lower_abi(abi: ast::Abi) -> Interned<str> {
|
||||
fn lower_abi(abi: ast::Abi) -> Symbol {
|
||||
match abi.abi_string() {
|
||||
Some(tok) => Interned::new_str(tok.text_without_quotes()),
|
||||
Some(tok) => Symbol::intern(tok.text_without_quotes()),
|
||||
// `extern` default to be `extern "C"`.
|
||||
_ => Interned::new_str("C"),
|
||||
_ => sym::C.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,16 +2,17 @@
|
||||
|
||||
use std::fmt::{self, Write};
|
||||
|
||||
use la_arena::{Idx, RawIdx};
|
||||
use span::ErasedFileAstId;
|
||||
|
||||
use crate::{
|
||||
generics::{TypeOrConstParamData, WherePredicate, WherePredicateTypeTarget},
|
||||
item_tree::{
|
||||
AttrOwner, Const, DefDatabase, Enum, ExternBlock, ExternCrate, Field, FieldAstId, Fields,
|
||||
FileItemTreeId, FnFlags, Function, GenericModItem, GenericParams, Impl, Interned, ItemTree,
|
||||
Macro2, MacroCall, MacroRules, Mod, ModItem, ModKind, Param, ParamAstId, Path, RawAttrs,
|
||||
RawVisibilityId, Static, Struct, Trait, TraitAlias, TypeAlias, TypeBound, TypeRef, Union,
|
||||
Use, UseTree, UseTreeKind, Variant,
|
||||
AttrOwner, Const, DefDatabase, Enum, ExternBlock, ExternCrate, Field, FieldParent,
|
||||
FieldsShape, FileItemTreeId, FnFlags, Function, GenericModItem, GenericParams, Impl,
|
||||
Interned, ItemTree, Macro2, MacroCall, MacroRules, Mod, ModItem, ModKind, Param, Path,
|
||||
RawAttrs, RawVisibilityId, Static, Struct, Trait, TraitAlias, TypeAlias, TypeBound,
|
||||
TypeRef, Union, Use, UseTree, UseTreeKind, Variant,
|
||||
},
|
||||
pretty::{print_path, print_type_bounds, print_type_ref},
|
||||
visibility::RawVisibility,
|
||||
@ -118,19 +119,17 @@ impl Printer<'_> {
|
||||
};
|
||||
}
|
||||
|
||||
fn print_fields(&mut self, fields: &Fields) {
|
||||
match fields {
|
||||
Fields::Record(fields) => {
|
||||
fn print_fields(&mut self, parent: FieldParent, kind: FieldsShape, fields: &[Field]) {
|
||||
match kind {
|
||||
FieldsShape::Record => {
|
||||
self.whitespace();
|
||||
w!(self, "{{");
|
||||
self.indented(|this| {
|
||||
for field in fields.clone() {
|
||||
let Field { visibility, name, type_ref, ast_id } = &this.tree[field];
|
||||
this.print_ast_id(match ast_id {
|
||||
FieldAstId::Record(it) => it.erase(),
|
||||
FieldAstId::Tuple(it) => it.erase(),
|
||||
});
|
||||
this.print_attrs_of(field, "\n");
|
||||
for (idx, Field { name, type_ref, visibility }) in fields.iter().enumerate() {
|
||||
this.print_attrs_of(
|
||||
AttrOwner::Field(parent, Idx::from_raw(RawIdx::from(idx as u32))),
|
||||
"\n",
|
||||
);
|
||||
this.print_visibility(*visibility);
|
||||
w!(this, "{}: ", name.display(self.db.upcast()));
|
||||
this.print_type_ref(type_ref);
|
||||
@ -139,16 +138,14 @@ impl Printer<'_> {
|
||||
});
|
||||
w!(self, "}}");
|
||||
}
|
||||
Fields::Tuple(fields) => {
|
||||
FieldsShape::Tuple => {
|
||||
w!(self, "(");
|
||||
self.indented(|this| {
|
||||
for field in fields.clone() {
|
||||
let Field { visibility, name, type_ref, ast_id } = &this.tree[field];
|
||||
this.print_ast_id(match ast_id {
|
||||
FieldAstId::Record(it) => it.erase(),
|
||||
FieldAstId::Tuple(it) => it.erase(),
|
||||
});
|
||||
this.print_attrs_of(field, "\n");
|
||||
for (idx, Field { name, type_ref, visibility }) in fields.iter().enumerate() {
|
||||
this.print_attrs_of(
|
||||
AttrOwner::Field(parent, Idx::from_raw(RawIdx::from(idx as u32))),
|
||||
"\n",
|
||||
);
|
||||
this.print_visibility(*visibility);
|
||||
w!(this, "{}: ", name.display(self.db.upcast()));
|
||||
this.print_type_ref(type_ref);
|
||||
@ -157,24 +154,30 @@ impl Printer<'_> {
|
||||
});
|
||||
w!(self, ")");
|
||||
}
|
||||
Fields::Unit => {}
|
||||
FieldsShape::Unit => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn print_fields_and_where_clause(&mut self, fields: &Fields, params: &GenericParams) {
|
||||
match fields {
|
||||
Fields::Record(_) => {
|
||||
fn print_fields_and_where_clause(
|
||||
&mut self,
|
||||
parent: FieldParent,
|
||||
kind: FieldsShape,
|
||||
fields: &[Field],
|
||||
params: &GenericParams,
|
||||
) {
|
||||
match kind {
|
||||
FieldsShape::Record => {
|
||||
if self.print_where_clause(params) {
|
||||
wln!(self);
|
||||
}
|
||||
self.print_fields(fields);
|
||||
self.print_fields(parent, kind, fields);
|
||||
}
|
||||
Fields::Unit => {
|
||||
FieldsShape::Unit => {
|
||||
self.print_where_clause(params);
|
||||
self.print_fields(fields);
|
||||
self.print_fields(parent, kind, fields);
|
||||
}
|
||||
Fields::Tuple(_) => {
|
||||
self.print_fields(fields);
|
||||
FieldsShape::Tuple => {
|
||||
self.print_fields(parent, kind, fields);
|
||||
self.print_where_clause(params);
|
||||
}
|
||||
}
|
||||
@ -280,25 +283,20 @@ impl Printer<'_> {
|
||||
w!(self, "(");
|
||||
if !params.is_empty() {
|
||||
self.indented(|this| {
|
||||
for param in params.clone() {
|
||||
this.print_attrs_of(param, "\n");
|
||||
let Param { type_ref, ast_id } = &this.tree[param];
|
||||
this.print_ast_id(match ast_id {
|
||||
ParamAstId::Param(it) => it.erase(),
|
||||
ParamAstId::SelfParam(it) => it.erase(),
|
||||
});
|
||||
match type_ref {
|
||||
Some(ty) => {
|
||||
if flags.contains(FnFlags::HAS_SELF_PARAM) {
|
||||
w!(this, "self: ");
|
||||
}
|
||||
this.print_type_ref(ty);
|
||||
wln!(this, ",");
|
||||
}
|
||||
None => {
|
||||
wln!(this, "...");
|
||||
}
|
||||
};
|
||||
for (idx, Param { type_ref }) in params.iter().enumerate() {
|
||||
this.print_attrs_of(
|
||||
AttrOwner::Param(it, Idx::from_raw(RawIdx::from(idx as u32))),
|
||||
"\n",
|
||||
);
|
||||
if idx == 0 && flags.contains(FnFlags::HAS_SELF_PARAM) {
|
||||
w!(this, "self: ");
|
||||
}
|
||||
if let Some(type_ref) = type_ref {
|
||||
this.print_type_ref(type_ref);
|
||||
} else {
|
||||
wln!(this, "...");
|
||||
}
|
||||
wln!(this, ",");
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -312,13 +310,19 @@ impl Printer<'_> {
|
||||
}
|
||||
}
|
||||
ModItem::Struct(it) => {
|
||||
let Struct { visibility, name, fields, generic_params, ast_id } = &self.tree[it];
|
||||
let Struct { visibility, name, fields, shape: kind, generic_params, ast_id } =
|
||||
&self.tree[it];
|
||||
self.print_ast_id(ast_id.erase());
|
||||
self.print_visibility(*visibility);
|
||||
w!(self, "struct {}", name.display(self.db.upcast()));
|
||||
self.print_generic_params(generic_params, it.into());
|
||||
self.print_fields_and_where_clause(fields, generic_params);
|
||||
if matches!(fields, Fields::Record(_)) {
|
||||
self.print_fields_and_where_clause(
|
||||
FieldParent::Struct(it),
|
||||
*kind,
|
||||
fields,
|
||||
generic_params,
|
||||
);
|
||||
if matches!(kind, FieldsShape::Record) {
|
||||
wln!(self);
|
||||
} else {
|
||||
wln!(self, ";");
|
||||
@ -330,12 +334,13 @@ impl Printer<'_> {
|
||||
self.print_visibility(*visibility);
|
||||
w!(self, "union {}", name.display(self.db.upcast()));
|
||||
self.print_generic_params(generic_params, it.into());
|
||||
self.print_fields_and_where_clause(fields, generic_params);
|
||||
if matches!(fields, Fields::Record(_)) {
|
||||
wln!(self);
|
||||
} else {
|
||||
wln!(self, ";");
|
||||
}
|
||||
self.print_fields_and_where_clause(
|
||||
FieldParent::Union(it),
|
||||
FieldsShape::Record,
|
||||
fields,
|
||||
generic_params,
|
||||
);
|
||||
wln!(self);
|
||||
}
|
||||
ModItem::Enum(it) => {
|
||||
let Enum { name, visibility, variants, generic_params, ast_id } = &self.tree[it];
|
||||
@ -346,11 +351,11 @@ impl Printer<'_> {
|
||||
self.print_where_clause_and_opening_brace(generic_params);
|
||||
self.indented(|this| {
|
||||
for variant in FileItemTreeId::range_iter(variants.clone()) {
|
||||
let Variant { name, fields, ast_id } = &this.tree[variant];
|
||||
let Variant { name, fields, shape: kind, ast_id } = &this.tree[variant];
|
||||
this.print_ast_id(ast_id.erase());
|
||||
this.print_attrs_of(variant, "\n");
|
||||
w!(this, "{}", name.display(self.db.upcast()));
|
||||
this.print_fields(fields);
|
||||
this.print_fields(FieldParent::Variant(variant), *kind, fields);
|
||||
wln!(this, ",");
|
||||
}
|
||||
});
|
||||
|
@ -129,40 +129,34 @@ enum E {
|
||||
#[derive(Debug)]
|
||||
// AstId: 2
|
||||
pub(self) struct Struct {
|
||||
// AstId: 6
|
||||
#[doc = " fld docs"]
|
||||
pub(self) fld: (),
|
||||
}
|
||||
|
||||
// AstId: 3
|
||||
pub(self) struct Tuple(
|
||||
// AstId: 7
|
||||
#[attr]
|
||||
pub(self) 0: u8,
|
||||
);
|
||||
|
||||
// AstId: 4
|
||||
pub(self) union Ize {
|
||||
// AstId: 8
|
||||
pub(self) a: (),
|
||||
// AstId: 9
|
||||
pub(self) b: (),
|
||||
}
|
||||
|
||||
// AstId: 5
|
||||
pub(self) enum E {
|
||||
// AstId: 10
|
||||
// AstId: 6
|
||||
#[doc = " comment on Unit"]
|
||||
Unit,
|
||||
// AstId: 11
|
||||
// AstId: 7
|
||||
#[doc = " comment on Tuple"]
|
||||
Tuple(
|
||||
// AstId: 13
|
||||
pub(self) 0: u8,
|
||||
),
|
||||
// AstId: 12
|
||||
// AstId: 8
|
||||
Struct {
|
||||
// AstId: 14
|
||||
#[doc = " comment on a: u8"]
|
||||
pub(self) a: u8,
|
||||
},
|
||||
@ -201,9 +195,7 @@ trait Tr: SuperTrait + 'lifetime {
|
||||
// AstId: 3
|
||||
pub(self) fn f(
|
||||
#[attr]
|
||||
// AstId: 5
|
||||
u8,
|
||||
// AstId: 6
|
||||
(),
|
||||
) -> () { ... }
|
||||
|
||||
@ -213,12 +205,11 @@ trait Tr: SuperTrait + 'lifetime {
|
||||
Self: SuperTrait,
|
||||
Self: 'lifetime
|
||||
{
|
||||
// AstId: 8
|
||||
// AstId: 6
|
||||
pub(self) type Assoc: AssocBound = Default;
|
||||
|
||||
// AstId: 9
|
||||
// AstId: 7
|
||||
pub(self) fn method(
|
||||
// AstId: 10
|
||||
self: &Self,
|
||||
) -> ();
|
||||
}
|
||||
@ -300,17 +291,11 @@ struct S {
|
||||
expect![[r#"
|
||||
// AstId: 1
|
||||
pub(self) struct S {
|
||||
// AstId: 2
|
||||
pub(self) a: self::Ty,
|
||||
// AstId: 3
|
||||
pub(self) b: super::SuperTy,
|
||||
// AstId: 4
|
||||
pub(self) c: super::super::SuperSuperTy,
|
||||
// AstId: 5
|
||||
pub(self) d: ::abs::Path,
|
||||
// AstId: 6
|
||||
pub(self) e: crate::Crate,
|
||||
// AstId: 7
|
||||
pub(self) f: plain::path::Ty,
|
||||
}
|
||||
"#]],
|
||||
@ -331,13 +316,9 @@ struct S {
|
||||
expect![[r#"
|
||||
// AstId: 1
|
||||
pub(self) struct S {
|
||||
// AstId: 2
|
||||
pub(self) a: Mixed::<'a, T, Item = (), OtherItem = u8>,
|
||||
// AstId: 3
|
||||
pub(self) b: Qualified::<Self=Fully>::Syntax,
|
||||
// AstId: 4
|
||||
pub(self) c: <TypeAnchored>::Path::<'a>,
|
||||
// AstId: 5
|
||||
pub(self) d: dyn for<'a> Trait::<'a>,
|
||||
}
|
||||
"#]],
|
||||
@ -371,15 +352,12 @@ trait Tr<'a, T: 'a>: Super where Self: for<'a> Tr<'a, T> {}
|
||||
T: 'a,
|
||||
T: 'b
|
||||
{
|
||||
// AstId: 8
|
||||
pub(self) field: &'a &'b T,
|
||||
}
|
||||
|
||||
// AstId: 2
|
||||
pub(self) struct Tuple<T, U>(
|
||||
// AstId: 9
|
||||
pub(self) 0: T,
|
||||
// AstId: 10
|
||||
pub(self) 1: U,
|
||||
)
|
||||
where
|
||||
@ -393,9 +371,8 @@ trait Tr<'a, T: 'a>: Super where Self: for<'a> Tr<'a, T> {}
|
||||
T: 'a,
|
||||
T: 'b
|
||||
{
|
||||
// AstId: 12
|
||||
// AstId: 9
|
||||
pub(self) fn f<G>(
|
||||
// AstId: 13
|
||||
impl Copy,
|
||||
) -> impl Copy
|
||||
where
|
||||
|
@ -3,8 +3,8 @@
|
||||
//! This attribute to tell the compiler about semi built-in std library
|
||||
//! features, such as Fn family of traits.
|
||||
use hir_expand::name::Name;
|
||||
use intern::{sym, Symbol};
|
||||
use rustc_hash::FxHashMap;
|
||||
use syntax::SmolStr;
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{
|
||||
@ -191,8 +191,7 @@ impl LangItems {
|
||||
}
|
||||
|
||||
pub(crate) fn lang_attr(db: &dyn DefDatabase, item: AttrDefId) -> Option<LangItem> {
|
||||
let attrs = db.attrs(item);
|
||||
attrs.by_key("lang").string_value().and_then(LangItem::from_str)
|
||||
db.attrs(item).lang_item()
|
||||
}
|
||||
|
||||
pub(crate) fn notable_traits_in_deps(
|
||||
@ -253,17 +252,16 @@ macro_rules! language_item_table {
|
||||
}
|
||||
|
||||
impl LangItem {
|
||||
pub fn name(self) -> SmolStr {
|
||||
pub fn name(self) -> &'static str {
|
||||
match self {
|
||||
$( LangItem::$variant => SmolStr::new(stringify!($name)), )*
|
||||
$( LangItem::$variant => stringify!($name), )*
|
||||
}
|
||||
}
|
||||
|
||||
/// Opposite of [`LangItem::name`]
|
||||
#[allow(clippy::should_implement_trait)]
|
||||
pub fn from_str(name: &str) -> Option<Self> {
|
||||
match name {
|
||||
$( stringify!($name) => Some(LangItem::$variant), )*
|
||||
pub fn from_symbol(sym: &Symbol) -> Option<Self> {
|
||||
match sym {
|
||||
$(sym if *sym == $module::$name => Some(LangItem::$variant), )*
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@ -274,7 +272,7 @@ macro_rules! language_item_table {
|
||||
impl LangItem {
|
||||
/// Opposite of [`LangItem::name`]
|
||||
pub fn from_name(name: &hir_expand::name::Name) -> Option<Self> {
|
||||
Self::from_str(name.as_str()?)
|
||||
Self::from_symbol(name.symbol())
|
||||
}
|
||||
|
||||
pub fn path(&self, db: &dyn DefDatabase, start_crate: CrateId) -> Option<Path> {
|
||||
@ -360,7 +358,7 @@ language_item_table! {
|
||||
DerefTarget, sym::deref_target, deref_target, Target::AssocTy, GenericRequirement::None;
|
||||
Receiver, sym::receiver, receiver_trait, Target::Trait, GenericRequirement::None;
|
||||
|
||||
Fn, kw::fn, fn_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
Fn, sym::fn_, fn_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
FnMut, sym::fn_mut, fn_mut_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
FnOnce, sym::fn_once, fn_once_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
|
||||
|
@ -46,7 +46,6 @@ pub mod body;
|
||||
pub mod resolver;
|
||||
|
||||
pub mod nameres;
|
||||
mod trace;
|
||||
|
||||
pub mod child_by_source;
|
||||
pub mod src;
|
||||
@ -76,9 +75,7 @@ use base_db::{
|
||||
CrateId,
|
||||
};
|
||||
use hir_expand::{
|
||||
builtin_attr_macro::BuiltinAttrExpander,
|
||||
builtin_derive_macro::BuiltinDeriveExpander,
|
||||
builtin_fn_macro::{BuiltinFnLikeExpander, EagerExpander},
|
||||
builtin::{BuiltinAttrExpander, BuiltinDeriveExpander, BuiltinFnLikeExpander, EagerExpander},
|
||||
db::ExpandDatabase,
|
||||
eager::expand_eager_macro_input,
|
||||
impl_intern_lookup,
|
||||
@ -862,7 +859,7 @@ impl GeneralConstId {
|
||||
.const_data(const_id)
|
||||
.name
|
||||
.as_ref()
|
||||
.and_then(|it| it.as_str())
|
||||
.map(|it| it.as_str())
|
||||
.unwrap_or("_")
|
||||
.to_owned(),
|
||||
GeneralConstId::ConstBlockId(id) => format!("{{anonymous const {id:?}}}"),
|
||||
@ -1437,7 +1434,10 @@ impl AsMacroCall for InFile<&ast::MacroCall> {
|
||||
});
|
||||
|
||||
let Some((call_site, path)) = path else {
|
||||
return Ok(ExpandResult::only_err(ExpandError::other("malformed macro invocation")));
|
||||
return Ok(ExpandResult::only_err(ExpandError::other(
|
||||
span_map.span_for_range(self.value.syntax().text_range()),
|
||||
"malformed macro invocation",
|
||||
)));
|
||||
};
|
||||
|
||||
macro_call_as_call_id_with_eager(
|
||||
|
@ -154,7 +154,7 @@ fn main() { file!(); }
|
||||
#[rustc_builtin_macro]
|
||||
macro_rules! file {() => {}}
|
||||
|
||||
fn main() { ""; }
|
||||
fn main() { "file"; }
|
||||
"##]],
|
||||
);
|
||||
}
|
||||
@ -439,7 +439,7 @@ macro_rules! include_bytes {
|
||||
($file:expr,) => {{ /* compiler built-in */ }};
|
||||
}
|
||||
|
||||
fn main() { include_bytes("foo"); }
|
||||
fn main() { include_bytes("foo");include_bytes(r"foo"); }
|
||||
"#,
|
||||
expect![[r##"
|
||||
#[rustc_builtin_macro]
|
||||
@ -448,7 +448,7 @@ macro_rules! include_bytes {
|
||||
($file:expr,) => {{ /* compiler built-in */ }};
|
||||
}
|
||||
|
||||
fn main() { include_bytes("foo"); }
|
||||
fn main() { include_bytes("foo");include_bytes(r"foo"); }
|
||||
"##]],
|
||||
);
|
||||
}
|
||||
@ -460,13 +460,13 @@ fn test_concat_expand() {
|
||||
#[rustc_builtin_macro]
|
||||
macro_rules! concat {}
|
||||
|
||||
fn main() { concat!("fo", "o", 0, r#"bar"#, "\n", false, '"', '\0'); }
|
||||
fn main() { concat!("fo", "o", 0, r#""bar""#, "\n", false, '"', '\0'); }
|
||||
"##,
|
||||
expect![[r##"
|
||||
#[rustc_builtin_macro]
|
||||
macro_rules! concat {}
|
||||
|
||||
fn main() { "foo0bar\nfalse\"\u{0}"; }
|
||||
fn main() { "foo0\"bar\"\nfalse\"\u{0}"; }
|
||||
"##]],
|
||||
);
|
||||
}
|
||||
@ -478,13 +478,13 @@ fn test_concat_bytes_expand() {
|
||||
#[rustc_builtin_macro]
|
||||
macro_rules! concat_bytes {}
|
||||
|
||||
fn main() { concat_bytes!(b'A', b"BC", [68, b'E', 70]); }
|
||||
fn main() { concat_bytes!(b'A', b"BC\"", [68, b'E', 70], br#"G""#,b'\0'); }
|
||||
"##,
|
||||
expect![[r#"
|
||||
#[rustc_builtin_macro]
|
||||
macro_rules! concat_bytes {}
|
||||
|
||||
fn main() { [b'A', 66, 67, 68, b'E', 70]; }
|
||||
fn main() { b"ABC\"DEFG\"\x00"; }
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
@ -1921,3 +1921,59 @@ fn f() {
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_edition_handling_out() {
|
||||
check(
|
||||
r#"
|
||||
//- /main.rs crate:main deps:old edition:2021
|
||||
macro_rules! r#try {
|
||||
($it:expr) => {
|
||||
$it?
|
||||
};
|
||||
}
|
||||
fn f() {
|
||||
old::invoke_bare_try!(0);
|
||||
}
|
||||
//- /old.rs crate:old edition:2015
|
||||
#[macro_export]
|
||||
macro_rules! invoke_bare_try {
|
||||
($it:expr) => {
|
||||
try!($it)
|
||||
};
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
macro_rules! r#try {
|
||||
($it:expr) => {
|
||||
$it?
|
||||
};
|
||||
}
|
||||
fn f() {
|
||||
try!(0);
|
||||
}
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_edition_handling_in() {
|
||||
check(
|
||||
r#"
|
||||
//- /main.rs crate:main deps:old edition:2021
|
||||
fn f() {
|
||||
old::parse_try_old!(try!{});
|
||||
}
|
||||
//- /old.rs crate:old edition:2015
|
||||
#[macro_export]
|
||||
macro_rules! parse_try_old {
|
||||
($it:expr) => {};
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
fn f() {
|
||||
;
|
||||
}
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
@ -1058,7 +1058,7 @@ macro_rules! concat {}
|
||||
macro_rules! line {}
|
||||
|
||||
fn main() {
|
||||
"event 0u32";
|
||||
"event 0";
|
||||
}
|
||||
|
||||
"##]],
|
||||
@ -1084,7 +1084,7 @@ fn main() {
|
||||
macro_rules! concat_bytes {}
|
||||
|
||||
fn main() {
|
||||
let x = /* error: unexpected token in input */[];
|
||||
let x = /* error: unexpected token */b"";
|
||||
}
|
||||
|
||||
"#]],
|
||||
|
@ -24,6 +24,7 @@ use hir_expand::{
|
||||
span_map::SpanMapRef,
|
||||
InFile, MacroFileId, MacroFileIdExt,
|
||||
};
|
||||
use intern::Symbol;
|
||||
use span::Span;
|
||||
use stdx::{format_to, format_to_acc};
|
||||
use syntax::{
|
||||
@ -55,7 +56,7 @@ pub fn identity_when_valid(_attr: TokenStream, item: TokenStream) -> TokenStream
|
||||
"#
|
||||
.into(),
|
||||
ProcMacro {
|
||||
name: "identity_when_valid".into(),
|
||||
name: Symbol::intern("identity_when_valid"),
|
||||
kind: ProcMacroKind::Attr,
|
||||
expander: sync::Arc::new(IdentityWhenValidProcMacroExpander),
|
||||
disabled: false,
|
||||
@ -121,7 +122,7 @@ pub fn identity_when_valid(_attr: TokenStream, item: TokenStream) -> TokenStream
|
||||
|
||||
let mut expn_text = String::new();
|
||||
if let Some(err) = exp.err {
|
||||
format_to!(expn_text, "/* error: {} */", err);
|
||||
format_to!(expn_text, "/* error: {} */", err.render_to_string(&db).0);
|
||||
}
|
||||
let (parse, token_map) = exp.value;
|
||||
if expect_errors {
|
||||
|
@ -59,14 +59,15 @@ mod tests;
|
||||
|
||||
use std::ops::Deref;
|
||||
|
||||
use base_db::{CrateId, FileId};
|
||||
use base_db::CrateId;
|
||||
use hir_expand::{
|
||||
name::Name, proc_macro::ProcMacroKind, ErasedAstId, HirFileId, InFile, MacroCallId, MacroDefId,
|
||||
};
|
||||
use intern::Symbol;
|
||||
use itertools::Itertools;
|
||||
use la_arena::Arena;
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use span::{Edition, FileAstId, ROOT_ERASED_FILE_AST_ID};
|
||||
use span::{Edition, EditionedFileId, FileAstId, FileId, ROOT_ERASED_FILE_AST_ID};
|
||||
use stdx::format_to;
|
||||
use syntax::{ast, SmolStr};
|
||||
use triomphe::Arc;
|
||||
@ -144,15 +145,13 @@ struct DefMapCrateData {
|
||||
/// Side table for resolving derive helpers.
|
||||
exported_derives: FxHashMap<MacroDefId, Box<[Name]>>,
|
||||
fn_proc_macro_mapping: FxHashMap<FunctionId, ProcMacroId>,
|
||||
/// The error that occurred when failing to load the proc-macro dll.
|
||||
proc_macro_loading_error: Option<Box<str>>,
|
||||
|
||||
/// Custom attributes registered with `#![register_attr]`.
|
||||
registered_attrs: Vec<SmolStr>,
|
||||
registered_attrs: Vec<Symbol>,
|
||||
/// Custom tool modules registered with `#![register_tool]`.
|
||||
registered_tools: Vec<SmolStr>,
|
||||
registered_tools: Vec<Symbol>,
|
||||
/// Unstable features of Rust enabled with `#![feature(A, B)]`.
|
||||
unstable_features: FxHashSet<SmolStr>,
|
||||
unstable_features: FxHashSet<Symbol>,
|
||||
/// #[rustc_coherence_is_core]
|
||||
rustc_coherence_is_core: bool,
|
||||
no_core: bool,
|
||||
@ -168,9 +167,8 @@ impl DefMapCrateData {
|
||||
extern_prelude: FxIndexMap::default(),
|
||||
exported_derives: FxHashMap::default(),
|
||||
fn_proc_macro_mapping: FxHashMap::default(),
|
||||
proc_macro_loading_error: None,
|
||||
registered_attrs: Vec::new(),
|
||||
registered_tools: PREDEFINED_TOOLS.into(),
|
||||
registered_tools: PREDEFINED_TOOLS.iter().map(|it| Symbol::intern(it)).collect(),
|
||||
unstable_features: FxHashSet::default(),
|
||||
rustc_coherence_is_core: false,
|
||||
no_core: false,
|
||||
@ -188,7 +186,6 @@ impl DefMapCrateData {
|
||||
registered_attrs,
|
||||
registered_tools,
|
||||
unstable_features,
|
||||
proc_macro_loading_error: _,
|
||||
rustc_coherence_is_core: _,
|
||||
no_core: _,
|
||||
no_std: _,
|
||||
@ -243,14 +240,14 @@ impl std::ops::Index<LocalModuleId> for DefMap {
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
|
||||
pub enum ModuleOrigin {
|
||||
CrateRoot {
|
||||
definition: FileId,
|
||||
definition: EditionedFileId,
|
||||
},
|
||||
/// Note that non-inline modules, by definition, live inside non-macro file.
|
||||
File {
|
||||
is_mod_rs: bool,
|
||||
declaration: FileAstId<ast::Module>,
|
||||
declaration_tree_id: ItemTreeId<Mod>,
|
||||
definition: FileId,
|
||||
definition: EditionedFileId,
|
||||
},
|
||||
Inline {
|
||||
definition_tree_id: ItemTreeId<Mod>,
|
||||
@ -276,7 +273,7 @@ impl ModuleOrigin {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn file_id(&self) -> Option<FileId> {
|
||||
pub fn file_id(&self) -> Option<EditionedFileId> {
|
||||
match self {
|
||||
ModuleOrigin::File { definition, .. } | ModuleOrigin::CrateRoot { definition } => {
|
||||
Some(*definition)
|
||||
@ -323,7 +320,7 @@ pub struct ModuleData {
|
||||
///
|
||||
/// [`None`] for block modules because they are always its `DefMap`'s root.
|
||||
pub parent: Option<LocalModuleId>,
|
||||
pub children: FxHashMap<Name, LocalModuleId>,
|
||||
pub children: FxIndexMap<Name, LocalModuleId>,
|
||||
pub scope: ItemScope,
|
||||
}
|
||||
|
||||
@ -338,7 +335,7 @@ impl DefMap {
|
||||
let _p = tracing::info_span!("crate_def_map_query", ?name).entered();
|
||||
|
||||
let module_data = ModuleData::new(
|
||||
ModuleOrigin::CrateRoot { definition: krate.root_file_id },
|
||||
ModuleOrigin::CrateRoot { definition: krate.root_file_id() },
|
||||
Visibility::Public,
|
||||
);
|
||||
|
||||
@ -349,7 +346,7 @@ impl DefMap {
|
||||
None,
|
||||
);
|
||||
let def_map =
|
||||
collector::collect_defs(db, def_map, TreeId::new(krate.root_file_id.into(), None));
|
||||
collector::collect_defs(db, def_map, TreeId::new(krate.root_file_id().into(), None));
|
||||
|
||||
Arc::new(def_map)
|
||||
}
|
||||
@ -432,7 +429,9 @@ impl DefMap {
|
||||
pub fn modules_for_file(&self, file_id: FileId) -> impl Iterator<Item = LocalModuleId> + '_ {
|
||||
self.modules
|
||||
.iter()
|
||||
.filter(move |(_id, data)| data.origin.file_id() == Some(file_id))
|
||||
.filter(move |(_id, data)| {
|
||||
data.origin.file_id().map(EditionedFileId::file_id) == Some(file_id)
|
||||
})
|
||||
.map(|(id, _data)| id)
|
||||
}
|
||||
|
||||
@ -447,15 +446,15 @@ impl DefMap {
|
||||
self.derive_helpers_in_scope.get(&id.map(|it| it.upcast())).map(Deref::deref)
|
||||
}
|
||||
|
||||
pub fn registered_tools(&self) -> &[SmolStr] {
|
||||
pub fn registered_tools(&self) -> &[Symbol] {
|
||||
&self.data.registered_tools
|
||||
}
|
||||
|
||||
pub fn registered_attrs(&self) -> &[SmolStr] {
|
||||
pub fn registered_attrs(&self) -> &[Symbol] {
|
||||
&self.data.registered_attrs
|
||||
}
|
||||
|
||||
pub fn is_unstable_feature_enabled(&self, feature: &str) -> bool {
|
||||
pub fn is_unstable_feature_enabled(&self, feature: &Symbol) -> bool {
|
||||
self.data.unstable_features.contains(feature)
|
||||
}
|
||||
|
||||
@ -471,10 +470,6 @@ impl DefMap {
|
||||
self.data.fn_proc_macro_mapping.get(&id).copied()
|
||||
}
|
||||
|
||||
pub fn proc_macro_loading_error(&self) -> Option<&str> {
|
||||
self.data.proc_macro_loading_error.as_deref()
|
||||
}
|
||||
|
||||
pub fn krate(&self) -> CrateId {
|
||||
self.krate
|
||||
}
|
||||
@ -593,10 +588,8 @@ impl DefMap {
|
||||
self.data.extern_prelude.iter().map(|(name, &def)| (name, def))
|
||||
}
|
||||
|
||||
pub(crate) fn macro_use_prelude(
|
||||
&self,
|
||||
) -> impl Iterator<Item = (&Name, (MacroId, Option<ExternCrateId>))> + '_ {
|
||||
self.macro_use_prelude.iter().map(|(name, &def)| (name, def))
|
||||
pub(crate) fn macro_use_prelude(&self) -> &FxHashMap<Name, (MacroId, Option<ExternCrateId>)> {
|
||||
&self.macro_use_prelude
|
||||
}
|
||||
|
||||
pub(crate) fn resolve_path(
|
||||
@ -668,7 +661,7 @@ impl ModuleData {
|
||||
origin,
|
||||
visibility,
|
||||
parent: None,
|
||||
children: FxHashMap::default(),
|
||||
children: Default::default(),
|
||||
scope: ItemScope::default(),
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ use hir_expand::{
|
||||
MacroCallId, MacroCallKind, MacroDefId,
|
||||
};
|
||||
use span::SyntaxContextId;
|
||||
use syntax::{ast, SmolStr};
|
||||
use syntax::ast;
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{
|
||||
@ -79,20 +79,20 @@ impl DefMap {
|
||||
let segments = path.segments();
|
||||
|
||||
if let Some(name) = segments.first() {
|
||||
let name = name.to_smol_str();
|
||||
let pred = |n: &_| *n == name;
|
||||
let name = name.symbol();
|
||||
let pred = |n: &_| *n == *name;
|
||||
|
||||
let is_tool = self.data.registered_tools.iter().map(SmolStr::as_str).any(pred);
|
||||
let is_tool = self.data.registered_tools.iter().any(pred);
|
||||
// FIXME: tool modules can be shadowed by actual modules
|
||||
if is_tool {
|
||||
return true;
|
||||
}
|
||||
|
||||
if segments.len() == 1 {
|
||||
if find_builtin_attr_idx(&name).is_some() {
|
||||
if find_builtin_attr_idx(name).is_some() {
|
||||
return true;
|
||||
}
|
||||
if self.data.registered_attrs.iter().map(SmolStr::as_str).any(pred) {
|
||||
if self.data.registered_attrs.iter().any(pred) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -5,24 +5,23 @@
|
||||
|
||||
use std::{cmp::Ordering, iter, mem, ops::Not};
|
||||
|
||||
use base_db::{CrateId, CrateOrigin, Dependency, FileId, LangCrateOrigin};
|
||||
use base_db::{CrateId, CrateOrigin, Dependency, LangCrateOrigin};
|
||||
use cfg::{CfgExpr, CfgOptions};
|
||||
use either::Either;
|
||||
use hir_expand::{
|
||||
attrs::{Attr, AttrId},
|
||||
builtin_attr_macro::find_builtin_attr,
|
||||
builtin_derive_macro::find_builtin_derive,
|
||||
builtin_fn_macro::find_builtin_macro,
|
||||
name::{name, AsName, Name},
|
||||
builtin::{find_builtin_attr, find_builtin_derive, find_builtin_macro},
|
||||
name::{AsName, Name},
|
||||
proc_macro::CustomProcMacroExpander,
|
||||
ExpandTo, HirFileId, InFile, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind,
|
||||
MacroFileIdExt,
|
||||
};
|
||||
use intern::Interned;
|
||||
use intern::{sym, Interned};
|
||||
use itertools::{izip, Itertools};
|
||||
use la_arena::Idx;
|
||||
use limit::Limit;
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use span::{Edition, ErasedFileAstId, FileAstId, SyntaxContextId};
|
||||
use span::{Edition, EditionedFileId, FileAstId, SyntaxContextId};
|
||||
use syntax::ast;
|
||||
use triomphe::Arc;
|
||||
|
||||
@ -31,8 +30,8 @@ use crate::{
|
||||
db::DefDatabase,
|
||||
item_scope::{ImportId, ImportOrExternCrate, ImportType, PerNsGlobImports},
|
||||
item_tree::{
|
||||
self, ExternCrate, Fields, FileItemTreeId, ImportKind, ItemTree, ItemTreeId, ItemTreeNode,
|
||||
Macro2, MacroCall, MacroRules, Mod, ModItem, ModKind, TreeId,
|
||||
self, AttrOwner, ExternCrate, FieldsShape, FileItemTreeId, ImportKind, ItemTree,
|
||||
ItemTreeId, ItemTreeNode, Macro2, MacroCall, MacroRules, Mod, ModItem, ModKind, TreeId,
|
||||
},
|
||||
macro_call_as_call_id, macro_call_as_call_id_with_eager,
|
||||
nameres::{
|
||||
@ -75,31 +74,11 @@ pub(super) fn collect_defs(db: &dyn DefDatabase, def_map: DefMap, tree_id: TreeI
|
||||
}
|
||||
|
||||
let proc_macros = if krate.is_proc_macro {
|
||||
match db.proc_macros().get(&def_map.krate) {
|
||||
Some(Ok(proc_macros)) => Ok(proc_macros
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(idx, it)| {
|
||||
let name = Name::new_text_dont_use(it.name.clone());
|
||||
(
|
||||
name,
|
||||
if !db.expand_proc_attr_macros() {
|
||||
CustomProcMacroExpander::dummy()
|
||||
} else if it.disabled {
|
||||
CustomProcMacroExpander::disabled()
|
||||
} else {
|
||||
CustomProcMacroExpander::new(hir_expand::proc_macro::ProcMacroId::new(
|
||||
idx as u32,
|
||||
))
|
||||
},
|
||||
)
|
||||
})
|
||||
.collect()),
|
||||
Some(Err(e)) => Err(e.clone().into_boxed_str()),
|
||||
None => Err("No proc-macros present for crate".to_owned().into_boxed_str()),
|
||||
}
|
||||
db.proc_macros()
|
||||
.for_crate(def_map.krate, db.syntax_context(tree_id.file_id()))
|
||||
.unwrap_or_default()
|
||||
} else {
|
||||
Ok(vec![])
|
||||
Default::default()
|
||||
};
|
||||
|
||||
let mut collector = DefCollector {
|
||||
@ -248,10 +227,10 @@ struct DefCollector<'a> {
|
||||
mod_dirs: FxHashMap<LocalModuleId, ModDir>,
|
||||
cfg_options: &'a CfgOptions,
|
||||
/// List of procedural macros defined by this crate. This is read from the dynamic library
|
||||
/// built by the build system, and is the list of proc. macros we can actually expand. It is
|
||||
/// empty when proc. macro support is disabled (in which case we still do name resolution for
|
||||
/// them).
|
||||
proc_macros: Result<Vec<(Name, CustomProcMacroExpander)>, Box<str>>,
|
||||
/// built by the build system, and is the list of proc-macros we can actually expand. It is
|
||||
/// empty when proc-macro support is disabled (in which case we still do name resolution for
|
||||
/// them). The bool signals whether the proc-macro has been explicitly disabled for name-resolution.
|
||||
proc_macros: Box<[(Name, CustomProcMacroExpander, bool)]>,
|
||||
is_proc_macro: bool,
|
||||
from_glob_import: PerNsGlobImports,
|
||||
/// If we fail to resolve an attribute on a `ModItem`, we fall back to ignoring the attribute.
|
||||
@ -269,15 +248,11 @@ impl DefCollector<'_> {
|
||||
let _p = tracing::info_span!("seed_with_top_level").entered();
|
||||
|
||||
let crate_graph = self.db.crate_graph();
|
||||
let file_id = crate_graph[self.def_map.krate].root_file_id;
|
||||
let file_id = crate_graph[self.def_map.krate].root_file_id();
|
||||
let item_tree = self.db.file_item_tree(file_id.into());
|
||||
let attrs = item_tree.top_level_attrs(self.db, self.def_map.krate);
|
||||
let crate_data = Arc::get_mut(&mut self.def_map.data).unwrap();
|
||||
|
||||
if let Err(e) = &self.proc_macros {
|
||||
crate_data.proc_macro_loading_error = Some(e.clone());
|
||||
}
|
||||
|
||||
let mut process = true;
|
||||
|
||||
// Process other crate-level attributes.
|
||||
@ -291,43 +266,43 @@ impl DefCollector<'_> {
|
||||
let Some(attr_name) = attr.path.as_ident() else { continue };
|
||||
|
||||
match () {
|
||||
() if *attr_name == hir_expand::name![recursion_limit] => {
|
||||
() if *attr_name == sym::recursion_limit.clone() => {
|
||||
if let Some(limit) = attr.string_value() {
|
||||
if let Ok(limit) = limit.parse() {
|
||||
if let Ok(limit) = limit.as_str().parse() {
|
||||
crate_data.recursion_limit = Some(limit);
|
||||
}
|
||||
}
|
||||
}
|
||||
() if *attr_name == hir_expand::name![crate_type] => {
|
||||
if let Some("proc-macro") = attr.string_value() {
|
||||
() if *attr_name == sym::crate_type.clone() => {
|
||||
if attr.string_value() == Some(&sym::proc_dash_macro) {
|
||||
self.is_proc_macro = true;
|
||||
}
|
||||
}
|
||||
() if *attr_name == hir_expand::name![no_core] => crate_data.no_core = true,
|
||||
() if *attr_name == hir_expand::name![no_std] => crate_data.no_std = true,
|
||||
() if attr_name.as_text().as_deref() == Some("rustc_coherence_is_core") => {
|
||||
() if *attr_name == sym::no_core.clone() => crate_data.no_core = true,
|
||||
() if *attr_name == sym::no_std.clone() => crate_data.no_std = true,
|
||||
() if *attr_name == sym::rustc_coherence_is_core.clone() => {
|
||||
crate_data.rustc_coherence_is_core = true;
|
||||
}
|
||||
() if *attr_name == hir_expand::name![feature] => {
|
||||
() if *attr_name == sym::feature.clone() => {
|
||||
let features = attr
|
||||
.parse_path_comma_token_tree(self.db.upcast())
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.filter_map(|(feat, _)| match feat.segments() {
|
||||
[name] => Some(name.to_smol_str()),
|
||||
[name] => Some(name.symbol().clone()),
|
||||
_ => None,
|
||||
});
|
||||
crate_data.unstable_features.extend(features);
|
||||
}
|
||||
() if *attr_name == hir_expand::name![register_attr] => {
|
||||
() if *attr_name == sym::register_attr.clone() => {
|
||||
if let Some(ident) = attr.single_ident_value() {
|
||||
crate_data.registered_attrs.push(ident.text.clone());
|
||||
crate_data.registered_attrs.push(ident.sym.clone());
|
||||
cov_mark::hit!(register_attr);
|
||||
}
|
||||
}
|
||||
() if *attr_name == hir_expand::name![register_tool] => {
|
||||
() if *attr_name == sym::register_tool.clone() => {
|
||||
if let Some(ident) = attr.single_ident_value() {
|
||||
crate_data.registered_tools.push(ident.text.clone());
|
||||
crate_data.registered_tools.push(ident.sym.clone());
|
||||
cov_mark::hit!(register_tool);
|
||||
}
|
||||
}
|
||||
@ -535,27 +510,30 @@ impl DefCollector<'_> {
|
||||
}
|
||||
|
||||
let krate = if self.def_map.data.no_std {
|
||||
name![core]
|
||||
} else if self.def_map.extern_prelude().any(|(name, _)| *name == name![std]) {
|
||||
name![std]
|
||||
Name::new_symbol_root(sym::core.clone())
|
||||
} else if self.def_map.extern_prelude().any(|(name, _)| *name == sym::std.clone()) {
|
||||
Name::new_symbol_root(sym::std.clone())
|
||||
} else {
|
||||
// If `std` does not exist for some reason, fall back to core. This mostly helps
|
||||
// keep r-a's own tests minimal.
|
||||
name![core]
|
||||
Name::new_symbol_root(sym::core.clone())
|
||||
};
|
||||
|
||||
let edition = match self.def_map.data.edition {
|
||||
Edition::Edition2015 => name![rust_2015],
|
||||
Edition::Edition2018 => name![rust_2018],
|
||||
Edition::Edition2021 => name![rust_2021],
|
||||
Edition::Edition2024 => name![rust_2024],
|
||||
Edition::Edition2015 => Name::new_symbol_root(sym::rust_2015.clone()),
|
||||
Edition::Edition2018 => Name::new_symbol_root(sym::rust_2018.clone()),
|
||||
Edition::Edition2021 => Name::new_symbol_root(sym::rust_2021.clone()),
|
||||
Edition::Edition2024 => Name::new_symbol_root(sym::rust_2024.clone()),
|
||||
};
|
||||
|
||||
let path_kind = match self.def_map.data.edition {
|
||||
Edition::Edition2015 => PathKind::Plain,
|
||||
_ => PathKind::Abs,
|
||||
};
|
||||
let path = ModPath::from_segments(path_kind, [krate, name![prelude], edition]);
|
||||
let path = ModPath::from_segments(
|
||||
path_kind,
|
||||
[krate, Name::new_symbol_root(sym::prelude.clone()), edition],
|
||||
);
|
||||
|
||||
let (per_ns, _) =
|
||||
self.def_map.resolve_path(self.db, DefMap::ROOT, &path, BuiltinShadowMode::Other, None);
|
||||
@ -601,11 +579,17 @@ impl DefCollector<'_> {
|
||||
fn_id: FunctionId,
|
||||
) {
|
||||
let kind = def.kind.to_basedb_kind();
|
||||
let (expander, kind) =
|
||||
match self.proc_macros.as_ref().map(|it| it.iter().find(|(n, _)| n == &def.name)) {
|
||||
Ok(Some(&(_, expander))) => (expander, kind),
|
||||
_ => (CustomProcMacroExpander::dummy(), kind),
|
||||
};
|
||||
let (expander, kind) = match self.proc_macros.iter().find(|(n, _, _)| n == &def.name) {
|
||||
Some(_)
|
||||
if kind == hir_expand::proc_macro::ProcMacroKind::Attr
|
||||
&& !self.db.expand_proc_attr_macros() =>
|
||||
{
|
||||
(CustomProcMacroExpander::disabled_proc_attr(), kind)
|
||||
}
|
||||
Some(&(_, _, true)) => (CustomProcMacroExpander::disabled(), kind),
|
||||
Some(&(_, expander, false)) => (expander, kind),
|
||||
None => (CustomProcMacroExpander::missing_expander(), kind),
|
||||
};
|
||||
|
||||
let proc_macro_id = ProcMacroLoc {
|
||||
container: self.def_map.crate_root(),
|
||||
@ -838,7 +822,7 @@ impl DefCollector<'_> {
|
||||
}
|
||||
|
||||
fn resolve_extern_crate(&self, name: &Name) -> Option<CrateRootModuleId> {
|
||||
if *name == name![self] {
|
||||
if *name == sym::self_.clone() {
|
||||
cov_mark::hit!(extern_crate_self_as);
|
||||
Some(self.def_map.crate_root())
|
||||
} else {
|
||||
@ -1018,7 +1002,7 @@ impl DefCollector<'_> {
|
||||
|
||||
fn update_recursive(
|
||||
&mut self,
|
||||
// The module for which `resolutions` have been resolve
|
||||
// The module for which `resolutions` have been resolved.
|
||||
module_id: LocalModuleId,
|
||||
resolutions: &[(Option<Name>, PerNs)],
|
||||
// All resolutions are imported with this visibility; the visibilities in
|
||||
@ -1036,10 +1020,9 @@ impl DefCollector<'_> {
|
||||
for (name, res) in resolutions {
|
||||
match name {
|
||||
Some(name) => {
|
||||
let scope = &mut self.def_map.modules[module_id].scope;
|
||||
changed |= scope.push_res_with_import(
|
||||
&mut self.from_glob_import,
|
||||
(module_id, name.clone()),
|
||||
changed |= self.push_res_and_update_glob_vis(
|
||||
module_id,
|
||||
name,
|
||||
res.with_visibility(vis),
|
||||
import,
|
||||
);
|
||||
@ -1105,6 +1088,84 @@ impl DefCollector<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
fn push_res_and_update_glob_vis(
|
||||
&mut self,
|
||||
module_id: LocalModuleId,
|
||||
name: &Name,
|
||||
mut defs: PerNs,
|
||||
def_import_type: Option<ImportType>,
|
||||
) -> bool {
|
||||
let mut changed = false;
|
||||
|
||||
if let Some(ImportType::Glob(_)) = def_import_type {
|
||||
let prev_defs = self.def_map[module_id].scope.get(name);
|
||||
|
||||
// Multiple globs may import the same item and they may override visibility from
|
||||
// previously resolved globs. Handle overrides here and leave the rest to
|
||||
// `ItemScope::push_res_with_import()`.
|
||||
if let Some((def, def_vis, _)) = defs.types {
|
||||
if let Some((prev_def, prev_vis, _)) = prev_defs.types {
|
||||
if def == prev_def
|
||||
&& self.from_glob_import.contains_type(module_id, name.clone())
|
||||
&& def_vis != prev_vis
|
||||
&& def_vis.max(prev_vis, &self.def_map) == Some(def_vis)
|
||||
{
|
||||
changed = true;
|
||||
// This import is being handled here, don't pass it down to
|
||||
// `ItemScope::push_res_with_import()`.
|
||||
defs.types = None;
|
||||
self.def_map.modules[module_id]
|
||||
.scope
|
||||
.update_visibility_types(name, def_vis);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some((def, def_vis, _)) = defs.values {
|
||||
if let Some((prev_def, prev_vis, _)) = prev_defs.values {
|
||||
if def == prev_def
|
||||
&& self.from_glob_import.contains_value(module_id, name.clone())
|
||||
&& def_vis != prev_vis
|
||||
&& def_vis.max(prev_vis, &self.def_map) == Some(def_vis)
|
||||
{
|
||||
changed = true;
|
||||
// See comment above.
|
||||
defs.values = None;
|
||||
self.def_map.modules[module_id]
|
||||
.scope
|
||||
.update_visibility_values(name, def_vis);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some((def, def_vis, _)) = defs.macros {
|
||||
if let Some((prev_def, prev_vis, _)) = prev_defs.macros {
|
||||
if def == prev_def
|
||||
&& self.from_glob_import.contains_macro(module_id, name.clone())
|
||||
&& def_vis != prev_vis
|
||||
&& def_vis.max(prev_vis, &self.def_map) == Some(def_vis)
|
||||
{
|
||||
changed = true;
|
||||
// See comment above.
|
||||
defs.macros = None;
|
||||
self.def_map.modules[module_id]
|
||||
.scope
|
||||
.update_visibility_macros(name, def_vis);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
changed |= self.def_map.modules[module_id].scope.push_res_with_import(
|
||||
&mut self.from_glob_import,
|
||||
(module_id, name.clone()),
|
||||
defs,
|
||||
def_import_type,
|
||||
);
|
||||
|
||||
changed
|
||||
}
|
||||
|
||||
fn resolve_macros(&mut self) -> ReachedFixedPoint {
|
||||
let mut macros = mem::take(&mut self.unresolved_macros);
|
||||
let mut resolved = Vec::new();
|
||||
@ -1331,25 +1392,23 @@ impl DefCollector<'_> {
|
||||
return recollect_without(self);
|
||||
}
|
||||
|
||||
let call_id = call_id();
|
||||
if let MacroDefKind::ProcMacro(_, exp, _) = def.kind {
|
||||
// If there's no expander for the proc macro (e.g.
|
||||
// because proc macros are disabled, or building the
|
||||
// proc macro crate failed), report this and skip
|
||||
// expansion like we would if it was disabled
|
||||
if exp.is_dummy() {
|
||||
self.def_map.diagnostics.push(DefDiagnostic::unresolved_proc_macro(
|
||||
if let Some(err) = exp.as_expand_error(def.krate) {
|
||||
self.def_map.diagnostics.push(DefDiagnostic::macro_error(
|
||||
directive.module_id,
|
||||
self.db.lookup_intern_macro_call(call_id).kind,
|
||||
def.krate,
|
||||
ast_id,
|
||||
(**path).clone(),
|
||||
err,
|
||||
));
|
||||
return recollect_without(self);
|
||||
}
|
||||
if exp.is_disabled() {
|
||||
return recollect_without(self);
|
||||
}
|
||||
}
|
||||
|
||||
let call_id = call_id();
|
||||
self.def_map.modules[directive.module_id]
|
||||
.scope
|
||||
.add_attr_macro_invoc(ast_id, call_id);
|
||||
@ -1388,10 +1447,14 @@ impl DefCollector<'_> {
|
||||
}
|
||||
let file_id = macro_call_id.as_file();
|
||||
|
||||
// Then, fetch and process the item tree. This will reuse the expansion result from above.
|
||||
let item_tree = self.db.file_item_tree(file_id);
|
||||
|
||||
let mod_dir = self.mod_dirs[&module_id].clone();
|
||||
let mod_dir = if macro_call_id.as_macro_file().is_include_macro(self.db.upcast()) {
|
||||
ModDir::root()
|
||||
} else {
|
||||
self.mod_dirs[&module_id].clone()
|
||||
};
|
||||
|
||||
ModCollector {
|
||||
def_collector: &mut *self,
|
||||
macro_depth: depth,
|
||||
@ -1568,10 +1631,7 @@ impl ModCollector<'_, '_> {
|
||||
let attrs = self.item_tree.attrs(db, krate, item.into());
|
||||
if let Some(cfg) = attrs.cfg() {
|
||||
if !self.is_cfg_enabled(&cfg) {
|
||||
self.emit_unconfigured_diagnostic(
|
||||
InFile::new(self.file_id(), item.ast_id(self.item_tree).erase()),
|
||||
&cfg,
|
||||
);
|
||||
self.emit_unconfigured_diagnostic(self.tree_id, item.into(), &cfg);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -1593,7 +1653,7 @@ impl ModCollector<'_, '_> {
|
||||
id: ItemTreeId::new(self.tree_id, item_tree_id),
|
||||
}
|
||||
.intern(db);
|
||||
let is_prelude = attrs.by_key("prelude_import").exists();
|
||||
let is_prelude = attrs.by_key(&sym::prelude_import).exists();
|
||||
Import::from_use(
|
||||
self.item_tree,
|
||||
ItemTreeId::new(self.tree_id, item_tree_id),
|
||||
@ -1618,7 +1678,7 @@ impl ModCollector<'_, '_> {
|
||||
self.process_macro_use_extern_crate(
|
||||
item_tree_id,
|
||||
id,
|
||||
attrs.by_key("macro_use").attrs(),
|
||||
attrs.by_key(&sym::macro_use).attrs(),
|
||||
);
|
||||
}
|
||||
|
||||
@ -1687,7 +1747,7 @@ impl ModCollector<'_, '_> {
|
||||
.into(),
|
||||
&it.name,
|
||||
vis,
|
||||
!matches!(it.fields, Fields::Record(_)),
|
||||
!matches!(it.shape, FieldsShape::Record),
|
||||
);
|
||||
}
|
||||
ModItem::Union(id) => {
|
||||
@ -1725,10 +1785,8 @@ impl ModCollector<'_, '_> {
|
||||
match is_enabled {
|
||||
Err(cfg) => {
|
||||
self.emit_unconfigured_diagnostic(
|
||||
InFile::new(
|
||||
self.file_id(),
|
||||
self.item_tree[variant.index()].ast_id.erase(),
|
||||
),
|
||||
self.tree_id,
|
||||
variant.into(),
|
||||
&cfg,
|
||||
);
|
||||
None
|
||||
@ -1891,8 +1949,8 @@ impl ModCollector<'_, '_> {
|
||||
}
|
||||
|
||||
fn collect_module(&mut self, module_id: FileItemTreeId<Mod>, attrs: &Attrs) {
|
||||
let path_attr = attrs.by_key("path").string_value_unescape();
|
||||
let is_macro_use = attrs.by_key("macro_use").exists();
|
||||
let path_attr = attrs.by_key(&sym::path).string_value_unescape();
|
||||
let is_macro_use = attrs.by_key(&sym::macro_use).exists();
|
||||
let module = &self.item_tree[module_id];
|
||||
match &module.kind {
|
||||
// inline module, just recurse
|
||||
@ -1944,7 +2002,8 @@ impl ModCollector<'_, '_> {
|
||||
match is_enabled {
|
||||
Err(cfg) => {
|
||||
self.emit_unconfigured_diagnostic(
|
||||
ast_id.map(|it| it.erase()),
|
||||
self.tree_id,
|
||||
AttrOwner::TopLevel,
|
||||
&cfg,
|
||||
);
|
||||
}
|
||||
@ -1968,7 +2027,7 @@ impl ModCollector<'_, '_> {
|
||||
let is_macro_use = is_macro_use
|
||||
|| item_tree
|
||||
.top_level_attrs(db, krate)
|
||||
.by_key("macro_use")
|
||||
.by_key(&sym::macro_use)
|
||||
.exists();
|
||||
if is_macro_use {
|
||||
self.import_all_legacy_macros(module_id);
|
||||
@ -1997,7 +2056,7 @@ impl ModCollector<'_, '_> {
|
||||
&mut self,
|
||||
name: Name,
|
||||
declaration: FileAstId<ast::Module>,
|
||||
definition: Option<(FileId, bool)>,
|
||||
definition: Option<(EditionedFileId, bool)>,
|
||||
visibility: &crate::visibility::RawVisibility,
|
||||
mod_tree_id: FileItemTreeId<Mod>,
|
||||
) -> LocalModuleId {
|
||||
@ -2118,14 +2177,12 @@ impl ModCollector<'_, '_> {
|
||||
let attrs = self.item_tree.attrs(self.def_collector.db, krate, ModItem::from(id).into());
|
||||
let ast_id = InFile::new(self.file_id(), mac.ast_id.upcast());
|
||||
|
||||
let export_attr = attrs.by_key("macro_export");
|
||||
let export_attr = attrs.by_key(&sym::macro_export);
|
||||
|
||||
let is_export = export_attr.exists();
|
||||
let local_inner = if is_export {
|
||||
export_attr.tt_values().flat_map(|it| it.token_trees.iter()).any(|it| match it {
|
||||
tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) => {
|
||||
ident.text.contains("local_inner_macros")
|
||||
}
|
||||
tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) => ident.sym == sym::local_inner_macros,
|
||||
_ => false,
|
||||
})
|
||||
} else {
|
||||
@ -2133,17 +2190,17 @@ impl ModCollector<'_, '_> {
|
||||
};
|
||||
|
||||
// Case 1: builtin macros
|
||||
let expander = if attrs.by_key("rustc_builtin_macro").exists() {
|
||||
let expander = if attrs.by_key(&sym::rustc_builtin_macro).exists() {
|
||||
// `#[rustc_builtin_macro = "builtin_name"]` overrides the `macro_rules!` name.
|
||||
let name;
|
||||
let name = match attrs.by_key("rustc_builtin_macro").string_value() {
|
||||
Some(it) => {
|
||||
name = Name::new_text_dont_use(it.into());
|
||||
let name = match attrs.by_key(&sym::rustc_builtin_macro).string_value_with_span() {
|
||||
Some((it, span)) => {
|
||||
name = Name::new_symbol(it.clone(), span.ctx);
|
||||
&name
|
||||
}
|
||||
None => {
|
||||
let explicit_name =
|
||||
attrs.by_key("rustc_builtin_macro").tt_values().next().and_then(|tt| {
|
||||
attrs.by_key(&sym::rustc_builtin_macro).tt_values().next().and_then(|tt| {
|
||||
match tt.token_trees.first() {
|
||||
Some(tt::TokenTree::Leaf(tt::Leaf::Ident(name))) => Some(name),
|
||||
_ => None,
|
||||
@ -2173,7 +2230,7 @@ impl ModCollector<'_, '_> {
|
||||
// Case 2: normal `macro_rules!` macro
|
||||
MacroExpander::Declarative
|
||||
};
|
||||
let allow_internal_unsafe = attrs.by_key("allow_internal_unsafe").exists();
|
||||
let allow_internal_unsafe = attrs.by_key(&sym::allow_internal_unsafe).exists();
|
||||
|
||||
let mut flags = MacroRulesLocFlags::empty();
|
||||
flags.set(MacroRulesLocFlags::LOCAL_INNER, local_inner);
|
||||
@ -2203,14 +2260,14 @@ impl ModCollector<'_, '_> {
|
||||
// Case 1: builtin macros
|
||||
let mut helpers_opt = None;
|
||||
let attrs = self.item_tree.attrs(self.def_collector.db, krate, ModItem::from(id).into());
|
||||
let expander = if attrs.by_key("rustc_builtin_macro").exists() {
|
||||
let expander = if attrs.by_key(&sym::rustc_builtin_macro).exists() {
|
||||
if let Some(expander) = find_builtin_macro(&mac.name) {
|
||||
match expander {
|
||||
Either::Left(it) => MacroExpander::BuiltIn(it),
|
||||
Either::Right(it) => MacroExpander::BuiltInEager(it),
|
||||
}
|
||||
} else if let Some(expander) = find_builtin_derive(&mac.name) {
|
||||
if let Some(attr) = attrs.by_key("rustc_builtin_macro").tt_values().next() {
|
||||
if let Some(attr) = attrs.by_key(&sym::rustc_builtin_macro).tt_values().next() {
|
||||
// NOTE: The item *may* have both `#[rustc_builtin_macro]` and `#[proc_macro_derive]`,
|
||||
// in which case rustc ignores the helper attributes from the latter, but it
|
||||
// "doesn't make sense in practice" (see rust-lang/rust#87027).
|
||||
@ -2243,7 +2300,7 @@ impl ModCollector<'_, '_> {
|
||||
// Case 2: normal `macro`
|
||||
MacroExpander::Declarative
|
||||
};
|
||||
let allow_internal_unsafe = attrs.by_key("allow_internal_unsafe").exists();
|
||||
let allow_internal_unsafe = attrs.by_key(&sym::allow_internal_unsafe).exists();
|
||||
|
||||
let macro_id = Macro2Loc {
|
||||
container: module,
|
||||
@ -2392,10 +2449,11 @@ impl ModCollector<'_, '_> {
|
||||
self.def_collector.cfg_options.check(cfg) != Some(false)
|
||||
}
|
||||
|
||||
fn emit_unconfigured_diagnostic(&mut self, ast_id: InFile<ErasedFileAstId>, cfg: &CfgExpr) {
|
||||
fn emit_unconfigured_diagnostic(&mut self, tree_id: TreeId, item: AttrOwner, cfg: &CfgExpr) {
|
||||
self.def_collector.def_map.diagnostics.push(DefDiagnostic::unconfigured_code(
|
||||
self.module_id,
|
||||
ast_id,
|
||||
tree_id,
|
||||
item,
|
||||
cfg.clone(),
|
||||
self.def_collector.cfg_options.clone(),
|
||||
));
|
||||
@ -2426,7 +2484,7 @@ mod tests {
|
||||
unresolved_macros: Vec::new(),
|
||||
mod_dirs: FxHashMap::default(),
|
||||
cfg_options: &CfgOptions::default(),
|
||||
proc_macros: Ok(vec![]),
|
||||
proc_macros: Default::default(),
|
||||
from_glob_import: Default::default(),
|
||||
skip_attrs: Default::default(),
|
||||
is_proc_macro: false,
|
||||
|
@ -2,14 +2,13 @@
|
||||
|
||||
use std::ops::Not;
|
||||
|
||||
use base_db::CrateId;
|
||||
use cfg::{CfgExpr, CfgOptions};
|
||||
use hir_expand::{attrs::AttrId, ErasedAstId, MacroCallKind};
|
||||
use hir_expand::{attrs::AttrId, ExpandErrorKind, MacroCallKind};
|
||||
use la_arena::Idx;
|
||||
use syntax::ast;
|
||||
|
||||
use crate::{
|
||||
item_tree::{self, ItemTreeId},
|
||||
item_tree::{self, AttrOwner, ItemTreeId, TreeId},
|
||||
nameres::LocalModuleId,
|
||||
path::ModPath,
|
||||
AstId,
|
||||
@ -17,47 +16,16 @@ use crate::{
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum DefDiagnosticKind {
|
||||
UnresolvedModule {
|
||||
ast: AstId<ast::Module>,
|
||||
candidates: Box<[String]>,
|
||||
},
|
||||
UnresolvedExternCrate {
|
||||
ast: AstId<ast::ExternCrate>,
|
||||
},
|
||||
UnresolvedImport {
|
||||
id: ItemTreeId<item_tree::Use>,
|
||||
index: Idx<ast::UseTree>,
|
||||
},
|
||||
UnconfiguredCode {
|
||||
ast: ErasedAstId,
|
||||
cfg: CfgExpr,
|
||||
opts: CfgOptions,
|
||||
},
|
||||
/// A proc-macro that is lacking an expander, this might be due to build scripts not yet having
|
||||
/// run or proc-macro expansion being disabled.
|
||||
UnresolvedProcMacro {
|
||||
ast: MacroCallKind,
|
||||
krate: CrateId,
|
||||
},
|
||||
UnresolvedMacroCall {
|
||||
ast: MacroCallKind,
|
||||
path: ModPath,
|
||||
},
|
||||
UnimplementedBuiltinMacro {
|
||||
ast: AstId<ast::Macro>,
|
||||
},
|
||||
InvalidDeriveTarget {
|
||||
ast: AstId<ast::Item>,
|
||||
id: usize,
|
||||
},
|
||||
MalformedDerive {
|
||||
ast: AstId<ast::Adt>,
|
||||
id: usize,
|
||||
},
|
||||
MacroDefError {
|
||||
ast: AstId<ast::Macro>,
|
||||
message: String,
|
||||
},
|
||||
UnresolvedModule { ast: AstId<ast::Module>, candidates: Box<[String]> },
|
||||
UnresolvedExternCrate { ast: AstId<ast::ExternCrate> },
|
||||
UnresolvedImport { id: ItemTreeId<item_tree::Use>, index: Idx<ast::UseTree> },
|
||||
UnconfiguredCode { tree: TreeId, item: AttrOwner, cfg: CfgExpr, opts: CfgOptions },
|
||||
UnresolvedMacroCall { ast: MacroCallKind, path: ModPath },
|
||||
UnimplementedBuiltinMacro { ast: AstId<ast::Macro> },
|
||||
InvalidDeriveTarget { ast: AstId<ast::Item>, id: usize },
|
||||
MalformedDerive { ast: AstId<ast::Adt>, id: usize },
|
||||
MacroDefError { ast: AstId<ast::Macro>, message: String },
|
||||
MacroError { ast: AstId<ast::Item>, path: ModPath, err: ExpandErrorKind },
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
@ -114,21 +82,26 @@ impl DefDiagnostic {
|
||||
Self { in_module: container, kind: DefDiagnosticKind::UnresolvedImport { id, index } }
|
||||
}
|
||||
|
||||
pub fn macro_error(
|
||||
container: LocalModuleId,
|
||||
ast: AstId<ast::Item>,
|
||||
path: ModPath,
|
||||
err: ExpandErrorKind,
|
||||
) -> Self {
|
||||
Self { in_module: container, kind: DefDiagnosticKind::MacroError { ast, path, err } }
|
||||
}
|
||||
|
||||
pub fn unconfigured_code(
|
||||
container: LocalModuleId,
|
||||
ast: ErasedAstId,
|
||||
tree: TreeId,
|
||||
item: AttrOwner,
|
||||
cfg: CfgExpr,
|
||||
opts: CfgOptions,
|
||||
) -> Self {
|
||||
Self { in_module: container, kind: DefDiagnosticKind::UnconfiguredCode { ast, cfg, opts } }
|
||||
}
|
||||
|
||||
pub fn unresolved_proc_macro(
|
||||
container: LocalModuleId,
|
||||
ast: MacroCallKind,
|
||||
krate: CrateId,
|
||||
) -> Self {
|
||||
Self { in_module: container, kind: DefDiagnosticKind::UnresolvedProcMacro { ast, krate } }
|
||||
Self {
|
||||
in_module: container,
|
||||
kind: DefDiagnosticKind::UnconfiguredCode { tree, item, cfg, opts },
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: Whats the difference between this and unresolved_proc_macro
|
||||
|
@ -1,8 +1,10 @@
|
||||
//! This module resolves `mod foo;` declaration to file.
|
||||
use arrayvec::ArrayVec;
|
||||
use base_db::{AnchoredPath, FileId};
|
||||
use hir_expand::{name::Name, HirFileIdExt, MacroFileIdExt};
|
||||
use base_db::AnchoredPath;
|
||||
use hir_expand::{name::Name, HirFileIdExt};
|
||||
use limit::Limit;
|
||||
use span::EditionedFileId;
|
||||
use syntax::ToSmolStr as _;
|
||||
|
||||
use crate::{db::DefDatabase, HirFileId};
|
||||
|
||||
@ -33,7 +35,7 @@ impl ModDir {
|
||||
let path = match attr_path {
|
||||
None => {
|
||||
let mut path = self.dir_path.clone();
|
||||
path.push(&name.unescaped().to_smol_str());
|
||||
path.push(&name.unescaped().display_no_db().to_smolstr());
|
||||
path
|
||||
}
|
||||
Some(attr_path) => {
|
||||
@ -63,7 +65,7 @@ impl ModDir {
|
||||
file_id: HirFileId,
|
||||
name: &Name,
|
||||
attr_path: Option<&str>,
|
||||
) -> Result<(FileId, bool, ModDir), Box<[String]>> {
|
||||
) -> Result<(EditionedFileId, bool, ModDir), Box<[String]>> {
|
||||
let name = name.unescaped();
|
||||
|
||||
let mut candidate_files = ArrayVec::<_, 2>::new();
|
||||
@ -71,10 +73,6 @@ impl ModDir {
|
||||
Some(attr_path) => {
|
||||
candidate_files.push(self.dir_path.join_attr(attr_path, self.root_non_dir_owner))
|
||||
}
|
||||
None if file_id.macro_file().map_or(false, |it| it.is_include_macro(db.upcast())) => {
|
||||
candidate_files.push(format!("{}.rs", name.display(db.upcast())));
|
||||
candidate_files.push(format!("{}/mod.rs", name.display(db.upcast())));
|
||||
}
|
||||
None => {
|
||||
candidate_files.push(format!(
|
||||
"{}{}.rs",
|
||||
@ -91,7 +89,7 @@ impl ModDir {
|
||||
|
||||
let orig_file_id = file_id.original_file_respecting_includes(db.upcast());
|
||||
for candidate in candidate_files.iter() {
|
||||
let path = AnchoredPath { anchor: orig_file_id, path: candidate.as_str() };
|
||||
let path = AnchoredPath { anchor: orig_file_id.file_id(), path: candidate.as_str() };
|
||||
if let Some(file_id) = db.resolve_path(path) {
|
||||
let is_mod_rs = candidate.ends_with("/mod.rs");
|
||||
|
||||
@ -102,7 +100,12 @@ impl ModDir {
|
||||
DirPath::new(format!("{}/", name.display(db.upcast())))
|
||||
};
|
||||
if let Some(mod_dir) = self.child(dir_path, !root_dir_owner) {
|
||||
return Ok((file_id, is_mod_rs, mod_dir));
|
||||
return Ok((
|
||||
// FIXME: Edition, is this rightr?
|
||||
EditionedFileId::new(file_id, orig_file_id.edition()),
|
||||
is_mod_rs,
|
||||
mod_dir,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ use triomphe::Arc;
|
||||
use crate::{
|
||||
db::DefDatabase,
|
||||
item_scope::{ImportOrExternCrate, BUILTIN_SCOPE},
|
||||
item_tree::Fields,
|
||||
item_tree::FieldsShape,
|
||||
nameres::{sub_namespace_match, BlockInfo, BuiltinShadowMode, DefMap, MacroSubNs},
|
||||
path::{ModPath, PathKind},
|
||||
per_ns::PerNs,
|
||||
@ -381,11 +381,11 @@ impl DefMap {
|
||||
.iter()
|
||||
.find_map(|&variant| {
|
||||
let variant_data = &tree[variant.lookup(db).id.value];
|
||||
(variant_data.name == *segment).then(|| match variant_data.fields {
|
||||
Fields::Record(_) => {
|
||||
(variant_data.name == *segment).then(|| match variant_data.shape {
|
||||
FieldsShape::Record => {
|
||||
PerNs::types(variant.into(), Visibility::Public, None)
|
||||
}
|
||||
Fields::Tuple(_) | Fields::Unit => PerNs::both(
|
||||
FieldsShape::Tuple | FieldsShape::Unit => PerNs::both(
|
||||
variant.into(),
|
||||
variant.into(),
|
||||
Visibility::Public,
|
||||
|
@ -1,6 +1,7 @@
|
||||
//! Nameres-specific procedural macro data and helpers.
|
||||
|
||||
use hir_expand::name::{AsName, Name};
|
||||
use intern::sym;
|
||||
|
||||
use crate::attr::Attrs;
|
||||
use crate::tt::{Leaf, TokenTree};
|
||||
@ -35,8 +36,8 @@ impl Attrs {
|
||||
Some(ProcMacroDef { name: func_name.clone(), kind: ProcMacroKind::Bang })
|
||||
} else if self.is_proc_macro_attribute() {
|
||||
Some(ProcMacroDef { name: func_name.clone(), kind: ProcMacroKind::Attr })
|
||||
} else if self.by_key("proc_macro_derive").exists() {
|
||||
let derive = self.by_key("proc_macro_derive").tt_values().next()?;
|
||||
} else if self.by_key(&sym::proc_macro_derive).exists() {
|
||||
let derive = self.by_key(&sym::proc_macro_derive).tt_values().next()?;
|
||||
let def = parse_macro_name_and_helper_attrs(&derive.token_trees)
|
||||
.map(|(name, helpers)| ProcMacroDef { name, kind: ProcMacroKind::Derive { helpers } });
|
||||
|
||||
@ -67,7 +68,7 @@ pub(crate) fn parse_macro_name_and_helper_attrs(tt: &[TokenTree]) -> Option<(Nam
|
||||
TokenTree::Leaf(Leaf::Punct(comma)),
|
||||
TokenTree::Leaf(Leaf::Ident(attributes)),
|
||||
TokenTree::Subtree(helpers)
|
||||
] if comma.char == ',' && attributes.text == "attributes" =>
|
||||
] if comma.char == ',' && attributes.sym == sym::attributes =>
|
||||
{
|
||||
let helpers = helpers
|
||||
.token_trees
|
||||
|
@ -367,3 +367,48 @@ use event::Event;
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn glob_may_override_visibility() {
|
||||
check(
|
||||
r#"
|
||||
mod reexport {
|
||||
use crate::defs::*;
|
||||
mod inner {
|
||||
pub use crate::defs::{Trait, function, makro};
|
||||
}
|
||||
pub use inner::*;
|
||||
}
|
||||
mod defs {
|
||||
pub trait Trait {}
|
||||
pub fn function() {}
|
||||
pub macro makro($t:item) { $t }
|
||||
}
|
||||
use reexport::*;
|
||||
"#,
|
||||
expect![[r#"
|
||||
crate
|
||||
Trait: t
|
||||
defs: t
|
||||
function: v
|
||||
makro: m
|
||||
reexport: t
|
||||
|
||||
crate::defs
|
||||
Trait: t
|
||||
function: v
|
||||
makro: m
|
||||
|
||||
crate::reexport
|
||||
Trait: t
|
||||
function: v
|
||||
inner: t
|
||||
makro: m
|
||||
|
||||
crate::reexport::inner
|
||||
Trait: ti
|
||||
function: vi
|
||||
makro: mi
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ fn check_def_map_is_not_recomputed(ra_fixture_initial: &str, ra_fixture_change:
|
||||
});
|
||||
assert!(format!("{events:?}").contains("crate_def_map"), "{events:#?}")
|
||||
}
|
||||
db.set_file_text(pos.file_id, ra_fixture_change);
|
||||
db.set_file_text(pos.file_id.file_id(), ra_fixture_change);
|
||||
|
||||
{
|
||||
let events = db.log_executed(|| {
|
||||
@ -266,7 +266,7 @@ fn quux() { 92 }
|
||||
m!(Y);
|
||||
m!(Z);
|
||||
"#;
|
||||
db.set_file_text(pos.file_id, new_text);
|
||||
db.set_file_text(pos.file_id.file_id(), new_text);
|
||||
|
||||
{
|
||||
let events = db.log_executed(|| {
|
||||
|
@ -1309,6 +1309,116 @@ pub mod ip_address {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn include_with_submod_file() {
|
||||
check(
|
||||
r#"
|
||||
//- minicore: include
|
||||
//- /lib.rs
|
||||
include!("out_dir/includes.rs");
|
||||
|
||||
//- /out_dir/includes.rs
|
||||
pub mod company_name {
|
||||
pub mod network {
|
||||
pub mod v1;
|
||||
}
|
||||
}
|
||||
//- /out_dir/company_name/network/v1.rs
|
||||
pub struct IpAddress {
|
||||
pub ip_type: &'static str,
|
||||
}
|
||||
/// Nested message and enum types in `IpAddress`.
|
||||
pub mod ip_address {
|
||||
pub enum IpType {
|
||||
IpV4(u32),
|
||||
}
|
||||
}
|
||||
|
||||
"#,
|
||||
expect![[r#"
|
||||
crate
|
||||
company_name: t
|
||||
|
||||
crate::company_name
|
||||
network: t
|
||||
|
||||
crate::company_name::network
|
||||
v1: t
|
||||
|
||||
crate::company_name::network::v1
|
||||
IpAddress: t
|
||||
ip_address: t
|
||||
|
||||
crate::company_name::network::v1::ip_address
|
||||
IpType: t
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn include_many_mods() {
|
||||
check(
|
||||
r#"
|
||||
//- /lib.rs
|
||||
#[rustc_builtin_macro]
|
||||
macro_rules! include { () => {} }
|
||||
|
||||
mod nested {
|
||||
include!("out_dir/includes.rs");
|
||||
|
||||
mod different_company {
|
||||
include!("out_dir/different_company/mod.rs");
|
||||
}
|
||||
|
||||
mod util;
|
||||
}
|
||||
|
||||
//- /nested/util.rs
|
||||
pub struct Helper {}
|
||||
//- /out_dir/includes.rs
|
||||
pub mod company_name {
|
||||
pub mod network {
|
||||
pub mod v1;
|
||||
}
|
||||
}
|
||||
//- /out_dir/company_name/network/v1.rs
|
||||
pub struct IpAddress {}
|
||||
//- /out_dir/different_company/mod.rs
|
||||
pub mod network;
|
||||
//- /out_dir/different_company/network.rs
|
||||
pub struct Url {}
|
||||
|
||||
"#,
|
||||
expect![[r#"
|
||||
crate
|
||||
nested: t
|
||||
|
||||
crate::nested
|
||||
company_name: t
|
||||
different_company: t
|
||||
util: t
|
||||
|
||||
crate::nested::company_name
|
||||
network: t
|
||||
|
||||
crate::nested::company_name::network
|
||||
v1: t
|
||||
|
||||
crate::nested::company_name::network::v1
|
||||
IpAddress: t
|
||||
|
||||
crate::nested::different_company
|
||||
network: t
|
||||
|
||||
crate::nested::different_company::network
|
||||
Url: t
|
||||
|
||||
crate::nested::util
|
||||
Helper: t
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn macro_use_imports_all_macro_types() {
|
||||
let db = TestDB::with_files(
|
||||
|
@ -13,7 +13,7 @@ use crate::{
|
||||
};
|
||||
use hir_expand::name::Name;
|
||||
use intern::Interned;
|
||||
use syntax::ast;
|
||||
use syntax::{ast, ToSmolStr};
|
||||
|
||||
pub use hir_expand::mod_path::{path, ModPath, PathKind};
|
||||
|
||||
@ -29,7 +29,7 @@ impl Display for ImportAlias {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
ImportAlias::Underscore => f.write_str("_"),
|
||||
ImportAlias::Alias(name) => f.write_str(&name.to_smol_str()),
|
||||
ImportAlias::Alias(name) => f.write_str(&name.display_no_db().to_smolstr()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,9 +6,9 @@ use crate::{lower::LowerCtx, type_ref::ConstRef};
|
||||
|
||||
use hir_expand::{
|
||||
mod_path::resolve_crate_root,
|
||||
name::{name, AsName},
|
||||
name::{AsName, Name},
|
||||
};
|
||||
use intern::Interned;
|
||||
use intern::{sym, Interned};
|
||||
use syntax::ast::{self, AstNode, HasGenericArgs, HasTypeBounds};
|
||||
|
||||
use crate::{
|
||||
@ -60,7 +60,7 @@ pub(super) fn lower_path(ctx: &LowerCtx<'_>, mut path: ast::Path) -> Option<Path
|
||||
segments.push(name);
|
||||
}
|
||||
ast::PathSegmentKind::SelfTypeKw => {
|
||||
segments.push(name![Self]);
|
||||
segments.push(Name::new_symbol_root(sym::Self_.clone()));
|
||||
}
|
||||
ast::PathSegmentKind::Type { type_ref, trait_ref } => {
|
||||
assert!(path.qualifier().is_none()); // this can only occur at the first segment
|
||||
@ -268,7 +268,7 @@ fn lower_generic_args_from_fn_path(
|
||||
let bindings = if let Some(ret_type) = ret_type {
|
||||
let type_ref = TypeRef::from_ast_opt(ctx, ret_type.ty());
|
||||
Box::new([AssociatedTypeBinding {
|
||||
name: name![Output],
|
||||
name: Name::new_symbol_root(sym::Output.clone()),
|
||||
args: None,
|
||||
type_ref: Some(type_ref),
|
||||
bounds: Box::default(),
|
||||
@ -277,7 +277,7 @@ fn lower_generic_args_from_fn_path(
|
||||
// -> ()
|
||||
let type_ref = TypeRef::Tuple(Vec::new());
|
||||
Box::new([AssociatedTypeBinding {
|
||||
name: name![Output],
|
||||
name: Name::new_symbol_root(sym::Output.clone()),
|
||||
args: None,
|
||||
type_ref: Some(type_ref),
|
||||
bounds: Box::default(),
|
||||
|
@ -200,7 +200,7 @@ pub(crate) fn print_type_ref(
|
||||
}
|
||||
if let Some(abi) = abi {
|
||||
buf.write_str("extern ")?;
|
||||
buf.write_str(abi)?;
|
||||
buf.write_str(abi.as_str())?;
|
||||
buf.write_char(' ')?;
|
||||
}
|
||||
write!(buf, "fn(")?;
|
||||
|
@ -2,11 +2,9 @@
|
||||
use std::{fmt, iter, mem};
|
||||
|
||||
use base_db::CrateId;
|
||||
use hir_expand::{
|
||||
name::{name, Name},
|
||||
MacroDefId,
|
||||
};
|
||||
use intern::Interned;
|
||||
use hir_expand::{name::Name, MacroDefId};
|
||||
use intern::{sym, Interned};
|
||||
use itertools::Itertools as _;
|
||||
use rustc_hash::FxHashSet;
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use triomphe::Arc;
|
||||
@ -197,12 +195,12 @@ impl Resolver {
|
||||
}
|
||||
}
|
||||
&Scope::ImplDefScope(impl_) => {
|
||||
if first_name == &name![Self] {
|
||||
if *first_name == sym::Self_.clone() {
|
||||
return Some((TypeNs::SelfType(impl_), remaining_idx(), None));
|
||||
}
|
||||
}
|
||||
&Scope::AdtScope(adt) => {
|
||||
if first_name == &name![Self] {
|
||||
if *first_name == sym::Self_.clone() {
|
||||
return Some((TypeNs::AdtSelfType(adt), remaining_idx(), None));
|
||||
}
|
||||
}
|
||||
@ -294,7 +292,7 @@ impl Resolver {
|
||||
}
|
||||
};
|
||||
let n_segments = path.segments().len();
|
||||
let tmp = name![self];
|
||||
let tmp = Name::new_symbol_root(sym::self_.clone());
|
||||
let first_name = if path.is_self() { &tmp } else { path.segments().first()? };
|
||||
let skip_to_mod = path.kind != PathKind::Plain && !path.is_self();
|
||||
if skip_to_mod {
|
||||
@ -325,7 +323,7 @@ impl Resolver {
|
||||
}
|
||||
}
|
||||
&Scope::ImplDefScope(impl_) => {
|
||||
if first_name == &name![Self] {
|
||||
if *first_name == sym::Self_.clone() {
|
||||
return Some(ResolveValueResult::ValueNs(
|
||||
ValueNs::ImplSelf(impl_),
|
||||
None,
|
||||
@ -352,7 +350,7 @@ impl Resolver {
|
||||
}
|
||||
}
|
||||
&Scope::ImplDefScope(impl_) => {
|
||||
if first_name == &name![Self] {
|
||||
if *first_name == sym::Self_.clone() {
|
||||
return Some(ResolveValueResult::Partial(
|
||||
TypeNs::SelfType(impl_),
|
||||
1,
|
||||
@ -361,7 +359,7 @@ impl Resolver {
|
||||
}
|
||||
}
|
||||
Scope::AdtScope(adt) => {
|
||||
if first_name == &name![Self] {
|
||||
if *first_name == sym::Self_.clone() {
|
||||
let ty = TypeNs::AdtSelfType(*adt);
|
||||
return Some(ResolveValueResult::Partial(ty, 1, None));
|
||||
}
|
||||
@ -425,7 +423,7 @@ impl Resolver {
|
||||
}
|
||||
|
||||
pub fn resolve_lifetime(&self, lifetime: &LifetimeRef) -> Option<LifetimeNs> {
|
||||
if lifetime.name == name::known::STATIC_LIFETIME {
|
||||
if lifetime.name == sym::tick_static.clone() {
|
||||
return Some(LifetimeNs::Static);
|
||||
}
|
||||
|
||||
@ -500,9 +498,11 @@ impl Resolver {
|
||||
res.add(name, ScopeDef::ModuleDef(ModuleDefId::MacroId(mac)));
|
||||
})
|
||||
});
|
||||
def_map.macro_use_prelude().for_each(|(name, (def, _extern_crate))| {
|
||||
res.add(name, ScopeDef::ModuleDef(def.into()));
|
||||
});
|
||||
def_map.macro_use_prelude().iter().sorted_by_key(|&(k, _)| k.clone()).for_each(
|
||||
|(name, &(def, _extern_crate))| {
|
||||
res.add(name, ScopeDef::ModuleDef(def.into()));
|
||||
},
|
||||
);
|
||||
def_map.extern_prelude().for_each(|(name, (def, _extern_crate))| {
|
||||
res.add(name, ScopeDef::ModuleDef(ModuleDefId::ModuleId(def.into())));
|
||||
});
|
||||
@ -781,10 +781,10 @@ impl Scope {
|
||||
}
|
||||
}
|
||||
Scope::ImplDefScope(i) => {
|
||||
acc.add(&name![Self], ScopeDef::ImplSelfType(*i));
|
||||
acc.add(&Name::new_symbol_root(sym::Self_.clone()), ScopeDef::ImplSelfType(*i));
|
||||
}
|
||||
Scope::AdtScope(i) => {
|
||||
acc.add(&name![Self], ScopeDef::AdtSelfType(*i));
|
||||
acc.add(&Name::new_symbol_root(sym::Self_.clone()), ScopeDef::AdtSelfType(*i));
|
||||
}
|
||||
Scope::ExprScope(scope) => {
|
||||
if let Some((label, name)) = scope.expr_scopes.label(scope.scope_id) {
|
||||
|
@ -6,9 +6,10 @@ use la_arena::ArenaMap;
|
||||
use syntax::{ast, AstNode, AstPtr};
|
||||
|
||||
use crate::{
|
||||
data::adt::lower_struct, db::DefDatabase, item_tree::ItemTreeNode, trace::Trace, GenericDefId,
|
||||
ItemTreeLoc, LocalFieldId, LocalLifetimeParamId, LocalTypeOrConstParamId, Lookup, UseId,
|
||||
VariantId,
|
||||
db::DefDatabase,
|
||||
item_tree::{AttrOwner, FieldParent, ItemTreeNode},
|
||||
GenericDefId, ItemTreeLoc, LocalFieldId, LocalLifetimeParamId, LocalTypeOrConstParamId, Lookup,
|
||||
UseId, VariantId,
|
||||
};
|
||||
|
||||
pub trait HasSource {
|
||||
@ -124,13 +125,13 @@ impl HasChildSource<LocalFieldId> for VariantId {
|
||||
|
||||
fn child_source(&self, db: &dyn DefDatabase) -> InFile<ArenaMap<LocalFieldId, Self::Value>> {
|
||||
let item_tree;
|
||||
let (src, fields, container) = match *self {
|
||||
let (src, parent, container) = match *self {
|
||||
VariantId::EnumVariantId(it) => {
|
||||
let lookup = it.lookup(db);
|
||||
item_tree = lookup.id.item_tree(db);
|
||||
(
|
||||
lookup.source(db).map(|it| it.kind()),
|
||||
&item_tree[lookup.id.value].fields,
|
||||
FieldParent::Variant(lookup.id.value),
|
||||
lookup.parent.lookup(db).container,
|
||||
)
|
||||
}
|
||||
@ -139,7 +140,7 @@ impl HasChildSource<LocalFieldId> for VariantId {
|
||||
item_tree = lookup.id.item_tree(db);
|
||||
(
|
||||
lookup.source(db).map(|it| it.kind()),
|
||||
&item_tree[lookup.id.value].fields,
|
||||
FieldParent::Struct(lookup.id.value),
|
||||
lookup.container,
|
||||
)
|
||||
}
|
||||
@ -148,13 +149,54 @@ impl HasChildSource<LocalFieldId> for VariantId {
|
||||
item_tree = lookup.id.item_tree(db);
|
||||
(
|
||||
lookup.source(db).map(|it| it.kind()),
|
||||
&item_tree[lookup.id.value].fields,
|
||||
FieldParent::Union(lookup.id.value),
|
||||
lookup.container,
|
||||
)
|
||||
}
|
||||
};
|
||||
let mut trace = Trace::new_for_map();
|
||||
lower_struct(db, &mut trace, &src, container.krate, &item_tree, fields);
|
||||
src.with_value(trace.into_map())
|
||||
|
||||
let mut map = ArenaMap::new();
|
||||
match &src.value {
|
||||
ast::StructKind::Tuple(fl) => {
|
||||
let cfg_options = &db.crate_graph()[container.krate].cfg_options;
|
||||
let mut idx = 0;
|
||||
for (i, fd) in fl.fields().enumerate() {
|
||||
let attrs = item_tree.attrs(
|
||||
db,
|
||||
container.krate,
|
||||
AttrOwner::make_field_indexed(parent, i),
|
||||
);
|
||||
if !attrs.is_cfg_enabled(cfg_options) {
|
||||
continue;
|
||||
}
|
||||
map.insert(
|
||||
LocalFieldId::from_raw(la_arena::RawIdx::from(idx)),
|
||||
Either::Left(fd.clone()),
|
||||
);
|
||||
idx += 1;
|
||||
}
|
||||
}
|
||||
ast::StructKind::Record(fl) => {
|
||||
let cfg_options = &db.crate_graph()[container.krate].cfg_options;
|
||||
let mut idx = 0;
|
||||
for (i, fd) in fl.fields().enumerate() {
|
||||
let attrs = item_tree.attrs(
|
||||
db,
|
||||
container.krate,
|
||||
AttrOwner::make_field_indexed(parent, i),
|
||||
);
|
||||
if !attrs.is_cfg_enabled(cfg_options) {
|
||||
continue;
|
||||
}
|
||||
map.insert(
|
||||
LocalFieldId::from_raw(la_arena::RawIdx::from(idx)),
|
||||
Either::Right(fd.clone()),
|
||||
);
|
||||
idx += 1;
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
InFile::new(src.file_id, map)
|
||||
}
|
||||
}
|
||||
|
@ -4,10 +4,10 @@ use std::{fmt, panic, sync::Mutex};
|
||||
|
||||
use base_db::{
|
||||
salsa::{self, Durability},
|
||||
AnchoredPath, CrateId, FileId, FileLoader, FileLoaderDelegate, FilePosition, SourceDatabase,
|
||||
Upcast,
|
||||
AnchoredPath, CrateId, FileLoader, FileLoaderDelegate, SourceDatabase, Upcast,
|
||||
};
|
||||
use hir_expand::{db::ExpandDatabase, InFile};
|
||||
use hir_expand::{db::ExpandDatabase, files::FilePosition, InFile};
|
||||
use span::{EditionedFileId, FileId};
|
||||
use syntax::{algo, ast, AstNode};
|
||||
use triomphe::Arc;
|
||||
|
||||
@ -85,7 +85,7 @@ impl TestDB {
|
||||
for &krate in self.relevant_crates(file_id).iter() {
|
||||
let crate_def_map = self.crate_def_map(krate);
|
||||
for (local_id, data) in crate_def_map.modules() {
|
||||
if data.origin.file_id() == Some(file_id) {
|
||||
if data.origin.file_id().map(EditionedFileId::file_id) == Some(file_id) {
|
||||
return crate_def_map.module_id(local_id);
|
||||
}
|
||||
}
|
||||
@ -94,7 +94,7 @@ impl TestDB {
|
||||
}
|
||||
|
||||
pub(crate) fn module_at_position(&self, position: FilePosition) -> ModuleId {
|
||||
let file_module = self.module_for_file(position.file_id);
|
||||
let file_module = self.module_for_file(position.file_id.file_id());
|
||||
let mut def_map = file_module.def_map(self);
|
||||
let module = self.mod_at_position(&def_map, position);
|
||||
|
||||
@ -122,7 +122,7 @@ impl TestDB {
|
||||
let mut res = DefMap::ROOT;
|
||||
for (module, data) in def_map.modules() {
|
||||
let src = data.definition_source(self);
|
||||
if src.file_id != position.file_id.into() {
|
||||
if src.file_id != position.file_id {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -148,7 +148,6 @@ impl TestDB {
|
||||
};
|
||||
|
||||
if size != Some(new_size) {
|
||||
cov_mark::hit!(submodule_in_testdb);
|
||||
size = Some(new_size);
|
||||
res = module;
|
||||
}
|
||||
@ -163,7 +162,7 @@ impl TestDB {
|
||||
let mut fn_def = None;
|
||||
for (_, module) in def_map.modules() {
|
||||
let file_id = module.definition_source(self).file_id;
|
||||
if file_id != position.file_id.into() {
|
||||
if file_id != position.file_id {
|
||||
continue;
|
||||
}
|
||||
for decl in module.scope.declarations() {
|
||||
|
@ -1,55 +0,0 @@
|
||||
//! Trace is a pretty niche data structure which is used when lowering a CST
|
||||
//! into HIR.
|
||||
//!
|
||||
//! Lowering process calculates two bits of information:
|
||||
//! * the lowered syntax itself
|
||||
//! * a mapping between lowered syntax and original syntax
|
||||
//!
|
||||
//! Due to the way salsa works, the mapping is usually hot lava, as it contains
|
||||
//! absolute offsets. The `Trace` structure (inspired, at least in name, by
|
||||
//! Kotlin's `BindingTrace`) allows use the same code to compute both
|
||||
//! projections.
|
||||
use la_arena::{Arena, ArenaMap, Idx, RawIdx};
|
||||
|
||||
// FIXME: This isn't really used anymore, at least not in a way where it does anything useful.
|
||||
// Check if we should get rid of this or make proper use of it instead.
|
||||
pub(crate) struct Trace<T, V> {
|
||||
arena: Option<Arena<T>>,
|
||||
map: Option<ArenaMap<Idx<T>, V>>,
|
||||
len: u32,
|
||||
}
|
||||
|
||||
impl<T, V> Trace<T, V> {
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn new_for_arena() -> Trace<T, V> {
|
||||
Trace { arena: Some(Arena::default()), map: None, len: 0 }
|
||||
}
|
||||
|
||||
pub(crate) fn new_for_map() -> Trace<T, V> {
|
||||
Trace { arena: None, map: Some(ArenaMap::default()), len: 0 }
|
||||
}
|
||||
|
||||
pub(crate) fn alloc(&mut self, value: impl FnOnce() -> V, data: impl FnOnce() -> T) -> Idx<T> {
|
||||
let id = if let Some(arena) = &mut self.arena {
|
||||
arena.alloc(data())
|
||||
} else {
|
||||
let id = Idx::<T>::from_raw(RawIdx::from(self.len));
|
||||
self.len += 1;
|
||||
id
|
||||
};
|
||||
|
||||
if let Some(map) = &mut self.map {
|
||||
map.insert(id, value());
|
||||
}
|
||||
id
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn into_arena(mut self) -> Arena<T> {
|
||||
self.arena.take().unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn into_map(mut self) -> ArenaMap<Idx<T>, V> {
|
||||
self.map.take().unwrap()
|
||||
}
|
||||
}
|
@ -4,19 +4,24 @@ use std::{borrow::Cow, fmt, ops};
|
||||
use base_db::CrateId;
|
||||
use cfg::CfgExpr;
|
||||
use either::Either;
|
||||
use intern::Interned;
|
||||
use mbe::{syntax_node_to_token_tree, DelimiterKind, DocCommentDesugarMode, Punct};
|
||||
use intern::{sym, Interned, Symbol};
|
||||
|
||||
use mbe::{
|
||||
desugar_doc_comment_text, syntax_node_to_token_tree, DelimiterKind, DocCommentDesugarMode,
|
||||
Punct,
|
||||
};
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use span::{Span, SyntaxContextId};
|
||||
use syntax::unescape;
|
||||
use syntax::{ast, format_smolstr, match_ast, AstNode, AstToken, SmolStr, SyntaxNode};
|
||||
use syntax::{ast, match_ast, AstNode, AstToken, SyntaxNode};
|
||||
use triomphe::ThinArc;
|
||||
|
||||
use crate::name::Name;
|
||||
use crate::{
|
||||
db::ExpandDatabase,
|
||||
mod_path::ModPath,
|
||||
span_map::SpanMapRef,
|
||||
tt::{self, Subtree},
|
||||
tt::{self, token_to_literal, Subtree},
|
||||
InFile,
|
||||
};
|
||||
|
||||
@ -52,13 +57,20 @@ impl RawAttrs {
|
||||
}
|
||||
Either::Right(comment) => comment.doc_comment().map(|doc| {
|
||||
let span = span_map.span_for_range(comment.syntax().text_range());
|
||||
let (text, kind) =
|
||||
desugar_doc_comment_text(doc, DocCommentDesugarMode::ProcMacro);
|
||||
Attr {
|
||||
id,
|
||||
input: Some(Box::new(AttrInput::Literal(tt::Literal {
|
||||
text: SmolStr::new(format_smolstr!("\"{}\"", Self::escape_chars(doc))),
|
||||
symbol: text,
|
||||
span,
|
||||
kind,
|
||||
suffix: None,
|
||||
}))),
|
||||
path: Interned::new(ModPath::from(crate::name!(doc))),
|
||||
path: Interned::new(ModPath::from(Name::new_symbol(
|
||||
sym::doc.clone(),
|
||||
span.ctx,
|
||||
))),
|
||||
ctxt: span.ctx,
|
||||
}
|
||||
}),
|
||||
@ -74,10 +86,6 @@ impl RawAttrs {
|
||||
RawAttrs { entries }
|
||||
}
|
||||
|
||||
fn escape_chars(s: &str) -> String {
|
||||
s.replace('\\', r#"\\"#).replace('"', r#"\""#)
|
||||
}
|
||||
|
||||
pub fn from_attrs_owner(
|
||||
db: &dyn ExpandDatabase,
|
||||
owner: InFile<&dyn ast::HasAttrs>,
|
||||
@ -115,7 +123,7 @@ impl RawAttrs {
|
||||
pub fn filter(self, db: &dyn ExpandDatabase, krate: CrateId) -> RawAttrs {
|
||||
let has_cfg_attrs = self
|
||||
.iter()
|
||||
.any(|attr| attr.path.as_ident().map_or(false, |name| *name == crate::name![cfg_attr]));
|
||||
.any(|attr| attr.path.as_ident().map_or(false, |name| *name == sym::cfg_attr.clone()));
|
||||
if !has_cfg_attrs {
|
||||
return self;
|
||||
}
|
||||
@ -125,7 +133,7 @@ impl RawAttrs {
|
||||
self.iter()
|
||||
.flat_map(|attr| -> SmallVec<[_; 1]> {
|
||||
let is_cfg_attr =
|
||||
attr.path.as_ident().map_or(false, |name| *name == crate::name![cfg_attr]);
|
||||
attr.path.as_ident().map_or(false, |name| *name == sym::cfg_attr.clone());
|
||||
if !is_cfg_attr {
|
||||
return smallvec![attr.clone()];
|
||||
}
|
||||
@ -234,10 +242,8 @@ impl Attr {
|
||||
})?);
|
||||
let span = span_map.span_for_range(range);
|
||||
let input = if let Some(ast::Expr::Literal(lit)) = ast.expr() {
|
||||
Some(Box::new(AttrInput::Literal(tt::Literal {
|
||||
text: lit.token().text().into(),
|
||||
span,
|
||||
})))
|
||||
let token = lit.token();
|
||||
Some(Box::new(AttrInput::Literal(token_to_literal(token.text(), span))))
|
||||
} else if let Some(tt) = ast.token_tree() {
|
||||
let tree = syntax_node_to_token_tree(
|
||||
tt.syntax(),
|
||||
@ -254,8 +260,8 @@ impl Attr {
|
||||
|
||||
fn from_tt(db: &dyn ExpandDatabase, mut tt: &[tt::TokenTree], id: AttrId) -> Option<Attr> {
|
||||
if matches!(tt,
|
||||
[tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { text, .. })), ..]
|
||||
if text == "unsafe"
|
||||
[tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { sym, .. })), ..]
|
||||
if *sym == sym::unsafe_
|
||||
) {
|
||||
match tt.get(1) {
|
||||
Some(tt::TokenTree::Subtree(subtree)) => tt = &subtree.token_trees,
|
||||
@ -304,26 +310,38 @@ impl Attr {
|
||||
|
||||
impl Attr {
|
||||
/// #[path = "string"]
|
||||
pub fn string_value(&self) -> Option<&str> {
|
||||
pub fn string_value(&self) -> Option<&Symbol> {
|
||||
match self.input.as_deref()? {
|
||||
AttrInput::Literal(it) => match it.text.strip_prefix('r') {
|
||||
Some(it) => it.trim_matches('#'),
|
||||
None => it.text.as_str(),
|
||||
}
|
||||
.strip_prefix('"')?
|
||||
.strip_suffix('"'),
|
||||
AttrInput::Literal(tt::Literal {
|
||||
symbol: text,
|
||||
kind: tt::LitKind::Str | tt::LitKind::StrRaw(_),
|
||||
..
|
||||
}) => Some(text),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// #[path = "string"]
|
||||
pub fn string_value_with_span(&self) -> Option<(&Symbol, span::Span)> {
|
||||
match self.input.as_deref()? {
|
||||
AttrInput::Literal(tt::Literal {
|
||||
symbol: text,
|
||||
kind: tt::LitKind::Str | tt::LitKind::StrRaw(_),
|
||||
span,
|
||||
suffix: _,
|
||||
}) => Some((text, *span)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn string_value_unescape(&self) -> Option<Cow<'_, str>> {
|
||||
match self.input.as_deref()? {
|
||||
AttrInput::Literal(it) => match it.text.strip_prefix('r') {
|
||||
Some(it) => {
|
||||
it.trim_matches('#').strip_prefix('"')?.strip_suffix('"').map(Cow::Borrowed)
|
||||
}
|
||||
None => it.text.strip_prefix('"')?.strip_suffix('"').and_then(unescape),
|
||||
},
|
||||
AttrInput::Literal(tt::Literal {
|
||||
symbol: text, kind: tt::LitKind::StrRaw(_), ..
|
||||
}) => Some(Cow::Borrowed(text.as_str())),
|
||||
AttrInput::Literal(tt::Literal { symbol: text, kind: tt::LitKind::Str, .. }) => {
|
||||
unescape(text.as_str())
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@ -369,7 +387,7 @@ impl Attr {
|
||||
}
|
||||
|
||||
pub fn cfg(&self) -> Option<CfgExpr> {
|
||||
if *self.path.as_ident()? == crate::name![cfg] {
|
||||
if *self.path.as_ident()? == sym::cfg.clone() {
|
||||
self.token_tree_value().map(CfgExpr::parse)
|
||||
} else {
|
||||
None
|
||||
|
15
src/tools/rust-analyzer/crates/hir-expand/src/builtin.rs
Normal file
15
src/tools/rust-analyzer/crates/hir-expand/src/builtin.rs
Normal file
@ -0,0 +1,15 @@
|
||||
//! Builtin macros and attributes
|
||||
#[macro_use]
|
||||
mod quote;
|
||||
|
||||
mod attr_macro;
|
||||
mod derive_macro;
|
||||
mod fn_macro;
|
||||
|
||||
pub use self::{
|
||||
attr_macro::{find_builtin_attr, pseudo_derive_attr_expansion, BuiltinAttrExpander},
|
||||
derive_macro::{find_builtin_derive, BuiltinDeriveExpander},
|
||||
fn_macro::{
|
||||
find_builtin_macro, include_input_to_file_id, BuiltinFnLikeExpander, EagerExpander,
|
||||
},
|
||||
};
|
@ -1,4 +1,5 @@
|
||||
//! Builtin attributes.
|
||||
use intern::sym;
|
||||
use span::{MacroCallId, Span};
|
||||
|
||||
use crate::{db::ExpandDatabase, name, tt, ExpandResult, MacroCallKind};
|
||||
@ -19,7 +20,7 @@ macro_rules! register_builtin {
|
||||
|
||||
fn find_by_name(name: &name::Name) -> Option<Self> {
|
||||
match name {
|
||||
$( id if id == &name::name![$name] => Some(BuiltinAttrExpander::$variant), )*
|
||||
$( id if id == &sym::$name => Some(BuiltinAttrExpander::$variant), )*
|
||||
_ => None,
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
//! Builtin derives.
|
||||
|
||||
use intern::sym;
|
||||
use itertools::izip;
|
||||
use mbe::DocCommentDesugarMode;
|
||||
use rustc_hash::FxHashSet;
|
||||
@ -8,18 +9,17 @@ use stdx::never;
|
||||
use tracing::debug;
|
||||
|
||||
use crate::{
|
||||
builtin::quote::{dollar_crate, quote},
|
||||
db::ExpandDatabase,
|
||||
hygiene::span_with_def_site_ctxt,
|
||||
name::{AsName, Name},
|
||||
quote::dollar_crate,
|
||||
name::{self, AsName, Name},
|
||||
span_map::ExpansionSpanMap,
|
||||
tt,
|
||||
tt, ExpandError, ExpandResult,
|
||||
};
|
||||
use syntax::ast::{
|
||||
self, AstNode, FieldList, HasAttrs, HasGenericParams, HasModuleItem, HasName, HasTypeBounds,
|
||||
};
|
||||
|
||||
use crate::{db::ExpandDatabase, name, quote, ExpandError, ExpandResult};
|
||||
|
||||
macro_rules! register_builtin {
|
||||
( $($trait:ident => $expand:ident),* ) => {
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
@ -36,7 +36,7 @@ macro_rules! register_builtin {
|
||||
|
||||
fn find_by_name(name: &name::Name) -> Option<Self> {
|
||||
match name {
|
||||
$( id if id == &name::name![$trait] => Some(BuiltinDeriveExpander::$trait), )*
|
||||
$( id if id == &sym::$trait => Some(BuiltinDeriveExpander::$trait), )*
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@ -81,7 +81,7 @@ enum VariantShape {
|
||||
}
|
||||
|
||||
fn tuple_field_iterator(span: Span, n: usize) -> impl Iterator<Item = tt::Ident> {
|
||||
(0..n).map(move |it| tt::Ident::new(format!("f{it}"), span))
|
||||
(0..n).map(move |it| tt::Ident::new(&format!("f{it}"), span))
|
||||
}
|
||||
|
||||
impl VariantShape {
|
||||
@ -128,13 +128,17 @@ impl VariantShape {
|
||||
}
|
||||
}
|
||||
|
||||
fn from(tm: &ExpansionSpanMap, value: Option<FieldList>) -> Result<Self, ExpandError> {
|
||||
fn from(
|
||||
call_site: Span,
|
||||
tm: &ExpansionSpanMap,
|
||||
value: Option<FieldList>,
|
||||
) -> Result<Self, ExpandError> {
|
||||
let r = match value {
|
||||
None => VariantShape::Unit,
|
||||
Some(FieldList::RecordFieldList(it)) => VariantShape::Struct(
|
||||
it.fields()
|
||||
.map(|it| it.name())
|
||||
.map(|it| name_to_token(tm, it))
|
||||
.map(|it| name_to_token(call_site, tm, it))
|
||||
.collect::<Result<_, _>>()?,
|
||||
),
|
||||
Some(FieldList::TupleFieldList(it)) => VariantShape::Tuple(it.fields().count()),
|
||||
@ -208,19 +212,20 @@ fn parse_adt(tt: &tt::Subtree, call_site: Span) -> Result<BasicAdtInfo, ExpandEr
|
||||
let (parsed, tm) = &mbe::token_tree_to_syntax_node(
|
||||
tt,
|
||||
mbe::TopEntryPoint::MacroItems,
|
||||
parser::Edition::CURRENT,
|
||||
parser::Edition::CURRENT_FIXME,
|
||||
);
|
||||
let macro_items = ast::MacroItems::cast(parsed.syntax_node())
|
||||
.ok_or_else(|| ExpandError::other("invalid item definition"))?;
|
||||
let item = macro_items.items().next().ok_or_else(|| ExpandError::other("no item found"))?;
|
||||
.ok_or_else(|| ExpandError::other(call_site, "invalid item definition"))?;
|
||||
let item =
|
||||
macro_items.items().next().ok_or_else(|| ExpandError::other(call_site, "no item found"))?;
|
||||
let adt = &ast::Adt::cast(item.syntax().clone())
|
||||
.ok_or_else(|| ExpandError::other("expected struct, enum or union"))?;
|
||||
.ok_or_else(|| ExpandError::other(call_site, "expected struct, enum or union"))?;
|
||||
let (name, generic_param_list, where_clause, shape) = match adt {
|
||||
ast::Adt::Struct(it) => (
|
||||
it.name(),
|
||||
it.generic_param_list(),
|
||||
it.where_clause(),
|
||||
AdtShape::Struct(VariantShape::from(tm, it.field_list())?),
|
||||
AdtShape::Struct(VariantShape::from(call_site, tm, it.field_list())?),
|
||||
),
|
||||
ast::Adt::Enum(it) => {
|
||||
let default_variant = it
|
||||
@ -240,8 +245,8 @@ fn parse_adt(tt: &tt::Subtree, call_site: Span) -> Result<BasicAdtInfo, ExpandEr
|
||||
.flat_map(|it| it.variants())
|
||||
.map(|it| {
|
||||
Ok((
|
||||
name_to_token(tm, it.name())?,
|
||||
VariantShape::from(tm, it.field_list())?,
|
||||
name_to_token(call_site, tm, it.name())?,
|
||||
VariantShape::from(call_site, tm, it.field_list())?,
|
||||
))
|
||||
})
|
||||
.collect::<Result<_, ExpandError>>()?,
|
||||
@ -356,20 +361,22 @@ fn parse_adt(tt: &tt::Subtree, call_site: Span) -> Result<BasicAdtInfo, ExpandEr
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
let name_token = name_to_token(tm, name)?;
|
||||
let name_token = name_to_token(call_site, tm, name)?;
|
||||
Ok(BasicAdtInfo { name: name_token, shape, param_types, where_clause, associated_types })
|
||||
}
|
||||
|
||||
fn name_to_token(
|
||||
call_site: Span,
|
||||
token_map: &ExpansionSpanMap,
|
||||
name: Option<ast::Name>,
|
||||
) -> Result<tt::Ident, ExpandError> {
|
||||
let name = name.ok_or_else(|| {
|
||||
debug!("parsed item has no name");
|
||||
ExpandError::other("missing name")
|
||||
ExpandError::other(call_site, "missing name")
|
||||
})?;
|
||||
let span = token_map.span_at(name.syntax().text_range().start());
|
||||
let name_token = tt::Ident { span, text: name.text().into() };
|
||||
|
||||
let name_token = tt::Ident::new(name.text().as_ref(), span);
|
||||
Ok(name_token)
|
||||
}
|
||||
|
||||
@ -691,14 +698,14 @@ fn partial_eq_expand(span: Span, tt: &tt::Subtree) -> ExpandResult<tt::Subtree>
|
||||
}
|
||||
[first, rest @ ..] => {
|
||||
let rest = rest.iter().map(|it| {
|
||||
let t1 = tt::Ident::new(format!("{}_self", it.text), it.span);
|
||||
let t2 = tt::Ident::new(format!("{}_other", it.text), it.span);
|
||||
let t1 = tt::Ident::new(&format!("{}_self", it.sym), it.span);
|
||||
let t2 = tt::Ident::new(&format!("{}_other", it.sym), it.span);
|
||||
let and_and = and_and(span);
|
||||
quote!(span =>#and_and #t1 .eq( #t2 ))
|
||||
});
|
||||
let first = {
|
||||
let t1 = tt::Ident::new(format!("{}_self", first.text), first.span);
|
||||
let t2 = tt::Ident::new(format!("{}_other", first.text), first.span);
|
||||
let t1 = tt::Ident::new(&format!("{}_self", first.sym), first.span);
|
||||
let t2 = tt::Ident::new(&format!("{}_other", first.sym), first.span);
|
||||
quote!(span =>#t1 .eq( #t2 ))
|
||||
};
|
||||
quote!(span =>#first ##rest)
|
||||
@ -728,7 +735,7 @@ fn self_and_other_patterns(
|
||||
let self_patterns = adt.shape.as_pattern_map(
|
||||
name,
|
||||
|it| {
|
||||
let t = tt::Ident::new(format!("{}_self", it.text), it.span);
|
||||
let t = tt::Ident::new(&format!("{}_self", it.sym), it.span);
|
||||
quote!(span =>#t)
|
||||
},
|
||||
span,
|
||||
@ -736,7 +743,7 @@ fn self_and_other_patterns(
|
||||
let other_patterns = adt.shape.as_pattern_map(
|
||||
name,
|
||||
|it| {
|
||||
let t = tt::Ident::new(format!("{}_other", it.text), it.span);
|
||||
let t = tt::Ident::new(&format!("{}_other", it.sym), it.span);
|
||||
quote!(span =>#t)
|
||||
},
|
||||
span,
|
||||
@ -774,8 +781,8 @@ fn ord_expand(span: Span, tt: &tt::Subtree) -> ExpandResult<tt::Subtree> {
|
||||
|(pat1, pat2, fields)| {
|
||||
let mut body = quote!(span =>#krate::cmp::Ordering::Equal);
|
||||
for f in fields.into_iter().rev() {
|
||||
let t1 = tt::Ident::new(format!("{}_self", f.text), f.span);
|
||||
let t2 = tt::Ident::new(format!("{}_other", f.text), f.span);
|
||||
let t1 = tt::Ident::new(&format!("{}_self", f.sym), f.span);
|
||||
let t2 = tt::Ident::new(&format!("{}_other", f.sym), f.span);
|
||||
body = compare(krate, quote!(span =>#t1), quote!(span =>#t2), body, span);
|
||||
}
|
||||
let fat_arrow = fat_arrow(span);
|
||||
@ -836,8 +843,8 @@ fn partial_ord_expand(span: Span, tt: &tt::Subtree) -> ExpandResult<tt::Subtree>
|
||||
let mut body =
|
||||
quote!(span =>#krate::option::Option::Some(#krate::cmp::Ordering::Equal));
|
||||
for f in fields.into_iter().rev() {
|
||||
let t1 = tt::Ident::new(format!("{}_self", f.text), f.span);
|
||||
let t2 = tt::Ident::new(format!("{}_other", f.text), f.span);
|
||||
let t1 = tt::Ident::new(&format!("{}_self", f.sym), f.span);
|
||||
let t2 = tt::Ident::new(&format!("{}_other", f.sym), f.span);
|
||||
body = compare(krate, quote!(span =>#t1), quote!(span =>#t2), body, span);
|
||||
}
|
||||
let fat_arrow = fat_arrow(span);
|
@ -1,21 +1,24 @@
|
||||
//! Builtin macro
|
||||
|
||||
use base_db::{AnchoredPath, FileId};
|
||||
use base_db::AnchoredPath;
|
||||
use cfg::CfgExpr;
|
||||
use either::Either;
|
||||
use itertools::Itertools;
|
||||
use mbe::{parse_exprs_with_sep, parse_to_token_tree};
|
||||
use span::{Edition, Span, SpanAnchor, SyntaxContextId, ROOT_ERASED_FILE_AST_ID};
|
||||
use syntax::ast::{self, AstToken};
|
||||
use intern::{sym, Symbol};
|
||||
use mbe::{parse_exprs_with_sep, parse_to_token_tree, DelimiterKind};
|
||||
use span::{Edition, EditionedFileId, Span, SpanAnchor, SyntaxContextId, ROOT_ERASED_FILE_AST_ID};
|
||||
use stdx::format_to;
|
||||
use syntax::{
|
||||
format_smolstr,
|
||||
unescape::{unescape_byte, unescape_char, unescape_unicode, Mode},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
builtin::quote::{dollar_crate, quote},
|
||||
db::ExpandDatabase,
|
||||
hygiene::{span_with_call_site_ctxt, span_with_def_site_ctxt},
|
||||
name::{self, known},
|
||||
quote,
|
||||
quote::dollar_crate,
|
||||
name,
|
||||
tt::{self, DelimSpan},
|
||||
ExpandError, ExpandResult, HirFileIdExt, MacroCallId, MacroFileIdExt,
|
||||
ExpandError, ExpandResult, HirFileIdExt, Lookup as _, MacroCallId,
|
||||
};
|
||||
|
||||
macro_rules! register_builtin {
|
||||
@ -31,7 +34,7 @@ macro_rules! register_builtin {
|
||||
}
|
||||
|
||||
impl BuiltinFnLikeExpander {
|
||||
pub fn expander(&self) -> fn (&dyn ExpandDatabase, MacroCallId, &tt::Subtree, Span) -> ExpandResult<tt::Subtree> {
|
||||
fn expander(&self) -> fn (&dyn ExpandDatabase, MacroCallId, &tt::Subtree, Span) -> ExpandResult<tt::Subtree> {
|
||||
match *self {
|
||||
$( BuiltinFnLikeExpander::$kind => $expand, )*
|
||||
}
|
||||
@ -39,7 +42,7 @@ macro_rules! register_builtin {
|
||||
}
|
||||
|
||||
impl EagerExpander {
|
||||
pub fn expander(&self) -> fn (&dyn ExpandDatabase, MacroCallId, &tt::Subtree, Span) -> ExpandResult<tt::Subtree> {
|
||||
fn expander(&self) -> fn (&dyn ExpandDatabase, MacroCallId, &tt::Subtree, Span) -> ExpandResult<tt::Subtree> {
|
||||
match *self {
|
||||
$( EagerExpander::$e_kind => $e_expand, )*
|
||||
}
|
||||
@ -48,8 +51,8 @@ macro_rules! register_builtin {
|
||||
|
||||
fn find_by_name(ident: &name::Name) -> Option<Either<BuiltinFnLikeExpander, EagerExpander>> {
|
||||
match ident {
|
||||
$( id if id == &name::name![$name] => Some(Either::Left(BuiltinFnLikeExpander::$kind)), )*
|
||||
$( id if id == &name::name![$e_name] => Some(Either::Right(EagerExpander::$e_kind)), )*
|
||||
$( id if id == &sym::$name => Some(Either::Left(BuiltinFnLikeExpander::$kind)), )*
|
||||
$( id if id == &sym::$e_name => Some(Either::Right(EagerExpander::$e_kind)), )*
|
||||
_ => return None,
|
||||
}
|
||||
}
|
||||
@ -142,7 +145,7 @@ register_builtin! {
|
||||
}
|
||||
|
||||
fn mk_pound(span: Span) -> tt::Subtree {
|
||||
crate::quote::IntoTt::to_subtree(
|
||||
crate::builtin::quote::IntoTt::to_subtree(
|
||||
vec![crate::tt::Leaf::Punct(crate::tt::Punct {
|
||||
char: '#',
|
||||
spacing: crate::tt::Spacing::Alone,
|
||||
@ -177,8 +180,10 @@ fn line_expand(
|
||||
ExpandResult::ok(tt::Subtree {
|
||||
delimiter: tt::Delimiter::invisible_spanned(span),
|
||||
token_trees: Box::new([tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal {
|
||||
text: "0u32".into(),
|
||||
symbol: sym::INTEGER_0.clone(),
|
||||
span,
|
||||
kind: tt::LitKind::Integer,
|
||||
suffix: Some(sym::u32.clone()),
|
||||
}))]),
|
||||
})
|
||||
}
|
||||
@ -223,7 +228,7 @@ fn assert_expand(
|
||||
span: Span,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
let call_site_span = span_with_call_site_ctxt(db, span, id);
|
||||
let args = parse_exprs_with_sep(tt, ',', call_site_span, Edition::CURRENT);
|
||||
let args = parse_exprs_with_sep(tt, ',', call_site_span, Edition::CURRENT_FIXME);
|
||||
let dollar_crate = dollar_crate(span);
|
||||
let expanded = match &*args {
|
||||
[cond, panic_args @ ..] => {
|
||||
@ -262,7 +267,7 @@ fn file_expand(
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
// FIXME: RA purposefully lacks knowledge of absolute file names
|
||||
// so just return "".
|
||||
let file_name = "";
|
||||
let file_name = "file";
|
||||
|
||||
let expanded = quote! {span =>
|
||||
#file_name
|
||||
@ -272,29 +277,9 @@ fn file_expand(
|
||||
}
|
||||
|
||||
fn format_args_expand(
|
||||
db: &dyn ExpandDatabase,
|
||||
id: MacroCallId,
|
||||
tt: &tt::Subtree,
|
||||
span: Span,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
format_args_expand_general(db, id, tt, "", span)
|
||||
}
|
||||
|
||||
fn format_args_nl_expand(
|
||||
db: &dyn ExpandDatabase,
|
||||
id: MacroCallId,
|
||||
tt: &tt::Subtree,
|
||||
span: Span,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
format_args_expand_general(db, id, tt, "\\n", span)
|
||||
}
|
||||
|
||||
fn format_args_expand_general(
|
||||
_db: &dyn ExpandDatabase,
|
||||
_id: MacroCallId,
|
||||
tt: &tt::Subtree,
|
||||
// FIXME: Make use of this so that mir interpretation works properly
|
||||
_end_string: &str,
|
||||
span: Span,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
let pound = mk_pound(span);
|
||||
@ -305,6 +290,28 @@ fn format_args_expand_general(
|
||||
})
|
||||
}
|
||||
|
||||
fn format_args_nl_expand(
|
||||
_db: &dyn ExpandDatabase,
|
||||
_id: MacroCallId,
|
||||
tt: &tt::Subtree,
|
||||
span: Span,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
let pound = mk_pound(span);
|
||||
let mut tt = tt.clone();
|
||||
tt.delimiter.kind = tt::DelimiterKind::Parenthesis;
|
||||
if let Some(tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal {
|
||||
symbol: text,
|
||||
kind: tt::LitKind::Str,
|
||||
..
|
||||
}))) = tt.token_trees.first_mut()
|
||||
{
|
||||
*text = Symbol::intern(&format_smolstr!("{}\\n", text.as_str()));
|
||||
}
|
||||
ExpandResult::ok(quote! {span =>
|
||||
builtin #pound format_args #tt
|
||||
})
|
||||
}
|
||||
|
||||
fn asm_expand(
|
||||
_db: &dyn ExpandDatabase,
|
||||
_id: MacroCallId,
|
||||
@ -367,8 +374,11 @@ fn panic_expand(
|
||||
let dollar_crate = dollar_crate(span);
|
||||
let call_site_span = span_with_call_site_ctxt(db, span, id);
|
||||
|
||||
let mac =
|
||||
if use_panic_2021(db, call_site_span) { known::panic_2021 } else { known::panic_2015 };
|
||||
let mac = if use_panic_2021(db, call_site_span) {
|
||||
sym::panic_2021.clone()
|
||||
} else {
|
||||
sym::panic_2015.clone()
|
||||
};
|
||||
|
||||
// Expand to a macro call `$crate::panic::panic_{edition}`
|
||||
let mut call = quote!(call_site_span =>#dollar_crate::panic::#mac!);
|
||||
@ -397,9 +407,9 @@ fn unreachable_expand(
|
||||
let call_site_span = span_with_call_site_ctxt(db, span, id);
|
||||
|
||||
let mac = if use_panic_2021(db, call_site_span) {
|
||||
known::unreachable_2021
|
||||
sym::unreachable_2021.clone()
|
||||
} else {
|
||||
known::unreachable_2015
|
||||
sym::unreachable_2015.clone()
|
||||
};
|
||||
|
||||
// Expand to a macro call `$crate::panic::panic_{edition}`
|
||||
@ -432,7 +442,7 @@ fn use_panic_2021(db: &dyn ExpandDatabase, span: Span) -> bool {
|
||||
// FIXME: Record allow_internal_unstable in the macro def (not been done yet because it
|
||||
// would consume quite a bit extra memory for all call locs...)
|
||||
// if let Some(features) = expn.def.allow_internal_unstable {
|
||||
// if features.iter().any(|&f| f == sym::edition_panic) {
|
||||
// if features.iter().any(|&f| f == sym::edition_panic.clone()) {
|
||||
// span = expn.call_site;
|
||||
// continue;
|
||||
// }
|
||||
@ -441,27 +451,6 @@ fn use_panic_2021(db: &dyn ExpandDatabase, span: Span) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
fn unquote_str(lit: &tt::Literal) -> Option<(String, Span)> {
|
||||
let span = lit.span;
|
||||
let lit = ast::make::tokens::literal(&lit.to_string());
|
||||
let token = ast::String::cast(lit)?;
|
||||
token.value().ok().map(|it| (it.into_owned(), span))
|
||||
}
|
||||
|
||||
fn unquote_char(lit: &tt::Literal) -> Option<(char, Span)> {
|
||||
let span = lit.span;
|
||||
let lit = ast::make::tokens::literal(&lit.to_string());
|
||||
let token = ast::Char::cast(lit)?;
|
||||
token.value().ok().zip(Some(span))
|
||||
}
|
||||
|
||||
fn unquote_byte_string(lit: &tt::Literal) -> Option<(Vec<u8>, Span)> {
|
||||
let span = lit.span;
|
||||
let lit = ast::make::tokens::literal(&lit.to_string());
|
||||
let token = ast::ByteString::cast(lit)?;
|
||||
token.value().ok().map(|it| (it.into_owned(), span))
|
||||
}
|
||||
|
||||
fn compile_error_expand(
|
||||
_db: &dyn ExpandDatabase,
|
||||
_id: MacroCallId,
|
||||
@ -469,11 +458,13 @@ fn compile_error_expand(
|
||||
span: Span,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
let err = match &*tt.token_trees {
|
||||
[tt::TokenTree::Leaf(tt::Leaf::Literal(it))] => match unquote_str(it) {
|
||||
Some((unquoted, _)) => ExpandError::other(unquoted.into_boxed_str()),
|
||||
None => ExpandError::other("`compile_error!` argument must be a string"),
|
||||
},
|
||||
_ => ExpandError::other("`compile_error!` argument must be a string"),
|
||||
[tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal {
|
||||
symbol: text,
|
||||
span: _,
|
||||
kind: tt::LitKind::Str | tt::LitKind::StrRaw(_),
|
||||
suffix: _,
|
||||
}))] => ExpandError::other(span, Box::from(unescape_str(text).as_str())),
|
||||
_ => ExpandError::other(span, "`compile_error!` argument must be a string"),
|
||||
};
|
||||
|
||||
ExpandResult { value: quote! {span =>}, err: Some(err) }
|
||||
@ -483,7 +474,7 @@ fn concat_expand(
|
||||
_db: &dyn ExpandDatabase,
|
||||
_arg_id: MacroCallId,
|
||||
tt: &tt::Subtree,
|
||||
_: Span,
|
||||
call_site: Span,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
let mut err = None;
|
||||
let mut text = String::new();
|
||||
@ -504,32 +495,49 @@ fn concat_expand(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
match t {
|
||||
tt::TokenTree::Leaf(tt::Leaf::Literal(it)) if i % 2 == 0 => {
|
||||
// concat works with string and char literals, so remove any quotes.
|
||||
// It also works with integer, float and boolean literals, so just use the rest
|
||||
// as-is.
|
||||
if let Some((c, span)) = unquote_char(it) {
|
||||
text.push(c);
|
||||
record_span(span);
|
||||
} else {
|
||||
let (component, span) =
|
||||
unquote_str(it).unwrap_or_else(|| (it.text.to_string(), it.span));
|
||||
text.push_str(&component);
|
||||
record_span(span);
|
||||
match it.kind {
|
||||
tt::LitKind::Char => {
|
||||
if let Ok(c) = unescape_char(it.symbol.as_str()) {
|
||||
text.extend(c.escape_default());
|
||||
}
|
||||
record_span(it.span);
|
||||
}
|
||||
tt::LitKind::Integer | tt::LitKind::Float => {
|
||||
format_to!(text, "{}", it.symbol.as_str())
|
||||
}
|
||||
tt::LitKind::Str => {
|
||||
text.push_str(it.symbol.as_str());
|
||||
record_span(it.span);
|
||||
}
|
||||
tt::LitKind::StrRaw(_) => {
|
||||
format_to!(text, "{}", it.symbol.as_str().escape_debug());
|
||||
record_span(it.span);
|
||||
}
|
||||
tt::LitKind::Byte
|
||||
| tt::LitKind::ByteStr
|
||||
| tt::LitKind::ByteStrRaw(_)
|
||||
| tt::LitKind::CStr
|
||||
| tt::LitKind::CStrRaw(_)
|
||||
| tt::LitKind::Err(_) => {
|
||||
err = Some(ExpandError::other(it.span, "unexpected literal"))
|
||||
}
|
||||
}
|
||||
}
|
||||
// handle boolean literals
|
||||
tt::TokenTree::Leaf(tt::Leaf::Ident(id))
|
||||
if i % 2 == 0 && (id.text == "true" || id.text == "false") =>
|
||||
if i % 2 == 0 && (id.sym == sym::true_ || id.sym == sym::false_) =>
|
||||
{
|
||||
text.push_str(id.text.as_str());
|
||||
text.push_str(id.sym.as_str());
|
||||
record_span(id.span);
|
||||
}
|
||||
tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) if i % 2 == 1 && punct.char == ',' => (),
|
||||
_ => {
|
||||
err.get_or_insert(mbe::ExpandError::UnexpectedToken.into());
|
||||
err.get_or_insert(ExpandError::other(call_site, "unexpected token"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -543,7 +551,7 @@ fn concat_bytes_expand(
|
||||
tt: &tt::Subtree,
|
||||
call_site: Span,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
let mut bytes = Vec::new();
|
||||
let mut bytes = String::new();
|
||||
let mut err = None;
|
||||
let mut span: Option<Span> = None;
|
||||
let mut record_span = |s: Span| match &mut span {
|
||||
@ -553,84 +561,97 @@ fn concat_bytes_expand(
|
||||
};
|
||||
for (i, t) in tt.token_trees.iter().enumerate() {
|
||||
match t {
|
||||
tt::TokenTree::Leaf(tt::Leaf::Literal(lit)) => {
|
||||
let token = ast::make::tokens::literal(&lit.to_string());
|
||||
record_span(lit.span);
|
||||
match token.kind() {
|
||||
syntax::SyntaxKind::BYTE => bytes.push(token.text().to_owned()),
|
||||
syntax::SyntaxKind::BYTE_STRING => {
|
||||
let components = unquote_byte_string(lit).map_or(vec![], |(it, _)| it);
|
||||
components.into_iter().for_each(|it| bytes.push(it.to_string()));
|
||||
tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal {
|
||||
symbol: text,
|
||||
span,
|
||||
kind,
|
||||
suffix: _,
|
||||
})) => {
|
||||
record_span(*span);
|
||||
match kind {
|
||||
tt::LitKind::Byte => {
|
||||
if let Ok(b) = unescape_byte(text.as_str()) {
|
||||
bytes.extend(
|
||||
b.escape_ascii().filter_map(|it| char::from_u32(it as u32)),
|
||||
);
|
||||
}
|
||||
}
|
||||
tt::LitKind::ByteStr => {
|
||||
bytes.push_str(text.as_str());
|
||||
}
|
||||
tt::LitKind::ByteStrRaw(_) => {
|
||||
bytes.extend(text.as_str().escape_debug());
|
||||
}
|
||||
_ => {
|
||||
err.get_or_insert(mbe::ExpandError::UnexpectedToken.into());
|
||||
err.get_or_insert(ExpandError::other(*span, "unexpected token"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) if i % 2 == 1 && punct.char == ',' => (),
|
||||
tt::TokenTree::Subtree(tree) if tree.delimiter.kind == tt::DelimiterKind::Bracket => {
|
||||
if let Err(e) = concat_bytes_expand_subtree(tree, &mut bytes, &mut record_span) {
|
||||
if let Err(e) =
|
||||
concat_bytes_expand_subtree(tree, &mut bytes, &mut record_span, call_site)
|
||||
{
|
||||
err.get_or_insert(e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
err.get_or_insert(mbe::ExpandError::UnexpectedToken.into());
|
||||
err.get_or_insert(ExpandError::other(call_site, "unexpected token"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
let value = tt::Subtree {
|
||||
delimiter: tt::Delimiter {
|
||||
open: call_site,
|
||||
close: call_site,
|
||||
kind: tt::DelimiterKind::Bracket,
|
||||
let span = span.unwrap_or(tt.delimiter.open);
|
||||
ExpandResult {
|
||||
value: tt::Subtree {
|
||||
delimiter: tt::Delimiter::invisible_spanned(span),
|
||||
token_trees: vec![tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal {
|
||||
symbol: Symbol::intern(&bytes),
|
||||
span,
|
||||
kind: tt::LitKind::ByteStr,
|
||||
suffix: None,
|
||||
}))]
|
||||
.into(),
|
||||
},
|
||||
token_trees: {
|
||||
Itertools::intersperse_with(
|
||||
bytes.into_iter().map(|it| {
|
||||
tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal {
|
||||
text: it.into(),
|
||||
span: span.unwrap_or(call_site),
|
||||
}))
|
||||
}),
|
||||
|| {
|
||||
tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct {
|
||||
char: ',',
|
||||
spacing: tt::Spacing::Alone,
|
||||
span: call_site,
|
||||
}))
|
||||
},
|
||||
)
|
||||
.collect()
|
||||
},
|
||||
};
|
||||
ExpandResult { value, err }
|
||||
err,
|
||||
}
|
||||
}
|
||||
|
||||
fn concat_bytes_expand_subtree(
|
||||
tree: &tt::Subtree,
|
||||
bytes: &mut Vec<String>,
|
||||
bytes: &mut String,
|
||||
mut record_span: impl FnMut(Span),
|
||||
err_span: Span,
|
||||
) -> Result<(), ExpandError> {
|
||||
for (ti, tt) in tree.token_trees.iter().enumerate() {
|
||||
match tt {
|
||||
tt::TokenTree::Leaf(tt::Leaf::Literal(it)) => {
|
||||
let lit = ast::make::tokens::literal(&it.to_string());
|
||||
match lit.kind() {
|
||||
syntax::SyntaxKind::BYTE | syntax::SyntaxKind::INT_NUMBER => {
|
||||
record_span(it.span);
|
||||
bytes.push(lit.text().to_owned())
|
||||
}
|
||||
_ => {
|
||||
return Err(mbe::ExpandError::UnexpectedToken.into());
|
||||
}
|
||||
tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal {
|
||||
symbol: text,
|
||||
span,
|
||||
kind: tt::LitKind::Byte,
|
||||
suffix: _,
|
||||
})) => {
|
||||
if let Ok(b) = unescape_byte(text.as_str()) {
|
||||
bytes.extend(b.escape_ascii().filter_map(|it| char::from_u32(it as u32)));
|
||||
}
|
||||
record_span(*span);
|
||||
}
|
||||
tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal {
|
||||
symbol: text,
|
||||
span,
|
||||
kind: tt::LitKind::Integer,
|
||||
suffix: _,
|
||||
})) => {
|
||||
record_span(*span);
|
||||
if let Ok(b) = text.as_str().parse::<u8>() {
|
||||
bytes.extend(b.escape_ascii().filter_map(|it| char::from_u32(it as u32)));
|
||||
}
|
||||
}
|
||||
tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) if ti % 2 == 1 && punct.char == ',' => (),
|
||||
_ => {
|
||||
return Err(mbe::ExpandError::UnexpectedToken.into());
|
||||
return Err(ExpandError::other(err_span, "unexpected token"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -648,16 +669,16 @@ fn concat_idents_expand(
|
||||
for (i, t) in tt.token_trees.iter().enumerate() {
|
||||
match t {
|
||||
tt::TokenTree::Leaf(tt::Leaf::Ident(id)) => {
|
||||
ident.push_str(id.text.as_str());
|
||||
ident.push_str(id.sym.as_str());
|
||||
}
|
||||
tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) if i % 2 == 1 && punct.char == ',' => (),
|
||||
_ => {
|
||||
err.get_or_insert(mbe::ExpandError::UnexpectedToken.into());
|
||||
err.get_or_insert(ExpandError::other(span, "unexpected token"));
|
||||
}
|
||||
}
|
||||
}
|
||||
// FIXME merge spans
|
||||
let ident = tt::Ident { text: ident.into(), span };
|
||||
let ident = tt::Ident { sym: Symbol::intern(&ident), span, is_raw: tt::IdentIsRaw::No };
|
||||
ExpandResult { value: quote!(span =>#ident), err }
|
||||
}
|
||||
|
||||
@ -666,28 +687,64 @@ fn relative_file(
|
||||
call_id: MacroCallId,
|
||||
path_str: &str,
|
||||
allow_recursion: bool,
|
||||
) -> Result<FileId, ExpandError> {
|
||||
let call_site = call_id.as_macro_file().parent(db).original_file_respecting_includes(db);
|
||||
err_span: Span,
|
||||
) -> Result<EditionedFileId, ExpandError> {
|
||||
let lookup = call_id.lookup(db);
|
||||
let call_site = lookup.kind.file_id().original_file_respecting_includes(db).file_id();
|
||||
let path = AnchoredPath { anchor: call_site, path: path_str };
|
||||
let res = db
|
||||
.resolve_path(path)
|
||||
.ok_or_else(|| ExpandError::other(format!("failed to load file `{path_str}`")))?;
|
||||
.ok_or_else(|| ExpandError::other(err_span, format!("failed to load file `{path_str}`")))?;
|
||||
// Prevent include itself
|
||||
if res == call_site && !allow_recursion {
|
||||
Err(ExpandError::other(format!("recursive inclusion of `{path_str}`")))
|
||||
Err(ExpandError::other(err_span, format!("recursive inclusion of `{path_str}`")))
|
||||
} else {
|
||||
Ok(res)
|
||||
Ok(EditionedFileId::new(res, db.crate_graph()[lookup.krate].edition))
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_string(tt: &tt::Subtree) -> Result<(String, Span), ExpandError> {
|
||||
fn parse_string(tt: &tt::Subtree) -> Result<(Symbol, Span), ExpandError> {
|
||||
tt.token_trees
|
||||
.first()
|
||||
.ok_or(tt.delimiter.open.cover(tt.delimiter.close))
|
||||
.and_then(|tt| match tt {
|
||||
tt::TokenTree::Leaf(tt::Leaf::Literal(it)) => unquote_str(it),
|
||||
_ => None,
|
||||
tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal {
|
||||
symbol: text,
|
||||
span,
|
||||
kind: tt::LitKind::Str,
|
||||
suffix: _,
|
||||
})) => Ok((unescape_str(text), *span)),
|
||||
tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal {
|
||||
symbol: text,
|
||||
span,
|
||||
kind: tt::LitKind::StrRaw(_),
|
||||
suffix: _,
|
||||
})) => Ok((text.clone(), *span)),
|
||||
// FIXME: We wrap expression fragments in parentheses which can break this expectation
|
||||
// here
|
||||
// Remove this once we handle none delims correctly
|
||||
tt::TokenTree::Subtree(tt) if tt.delimiter.kind == DelimiterKind::Parenthesis => {
|
||||
tt.token_trees.first().and_then(|tt| match tt {
|
||||
tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal {
|
||||
symbol: text,
|
||||
span,
|
||||
kind: tt::LitKind::Str,
|
||||
suffix: _,
|
||||
})) => Some((unescape_str(text), *span)),
|
||||
tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal {
|
||||
symbol: text,
|
||||
span,
|
||||
kind: tt::LitKind::StrRaw(_),
|
||||
suffix: _,
|
||||
})) => Some((text.clone(), *span)),
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
.ok_or(tt.delimiter.open.cover(tt.delimiter.close)),
|
||||
::tt::TokenTree::Leaf(l) => Err(*l.span()),
|
||||
::tt::TokenTree::Subtree(tt) => Err(tt.delimiter.open.cover(tt.delimiter.close)),
|
||||
})
|
||||
.ok_or(mbe::ExpandError::ConversionError.into())
|
||||
.map_err(|span| ExpandError::other(span, "expected string literal"))
|
||||
}
|
||||
|
||||
fn include_expand(
|
||||
@ -703,14 +760,15 @@ fn include_expand(
|
||||
}
|
||||
};
|
||||
match parse_to_token_tree(
|
||||
file_id.edition(),
|
||||
SpanAnchor { file_id, ast_id: ROOT_ERASED_FILE_AST_ID },
|
||||
SyntaxContextId::ROOT,
|
||||
&db.file_text(file_id),
|
||||
&db.file_text(file_id.file_id()),
|
||||
) {
|
||||
Some(it) => ExpandResult::ok(it),
|
||||
None => ExpandResult::new(
|
||||
tt::Subtree::empty(DelimSpan { open: span, close: span }),
|
||||
ExpandError::other("failed to parse included file"),
|
||||
ExpandError::other(span, "failed to parse included file"),
|
||||
),
|
||||
}
|
||||
}
|
||||
@ -719,8 +777,9 @@ pub fn include_input_to_file_id(
|
||||
db: &dyn ExpandDatabase,
|
||||
arg_id: MacroCallId,
|
||||
arg: &tt::Subtree,
|
||||
) -> Result<FileId, ExpandError> {
|
||||
relative_file(db, arg_id, &parse_string(arg)?.0, false)
|
||||
) -> Result<EditionedFileId, ExpandError> {
|
||||
let (s, span) = parse_string(arg)?;
|
||||
relative_file(db, arg_id, s.as_str(), false, span)
|
||||
}
|
||||
|
||||
fn include_bytes_expand(
|
||||
@ -733,8 +792,10 @@ fn include_bytes_expand(
|
||||
let res = tt::Subtree {
|
||||
delimiter: tt::Delimiter::invisible_spanned(span),
|
||||
token_trees: Box::new([tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal {
|
||||
text: r#"b"""#.into(),
|
||||
symbol: Symbol::empty(),
|
||||
span,
|
||||
kind: tt::LitKind::ByteStrRaw(1),
|
||||
suffix: None,
|
||||
}))]),
|
||||
};
|
||||
ExpandResult::ok(res)
|
||||
@ -757,22 +818,22 @@ fn include_str_expand(
|
||||
// it's unusual to `include_str!` a Rust file), but we can return an empty string.
|
||||
// Ideally, we'd be able to offer a precise expansion if the user asks for macro
|
||||
// expansion.
|
||||
let file_id = match relative_file(db, arg_id, &path, true) {
|
||||
let file_id = match relative_file(db, arg_id, path.as_str(), true, span) {
|
||||
Ok(file_id) => file_id,
|
||||
Err(_) => {
|
||||
return ExpandResult::ok(quote!(span =>""));
|
||||
}
|
||||
};
|
||||
|
||||
let text = db.file_text(file_id);
|
||||
let text = db.file_text(file_id.file_id());
|
||||
let text = &*text;
|
||||
|
||||
ExpandResult::ok(quote!(span =>#text))
|
||||
}
|
||||
|
||||
fn get_env_inner(db: &dyn ExpandDatabase, arg_id: MacroCallId, key: &str) -> Option<String> {
|
||||
fn get_env_inner(db: &dyn ExpandDatabase, arg_id: MacroCallId, key: &Symbol) -> Option<String> {
|
||||
let krate = db.lookup_intern_macro_call(arg_id).krate;
|
||||
db.crate_graph()[krate].env.get(key)
|
||||
db.crate_graph()[krate].env.get(key.as_str()).map(|it| it.escape_debug().to_string())
|
||||
}
|
||||
|
||||
fn env_expand(
|
||||
@ -792,8 +853,11 @@ fn env_expand(
|
||||
let s = get_env_inner(db, arg_id, &key).unwrap_or_else(|| {
|
||||
// The only variable rust-analyzer ever sets is `OUT_DIR`, so only diagnose that to avoid
|
||||
// unnecessary diagnostics for eg. `CARGO_PKG_NAME`.
|
||||
if key == "OUT_DIR" {
|
||||
err = Some(ExpandError::other(r#"`OUT_DIR` not set, enable "build scripts" to fix"#));
|
||||
if key.as_str() == "OUT_DIR" {
|
||||
err = Some(ExpandError::other(
|
||||
span,
|
||||
r#"`OUT_DIR` not set, enable "build scripts" to fix"#,
|
||||
));
|
||||
}
|
||||
|
||||
// If the variable is unset, still return a dummy string to help type inference along.
|
||||
@ -842,6 +906,21 @@ fn quote_expand(
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
ExpandResult::new(
|
||||
tt::Subtree::empty(tt::DelimSpan { open: span, close: span }),
|
||||
ExpandError::other("quote! is not implemented"),
|
||||
ExpandError::other(span, "quote! is not implemented"),
|
||||
)
|
||||
}
|
||||
|
||||
fn unescape_str(s: &Symbol) -> Symbol {
|
||||
if s.as_str().contains('\\') {
|
||||
let s = s.as_str();
|
||||
let mut buf = String::with_capacity(s.len());
|
||||
unescape_unicode(s, Mode::Str, &mut |_, c| {
|
||||
if let Ok(c) = c {
|
||||
buf.push(c)
|
||||
}
|
||||
});
|
||||
Symbol::intern(&buf)
|
||||
} else {
|
||||
s.clone()
|
||||
}
|
||||
}
|
@ -1,13 +1,14 @@
|
||||
//! A simplified version of quote-crate like quasi quote macro
|
||||
#![allow(clippy::crate_in_macro_def)]
|
||||
|
||||
use intern::{sym, Symbol};
|
||||
use span::Span;
|
||||
use syntax::format_smolstr;
|
||||
use tt::IdentIsRaw;
|
||||
|
||||
use crate::name::Name;
|
||||
|
||||
pub(crate) const fn dollar_crate(span: Span) -> tt::Ident<Span> {
|
||||
tt::Ident { text: syntax::SmolStr::new_static("$crate"), span }
|
||||
pub(crate) fn dollar_crate(span: Span) -> tt::Ident<Span> {
|
||||
tt::Ident { sym: sym::dollar_crate.clone(), span, is_raw: tt::IdentIsRaw::No }
|
||||
}
|
||||
|
||||
// A helper macro quote macro
|
||||
@ -16,22 +17,21 @@ pub(crate) const fn dollar_crate(span: Span) -> tt::Ident<Span> {
|
||||
// 2. #()* pattern repetition not supported now
|
||||
// * But we can do it manually, see `test_quote_derive_copy_hack`
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! __quote {
|
||||
macro_rules! quote_impl__ {
|
||||
($span:ident) => {
|
||||
Vec::<$crate::tt::TokenTree>::new()
|
||||
};
|
||||
|
||||
( @SUBTREE($span:ident) $delim:ident $($tt:tt)* ) => {
|
||||
{
|
||||
let children = $crate::__quote!($span $($tt)*);
|
||||
let children = $crate::builtin::quote::__quote!($span $($tt)*);
|
||||
$crate::tt::Subtree {
|
||||
delimiter: crate::tt::Delimiter {
|
||||
kind: crate::tt::DelimiterKind::$delim,
|
||||
open: $span,
|
||||
close: $span,
|
||||
},
|
||||
token_trees: $crate::quote::IntoTt::to_tokens(children).into_boxed_slice(),
|
||||
token_trees: $crate::builtin::quote::IntoTt::to_tokens(children).into_boxed_slice(),
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -68,9 +68,9 @@ macro_rules! __quote {
|
||||
// hash variable
|
||||
($span:ident # $first:ident $($tail:tt)* ) => {
|
||||
{
|
||||
let token = $crate::quote::ToTokenTree::to_token($first, $span);
|
||||
let token = $crate::builtin::quote::ToTokenTree::to_token($first, $span);
|
||||
let mut tokens = vec![token.into()];
|
||||
let mut tail_tokens = $crate::quote::IntoTt::to_tokens($crate::__quote!($span $($tail)*));
|
||||
let mut tail_tokens = $crate::builtin::quote::IntoTt::to_tokens($crate::builtin::quote::__quote!($span $($tail)*));
|
||||
tokens.append(&mut tail_tokens);
|
||||
tokens
|
||||
}
|
||||
@ -78,64 +78,66 @@ macro_rules! __quote {
|
||||
|
||||
($span:ident ## $first:ident $($tail:tt)* ) => {
|
||||
{
|
||||
let mut tokens = $first.into_iter().map(|it| $crate::quote::ToTokenTree::to_token(it, $span)).collect::<Vec<crate::tt::TokenTree>>();
|
||||
let mut tail_tokens = $crate::quote::IntoTt::to_tokens($crate::__quote!($span $($tail)*));
|
||||
let mut tokens = $first.into_iter().map(|it| $crate::builtin::quote::ToTokenTree::to_token(it, $span)).collect::<Vec<crate::tt::TokenTree>>();
|
||||
let mut tail_tokens = $crate::builtin::quote::IntoTt::to_tokens($crate::builtin::quote::__quote!($span $($tail)*));
|
||||
tokens.append(&mut tail_tokens);
|
||||
tokens
|
||||
}
|
||||
};
|
||||
|
||||
// Brace
|
||||
($span:ident { $($tt:tt)* } ) => { $crate::__quote!(@SUBTREE($span) Brace $($tt)*) };
|
||||
($span:ident { $($tt:tt)* } ) => { $crate::builtin::quote::__quote!(@SUBTREE($span) Brace $($tt)*) };
|
||||
// Bracket
|
||||
($span:ident [ $($tt:tt)* ] ) => { $crate::__quote!(@SUBTREE($span) Bracket $($tt)*) };
|
||||
($span:ident [ $($tt:tt)* ] ) => { $crate::builtin::quote::__quote!(@SUBTREE($span) Bracket $($tt)*) };
|
||||
// Parenthesis
|
||||
($span:ident ( $($tt:tt)* ) ) => { $crate::__quote!(@SUBTREE($span) Parenthesis $($tt)*) };
|
||||
($span:ident ( $($tt:tt)* ) ) => { $crate::builtin::quote::__quote!(@SUBTREE($span) Parenthesis $($tt)*) };
|
||||
|
||||
// Literal
|
||||
($span:ident $tt:literal ) => { vec![$crate::quote::ToTokenTree::to_token($tt, $span).into()] };
|
||||
($span:ident $tt:literal ) => { vec![$crate::builtin::quote::ToTokenTree::to_token($tt, $span).into()] };
|
||||
// Ident
|
||||
($span:ident $tt:ident ) => {
|
||||
vec![ {
|
||||
crate::tt::Leaf::Ident(crate::tt::Ident {
|
||||
text: stringify!($tt).into(),
|
||||
sym: intern::Symbol::intern(stringify!($tt)),
|
||||
span: $span,
|
||||
is_raw: tt::IdentIsRaw::No,
|
||||
}).into()
|
||||
}]
|
||||
};
|
||||
|
||||
// Puncts
|
||||
// FIXME: Not all puncts are handled
|
||||
($span:ident -> ) => {$crate::__quote!(@PUNCT($span) '-', '>')};
|
||||
($span:ident & ) => {$crate::__quote!(@PUNCT($span) '&')};
|
||||
($span:ident , ) => {$crate::__quote!(@PUNCT($span) ',')};
|
||||
($span:ident : ) => {$crate::__quote!(@PUNCT($span) ':')};
|
||||
($span:ident ; ) => {$crate::__quote!(@PUNCT($span) ';')};
|
||||
($span:ident :: ) => {$crate::__quote!(@PUNCT($span) ':', ':')};
|
||||
($span:ident . ) => {$crate::__quote!(@PUNCT($span) '.')};
|
||||
($span:ident < ) => {$crate::__quote!(@PUNCT($span) '<')};
|
||||
($span:ident > ) => {$crate::__quote!(@PUNCT($span) '>')};
|
||||
($span:ident ! ) => {$crate::__quote!(@PUNCT($span) '!')};
|
||||
($span:ident -> ) => {$crate::builtin::quote::__quote!(@PUNCT($span) '-', '>')};
|
||||
($span:ident & ) => {$crate::builtin::quote::__quote!(@PUNCT($span) '&')};
|
||||
($span:ident , ) => {$crate::builtin::quote::__quote!(@PUNCT($span) ',')};
|
||||
($span:ident : ) => {$crate::builtin::quote::__quote!(@PUNCT($span) ':')};
|
||||
($span:ident ; ) => {$crate::builtin::quote::__quote!(@PUNCT($span) ';')};
|
||||
($span:ident :: ) => {$crate::builtin::quote::__quote!(@PUNCT($span) ':', ':')};
|
||||
($span:ident . ) => {$crate::builtin::quote::__quote!(@PUNCT($span) '.')};
|
||||
($span:ident < ) => {$crate::builtin::quote::__quote!(@PUNCT($span) '<')};
|
||||
($span:ident > ) => {$crate::builtin::quote::__quote!(@PUNCT($span) '>')};
|
||||
($span:ident ! ) => {$crate::builtin::quote::__quote!(@PUNCT($span) '!')};
|
||||
|
||||
($span:ident $first:tt $($tail:tt)+ ) => {
|
||||
{
|
||||
let mut tokens = $crate::quote::IntoTt::to_tokens($crate::__quote!($span $first ));
|
||||
let mut tail_tokens = $crate::quote::IntoTt::to_tokens($crate::__quote!($span $($tail)*));
|
||||
let mut tokens = $crate::builtin::quote::IntoTt::to_tokens($crate::builtin::quote::__quote!($span $first ));
|
||||
let mut tail_tokens = $crate::builtin::quote::IntoTt::to_tokens($crate::builtin::quote::__quote!($span $($tail)*));
|
||||
|
||||
tokens.append(&mut tail_tokens);
|
||||
tokens
|
||||
}
|
||||
};
|
||||
}
|
||||
pub(super) use quote_impl__ as __quote;
|
||||
|
||||
/// FIXME:
|
||||
/// It probably should implement in proc-macro
|
||||
#[macro_export]
|
||||
macro_rules! quote {
|
||||
macro_rules! quote_impl {
|
||||
($span:ident=> $($tt:tt)* ) => {
|
||||
$crate::quote::IntoTt::to_subtree($crate::__quote!($span $($tt)*), $span)
|
||||
$crate::builtin::quote::IntoTt::to_subtree($crate::builtin::quote::__quote!($span $($tt)*), $span)
|
||||
}
|
||||
}
|
||||
pub(super) use quote_impl as quote;
|
||||
|
||||
pub(crate) trait IntoTt {
|
||||
fn to_subtree(self, span: Span) -> crate::tt::Subtree;
|
||||
@ -175,12 +177,6 @@ impl ToTokenTree for crate::tt::TokenTree {
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokenTree for &crate::tt::TokenTree {
|
||||
fn to_token(self, _: Span) -> crate::tt::TokenTree {
|
||||
self.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokenTree for crate::tt::Subtree {
|
||||
fn to_token(self, _: Span) -> crate::tt::TokenTree {
|
||||
self.into()
|
||||
@ -196,42 +192,57 @@ macro_rules! impl_to_to_tokentrees {
|
||||
leaf.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokenTree for &$ty {
|
||||
fn to_token($this, $span: Span) -> crate::tt::TokenTree {
|
||||
let leaf: crate::tt::Leaf = $im.clone().into();
|
||||
leaf.into()
|
||||
}
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ToTokenTree + Clone> ToTokenTree for &T {
|
||||
fn to_token(self, span: Span) -> crate::tt::TokenTree {
|
||||
self.clone().to_token(span)
|
||||
}
|
||||
}
|
||||
|
||||
impl_to_to_tokentrees! {
|
||||
span: u32 => self { crate::tt::Literal{text: self.to_string().into(), span} };
|
||||
span: usize => self { crate::tt::Literal{text: self.to_string().into(), span} };
|
||||
span: i32 => self { crate::tt::Literal{text: self.to_string().into(), span} };
|
||||
span: bool => self { crate::tt::Ident{text: self.to_string().into(), span} };
|
||||
span: u32 => self { crate::tt::Literal{symbol: Symbol::integer(self as _), span, kind: tt::LitKind::Integer, suffix: None } };
|
||||
span: usize => self { crate::tt::Literal{symbol: Symbol::integer(self as _), span, kind: tt::LitKind::Integer, suffix: None } };
|
||||
span: i32 => self { crate::tt::Literal{symbol: Symbol::integer(self as _), span, kind: tt::LitKind::Integer, suffix: None } };
|
||||
span: bool => self { crate::tt::Ident{sym: if self { sym::true_.clone() } else { sym::false_.clone() }, span, is_raw: tt::IdentIsRaw::No } };
|
||||
_span: crate::tt::Leaf => self { self };
|
||||
_span: crate::tt::Literal => self { self };
|
||||
_span: crate::tt::Ident => self { self };
|
||||
_span: crate::tt::Punct => self { self };
|
||||
span: &str => self { crate::tt::Literal{text: format_smolstr!("\"{}\"", self.escape_default()), span}};
|
||||
span: String => self { crate::tt::Literal{text: format_smolstr!("\"{}\"", self.escape_default()), span}};
|
||||
span: Name => self { crate::tt::Ident{text: self.to_smol_str(), span}};
|
||||
span: &str => self { crate::tt::Literal{symbol: Symbol::intern(self), span, kind: tt::LitKind::Str, suffix: None }};
|
||||
span: String => self { crate::tt::Literal{symbol: Symbol::intern(&self), span, kind: tt::LitKind::Str, suffix: None }};
|
||||
span: Name => self {
|
||||
let (is_raw, s) = IdentIsRaw::split_from_symbol(self.as_str());
|
||||
crate::tt::Ident{sym: Symbol::intern(s), span, is_raw }
|
||||
};
|
||||
span: Symbol => self {
|
||||
let (is_raw, s) = IdentIsRaw::split_from_symbol(self.as_str());
|
||||
crate::tt::Ident{sym: Symbol::intern(s), span, is_raw }
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::tt;
|
||||
use base_db::FileId;
|
||||
use ::tt::IdentIsRaw;
|
||||
use expect_test::expect;
|
||||
use intern::Symbol;
|
||||
use span::{SpanAnchor, SyntaxContextId, ROOT_ERASED_FILE_AST_ID};
|
||||
use syntax::{TextRange, TextSize};
|
||||
|
||||
use super::quote;
|
||||
|
||||
const DUMMY: tt::Span = tt::Span {
|
||||
range: TextRange::empty(TextSize::new(0)),
|
||||
anchor: SpanAnchor { file_id: FileId::from_raw(0xe4e4e), ast_id: ROOT_ERASED_FILE_AST_ID },
|
||||
anchor: SpanAnchor {
|
||||
file_id: span::EditionedFileId::new(
|
||||
span::FileId::from_raw(0xe4e4e),
|
||||
span::Edition::CURRENT,
|
||||
),
|
||||
ast_id: ROOT_ERASED_FILE_AST_ID,
|
||||
},
|
||||
ctx: SyntaxContextId::ROOT,
|
||||
};
|
||||
|
||||
@ -257,7 +268,8 @@ mod tests {
|
||||
}
|
||||
|
||||
fn mk_ident(name: &str) -> crate::tt::Ident {
|
||||
crate::tt::Ident { text: name.into(), span: DUMMY }
|
||||
let (is_raw, s) = IdentIsRaw::split_from_symbol(name);
|
||||
crate::tt::Ident { sym: Symbol::intern(s), span: DUMMY, is_raw }
|
||||
}
|
||||
|
||||
#[test]
|
@ -3,13 +3,13 @@ use std::iter::Peekable;
|
||||
|
||||
use base_db::CrateId;
|
||||
use cfg::{CfgAtom, CfgExpr};
|
||||
use intern::{sym, Symbol};
|
||||
use rustc_hash::FxHashSet;
|
||||
use syntax::{
|
||||
ast::{self, Attr, HasAttrs, Meta, VariantList},
|
||||
AstNode, NodeOrToken, SyntaxElement, SyntaxKind, SyntaxNode, T,
|
||||
};
|
||||
use tracing::{debug, warn};
|
||||
use tt::SmolStr;
|
||||
|
||||
use crate::{db::ExpandDatabase, proc_macro::ProcMacroKind, MacroCallLoc, MacroDefKind};
|
||||
|
||||
@ -263,13 +263,13 @@ where
|
||||
let name = match iter.next() {
|
||||
None => return None,
|
||||
Some(NodeOrToken::Token(element)) => match element.kind() {
|
||||
syntax::T![ident] => SmolStr::new(element.text()),
|
||||
syntax::T![ident] => Symbol::intern(element.text()),
|
||||
_ => return Some(CfgExpr::Invalid),
|
||||
},
|
||||
Some(_) => return Some(CfgExpr::Invalid),
|
||||
};
|
||||
let result = match name.as_str() {
|
||||
"all" | "any" | "not" => {
|
||||
let result = match &name {
|
||||
s if [&sym::all, &sym::any, &sym::not].contains(&s) => {
|
||||
let mut preds = Vec::new();
|
||||
let Some(NodeOrToken::Node(tree)) = iter.next() else {
|
||||
return Some(CfgExpr::Invalid);
|
||||
@ -286,10 +286,12 @@ where
|
||||
preds.push(pred);
|
||||
}
|
||||
}
|
||||
let group = match name.as_str() {
|
||||
"all" => CfgExpr::All(preds),
|
||||
"any" => CfgExpr::Any(preds),
|
||||
"not" => CfgExpr::Not(Box::new(preds.pop().unwrap_or(CfgExpr::Invalid))),
|
||||
let group = match &name {
|
||||
s if *s == sym::all => CfgExpr::All(preds.into_boxed_slice()),
|
||||
s if *s == sym::any => CfgExpr::Any(preds.into_boxed_slice()),
|
||||
s if *s == sym::not => {
|
||||
CfgExpr::Not(Box::new(preds.pop().unwrap_or(CfgExpr::Invalid)))
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
Some(group)
|
||||
@ -302,8 +304,10 @@ where
|
||||
if (value_token.kind() == syntax::SyntaxKind::STRING) =>
|
||||
{
|
||||
let value = value_token.text();
|
||||
let value = SmolStr::new(value.trim_matches('"'));
|
||||
Some(CfgExpr::Atom(CfgAtom::KeyValue { key: name, value }))
|
||||
Some(CfgExpr::Atom(CfgAtom::KeyValue {
|
||||
key: name,
|
||||
value: Symbol::intern(value.trim_matches('"')),
|
||||
}))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
@ -339,7 +343,7 @@ mod tests {
|
||||
assert_eq!(node.syntax().text_range().start(), 0.into());
|
||||
|
||||
let cfg = parse_from_attr_meta(node.meta().unwrap()).unwrap();
|
||||
let actual = format!("#![cfg({})]", DnfExpr::new(cfg));
|
||||
let actual = format!("#![cfg({})]", DnfExpr::new(&cfg));
|
||||
expect.assert_eq(&actual);
|
||||
}
|
||||
#[test]
|
||||
|
@ -25,8 +25,7 @@ impl ChangeWithProcMacros {
|
||||
|
||||
pub fn apply(self, db: &mut (impl ExpandDatabase + SourceDatabaseExt)) {
|
||||
self.source_change.apply(db);
|
||||
if let Some(mut proc_macros) = self.proc_macros {
|
||||
proc_macros.shrink_to_fit();
|
||||
if let Some(proc_macros) = self.proc_macros {
|
||||
db.set_proc_macros_with_durability(Arc::new(proc_macros), Durability::HIGH);
|
||||
}
|
||||
if let Some(target_data_layouts) = self.target_data_layouts {
|
||||
|
@ -1,18 +1,17 @@
|
||||
//! Defines database & queries for macro expansion.
|
||||
|
||||
use base_db::{salsa, CrateId, FileId, SourceDatabase};
|
||||
use base_db::{salsa, CrateId, SourceDatabase};
|
||||
use either::Either;
|
||||
use limit::Limit;
|
||||
use mbe::{syntax_node_to_token_tree, DocCommentDesugarMode, MatchedArmIndex};
|
||||
use rustc_hash::FxHashSet;
|
||||
use span::{AstIdMap, Span, SyntaxContextData, SyntaxContextId};
|
||||
use span::{AstIdMap, EditionedFileId, Span, SyntaxContextData, SyntaxContextId};
|
||||
use syntax::{ast, AstNode, Parse, SyntaxElement, SyntaxError, SyntaxNode, SyntaxToken, T};
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{
|
||||
attrs::{collect_attrs, AttrId},
|
||||
builtin_attr_macro::pseudo_derive_attr_expansion,
|
||||
builtin_fn_macro::EagerExpander,
|
||||
builtin::pseudo_derive_attr_expansion,
|
||||
cfg_process,
|
||||
declarative::DeclarativeMacroExpander,
|
||||
fixup::{self, SyntaxFixupUndoInfo},
|
||||
@ -20,9 +19,9 @@ use crate::{
|
||||
proc_macro::ProcMacros,
|
||||
span_map::{RealSpanMap, SpanMap, SpanMapRef},
|
||||
tt, AstId, BuiltinAttrExpander, BuiltinDeriveExpander, BuiltinFnLikeExpander,
|
||||
CustomProcMacroExpander, EagerCallInfo, ExpandError, ExpandResult, ExpandTo, ExpansionSpanMap,
|
||||
HirFileId, HirFileIdRepr, MacroCallId, MacroCallKind, MacroCallLoc, MacroDefId, MacroDefKind,
|
||||
MacroFileId,
|
||||
CustomProcMacroExpander, EagerCallInfo, EagerExpander, ExpandError, ExpandResult, ExpandTo,
|
||||
ExpansionSpanMap, HirFileId, HirFileIdRepr, Lookup, MacroCallId, MacroCallKind, MacroCallLoc,
|
||||
MacroDefId, MacroDefKind, MacroFileId,
|
||||
};
|
||||
/// This is just to ensure the types of smart_macro_arg and macro_arg are the same
|
||||
type MacroArgResult = (Arc<tt::Subtree>, SyntaxFixupUndoInfo, Span);
|
||||
@ -62,10 +61,8 @@ pub trait ExpandDatabase: SourceDatabase {
|
||||
/// file or a macro expansion.
|
||||
#[salsa::transparent]
|
||||
fn parse_or_expand(&self, file_id: HirFileId) -> SyntaxNode;
|
||||
#[salsa::transparent]
|
||||
fn parse_or_expand_with_err(&self, file_id: HirFileId) -> ExpandResult<Parse<SyntaxNode>>;
|
||||
/// Implementation for the macro case.
|
||||
// This query is LRU cached
|
||||
#[salsa::lru]
|
||||
fn parse_macro_expansion(
|
||||
&self,
|
||||
macro_file: MacroFileId,
|
||||
@ -78,7 +75,7 @@ pub trait ExpandDatabase: SourceDatabase {
|
||||
#[salsa::invoke(crate::span_map::expansion_span_map)]
|
||||
fn expansion_span_map(&self, file_id: MacroFileId) -> Arc<ExpansionSpanMap>;
|
||||
#[salsa::invoke(crate::span_map::real_span_map)]
|
||||
fn real_span_map(&self, file_id: FileId) -> Arc<RealSpanMap>;
|
||||
fn real_span_map(&self, file_id: EditionedFileId) -> Arc<RealSpanMap>;
|
||||
|
||||
/// Macro ids. That's probably the tricksiest bit in rust-analyzer, and the
|
||||
/// reason why we use salsa at all.
|
||||
@ -99,6 +96,7 @@ pub trait ExpandDatabase: SourceDatabase {
|
||||
/// Lowers syntactic macro call to a token tree representation. That's a firewall
|
||||
/// query, only typing in the macro call itself changes the returned
|
||||
/// subtree.
|
||||
#[deprecated = "calling this is incorrect, call `macro_arg_considering_derives` instead"]
|
||||
fn macro_arg(&self, id: MacroCallId) -> MacroArgResult;
|
||||
#[salsa::transparent]
|
||||
fn macro_arg_considering_derives(
|
||||
@ -133,6 +131,19 @@ pub trait ExpandDatabase: SourceDatabase {
|
||||
&self,
|
||||
macro_call: MacroCallId,
|
||||
) -> Option<Arc<ExpandResult<Arc<[SyntaxError]>>>>;
|
||||
#[salsa::transparent]
|
||||
fn syntax_context(&self, file: HirFileId) -> SyntaxContextId;
|
||||
}
|
||||
|
||||
fn syntax_context(db: &dyn ExpandDatabase, file: HirFileId) -> SyntaxContextId {
|
||||
match file.repr() {
|
||||
HirFileIdRepr::FileId(_) => SyntaxContextId::ROOT,
|
||||
HirFileIdRepr::MacroFile(m) => {
|
||||
db.macro_arg_considering_derives(m.macro_call_id, &m.macro_call_id.lookup(db).kind)
|
||||
.2
|
||||
.ctx
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// This expands the given macro call, but with different arguments. This is
|
||||
@ -248,39 +259,38 @@ pub fn expand_speculative(
|
||||
|
||||
// Do the actual expansion, we need to directly expand the proc macro due to the attribute args
|
||||
// Otherwise the expand query will fetch the non speculative attribute args and pass those instead.
|
||||
let mut speculative_expansion =
|
||||
match loc.def.kind {
|
||||
MacroDefKind::ProcMacro(ast, expander, _) => {
|
||||
let span = db.proc_macro_span(ast);
|
||||
tt.delimiter = tt::Delimiter::invisible_spanned(span);
|
||||
expander.expand(
|
||||
db,
|
||||
loc.def.krate,
|
||||
loc.krate,
|
||||
&tt,
|
||||
attr_arg.as_ref(),
|
||||
span_with_def_site_ctxt(db, span, actual_macro_call),
|
||||
span_with_call_site_ctxt(db, span, actual_macro_call),
|
||||
span_with_mixed_site_ctxt(db, span, actual_macro_call),
|
||||
)
|
||||
}
|
||||
MacroDefKind::BuiltInAttr(_, it) if it.is_derive() => {
|
||||
pseudo_derive_attr_expansion(&tt, attr_arg.as_ref()?, span)
|
||||
}
|
||||
MacroDefKind::Declarative(it) => db
|
||||
.decl_macro_expander(loc.krate, it)
|
||||
.expand_unhygienic(db, tt, loc.def.krate, span, loc.def.edition),
|
||||
MacroDefKind::BuiltIn(_, it) => {
|
||||
it.expand(db, actual_macro_call, &tt, span).map_err(Into::into)
|
||||
}
|
||||
MacroDefKind::BuiltInDerive(_, it) => {
|
||||
it.expand(db, actual_macro_call, &tt, span).map_err(Into::into)
|
||||
}
|
||||
MacroDefKind::BuiltInEager(_, it) => {
|
||||
it.expand(db, actual_macro_call, &tt, span).map_err(Into::into)
|
||||
}
|
||||
MacroDefKind::BuiltInAttr(_, it) => it.expand(db, actual_macro_call, &tt, span),
|
||||
};
|
||||
let mut speculative_expansion = match loc.def.kind {
|
||||
MacroDefKind::ProcMacro(ast, expander, _) => {
|
||||
let span = db.proc_macro_span(ast);
|
||||
tt.delimiter = tt::Delimiter::invisible_spanned(span);
|
||||
expander.expand(
|
||||
db,
|
||||
loc.def.krate,
|
||||
loc.krate,
|
||||
&tt,
|
||||
attr_arg.as_ref(),
|
||||
span_with_def_site_ctxt(db, span, actual_macro_call),
|
||||
span_with_call_site_ctxt(db, span, actual_macro_call),
|
||||
span_with_mixed_site_ctxt(db, span, actual_macro_call),
|
||||
)
|
||||
}
|
||||
MacroDefKind::BuiltInAttr(_, it) if it.is_derive() => {
|
||||
pseudo_derive_attr_expansion(&tt, attr_arg.as_ref()?, span)
|
||||
}
|
||||
MacroDefKind::Declarative(it) => {
|
||||
db.decl_macro_expander(loc.krate, it).expand_unhygienic(tt, span, loc.def.edition)
|
||||
}
|
||||
MacroDefKind::BuiltIn(_, it) => {
|
||||
it.expand(db, actual_macro_call, &tt, span).map_err(Into::into)
|
||||
}
|
||||
MacroDefKind::BuiltInDerive(_, it) => {
|
||||
it.expand(db, actual_macro_call, &tt, span).map_err(Into::into)
|
||||
}
|
||||
MacroDefKind::BuiltInEager(_, it) => {
|
||||
it.expand(db, actual_macro_call, &tt, span).map_err(Into::into)
|
||||
}
|
||||
MacroDefKind::BuiltInAttr(_, it) => it.expand(db, actual_macro_call, &tt, span),
|
||||
};
|
||||
|
||||
let expand_to = loc.expand_to();
|
||||
|
||||
@ -314,18 +324,6 @@ fn parse_or_expand(db: &dyn ExpandDatabase, file_id: HirFileId) -> SyntaxNode {
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_or_expand_with_err(
|
||||
db: &dyn ExpandDatabase,
|
||||
file_id: HirFileId,
|
||||
) -> ExpandResult<Parse<SyntaxNode>> {
|
||||
match file_id.repr() {
|
||||
HirFileIdRepr::FileId(file_id) => ExpandResult::ok(db.parse(file_id).to_syntax()),
|
||||
HirFileIdRepr::MacroFile(macro_file) => {
|
||||
db.parse_macro_expansion(macro_file).map(|(it, _)| it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: We should verify that the parsed node is one of the many macro node variants we expect
|
||||
// instead of having it be untyped
|
||||
fn parse_macro_expansion(
|
||||
@ -334,7 +332,7 @@ fn parse_macro_expansion(
|
||||
) -> ExpandResult<(Parse<SyntaxNode>, Arc<ExpansionSpanMap>)> {
|
||||
let _p = tracing::info_span!("parse_macro_expansion").entered();
|
||||
let loc = db.lookup_intern_macro_call(macro_file.macro_call_id);
|
||||
let edition = loc.def.edition;
|
||||
let def_edition = loc.def.edition;
|
||||
let expand_to = loc.expand_to();
|
||||
let mbe::ValueResult { value: (tt, matched_arm), err } =
|
||||
macro_expand(db, macro_file.macro_call_id, loc);
|
||||
@ -345,7 +343,7 @@ fn parse_macro_expansion(
|
||||
CowArc::Owned(it) => it,
|
||||
},
|
||||
expand_to,
|
||||
edition,
|
||||
def_edition,
|
||||
);
|
||||
rev_token_map.matched_arm = matched_arm;
|
||||
|
||||
@ -384,6 +382,7 @@ pub(crate) fn parse_with_map(
|
||||
/// Other wise return the [macro_arg] for the macro_call_id.
|
||||
///
|
||||
/// This is not connected to the database so it does not cached the result. However, the inner [macro_arg] query is
|
||||
#[allow(deprecated)] // we are macro_arg_considering_derives
|
||||
fn macro_arg_considering_derives(
|
||||
db: &dyn ExpandDatabase,
|
||||
id: MacroCallId,
|
||||
@ -735,11 +734,14 @@ fn check_tt_count(tt: &tt::Subtree) -> Result<(), ExpandResult<()>> {
|
||||
if TOKEN_LIMIT.check(count).is_err() {
|
||||
Err(ExpandResult {
|
||||
value: (),
|
||||
err: Some(ExpandError::other(format!(
|
||||
"macro invocation exceeds token limit: produced {} tokens, limit is {}",
|
||||
count,
|
||||
TOKEN_LIMIT.inner(),
|
||||
))),
|
||||
err: Some(ExpandError::other(
|
||||
tt.delimiter.open,
|
||||
format!(
|
||||
"macro invocation exceeds token limit: produced {} tokens, limit is {}",
|
||||
count,
|
||||
TOKEN_LIMIT.inner(),
|
||||
),
|
||||
)),
|
||||
})
|
||||
} else {
|
||||
Ok(())
|
||||
|
@ -1,7 +1,7 @@
|
||||
//! Compiled declarative macro expanders (`macro_rules!`` and `macro`)
|
||||
use std::sync::OnceLock;
|
||||
|
||||
use base_db::{CrateId, VersionReq};
|
||||
use base_db::CrateId;
|
||||
use intern::sym;
|
||||
use mbe::DocCommentDesugarMode;
|
||||
use span::{Edition, MacroCallId, Span, SyntaxContextId};
|
||||
use stdx::TupleExt;
|
||||
@ -12,7 +12,7 @@ use crate::{
|
||||
attrs::RawAttrs,
|
||||
db::ExpandDatabase,
|
||||
hygiene::{apply_mark, Transparency},
|
||||
tt, AstId, ExpandError, ExpandResult, Lookup,
|
||||
tt, AstId, ExpandError, ExpandErrorKind, ExpandResult, Lookup,
|
||||
};
|
||||
|
||||
/// Old-style `macro_rules` or the new macros 2.0
|
||||
@ -22,9 +22,6 @@ pub struct DeclarativeMacroExpander {
|
||||
pub transparency: Transparency,
|
||||
}
|
||||
|
||||
// FIXME: Remove this once we drop support for 1.76
|
||||
static REQUIREMENT: OnceLock<VersionReq> = OnceLock::new();
|
||||
|
||||
impl DeclarativeMacroExpander {
|
||||
pub fn expand(
|
||||
&self,
|
||||
@ -34,29 +31,16 @@ impl DeclarativeMacroExpander {
|
||||
span: Span,
|
||||
) -> ExpandResult<(tt::Subtree, Option<u32>)> {
|
||||
let loc = db.lookup_intern_macro_call(call_id);
|
||||
let toolchain = db.toolchain(loc.def.krate);
|
||||
let new_meta_vars = toolchain.as_ref().map_or(false, |version| {
|
||||
REQUIREMENT.get_or_init(|| VersionReq::parse(">=1.76").unwrap()).matches(
|
||||
&base_db::Version {
|
||||
pre: base_db::Prerelease::EMPTY,
|
||||
build: base_db::BuildMetadata::EMPTY,
|
||||
major: version.major,
|
||||
minor: version.minor,
|
||||
patch: version.patch,
|
||||
},
|
||||
)
|
||||
});
|
||||
match self.mac.err() {
|
||||
Some(_) => ExpandResult::new(
|
||||
(tt::Subtree::empty(tt::DelimSpan { open: span, close: span }), None),
|
||||
ExpandError::MacroDefinition,
|
||||
ExpandError::new(span, ExpandErrorKind::MacroDefinition),
|
||||
),
|
||||
None => self
|
||||
.mac
|
||||
.expand(
|
||||
&tt,
|
||||
|s| s.ctx = apply_mark(db, s.ctx, call_id, self.transparency),
|
||||
new_meta_vars,
|
||||
span,
|
||||
loc.def.edition,
|
||||
)
|
||||
@ -66,32 +50,18 @@ impl DeclarativeMacroExpander {
|
||||
|
||||
pub fn expand_unhygienic(
|
||||
&self,
|
||||
db: &dyn ExpandDatabase,
|
||||
tt: tt::Subtree,
|
||||
krate: CrateId,
|
||||
call_site: Span,
|
||||
def_site_edition: Edition,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
let toolchain = db.toolchain(krate);
|
||||
let new_meta_vars = toolchain.as_ref().map_or(false, |version| {
|
||||
REQUIREMENT.get_or_init(|| VersionReq::parse(">=1.76").unwrap()).matches(
|
||||
&base_db::Version {
|
||||
pre: base_db::Prerelease::EMPTY,
|
||||
build: base_db::BuildMetadata::EMPTY,
|
||||
major: version.major,
|
||||
minor: version.minor,
|
||||
patch: version.patch,
|
||||
},
|
||||
)
|
||||
});
|
||||
match self.mac.err() {
|
||||
Some(_) => ExpandResult::new(
|
||||
tt::Subtree::empty(tt::DelimSpan { open: call_site, close: call_site }),
|
||||
ExpandError::MacroDefinition,
|
||||
ExpandError::new(call_site, ExpandErrorKind::MacroDefinition),
|
||||
),
|
||||
None => self
|
||||
.mac
|
||||
.expand(&tt, |_| (), new_meta_vars, call_site, def_site_edition)
|
||||
.expand(&tt, |_| (), call_site, def_site_edition)
|
||||
.map(TupleExt::head)
|
||||
.map_err(Into::into),
|
||||
}
|
||||
@ -111,35 +81,24 @@ impl DeclarativeMacroExpander {
|
||||
match &*attrs
|
||||
.iter()
|
||||
.find(|it| {
|
||||
it.path.as_ident().and_then(|it| it.as_str())
|
||||
== Some("rustc_macro_transparency")
|
||||
it.path
|
||||
.as_ident()
|
||||
.map(|it| *it == sym::rustc_macro_transparency.clone())
|
||||
.unwrap_or(false)
|
||||
})?
|
||||
.token_tree_value()?
|
||||
.token_trees
|
||||
{
|
||||
[tt::TokenTree::Leaf(tt::Leaf::Ident(i)), ..] => match &*i.text {
|
||||
"transparent" => Some(Transparency::Transparent),
|
||||
"semitransparent" => Some(Transparency::SemiTransparent),
|
||||
"opaque" => Some(Transparency::Opaque),
|
||||
[tt::TokenTree::Leaf(tt::Leaf::Ident(i)), ..] => match &i.sym {
|
||||
s if *s == sym::transparent => Some(Transparency::Transparent),
|
||||
s if *s == sym::semitransparent => Some(Transparency::SemiTransparent),
|
||||
s if *s == sym::opaque => Some(Transparency::Opaque),
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
};
|
||||
let toolchain = db.toolchain(def_crate);
|
||||
let new_meta_vars = toolchain.as_ref().map_or(false, |version| {
|
||||
REQUIREMENT.get_or_init(|| VersionReq::parse(">=1.76").unwrap()).matches(
|
||||
&base_db::Version {
|
||||
pre: base_db::Prerelease::EMPTY,
|
||||
build: base_db::BuildMetadata::EMPTY,
|
||||
major: version.major,
|
||||
minor: version.minor,
|
||||
patch: version.patch,
|
||||
},
|
||||
)
|
||||
});
|
||||
|
||||
let edition = |ctx: SyntaxContextId| {
|
||||
let ctx_edition = |ctx: SyntaxContextId| {
|
||||
let crate_graph = db.crate_graph();
|
||||
if ctx.is_root() {
|
||||
crate_graph[def_crate].edition
|
||||
@ -162,7 +121,7 @@ impl DeclarativeMacroExpander {
|
||||
DocCommentDesugarMode::Mbe,
|
||||
);
|
||||
|
||||
mbe::DeclarativeMacro::parse_macro_rules(&tt, edition, new_meta_vars)
|
||||
mbe::DeclarativeMacro::parse_macro_rules(&tt, ctx_edition)
|
||||
}
|
||||
None => mbe::DeclarativeMacro::from_err(mbe::ParseError::Expected(
|
||||
"expected a token tree".into(),
|
||||
@ -190,12 +149,7 @@ impl DeclarativeMacroExpander {
|
||||
DocCommentDesugarMode::Mbe,
|
||||
);
|
||||
|
||||
mbe::DeclarativeMacro::parse_macro2(
|
||||
args.as_ref(),
|
||||
&body,
|
||||
edition,
|
||||
new_meta_vars,
|
||||
)
|
||||
mbe::DeclarativeMacro::parse_macro2(args.as_ref(), &body, ctx_edition)
|
||||
}
|
||||
None => mbe::DeclarativeMacro::from_err(mbe::ParseError::Expected(
|
||||
"expected a token tree".into(),
|
||||
|
@ -54,6 +54,7 @@ pub fn expand_eager_macro_input(
|
||||
ctxt: call_site,
|
||||
}
|
||||
.intern(db);
|
||||
#[allow(deprecated)] // builtin eager macros are never derives
|
||||
let (_, _, span) = db.macro_arg(arg_id);
|
||||
let ExpandResult { value: (arg_exp, arg_exp_map), err: parse_err } =
|
||||
db.parse_macro_expansion(arg_id.as_macro_file());
|
||||
@ -175,14 +176,19 @@ fn eager_macro_recur(
|
||||
Some(path) => match macro_resolver(&path) {
|
||||
Some(def) => def,
|
||||
None => {
|
||||
error =
|
||||
Some(ExpandError::other(format!("unresolved macro {}", path.display(db))));
|
||||
error = Some(ExpandError::other(
|
||||
span_map.span_at(call.syntax().text_range().start()),
|
||||
format!("unresolved macro {}", path.display(db)),
|
||||
));
|
||||
offset += call.syntax().text_range().len();
|
||||
continue;
|
||||
}
|
||||
},
|
||||
None => {
|
||||
error = Some(ExpandError::other("malformed macro invocation"));
|
||||
error = Some(ExpandError::other(
|
||||
span_map.span_at(call.syntax().text_range().start()),
|
||||
"malformed macro invocation",
|
||||
));
|
||||
offset += call.syntax().text_range().len();
|
||||
continue;
|
||||
}
|
||||
|
@ -3,8 +3,8 @@ use std::borrow::Borrow;
|
||||
|
||||
use either::Either;
|
||||
use span::{
|
||||
AstIdNode, ErasedFileAstId, FileAstId, FileId, FileRange, HirFileId, HirFileIdRepr,
|
||||
MacroFileId, SyntaxContextId,
|
||||
AstIdNode, EditionedFileId, ErasedFileAstId, FileAstId, HirFileId, HirFileIdRepr, MacroFileId,
|
||||
SyntaxContextId,
|
||||
};
|
||||
use syntax::{AstNode, AstPtr, SyntaxNode, SyntaxNodePtr, SyntaxToken, TextRange, TextSize};
|
||||
|
||||
@ -27,7 +27,36 @@ pub struct InFileWrapper<FileKind, T> {
|
||||
}
|
||||
pub type InFile<T> = InFileWrapper<HirFileId, T>;
|
||||
pub type InMacroFile<T> = InFileWrapper<MacroFileId, T>;
|
||||
pub type InRealFile<T> = InFileWrapper<FileId, T>;
|
||||
pub type InRealFile<T> = InFileWrapper<EditionedFileId, T>;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
|
||||
pub struct FilePositionWrapper<FileKind> {
|
||||
pub file_id: FileKind,
|
||||
pub offset: TextSize,
|
||||
}
|
||||
pub type HirFilePosition = FilePositionWrapper<HirFileId>;
|
||||
pub type MacroFilePosition = FilePositionWrapper<MacroFileId>;
|
||||
pub type FilePosition = FilePositionWrapper<EditionedFileId>;
|
||||
|
||||
impl From<FilePositionWrapper<EditionedFileId>> for FilePositionWrapper<span::FileId> {
|
||||
fn from(value: FilePositionWrapper<EditionedFileId>) -> Self {
|
||||
FilePositionWrapper { file_id: value.file_id.into(), offset: value.offset }
|
||||
}
|
||||
}
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
|
||||
pub struct FileRangeWrapper<FileKind> {
|
||||
pub file_id: FileKind,
|
||||
pub range: TextRange,
|
||||
}
|
||||
pub type HirFileRange = FileRangeWrapper<HirFileId>;
|
||||
pub type MacroFileRange = FileRangeWrapper<MacroFileId>;
|
||||
pub type FileRange = FileRangeWrapper<EditionedFileId>;
|
||||
|
||||
impl From<FileRangeWrapper<EditionedFileId>> for FileRangeWrapper<span::FileId> {
|
||||
fn from(value: FileRangeWrapper<EditionedFileId>) -> Self {
|
||||
FileRangeWrapper { file_id: value.file_id.into(), range: value.range }
|
||||
}
|
||||
}
|
||||
|
||||
/// `AstId` points to an AST node in any file.
|
||||
///
|
||||
@ -128,7 +157,7 @@ trait FileIdToSyntax: Copy {
|
||||
fn file_syntax(self, db: &dyn db::ExpandDatabase) -> SyntaxNode;
|
||||
}
|
||||
|
||||
impl FileIdToSyntax for FileId {
|
||||
impl FileIdToSyntax for EditionedFileId {
|
||||
fn file_syntax(self, db: &dyn db::ExpandDatabase) -> SyntaxNode {
|
||||
db.parse(self).syntax_node()
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
//! To make attribute macros work reliably when typing, we need to take care to
|
||||
//! fix up syntax errors in the code we're passing to them.
|
||||
|
||||
use intern::sym;
|
||||
use mbe::DocCommentDesugarMode;
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use smallvec::SmallVec;
|
||||
@ -80,12 +81,13 @@ pub(crate) fn fixup_syntax(
|
||||
original.push(original_tree);
|
||||
let span = span_map.span_for_range(node_range);
|
||||
let replacement = Leaf::Ident(Ident {
|
||||
text: "__ra_fixup".into(),
|
||||
sym: sym::__ra_fixup.clone(),
|
||||
span: Span {
|
||||
range: TextRange::new(TextSize::new(idx), FIXUP_DUMMY_RANGE_END),
|
||||
anchor: SpanAnchor { ast_id: FIXUP_DUMMY_AST_ID, ..span.anchor },
|
||||
ctx: span.ctx,
|
||||
},
|
||||
is_raw: tt::IdentIsRaw::No,
|
||||
});
|
||||
append.insert(node.clone().into(), vec![replacement]);
|
||||
preorder.skip_subtree();
|
||||
@ -99,8 +101,9 @@ pub(crate) fn fixup_syntax(
|
||||
// incomplete field access: some_expr.|
|
||||
append.insert(node.clone().into(), vec![
|
||||
Leaf::Ident(Ident {
|
||||
text: "__ra_fixup".into(),
|
||||
sym: sym::__ra_fixup.clone(),
|
||||
span: fake_span(node_range),
|
||||
is_raw: tt::IdentIsRaw::No
|
||||
}),
|
||||
]);
|
||||
}
|
||||
@ -136,8 +139,9 @@ pub(crate) fn fixup_syntax(
|
||||
};
|
||||
append.insert(if_token.into(), vec![
|
||||
Leaf::Ident(Ident {
|
||||
text: "__ra_fixup".into(),
|
||||
span: fake_span(node_range)
|
||||
sym: sym::__ra_fixup.clone(),
|
||||
span: fake_span(node_range),
|
||||
is_raw: tt::IdentIsRaw::No
|
||||
}),
|
||||
]);
|
||||
}
|
||||
@ -166,8 +170,9 @@ pub(crate) fn fixup_syntax(
|
||||
};
|
||||
append.insert(while_token.into(), vec![
|
||||
Leaf::Ident(Ident {
|
||||
text: "__ra_fixup".into(),
|
||||
span: fake_span(node_range)
|
||||
sym: sym::__ra_fixup.clone(),
|
||||
span: fake_span(node_range),
|
||||
is_raw: tt::IdentIsRaw::No
|
||||
}),
|
||||
]);
|
||||
}
|
||||
@ -213,8 +218,9 @@ pub(crate) fn fixup_syntax(
|
||||
};
|
||||
append.insert(match_token.into(), vec![
|
||||
Leaf::Ident(Ident {
|
||||
text: "__ra_fixup".into(),
|
||||
span: fake_span(node_range)
|
||||
sym: sym::__ra_fixup.clone(),
|
||||
span: fake_span(node_range),
|
||||
is_raw: tt::IdentIsRaw::No
|
||||
}),
|
||||
]);
|
||||
}
|
||||
@ -242,13 +248,14 @@ pub(crate) fn fixup_syntax(
|
||||
};
|
||||
|
||||
let [pat, in_token, iter] = [
|
||||
"_",
|
||||
"in",
|
||||
"__ra_fixup"
|
||||
].map(|text|
|
||||
sym::underscore.clone(),
|
||||
sym::in_.clone(),
|
||||
sym::__ra_fixup.clone(),
|
||||
].map(|sym|
|
||||
Leaf::Ident(Ident {
|
||||
text: text.into(),
|
||||
span: fake_span(node_range)
|
||||
sym,
|
||||
span: fake_span(node_range),
|
||||
is_raw: tt::IdentIsRaw::No
|
||||
}),
|
||||
);
|
||||
|
||||
@ -280,8 +287,9 @@ pub(crate) fn fixup_syntax(
|
||||
if it.name_ref().is_some() && it.expr().is_none() {
|
||||
append.insert(colon.into(), vec![
|
||||
Leaf::Ident(Ident {
|
||||
text: "__ra_fixup".into(),
|
||||
span: fake_span(node_range)
|
||||
sym: sym::__ra_fixup.clone(),
|
||||
span: fake_span(node_range),
|
||||
is_raw: tt::IdentIsRaw::No
|
||||
})
|
||||
]);
|
||||
}
|
||||
@ -292,8 +300,9 @@ pub(crate) fn fixup_syntax(
|
||||
if it.segment().is_none() {
|
||||
append.insert(colon.into(), vec![
|
||||
Leaf::Ident(Ident {
|
||||
text: "__ra_fixup".into(),
|
||||
span: fake_span(node_range)
|
||||
sym: sym::__ra_fixup.clone(),
|
||||
span: fake_span(node_range),
|
||||
is_raw: tt::IdentIsRaw::No
|
||||
})
|
||||
]);
|
||||
}
|
||||
@ -325,8 +334,9 @@ pub(crate) fn fixup_syntax(
|
||||
if it.body().is_none() {
|
||||
append.insert(node.into(), vec![
|
||||
Leaf::Ident(Ident {
|
||||
text: "__ra_fixup".into(),
|
||||
span: fake_span(node_range)
|
||||
sym: sym::__ra_fixup.clone(),
|
||||
span: fake_span(node_range),
|
||||
is_raw: tt::IdentIsRaw::No
|
||||
})
|
||||
]);
|
||||
}
|
||||
@ -423,9 +433,9 @@ fn reverse_fixups_(tt: &mut Subtree, undo_info: &[Subtree]) {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use base_db::FileId;
|
||||
use expect_test::{expect, Expect};
|
||||
use mbe::DocCommentDesugarMode;
|
||||
use span::{Edition, EditionedFileId, FileId};
|
||||
use syntax::TextRange;
|
||||
use triomphe::Arc;
|
||||
|
||||
@ -439,9 +449,9 @@ mod tests {
|
||||
// `TokenTree`s, see the last assertion in `check()`.
|
||||
fn check_leaf_eq(a: &tt::Leaf, b: &tt::Leaf) -> bool {
|
||||
match (a, b) {
|
||||
(tt::Leaf::Literal(a), tt::Leaf::Literal(b)) => a.text == b.text,
|
||||
(tt::Leaf::Literal(a), tt::Leaf::Literal(b)) => a.symbol == b.symbol,
|
||||
(tt::Leaf::Punct(a), tt::Leaf::Punct(b)) => a.char == b.char,
|
||||
(tt::Leaf::Ident(a), tt::Leaf::Ident(b)) => a.text == b.text,
|
||||
(tt::Leaf::Ident(a), tt::Leaf::Ident(b)) => a.sym == b.sym,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
@ -463,7 +473,10 @@ mod tests {
|
||||
#[track_caller]
|
||||
fn check(ra_fixture: &str, mut expect: Expect) {
|
||||
let parsed = syntax::SourceFile::parse(ra_fixture, span::Edition::CURRENT);
|
||||
let span_map = SpanMap::RealSpanMap(Arc::new(RealSpanMap::absolute(FileId::from_raw(0))));
|
||||
let span_map = SpanMap::RealSpanMap(Arc::new(RealSpanMap::absolute(EditionedFileId::new(
|
||||
FileId::from_raw(0),
|
||||
Edition::CURRENT,
|
||||
))));
|
||||
let fixups = super::fixup_syntax(
|
||||
span_map.as_ref(),
|
||||
&parsed.syntax_node(),
|
||||
|
@ -10,6 +10,7 @@
|
||||
|
||||
use std::sync::OnceLock;
|
||||
|
||||
use intern::Symbol;
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
pub struct BuiltinAttribute {
|
||||
@ -26,11 +27,16 @@ pub struct AttributeTemplate {
|
||||
pub name_value_str: Option<&'static str>,
|
||||
}
|
||||
|
||||
pub fn find_builtin_attr_idx(name: &str) -> Option<usize> {
|
||||
static BUILTIN_LOOKUP_TABLE: OnceLock<FxHashMap<&'static str, usize>> = OnceLock::new();
|
||||
pub fn find_builtin_attr_idx(name: &Symbol) -> Option<usize> {
|
||||
static BUILTIN_LOOKUP_TABLE: OnceLock<FxHashMap<Symbol, usize>> = OnceLock::new();
|
||||
BUILTIN_LOOKUP_TABLE
|
||||
.get_or_init(|| {
|
||||
INERT_ATTRIBUTES.iter().map(|attr| attr.name).enumerate().map(|(a, b)| (b, a)).collect()
|
||||
INERT_ATTRIBUTES
|
||||
.iter()
|
||||
.map(|attr| attr.name)
|
||||
.enumerate()
|
||||
.map(|(a, b)| (Symbol::intern(b), a))
|
||||
.collect()
|
||||
})
|
||||
.get(name)
|
||||
.copied()
|
||||
@ -553,7 +559,7 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
|
||||
),
|
||||
|
||||
BuiltinAttribute {
|
||||
// name: sym::rustc_diagnostic_item,
|
||||
// name: sym::rustc_diagnostic_item.clone(),
|
||||
name: "rustc_diagnostic_item",
|
||||
// FIXME: This can be `true` once we always use `tcx.is_diagnostic_item`.
|
||||
// only_local: false,
|
||||
@ -562,7 +568,7 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
|
||||
// duplicates: ErrorFollowing,
|
||||
// gate: Gated(
|
||||
// Stability::Unstable,
|
||||
// sym::rustc_attrs,
|
||||
// sym::rustc_attrs.clone(),
|
||||
// "diagnostic items compiler internal support for linting",
|
||||
// cfg_fn!(rustc_attrs),
|
||||
// ),
|
||||
|
@ -6,9 +6,7 @@
|
||||
#![cfg_attr(feature = "in-rust-tree", feature(rustc_private))]
|
||||
|
||||
pub mod attrs;
|
||||
pub mod builtin_attr_macro;
|
||||
pub mod builtin_derive_macro;
|
||||
pub mod builtin_fn_macro;
|
||||
pub mod builtin;
|
||||
pub mod change;
|
||||
pub mod db;
|
||||
pub mod declarative;
|
||||
@ -19,21 +17,21 @@ pub mod inert_attr_macro;
|
||||
pub mod mod_path;
|
||||
pub mod name;
|
||||
pub mod proc_macro;
|
||||
pub mod quote;
|
||||
pub mod span_map;
|
||||
|
||||
mod cfg_process;
|
||||
mod fixup;
|
||||
|
||||
use attrs::collect_attrs;
|
||||
use rustc_hash::FxHashMap;
|
||||
use triomphe::Arc;
|
||||
|
||||
use std::{fmt, hash::Hash};
|
||||
use std::hash::Hash;
|
||||
|
||||
use base_db::{salsa::InternValueTrivial, CrateId, FileId};
|
||||
use base_db::{salsa::InternValueTrivial, CrateId};
|
||||
use either::Either;
|
||||
use span::{
|
||||
Edition, ErasedFileAstId, FileAstId, FileRange, HirFileIdRepr, Span, SpanAnchor,
|
||||
Edition, EditionedFileId, ErasedFileAstId, FileAstId, HirFileIdRepr, Span, SpanAnchor,
|
||||
SyntaxContextData, SyntaxContextId,
|
||||
};
|
||||
use syntax::{
|
||||
@ -43,23 +41,24 @@ use syntax::{
|
||||
|
||||
use crate::{
|
||||
attrs::AttrId,
|
||||
builtin_attr_macro::BuiltinAttrExpander,
|
||||
builtin_derive_macro::BuiltinDeriveExpander,
|
||||
builtin_fn_macro::{BuiltinFnLikeExpander, EagerExpander},
|
||||
builtin::{
|
||||
include_input_to_file_id, BuiltinAttrExpander, BuiltinDeriveExpander,
|
||||
BuiltinFnLikeExpander, EagerExpander,
|
||||
},
|
||||
db::ExpandDatabase,
|
||||
mod_path::ModPath,
|
||||
proc_macro::{CustomProcMacroExpander, ProcMacroKind},
|
||||
span_map::{ExpansionSpanMap, SpanMap},
|
||||
};
|
||||
|
||||
pub use crate::files::{AstId, ErasedAstId, InFile, InMacroFile, InRealFile};
|
||||
pub use crate::files::{AstId, ErasedAstId, FileRange, InFile, InMacroFile, InRealFile};
|
||||
|
||||
pub use mbe::{DeclarativeMacro, ValueResult};
|
||||
pub use span::{HirFileId, MacroCallId, MacroFileId};
|
||||
|
||||
pub mod tt {
|
||||
pub use span::Span;
|
||||
pub use tt::{DelimiterKind, Spacing};
|
||||
pub use tt::{token_to_literal, DelimiterKind, IdentIsRaw, LitKind, Spacing};
|
||||
|
||||
pub type Delimiter = ::tt::Delimiter<Span>;
|
||||
pub type DelimSpan = ::tt::DelimSpan<Span>;
|
||||
@ -125,46 +124,79 @@ impl_intern_lookup!(
|
||||
pub type ExpandResult<T> = ValueResult<T, ExpandError>;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
|
||||
pub enum ExpandError {
|
||||
UnresolvedProcMacro(CrateId),
|
||||
/// The macro expansion is disabled.
|
||||
MacroDisabled,
|
||||
MacroDefinition,
|
||||
Mbe(mbe::ExpandError),
|
||||
RecursionOverflow,
|
||||
Other(Arc<Box<str>>),
|
||||
ProcMacroPanic(Arc<Box<str>>),
|
||||
pub struct ExpandError {
|
||||
inner: Arc<(ExpandErrorKind, Span)>,
|
||||
}
|
||||
|
||||
impl ExpandError {
|
||||
pub fn other(msg: impl Into<Box<str>>) -> Self {
|
||||
ExpandError::Other(Arc::new(msg.into()))
|
||||
pub fn new(span: Span, kind: ExpandErrorKind) -> Self {
|
||||
ExpandError { inner: Arc::new((kind, span)) }
|
||||
}
|
||||
pub fn other(span: Span, msg: impl Into<Box<str>>) -> Self {
|
||||
ExpandError { inner: Arc::new((ExpandErrorKind::Other(msg.into()), span)) }
|
||||
}
|
||||
pub fn kind(&self) -> &ExpandErrorKind {
|
||||
&self.inner.0
|
||||
}
|
||||
pub fn span(&self) -> Span {
|
||||
self.inner.1
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
|
||||
pub enum ExpandErrorKind {
|
||||
/// Attribute macro expansion is disabled.
|
||||
ProcMacroAttrExpansionDisabled,
|
||||
MissingProcMacroExpander(CrateId),
|
||||
/// The macro for this call is disabled.
|
||||
MacroDisabled,
|
||||
/// The macro definition has errors.
|
||||
MacroDefinition,
|
||||
Mbe(mbe::ExpandErrorKind),
|
||||
RecursionOverflow,
|
||||
Other(Box<str>),
|
||||
ProcMacroPanic(Box<str>),
|
||||
}
|
||||
|
||||
impl ExpandError {
|
||||
pub fn render_to_string(&self, db: &dyn ExpandDatabase) -> (String, bool) {
|
||||
self.inner.0.render_to_string(db)
|
||||
}
|
||||
}
|
||||
|
||||
impl ExpandErrorKind {
|
||||
pub fn render_to_string(&self, db: &dyn ExpandDatabase) -> (String, bool) {
|
||||
match self {
|
||||
ExpandErrorKind::ProcMacroAttrExpansionDisabled => {
|
||||
("procedural attribute macro expansion is disabled".to_owned(), false)
|
||||
}
|
||||
ExpandErrorKind::MacroDisabled => {
|
||||
("proc-macro is explicitly disabled".to_owned(), false)
|
||||
}
|
||||
&ExpandErrorKind::MissingProcMacroExpander(def_crate) => {
|
||||
match db.proc_macros().get_error_for_crate(def_crate) {
|
||||
Some((e, hard_err)) => (e.to_owned(), hard_err),
|
||||
None => ("missing expander".to_owned(), true),
|
||||
}
|
||||
}
|
||||
ExpandErrorKind::MacroDefinition => {
|
||||
("macro definition has parse errors".to_owned(), true)
|
||||
}
|
||||
ExpandErrorKind::Mbe(e) => (e.to_string(), true),
|
||||
ExpandErrorKind::RecursionOverflow => {
|
||||
("overflow expanding the original macro".to_owned(), true)
|
||||
}
|
||||
ExpandErrorKind::Other(e) => ((**e).to_owned(), true),
|
||||
ExpandErrorKind::ProcMacroPanic(e) => ((**e).to_owned(), true),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<mbe::ExpandError> for ExpandError {
|
||||
fn from(mbe: mbe::ExpandError) -> Self {
|
||||
Self::Mbe(mbe)
|
||||
ExpandError { inner: Arc::new((ExpandErrorKind::Mbe(mbe.inner.1.clone()), mbe.inner.0)) }
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ExpandError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
ExpandError::UnresolvedProcMacro(_) => f.write_str("unresolved proc-macro"),
|
||||
ExpandError::Mbe(it) => it.fmt(f),
|
||||
ExpandError::RecursionOverflow => f.write_str("overflow expanding the original macro"),
|
||||
ExpandError::ProcMacroPanic(it) => {
|
||||
f.write_str("proc-macro panicked: ")?;
|
||||
f.write_str(it)
|
||||
}
|
||||
ExpandError::Other(it) => f.write_str(it),
|
||||
ExpandError::MacroDisabled => f.write_str("macro disabled"),
|
||||
ExpandError::MacroDefinition => f.write_str("macro definition has parse errors"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct MacroCallLoc {
|
||||
pub def: MacroDefId,
|
||||
@ -243,11 +275,11 @@ pub enum MacroCallKind {
|
||||
|
||||
pub trait HirFileIdExt {
|
||||
/// Returns the original file of this macro call hierarchy.
|
||||
fn original_file(self, db: &dyn ExpandDatabase) -> FileId;
|
||||
fn original_file(self, db: &dyn ExpandDatabase) -> EditionedFileId;
|
||||
|
||||
/// Returns the original file of this macro call hierarchy while going into the included file if
|
||||
/// one of the calls comes from an `include!``.
|
||||
fn original_file_respecting_includes(self, db: &dyn ExpandDatabase) -> FileId;
|
||||
fn original_file_respecting_includes(self, db: &dyn ExpandDatabase) -> EditionedFileId;
|
||||
|
||||
/// If this is a macro call, returns the syntax node of the very first macro call this file resides in.
|
||||
fn original_call_node(self, db: &dyn ExpandDatabase) -> Option<InRealFile<SyntaxNode>>;
|
||||
@ -256,7 +288,7 @@ pub trait HirFileIdExt {
|
||||
}
|
||||
|
||||
impl HirFileIdExt for HirFileId {
|
||||
fn original_file(self, db: &dyn ExpandDatabase) -> FileId {
|
||||
fn original_file(self, db: &dyn ExpandDatabase) -> EditionedFileId {
|
||||
let mut file_id = self;
|
||||
loop {
|
||||
match file_id.repr() {
|
||||
@ -268,7 +300,7 @@ impl HirFileIdExt for HirFileId {
|
||||
}
|
||||
}
|
||||
|
||||
fn original_file_respecting_includes(mut self, db: &dyn ExpandDatabase) -> FileId {
|
||||
fn original_file_respecting_includes(mut self, db: &dyn ExpandDatabase) -> EditionedFileId {
|
||||
loop {
|
||||
match self.repr() {
|
||||
HirFileIdRepr::FileId(id) => break id,
|
||||
@ -276,11 +308,9 @@ impl HirFileIdExt for HirFileId {
|
||||
let loc = db.lookup_intern_macro_call(file.macro_call_id);
|
||||
if loc.def.is_include() {
|
||||
if let MacroCallKind::FnLike { eager: Some(eager), .. } = &loc.kind {
|
||||
if let Ok(it) = builtin_fn_macro::include_input_to_file_id(
|
||||
db,
|
||||
file.macro_call_id,
|
||||
&eager.arg,
|
||||
) {
|
||||
if let Ok(it) =
|
||||
include_input_to_file_id(db, file.macro_call_id, &eager.arg)
|
||||
{
|
||||
break it;
|
||||
}
|
||||
}
|
||||
@ -568,12 +598,10 @@ impl MacroCallLoc {
|
||||
&self,
|
||||
db: &dyn ExpandDatabase,
|
||||
macro_call_id: MacroCallId,
|
||||
) -> Option<FileId> {
|
||||
) -> Option<EditionedFileId> {
|
||||
if self.def.is_include() {
|
||||
if let MacroCallKind::FnLike { eager: Some(eager), .. } = &self.kind {
|
||||
if let Ok(it) =
|
||||
builtin_fn_macro::include_input_to_file_id(db, macro_call_id, &eager.arg)
|
||||
{
|
||||
if let Ok(it) = include_input_to_file_id(db, macro_call_id, &eager.arg) {
|
||||
return Some(it);
|
||||
}
|
||||
}
|
||||
|
@ -8,10 +8,11 @@ use std::{
|
||||
use crate::{
|
||||
db::ExpandDatabase,
|
||||
hygiene::{marks_rev, SyntaxContextExt, Transparency},
|
||||
name::{known, AsName, Name},
|
||||
name::{AsName, Name},
|
||||
tt,
|
||||
};
|
||||
use base_db::CrateId;
|
||||
use intern::sym;
|
||||
use smallvec::SmallVec;
|
||||
use span::SyntaxContextId;
|
||||
use syntax::{ast, AstNode};
|
||||
@ -106,10 +107,7 @@ impl ModPath {
|
||||
PathKind::Abs => 0,
|
||||
PathKind::DollarCrate(_) => "$crate".len(),
|
||||
};
|
||||
self.segments()
|
||||
.iter()
|
||||
.map(|segment| segment.as_str().map_or(0, str::len))
|
||||
.fold(base, core::ops::Add::add)
|
||||
self.segments().iter().map(|segment| segment.as_str().len()).fold(base, core::ops::Add::add)
|
||||
}
|
||||
|
||||
pub fn is_ident(&self) -> bool {
|
||||
@ -123,7 +121,7 @@ impl ModPath {
|
||||
#[allow(non_snake_case)]
|
||||
pub fn is_Self(&self) -> bool {
|
||||
self.kind == PathKind::Plain
|
||||
&& matches!(&*self.segments, [name] if *name == known::SELF_TYPE)
|
||||
&& matches!(&*self.segments, [name] if *name == sym::Self_.clone())
|
||||
}
|
||||
|
||||
/// If this path is a single identifier, like `foo`, return its name.
|
||||
@ -265,9 +263,10 @@ fn convert_path(
|
||||
res
|
||||
}
|
||||
}
|
||||
ast::PathSegmentKind::SelfTypeKw => {
|
||||
ModPath::from_segments(PathKind::Plain, Some(known::SELF_TYPE))
|
||||
}
|
||||
ast::PathSegmentKind::SelfTypeKw => ModPath::from_segments(
|
||||
PathKind::Plain,
|
||||
Some(Name::new_symbol(sym::Self_.clone(), SyntaxContextId::ROOT)),
|
||||
),
|
||||
ast::PathSegmentKind::CrateKw => ModPath::from_segments(PathKind::Crate, iter::empty()),
|
||||
ast::PathSegmentKind::SelfKw => handle_super_kw(0)?,
|
||||
ast::PathSegmentKind::SuperKw => handle_super_kw(1)?,
|
||||
@ -317,30 +316,36 @@ fn convert_path_tt(db: &dyn ExpandDatabase, tt: &[tt::TokenTree]) -> Option<ModP
|
||||
tt::Leaf::Punct(tt::Punct { char: ':', .. }) => PathKind::Abs,
|
||||
_ => return None,
|
||||
},
|
||||
tt::Leaf::Ident(tt::Ident { text, span }) if text == "$crate" => {
|
||||
tt::Leaf::Ident(tt::Ident { sym: text, span, .. }) if *text == sym::dollar_crate => {
|
||||
resolve_crate_root(db, span.ctx).map(PathKind::DollarCrate).unwrap_or(PathKind::Crate)
|
||||
}
|
||||
tt::Leaf::Ident(tt::Ident { text, .. }) if text == "self" => PathKind::SELF,
|
||||
tt::Leaf::Ident(tt::Ident { text, .. }) if text == "super" => {
|
||||
tt::Leaf::Ident(tt::Ident { sym: text, .. }) if *text == sym::self_ => PathKind::SELF,
|
||||
tt::Leaf::Ident(tt::Ident { sym: text, .. }) if *text == sym::super_ => {
|
||||
let mut deg = 1;
|
||||
while let Some(tt::Leaf::Ident(tt::Ident { text, .. })) = leaves.next() {
|
||||
if text != "super" {
|
||||
segments.push(Name::new_text_dont_use(text.clone()));
|
||||
while let Some(tt::Leaf::Ident(tt::Ident { sym: text, span, is_raw })) = leaves.next() {
|
||||
if *text != sym::super_ {
|
||||
segments.push(Name::new_symbol_maybe_raw(text.clone(), *is_raw, span.ctx));
|
||||
break;
|
||||
}
|
||||
deg += 1;
|
||||
}
|
||||
PathKind::Super(deg)
|
||||
}
|
||||
tt::Leaf::Ident(tt::Ident { text, .. }) if text == "crate" => PathKind::Crate,
|
||||
tt::Leaf::Ident(tt::Ident { sym: text, .. }) if *text == sym::crate_ => PathKind::Crate,
|
||||
tt::Leaf::Ident(ident) => {
|
||||
segments.push(Name::new_text_dont_use(ident.text.clone()));
|
||||
segments.push(Name::new_symbol_maybe_raw(
|
||||
ident.sym.clone(),
|
||||
ident.is_raw,
|
||||
ident.span.ctx,
|
||||
));
|
||||
PathKind::Plain
|
||||
}
|
||||
_ => return None,
|
||||
};
|
||||
segments.extend(leaves.filter_map(|leaf| match leaf {
|
||||
::tt::Leaf::Ident(ident) => Some(Name::new_text_dont_use(ident.text.clone())),
|
||||
::tt::Leaf::Ident(ident) => {
|
||||
Some(Name::new_symbol_maybe_raw(ident.sym.clone(), ident.is_raw, ident.span.ctx))
|
||||
}
|
||||
_ => None,
|
||||
}));
|
||||
Some(ModPath { kind, segments })
|
||||
@ -385,6 +390,8 @@ macro_rules! __known_path {
|
||||
(core::ops::RangeInclusive) => {};
|
||||
(core::future::Future) => {};
|
||||
(core::future::IntoFuture) => {};
|
||||
(core::fmt::Debug) => {};
|
||||
(std::fmt::format) => {};
|
||||
(core::ops::Try) => {};
|
||||
($path:path) => {
|
||||
compile_error!("Please register your known path in the path module")
|
||||
@ -396,7 +403,7 @@ macro_rules! __path {
|
||||
($start:ident $(:: $seg:ident)*) => ({
|
||||
$crate::__known_path!($start $(:: $seg)*);
|
||||
$crate::mod_path::ModPath::from_segments($crate::mod_path::PathKind::Abs, vec![
|
||||
$crate::mod_path::__name![$start], $($crate::mod_path::__name![$seg],)*
|
||||
$crate::name::Name::new_symbol_root(intern::sym::$start.clone()), $($crate::name::Name::new_symbol_root(intern::sym::$seg.clone()),)*
|
||||
])
|
||||
});
|
||||
}
|
||||
|
@ -2,7 +2,9 @@
|
||||
|
||||
use std::fmt;
|
||||
|
||||
use syntax::{ast, format_smolstr, utils::is_raw_identifier, SmolStr};
|
||||
use intern::{sym, Symbol};
|
||||
use span::SyntaxContextId;
|
||||
use syntax::{ast, utils::is_raw_identifier};
|
||||
|
||||
/// `Name` is a wrapper around string, which is used in hir for both references
|
||||
/// and declarations. In theory, names should also carry hygiene info, but we are
|
||||
@ -11,66 +13,93 @@ use syntax::{ast, format_smolstr, utils::is_raw_identifier, SmolStr};
|
||||
/// Note that `Name` holds and prints escaped name i.e. prefixed with "r#" when it
|
||||
/// is a raw identifier. Use [`unescaped()`][Name::unescaped] when you need the
|
||||
/// name without "r#".
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub struct Name(Repr);
|
||||
|
||||
/// Wrapper of `Name` to print the name without "r#" even when it is a raw identifier.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub struct UnescapedName<'a>(&'a Name);
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
enum Repr {
|
||||
Text(SmolStr),
|
||||
TupleField(usize),
|
||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
||||
pub struct Name {
|
||||
symbol: Symbol,
|
||||
ctx: (),
|
||||
// FIXME: We should probably encode rawness as a property here instead, once we have hygiene
|
||||
// in here we've got 4 bytes of padding to fill anyways
|
||||
}
|
||||
|
||||
impl UnescapedName<'_> {
|
||||
/// Returns the textual representation of this name as a [`SmolStr`]. Prefer using this over
|
||||
/// [`ToString::to_string`] if possible as this conversion is cheaper in the general case.
|
||||
pub fn to_smol_str(&self) -> SmolStr {
|
||||
match &self.0 .0 {
|
||||
Repr::Text(it) => {
|
||||
if let Some(stripped) = it.strip_prefix("r#") {
|
||||
SmolStr::new(stripped)
|
||||
} else {
|
||||
it.clone()
|
||||
}
|
||||
}
|
||||
Repr::TupleField(it) => SmolStr::new(it.to_string()),
|
||||
}
|
||||
impl fmt::Debug for Name {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("Name")
|
||||
.field("symbol", &self.symbol.as_str())
|
||||
.field("ctx", &self.ctx)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for Name {
|
||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||
self.symbol.as_str().cmp(other.symbol.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for Name {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<Symbol> for Name {
|
||||
fn eq(&self, sym: &Symbol) -> bool {
|
||||
self.symbol == *sym
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<Name> for Symbol {
|
||||
fn eq(&self, name: &Name) -> bool {
|
||||
*self == name.symbol
|
||||
}
|
||||
}
|
||||
|
||||
/// Wrapper of `Name` to print the name without "r#" even when it is a raw identifier.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct UnescapedName<'a>(&'a Name);
|
||||
|
||||
impl UnescapedName<'_> {
|
||||
pub fn display(&self, db: &dyn crate::db::ExpandDatabase) -> impl fmt::Display + '_ {
|
||||
_ = db;
|
||||
UnescapedDisplay { name: self }
|
||||
}
|
||||
#[doc(hidden)]
|
||||
pub fn display_no_db(&self) -> impl fmt::Display + '_ {
|
||||
UnescapedDisplay { name: self }
|
||||
}
|
||||
}
|
||||
|
||||
impl Name {
|
||||
/// Note: this is private to make creating name from random string hard.
|
||||
/// Hopefully, this should allow us to integrate hygiene cleaner in the
|
||||
/// future, and to switch to interned representation of names.
|
||||
const fn new_text(text: SmolStr) -> Name {
|
||||
Name(Repr::Text(text))
|
||||
fn new_text(text: &str) -> Name {
|
||||
Name { symbol: Symbol::intern(text), ctx: () }
|
||||
}
|
||||
|
||||
// FIXME: See above, unfortunately some places really need this right now
|
||||
#[doc(hidden)]
|
||||
pub const fn new_text_dont_use(text: SmolStr) -> Name {
|
||||
Name(Repr::Text(text))
|
||||
pub fn new(text: &str, raw: tt::IdentIsRaw, ctx: SyntaxContextId) -> Name {
|
||||
_ = ctx;
|
||||
Name {
|
||||
symbol: if raw.yes() {
|
||||
Symbol::intern(&format!("{}{text}", raw.as_str()))
|
||||
} else {
|
||||
Symbol::intern(text)
|
||||
},
|
||||
ctx: (),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_tuple_field(idx: usize) -> Name {
|
||||
Name(Repr::TupleField(idx))
|
||||
Name { symbol: Symbol::intern(&idx.to_string()), ctx: () }
|
||||
}
|
||||
|
||||
pub fn new_lifetime(lt: &ast::Lifetime) -> Name {
|
||||
Self::new_text(lt.text().into())
|
||||
Name { symbol: Symbol::intern(lt.text().as_str()), ctx: () }
|
||||
}
|
||||
|
||||
/// Shortcut to create a name from a string literal.
|
||||
const fn new_static(text: &'static str) -> Name {
|
||||
Name::new_text(SmolStr::new_static(text))
|
||||
fn new_ref(text: &str) -> Name {
|
||||
Name { symbol: Symbol::intern(text), ctx: () }
|
||||
}
|
||||
|
||||
/// Resolve a name from the text of token.
|
||||
@ -78,14 +107,12 @@ impl Name {
|
||||
match raw_text.strip_prefix("r#") {
|
||||
// When `raw_text` starts with "r#" but the name does not coincide with any
|
||||
// keyword, we never need the prefix so we strip it.
|
||||
Some(text) if !is_raw_identifier(text) => Name::new_text(SmolStr::new(text)),
|
||||
Some(text) if !is_raw_identifier(text) => Name::new_ref(text),
|
||||
// Keywords (in the current edition) *can* be used as a name in earlier editions of
|
||||
// Rust, e.g. "try" in Rust 2015. Even in such cases, we keep track of them in their
|
||||
// escaped form.
|
||||
None if is_raw_identifier(raw_text) => {
|
||||
Name::new_text(format_smolstr!("r#{}", raw_text))
|
||||
}
|
||||
_ => Name::new_text(raw_text.into()),
|
||||
None if is_raw_identifier(raw_text) => Name::new_text(&format!("r#{}", raw_text)),
|
||||
_ => Name::new_text(raw_text),
|
||||
}
|
||||
}
|
||||
|
||||
@ -98,8 +125,8 @@ impl Name {
|
||||
/// Ideally, we want a `gensym` semantics for missing names -- each missing
|
||||
/// name is equal only to itself. It's not clear how to implement this in
|
||||
/// salsa though, so we punt on that bit for a moment.
|
||||
pub const fn missing() -> Name {
|
||||
Name::new_static("[missing name]")
|
||||
pub fn missing() -> Name {
|
||||
Name { symbol: sym::MISSING_NAME.clone(), ctx: () }
|
||||
}
|
||||
|
||||
/// Returns true if this is a fake name for things missing in the source code. See
|
||||
@ -115,41 +142,17 @@ impl Name {
|
||||
/// creating desugared locals and labels. The caller is responsible for picking an index
|
||||
/// that is stable across re-executions
|
||||
pub fn generate_new_name(idx: usize) -> Name {
|
||||
Name::new_text(format_smolstr!("<ra@gennew>{idx}"))
|
||||
Name::new_text(&format!("<ra@gennew>{idx}"))
|
||||
}
|
||||
|
||||
/// Returns the tuple index this name represents if it is a tuple field.
|
||||
pub fn as_tuple_index(&self) -> Option<usize> {
|
||||
match self.0 {
|
||||
Repr::TupleField(idx) => Some(idx),
|
||||
_ => None,
|
||||
}
|
||||
self.symbol.as_str().parse().ok()
|
||||
}
|
||||
|
||||
/// Returns the text this name represents if it isn't a tuple field.
|
||||
pub fn as_text(&self) -> Option<SmolStr> {
|
||||
match &self.0 {
|
||||
Repr::Text(it) => Some(it.clone()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the text this name represents if it isn't a tuple field.
|
||||
pub fn as_str(&self) -> Option<&str> {
|
||||
match &self.0 {
|
||||
Repr::Text(it) => Some(it),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the textual representation of this name as a [`SmolStr`].
|
||||
/// Prefer using this over [`ToString::to_string`] if possible as this conversion is cheaper in
|
||||
/// the general case.
|
||||
pub fn to_smol_str(&self) -> SmolStr {
|
||||
match &self.0 {
|
||||
Repr::Text(it) => it.clone(),
|
||||
Repr::TupleField(it) => SmolStr::new(it.to_string()),
|
||||
}
|
||||
pub fn as_str(&self) -> &str {
|
||||
self.symbol.as_str()
|
||||
}
|
||||
|
||||
pub fn unescaped(&self) -> UnescapedName<'_> {
|
||||
@ -157,16 +160,41 @@ impl Name {
|
||||
}
|
||||
|
||||
pub fn is_escaped(&self) -> bool {
|
||||
match &self.0 {
|
||||
Repr::Text(it) => it.starts_with("r#"),
|
||||
Repr::TupleField(_) => false,
|
||||
}
|
||||
self.symbol.as_str().starts_with("r#")
|
||||
}
|
||||
|
||||
pub fn display<'a>(&'a self, db: &dyn crate::db::ExpandDatabase) -> impl fmt::Display + 'a {
|
||||
_ = db;
|
||||
Display { name: self }
|
||||
}
|
||||
|
||||
// FIXME: Remove this
|
||||
#[doc(hidden)]
|
||||
pub fn display_no_db(&self) -> impl fmt::Display + '_ {
|
||||
Display { name: self }
|
||||
}
|
||||
|
||||
pub fn symbol(&self) -> &Symbol {
|
||||
&self.symbol
|
||||
}
|
||||
|
||||
pub const fn new_symbol(symbol: Symbol, ctx: SyntaxContextId) -> Self {
|
||||
_ = ctx;
|
||||
Self { symbol, ctx: () }
|
||||
}
|
||||
|
||||
pub fn new_symbol_maybe_raw(sym: Symbol, raw: tt::IdentIsRaw, ctx: SyntaxContextId) -> Self {
|
||||
if raw.no() {
|
||||
Self { symbol: sym, ctx: () }
|
||||
} else {
|
||||
Name::new(sym.as_str(), raw, ctx)
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: This needs to go once we have hygiene
|
||||
pub const fn new_symbol_root(sym: Symbol) -> Self {
|
||||
Self { symbol: sym, ctx: () }
|
||||
}
|
||||
}
|
||||
|
||||
struct Display<'a> {
|
||||
@ -175,10 +203,7 @@ struct Display<'a> {
|
||||
|
||||
impl fmt::Display for Display<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match &self.name.0 {
|
||||
Repr::Text(text) => fmt::Display::fmt(&text, f),
|
||||
Repr::TupleField(idx) => fmt::Display::fmt(&idx, f),
|
||||
}
|
||||
fmt::Display::fmt(self.name.symbol.as_str(), f)
|
||||
}
|
||||
}
|
||||
|
||||
@ -188,13 +213,9 @@ struct UnescapedDisplay<'a> {
|
||||
|
||||
impl fmt::Display for UnescapedDisplay<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match &self.name.0 .0 {
|
||||
Repr::Text(text) => {
|
||||
let text = text.strip_prefix("r#").unwrap_or(text);
|
||||
fmt::Display::fmt(&text, f)
|
||||
}
|
||||
Repr::TupleField(idx) => fmt::Display::fmt(&idx, f),
|
||||
}
|
||||
let symbol = &self.name.0.symbol.as_str();
|
||||
let text = symbol.strip_prefix("r#").unwrap_or(symbol);
|
||||
fmt::Display::fmt(&text, f)
|
||||
}
|
||||
}
|
||||
|
||||
@ -228,7 +249,7 @@ impl AsName for ast::NameOrNameRef {
|
||||
|
||||
impl<Span> AsName for tt::Ident<Span> {
|
||||
fn as_name(&self) -> Name {
|
||||
Name::resolve(&self.text)
|
||||
Name::resolve(self.sym.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
@ -246,251 +267,6 @@ impl AsName for ast::FieldKind {
|
||||
|
||||
impl AsName for base_db::Dependency {
|
||||
fn as_name(&self) -> Name {
|
||||
Name::new_text(SmolStr::new(&*self.name))
|
||||
Name::new_text(&self.name)
|
||||
}
|
||||
}
|
||||
|
||||
pub mod known {
|
||||
macro_rules! known_names {
|
||||
($($ident:ident),* $(,)?) => {
|
||||
$(
|
||||
#[allow(bad_style)]
|
||||
pub const $ident: super::Name =
|
||||
super::Name::new_static(stringify!($ident));
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
known_names!(
|
||||
// Primitives
|
||||
isize,
|
||||
i8,
|
||||
i16,
|
||||
i32,
|
||||
i64,
|
||||
i128,
|
||||
usize,
|
||||
u8,
|
||||
u16,
|
||||
u32,
|
||||
u64,
|
||||
u128,
|
||||
f16,
|
||||
f32,
|
||||
f64,
|
||||
f128,
|
||||
bool,
|
||||
char,
|
||||
str,
|
||||
// Special names
|
||||
macro_rules,
|
||||
doc,
|
||||
cfg,
|
||||
cfg_attr,
|
||||
register_attr,
|
||||
register_tool,
|
||||
// Components of known path (value or mod name)
|
||||
std,
|
||||
core,
|
||||
alloc,
|
||||
iter,
|
||||
ops,
|
||||
fmt,
|
||||
future,
|
||||
result,
|
||||
string,
|
||||
boxed,
|
||||
option,
|
||||
prelude,
|
||||
rust_2015,
|
||||
rust_2018,
|
||||
rust_2021,
|
||||
rust_2024,
|
||||
v1,
|
||||
new_display,
|
||||
new_debug,
|
||||
new_lower_exp,
|
||||
new_upper_exp,
|
||||
new_octal,
|
||||
new_pointer,
|
||||
new_binary,
|
||||
new_lower_hex,
|
||||
new_upper_hex,
|
||||
from_usize,
|
||||
panic_2015,
|
||||
panic_2021,
|
||||
unreachable_2015,
|
||||
unreachable_2021,
|
||||
// Components of known path (type name)
|
||||
Iterator,
|
||||
IntoIterator,
|
||||
Item,
|
||||
IntoIter,
|
||||
Try,
|
||||
Ok,
|
||||
Future,
|
||||
IntoFuture,
|
||||
Result,
|
||||
Option,
|
||||
Output,
|
||||
Target,
|
||||
Box,
|
||||
RangeFrom,
|
||||
RangeFull,
|
||||
RangeInclusive,
|
||||
RangeToInclusive,
|
||||
RangeTo,
|
||||
Range,
|
||||
String,
|
||||
Neg,
|
||||
Not,
|
||||
None,
|
||||
Index,
|
||||
Left,
|
||||
Right,
|
||||
Center,
|
||||
Unknown,
|
||||
Is,
|
||||
Param,
|
||||
Implied,
|
||||
// Components of known path (function name)
|
||||
filter_map,
|
||||
next,
|
||||
iter_mut,
|
||||
len,
|
||||
is_empty,
|
||||
as_str,
|
||||
new,
|
||||
new_v1_formatted,
|
||||
none,
|
||||
// Builtin macros
|
||||
asm,
|
||||
assert,
|
||||
column,
|
||||
compile_error,
|
||||
concat_idents,
|
||||
concat_bytes,
|
||||
concat,
|
||||
const_format_args,
|
||||
core_panic,
|
||||
env,
|
||||
file,
|
||||
format,
|
||||
format_args_nl,
|
||||
format_args,
|
||||
global_asm,
|
||||
include_bytes,
|
||||
include_str,
|
||||
include,
|
||||
line,
|
||||
llvm_asm,
|
||||
log_syntax,
|
||||
module_path,
|
||||
option_env,
|
||||
quote,
|
||||
std_panic,
|
||||
stringify,
|
||||
trace_macros,
|
||||
unreachable,
|
||||
// Builtin derives
|
||||
Copy,
|
||||
Clone,
|
||||
Default,
|
||||
Debug,
|
||||
Hash,
|
||||
Ord,
|
||||
PartialOrd,
|
||||
Eq,
|
||||
PartialEq,
|
||||
// Builtin attributes
|
||||
bench,
|
||||
cfg_accessible,
|
||||
cfg_eval,
|
||||
crate_type,
|
||||
derive,
|
||||
derive_const,
|
||||
global_allocator,
|
||||
no_core,
|
||||
no_std,
|
||||
test,
|
||||
test_case,
|
||||
recursion_limit,
|
||||
feature,
|
||||
// known methods of lang items
|
||||
call_once,
|
||||
call_mut,
|
||||
call,
|
||||
eq,
|
||||
ne,
|
||||
ge,
|
||||
gt,
|
||||
le,
|
||||
lt,
|
||||
// known fields of lang items
|
||||
pieces,
|
||||
// lang items
|
||||
add_assign,
|
||||
add,
|
||||
bitand_assign,
|
||||
bitand,
|
||||
bitor_assign,
|
||||
bitor,
|
||||
bitxor_assign,
|
||||
bitxor,
|
||||
branch,
|
||||
deref_mut,
|
||||
deref,
|
||||
div_assign,
|
||||
div,
|
||||
drop,
|
||||
fn_mut,
|
||||
fn_once,
|
||||
future_trait,
|
||||
index,
|
||||
index_mut,
|
||||
into_future,
|
||||
mul_assign,
|
||||
mul,
|
||||
neg,
|
||||
not,
|
||||
owned_box,
|
||||
partial_ord,
|
||||
poll,
|
||||
r#fn,
|
||||
rem_assign,
|
||||
rem,
|
||||
shl_assign,
|
||||
shl,
|
||||
shr_assign,
|
||||
shr,
|
||||
sub_assign,
|
||||
sub,
|
||||
unsafe_cell,
|
||||
va_list
|
||||
);
|
||||
|
||||
// self/Self cannot be used as an identifier
|
||||
pub const SELF_PARAM: super::Name = super::Name::new_static("self");
|
||||
pub const SELF_TYPE: super::Name = super::Name::new_static("Self");
|
||||
|
||||
pub const STATIC_LIFETIME: super::Name = super::Name::new_static("'static");
|
||||
pub const DOLLAR_CRATE: super::Name = super::Name::new_static("$crate");
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! name {
|
||||
(self) => {
|
||||
$crate::name::known::SELF_PARAM
|
||||
};
|
||||
(Self) => {
|
||||
$crate::name::known::SELF_TYPE
|
||||
};
|
||||
('static) => {
|
||||
$crate::name::known::STATIC_LIFETIME
|
||||
};
|
||||
($ident:ident) => {
|
||||
$crate::name::known::$ident
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
pub use crate::name;
|
||||
|
@ -4,22 +4,11 @@ use core::fmt;
|
||||
use std::{panic::RefUnwindSafe, sync};
|
||||
|
||||
use base_db::{CrateId, Env};
|
||||
use intern::Symbol;
|
||||
use rustc_hash::FxHashMap;
|
||||
use span::Span;
|
||||
use stdx::never;
|
||||
use syntax::SmolStr;
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{db::ExpandDatabase, tt, ExpandError, ExpandResult};
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct ProcMacroId(u32);
|
||||
|
||||
impl ProcMacroId {
|
||||
pub fn new(u32: u32) -> Self {
|
||||
ProcMacroId(u32)
|
||||
}
|
||||
}
|
||||
use crate::{db::ExpandDatabase, tt, ExpandError, ExpandErrorKind, ExpandResult};
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)]
|
||||
pub enum ProcMacroKind {
|
||||
@ -28,7 +17,10 @@ pub enum ProcMacroKind {
|
||||
Attr,
|
||||
}
|
||||
|
||||
/// A proc-macro expander implementation.
|
||||
pub trait ProcMacroExpander: fmt::Debug + Send + Sync + RefUnwindSafe {
|
||||
/// Run the expander with the given input subtree, optional attribute input subtree (for
|
||||
/// [`ProcMacroKind::Attr`]), environment variables, and span information.
|
||||
fn expand(
|
||||
&self,
|
||||
subtree: &tt::Subtree,
|
||||
@ -42,57 +34,165 @@ pub trait ProcMacroExpander: fmt::Debug + Send + Sync + RefUnwindSafe {
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ProcMacroExpansionError {
|
||||
/// The proc-macro panicked.
|
||||
Panic(String),
|
||||
/// Things like "proc macro server was killed by OOM".
|
||||
/// The server itself errored out.
|
||||
System(String),
|
||||
}
|
||||
|
||||
pub type ProcMacroLoadResult = Result<Vec<ProcMacro>, String>;
|
||||
pub type ProcMacroLoadResult = Result<Vec<ProcMacro>, (String, bool)>;
|
||||
type StoredProcMacroLoadResult = Result<Box<[ProcMacro]>, (Box<str>, bool)>;
|
||||
|
||||
pub type ProcMacros = FxHashMap<CrateId, ProcMacroLoadResult>;
|
||||
#[derive(Default, Debug)]
|
||||
pub struct ProcMacrosBuilder(FxHashMap<CrateId, StoredProcMacroLoadResult>);
|
||||
impl ProcMacrosBuilder {
|
||||
pub fn insert(&mut self, proc_macros_crate: CrateId, proc_macro: ProcMacroLoadResult) {
|
||||
self.0.insert(
|
||||
proc_macros_crate,
|
||||
match proc_macro {
|
||||
Ok(it) => Ok(it.into_boxed_slice()),
|
||||
Err((e, hard_err)) => Err((e.into_boxed_str(), hard_err)),
|
||||
},
|
||||
);
|
||||
}
|
||||
pub fn build(mut self) -> ProcMacros {
|
||||
self.0.shrink_to_fit();
|
||||
ProcMacros(self.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
pub struct ProcMacros(FxHashMap<CrateId, StoredProcMacroLoadResult>);
|
||||
|
||||
impl FromIterator<(CrateId, ProcMacroLoadResult)> for ProcMacros {
|
||||
fn from_iter<T: IntoIterator<Item = (CrateId, ProcMacroLoadResult)>>(iter: T) -> Self {
|
||||
let mut builder = ProcMacrosBuilder::default();
|
||||
for (k, v) in iter {
|
||||
builder.insert(k, v);
|
||||
}
|
||||
builder.build()
|
||||
}
|
||||
}
|
||||
|
||||
impl ProcMacros {
|
||||
fn get(&self, krate: CrateId, idx: u32, err_span: Span) -> Result<&ProcMacro, ExpandError> {
|
||||
let proc_macros = match self.0.get(&krate) {
|
||||
Some(Ok(proc_macros)) => proc_macros,
|
||||
Some(Err(_)) | None => {
|
||||
return Err(ExpandError::other(
|
||||
err_span,
|
||||
"internal error: no proc macros for crate",
|
||||
));
|
||||
}
|
||||
};
|
||||
proc_macros.get(idx as usize).ok_or_else(|| {
|
||||
ExpandError::other(err_span,
|
||||
format!(
|
||||
"internal error: proc-macro index out of bounds: the length is {} but the index is {}",
|
||||
proc_macros.len(),
|
||||
idx
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_error_for_crate(&self, krate: CrateId) -> Option<(&str, bool)> {
|
||||
self.0.get(&krate).and_then(|it| it.as_ref().err()).map(|(e, hard_err)| (&**e, *hard_err))
|
||||
}
|
||||
|
||||
/// Fetch the [`CustomProcMacroExpander`]s and their corresponding names for the given crate.
|
||||
pub fn for_crate(
|
||||
&self,
|
||||
krate: CrateId,
|
||||
def_site_ctx: span::SyntaxContextId,
|
||||
) -> Option<Box<[(crate::name::Name, CustomProcMacroExpander, bool)]>> {
|
||||
match self.0.get(&krate) {
|
||||
Some(Ok(proc_macros)) => Some({
|
||||
proc_macros
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(idx, it)| {
|
||||
let name = crate::name::Name::new_symbol(it.name.clone(), def_site_ctx);
|
||||
(name, CustomProcMacroExpander::new(idx as u32), it.disabled)
|
||||
})
|
||||
.collect()
|
||||
}),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A loaded proc-macro.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ProcMacro {
|
||||
pub name: SmolStr,
|
||||
/// The name of the proc macro.
|
||||
pub name: Symbol,
|
||||
pub kind: ProcMacroKind,
|
||||
/// The expander handle for this proc macro.
|
||||
pub expander: sync::Arc<dyn ProcMacroExpander>,
|
||||
/// Whether this proc-macro is disabled for early name resolution. Notably, the
|
||||
/// [`Self::expander`] is still usable.
|
||||
pub disabled: bool,
|
||||
}
|
||||
|
||||
/// A custom proc-macro expander handle. This handle together with its crate resolves to a [`ProcMacro`]
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
|
||||
pub struct CustomProcMacroExpander {
|
||||
proc_macro_id: ProcMacroId,
|
||||
proc_macro_id: u32,
|
||||
}
|
||||
|
||||
impl CustomProcMacroExpander {
|
||||
const DUMMY_ID: u32 = !0;
|
||||
const MISSING_EXPANDER: u32 = !0;
|
||||
const DISABLED_ID: u32 = !1;
|
||||
const PROC_MACRO_ATTR_DISABLED: u32 = !2;
|
||||
|
||||
pub fn new(proc_macro_id: ProcMacroId) -> Self {
|
||||
assert_ne!(proc_macro_id.0, Self::DUMMY_ID);
|
||||
assert_ne!(proc_macro_id.0, Self::DISABLED_ID);
|
||||
pub fn new(proc_macro_id: u32) -> Self {
|
||||
assert_ne!(proc_macro_id, Self::MISSING_EXPANDER);
|
||||
assert_ne!(proc_macro_id, Self::DISABLED_ID);
|
||||
assert_ne!(proc_macro_id, Self::PROC_MACRO_ATTR_DISABLED);
|
||||
Self { proc_macro_id }
|
||||
}
|
||||
|
||||
/// A dummy expander that always errors. This is used for proc-macros that are missing, usually
|
||||
/// due to them not being built yet.
|
||||
pub const fn dummy() -> Self {
|
||||
Self { proc_macro_id: ProcMacroId(Self::DUMMY_ID) }
|
||||
}
|
||||
|
||||
/// The macro was not yet resolved.
|
||||
pub const fn is_dummy(&self) -> bool {
|
||||
self.proc_macro_id.0 == Self::DUMMY_ID
|
||||
/// An expander that always errors due to the actual proc-macro expander missing.
|
||||
pub const fn missing_expander() -> Self {
|
||||
Self { proc_macro_id: Self::MISSING_EXPANDER }
|
||||
}
|
||||
|
||||
/// A dummy expander that always errors. This expander is used for macros that have been disabled.
|
||||
pub const fn disabled() -> Self {
|
||||
Self { proc_macro_id: ProcMacroId(Self::DISABLED_ID) }
|
||||
Self { proc_macro_id: Self::DISABLED_ID }
|
||||
}
|
||||
|
||||
/// A dummy expander that always errors. This expander is used for attribute macros when
|
||||
/// proc-macro attribute expansion is disabled.
|
||||
pub const fn disabled_proc_attr() -> Self {
|
||||
Self { proc_macro_id: Self::PROC_MACRO_ATTR_DISABLED }
|
||||
}
|
||||
|
||||
/// The macro-expander is missing or has yet to be build.
|
||||
pub const fn is_missing(&self) -> bool {
|
||||
self.proc_macro_id == Self::MISSING_EXPANDER
|
||||
}
|
||||
|
||||
/// The macro is explicitly disabled and cannot be expanded.
|
||||
pub const fn is_disabled(&self) -> bool {
|
||||
self.proc_macro_id.0 == Self::DISABLED_ID
|
||||
self.proc_macro_id == Self::DISABLED_ID
|
||||
}
|
||||
|
||||
/// The macro is explicitly disabled due to proc-macro attribute expansion being disabled.
|
||||
pub const fn is_disabled_proc_attr(&self) -> bool {
|
||||
self.proc_macro_id == Self::PROC_MACRO_ATTR_DISABLED
|
||||
}
|
||||
|
||||
/// The macro is explicitly disabled due to proc-macro attribute expansion being disabled.
|
||||
pub fn as_expand_error(&self, def_crate: CrateId) -> Option<ExpandErrorKind> {
|
||||
match self.proc_macro_id {
|
||||
Self::PROC_MACRO_ATTR_DISABLED => Some(ExpandErrorKind::ProcMacroAttrExpansionDisabled),
|
||||
Self::DISABLED_ID => Some(ExpandErrorKind::MacroDisabled),
|
||||
Self::MISSING_EXPANDER => Some(ExpandErrorKind::MissingProcMacroExpander(def_crate)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn expand(
|
||||
@ -107,38 +207,27 @@ impl CustomProcMacroExpander {
|
||||
mixed_site: Span,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
match self.proc_macro_id {
|
||||
ProcMacroId(Self::DUMMY_ID) => ExpandResult::new(
|
||||
Self::PROC_MACRO_ATTR_DISABLED => ExpandResult::new(
|
||||
tt::Subtree::empty(tt::DelimSpan { open: call_site, close: call_site }),
|
||||
ExpandError::UnresolvedProcMacro(def_crate),
|
||||
ExpandError::new(call_site, ExpandErrorKind::ProcMacroAttrExpansionDisabled),
|
||||
),
|
||||
ProcMacroId(Self::DISABLED_ID) => ExpandResult::new(
|
||||
Self::MISSING_EXPANDER => ExpandResult::new(
|
||||
tt::Subtree::empty(tt::DelimSpan { open: call_site, close: call_site }),
|
||||
ExpandError::MacroDisabled,
|
||||
ExpandError::new(call_site, ExpandErrorKind::MissingProcMacroExpander(def_crate)),
|
||||
),
|
||||
ProcMacroId(id) => {
|
||||
Self::DISABLED_ID => ExpandResult::new(
|
||||
tt::Subtree::empty(tt::DelimSpan { open: call_site, close: call_site }),
|
||||
ExpandError::new(call_site, ExpandErrorKind::MacroDisabled),
|
||||
),
|
||||
id => {
|
||||
let proc_macros = db.proc_macros();
|
||||
let proc_macros = match proc_macros.get(&def_crate) {
|
||||
Some(Ok(proc_macros)) => proc_macros,
|
||||
Some(Err(_)) | None => {
|
||||
never!("Non-dummy expander even though there are no proc macros");
|
||||
let proc_macro = match proc_macros.get(def_crate, id, call_site) {
|
||||
Ok(proc_macro) => proc_macro,
|
||||
Err(e) => {
|
||||
return ExpandResult::new(
|
||||
tt::Subtree::empty(tt::DelimSpan { open: call_site, close: call_site }),
|
||||
ExpandError::other("Internal error"),
|
||||
);
|
||||
}
|
||||
};
|
||||
let proc_macro = match proc_macros.get(id as usize) {
|
||||
Some(proc_macro) => proc_macro,
|
||||
None => {
|
||||
never!(
|
||||
"Proc macro index out of bounds: the length is {} but the index is {}",
|
||||
proc_macros.len(),
|
||||
id
|
||||
);
|
||||
return ExpandResult::new(
|
||||
tt::Subtree::empty(tt::DelimSpan { open: call_site, close: call_site }),
|
||||
ExpandError::other("Internal error: proc-macro index out of bounds"),
|
||||
);
|
||||
e,
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
@ -153,12 +242,18 @@ impl CustomProcMacroExpander {
|
||||
ProcMacroExpansionError::System(text)
|
||||
if proc_macro.kind == ProcMacroKind::Attr =>
|
||||
{
|
||||
ExpandResult { value: tt.clone(), err: Some(ExpandError::other(text)) }
|
||||
ExpandResult {
|
||||
value: tt.clone(),
|
||||
err: Some(ExpandError::other(call_site, text)),
|
||||
}
|
||||
}
|
||||
ProcMacroExpansionError::System(text)
|
||||
| ProcMacroExpansionError::Panic(text) => ExpandResult::new(
|
||||
tt::Subtree::empty(tt::DelimSpan { open: call_site, close: call_site }),
|
||||
ExpandError::ProcMacroPanic(Arc::new(text.into_boxed_str())),
|
||||
ExpandError::new(
|
||||
call_site,
|
||||
ExpandErrorKind::ProcMacroPanic(text.into_boxed_str()),
|
||||
),
|
||||
),
|
||||
},
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
//! Span maps for real files and macro expansions.
|
||||
|
||||
use span::{FileId, HirFileId, HirFileIdRepr, MacroFileId, Span, SyntaxContextId};
|
||||
use span::{EditionedFileId, HirFileId, HirFileIdRepr, MacroFileId, Span, SyntaxContextId};
|
||||
use stdx::TupleExt;
|
||||
use syntax::{ast, AstNode, TextRange};
|
||||
use triomphe::Arc;
|
||||
@ -79,7 +79,7 @@ impl SpanMapRef<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn real_span_map(db: &dyn ExpandDatabase, file_id: FileId) -> Arc<RealSpanMap> {
|
||||
pub(crate) fn real_span_map(db: &dyn ExpandDatabase, file_id: EditionedFileId) -> Arc<RealSpanMap> {
|
||||
use syntax::ast::HasModuleItem;
|
||||
let mut pairs = vec![(syntax::TextSize::new(0), span::ROOT_ERASED_FILE_AST_ID)];
|
||||
let ast_id_map = db.ast_id_map(file_id.into());
|
||||
|
@ -5,7 +5,8 @@
|
||||
|
||||
use chalk_ir::cast::Cast;
|
||||
use hir_def::lang_item::LangItem;
|
||||
use hir_expand::name::name;
|
||||
use hir_expand::name::Name;
|
||||
use intern::sym;
|
||||
use limit::Limit;
|
||||
use triomphe::Arc;
|
||||
|
||||
@ -151,7 +152,9 @@ pub(crate) fn deref_by_trait(
|
||||
|
||||
let deref_trait =
|
||||
db.lang_item(table.trait_env.krate, LangItem::Deref).and_then(|l| l.as_trait())?;
|
||||
let target = db.trait_data(deref_trait).associated_type_by_name(&name![Target])?;
|
||||
let target = db
|
||||
.trait_data(deref_trait)
|
||||
.associated_type_by_name(&Name::new_symbol_root(sym::Target.clone()))?;
|
||||
|
||||
let projection = {
|
||||
let b = TyBuilder::subst_for_def(db, deref_trait, None);
|
||||
|
@ -3,6 +3,8 @@
|
||||
use core::ops;
|
||||
use std::{iter, ops::ControlFlow, sync::Arc};
|
||||
|
||||
use hir_expand::name::Name;
|
||||
use intern::sym;
|
||||
use tracing::debug;
|
||||
|
||||
use chalk_ir::{cast::Caster, fold::shift::Shift, CanonicalVarKinds};
|
||||
@ -16,7 +18,6 @@ use hir_def::{
|
||||
AssocItemId, BlockId, CallableDefId, GenericDefId, HasModule, ItemContainerId, Lookup,
|
||||
TypeAliasId, VariantId,
|
||||
};
|
||||
use hir_expand::name::name;
|
||||
|
||||
use crate::{
|
||||
db::{HirDatabase, InternedCoroutine},
|
||||
@ -288,15 +289,16 @@ impl chalk_solve::RustIrDatabase<Interner> for ChalkContext<'_> {
|
||||
chalk_ir::Binders::new(binders, bound)
|
||||
}
|
||||
crate::ImplTraitId::AsyncBlockTypeImplTrait(..) => {
|
||||
if let Some((future_trait, future_output)) = self
|
||||
.db
|
||||
.lang_item(self.krate, LangItem::Future)
|
||||
.and_then(|item| item.as_trait())
|
||||
.and_then(|trait_| {
|
||||
let alias =
|
||||
self.db.trait_data(trait_).associated_type_by_name(&name![Output])?;
|
||||
Some((trait_, alias))
|
||||
})
|
||||
if let Some((future_trait, future_output)) =
|
||||
self.db
|
||||
.lang_item(self.krate, LangItem::Future)
|
||||
.and_then(|item| item.as_trait())
|
||||
.and_then(|trait_| {
|
||||
let alias = self.db.trait_data(trait_).associated_type_by_name(
|
||||
&Name::new_symbol_root(sym::Output.clone()),
|
||||
)?;
|
||||
Some((trait_, alias))
|
||||
})
|
||||
{
|
||||
// Making up Symbol’s value as variable is void: AsyncBlock<T>:
|
||||
//
|
||||
|
@ -1,10 +1,10 @@
|
||||
use base_db::FileId;
|
||||
use chalk_ir::Substitution;
|
||||
use hir_def::db::DefDatabase;
|
||||
use rustc_apfloat::{
|
||||
ieee::{Half as f16, Quad as f128},
|
||||
Float,
|
||||
};
|
||||
use span::EditionedFileId;
|
||||
use test_fixture::WithFixture;
|
||||
use test_utils::skip_slow_tests;
|
||||
|
||||
@ -102,8 +102,8 @@ fn pretty_print_err(e: ConstEvalError, db: TestDB) -> String {
|
||||
err
|
||||
}
|
||||
|
||||
fn eval_goal(db: &TestDB, file_id: FileId) -> Result<Const, ConstEvalError> {
|
||||
let module_id = db.module_for_file(file_id);
|
||||
fn eval_goal(db: &TestDB, file_id: EditionedFileId) -> Result<Const, ConstEvalError> {
|
||||
let module_id = db.module_for_file(file_id.file_id());
|
||||
let def_map = module_id.def_map(db);
|
||||
let scope = &def_map[module_id.local_id].scope;
|
||||
let const_id = scope
|
||||
|
@ -61,6 +61,7 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
|
||||
) -> Result<Arc<MirBody>, MirLowerError>;
|
||||
|
||||
#[salsa::invoke(crate::mir::borrowck_query)]
|
||||
#[salsa::lru]
|
||||
fn borrowck(&self, def: DefWithBodyId) -> Result<Arc<[BorrowckResult]>, MirLowerError>;
|
||||
|
||||
#[salsa::invoke(crate::consteval::const_eval_query)]
|
||||
|
@ -17,17 +17,18 @@ use std::fmt;
|
||||
|
||||
use hir_def::{
|
||||
data::adt::VariantData, db::DefDatabase, hir::Pat, src::HasSource, AdtId, AttrDefId, ConstId,
|
||||
EnumId, FunctionId, ItemContainerId, Lookup, ModuleDefId, ModuleId, StaticId, StructId,
|
||||
TraitId, TypeAliasId,
|
||||
EnumId, EnumVariantId, FunctionId, ItemContainerId, Lookup, ModuleDefId, ModuleId, StaticId,
|
||||
StructId, TraitId, TypeAliasId,
|
||||
};
|
||||
use hir_expand::{
|
||||
name::{AsName, Name},
|
||||
HirFileId, MacroFileIdExt,
|
||||
};
|
||||
use intern::sym;
|
||||
use stdx::{always, never};
|
||||
use syntax::{
|
||||
ast::{self, HasName},
|
||||
AstNode, AstPtr,
|
||||
AstNode, AstPtr, ToSmolStr,
|
||||
};
|
||||
|
||||
use crate::db::HirDatabase;
|
||||
@ -163,8 +164,8 @@ impl<'a> DeclValidator<'a> {
|
||||
let is_allowed = |def_id| {
|
||||
let attrs = self.db.attrs(def_id);
|
||||
// don't bug the user about directly no_mangle annotated stuff, they can't do anything about it
|
||||
(!recursing && attrs.by_key("no_mangle").exists())
|
||||
|| attrs.by_key("allow").tt_values().any(|tt| {
|
||||
(!recursing && attrs.by_key(&sym::no_mangle).exists())
|
||||
|| attrs.by_key(&sym::allow).tt_values().any(|tt| {
|
||||
let allows = tt.to_string();
|
||||
allows.contains(allow_name)
|
||||
|| allows.contains(allow::BAD_STYLE)
|
||||
@ -247,7 +248,7 @@ impl<'a> DeclValidator<'a> {
|
||||
// Check the module name.
|
||||
let Some(module_name) = module_id.name(self.db.upcast()) else { return };
|
||||
let Some(module_name_replacement) =
|
||||
module_name.as_str().and_then(to_lower_snake_case).map(|new_name| Replacement {
|
||||
to_lower_snake_case(module_name.as_str()).map(|new_name| Replacement {
|
||||
current_name: module_name,
|
||||
suggested_text: new_name,
|
||||
expected_case: CaseType::LowerSnakeCase,
|
||||
@ -325,7 +326,9 @@ impl<'a> DeclValidator<'a> {
|
||||
let bind_name = &body.bindings[*id].name;
|
||||
let replacement = Replacement {
|
||||
current_name: bind_name.clone(),
|
||||
suggested_text: to_lower_snake_case(&bind_name.to_smol_str())?,
|
||||
suggested_text: to_lower_snake_case(
|
||||
&bind_name.display_no_db().to_smolstr(),
|
||||
)?,
|
||||
expected_case: CaseType::LowerSnakeCase,
|
||||
};
|
||||
Some((pat_id, replacement))
|
||||
@ -353,17 +356,16 @@ impl<'a> DeclValidator<'a> {
|
||||
continue;
|
||||
};
|
||||
|
||||
let is_param = ast::Param::can_cast(parent.kind());
|
||||
// We have to check that it's either `let var = ...` or `var @ Variant(_)` statement,
|
||||
// because e.g. match arms are patterns as well.
|
||||
// In other words, we check that it's a named variable binding.
|
||||
let is_binding = ast::LetStmt::can_cast(parent.kind())
|
||||
|| (ast::MatchArm::can_cast(parent.kind()) && ident_pat.at_token().is_some());
|
||||
if !(is_param || is_binding) {
|
||||
// This pattern is not an actual variable declaration, e.g. `Some(val) => {..}` match arm.
|
||||
let is_shorthand = ast::RecordPatField::cast(parent.clone())
|
||||
.map(|parent| parent.name_ref().is_none())
|
||||
.unwrap_or_default();
|
||||
if is_shorthand {
|
||||
// We don't check shorthand field patterns, such as 'field' in `Thing { field }`,
|
||||
// since the shorthand isn't the declaration.
|
||||
continue;
|
||||
}
|
||||
|
||||
let is_param = ast::Param::can_cast(parent.kind());
|
||||
let ident_type = if is_param { IdentType::Parameter } else { IdentType::Variable };
|
||||
|
||||
self.create_incorrect_case_diagnostic_for_ast_node(
|
||||
@ -406,10 +408,12 @@ impl<'a> DeclValidator<'a> {
|
||||
let mut struct_fields_replacements = fields
|
||||
.iter()
|
||||
.filter_map(|(_, field)| {
|
||||
to_lower_snake_case(&field.name.to_smol_str()).map(|new_name| Replacement {
|
||||
current_name: field.name.clone(),
|
||||
suggested_text: new_name,
|
||||
expected_case: CaseType::LowerSnakeCase,
|
||||
to_lower_snake_case(&field.name.display_no_db().to_smolstr()).map(|new_name| {
|
||||
Replacement {
|
||||
current_name: field.name.clone(),
|
||||
suggested_text: new_name,
|
||||
expected_case: CaseType::LowerSnakeCase,
|
||||
}
|
||||
})
|
||||
})
|
||||
.peekable();
|
||||
@ -489,11 +493,16 @@ impl<'a> DeclValidator<'a> {
|
||||
/// Check incorrect names for enum variants.
|
||||
fn validate_enum_variants(&mut self, enum_id: EnumId) {
|
||||
let data = self.db.enum_data(enum_id);
|
||||
|
||||
for (variant_id, _) in data.variants.iter() {
|
||||
self.validate_enum_variant_fields(*variant_id);
|
||||
}
|
||||
|
||||
let mut enum_variants_replacements = data
|
||||
.variants
|
||||
.iter()
|
||||
.filter_map(|(_, name)| {
|
||||
to_camel_case(&name.to_smol_str()).map(|new_name| Replacement {
|
||||
to_camel_case(&name.display_no_db().to_smolstr()).map(|new_name| Replacement {
|
||||
current_name: name.clone(),
|
||||
suggested_text: new_name,
|
||||
expected_case: CaseType::UpperCamelCase,
|
||||
@ -551,6 +560,77 @@ impl<'a> DeclValidator<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Check incorrect names for fields of enum variant.
|
||||
fn validate_enum_variant_fields(&mut self, variant_id: EnumVariantId) {
|
||||
let variant_data = self.db.enum_variant_data(variant_id);
|
||||
let VariantData::Record(fields) = variant_data.variant_data.as_ref() else {
|
||||
return;
|
||||
};
|
||||
let mut variant_field_replacements = fields
|
||||
.iter()
|
||||
.filter_map(|(_, field)| {
|
||||
to_lower_snake_case(&field.name.display_no_db().to_smolstr()).map(|new_name| {
|
||||
Replacement {
|
||||
current_name: field.name.clone(),
|
||||
suggested_text: new_name,
|
||||
expected_case: CaseType::LowerSnakeCase,
|
||||
}
|
||||
})
|
||||
})
|
||||
.peekable();
|
||||
|
||||
// XXX: only look at sources if we do have incorrect names
|
||||
if variant_field_replacements.peek().is_none() {
|
||||
return;
|
||||
}
|
||||
|
||||
let variant_loc = variant_id.lookup(self.db.upcast());
|
||||
let variant_src = variant_loc.source(self.db.upcast());
|
||||
|
||||
let Some(ast::FieldList::RecordFieldList(variant_fields_list)) =
|
||||
variant_src.value.field_list()
|
||||
else {
|
||||
always!(
|
||||
variant_field_replacements.peek().is_none(),
|
||||
"Replacements ({:?}) were generated for an enum variant \
|
||||
which had no fields list: {:?}",
|
||||
variant_field_replacements.collect::<Vec<_>>(),
|
||||
variant_src
|
||||
);
|
||||
return;
|
||||
};
|
||||
let mut variant_variants_iter = variant_fields_list.fields();
|
||||
for field_replacement in variant_field_replacements {
|
||||
// We assume that parameters in replacement are in the same order as in the
|
||||
// actual params list, but just some of them (ones that named correctly) are skipped.
|
||||
let field = loop {
|
||||
if let Some(field) = variant_variants_iter.next() {
|
||||
let Some(field_name) = field.name() else {
|
||||
continue;
|
||||
};
|
||||
if field_name.as_name() == field_replacement.current_name {
|
||||
break field;
|
||||
}
|
||||
} else {
|
||||
never!(
|
||||
"Replacement ({:?}) was generated for an enum variant field \
|
||||
which was not found: {:?}",
|
||||
field_replacement,
|
||||
variant_src
|
||||
);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
self.create_incorrect_case_diagnostic_for_ast_node(
|
||||
field_replacement,
|
||||
variant_src.file_id,
|
||||
&field,
|
||||
IdentType::Field,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn validate_const(&mut self, const_id: ConstId) {
|
||||
let container = const_id.lookup(self.db.upcast()).container;
|
||||
if self.is_trait_impl_container(container) {
|
||||
@ -631,9 +711,11 @@ impl<'a> DeclValidator<'a> {
|
||||
CaseType::UpperSnakeCase => to_upper_snake_case,
|
||||
CaseType::UpperCamelCase => to_camel_case,
|
||||
};
|
||||
let Some(replacement) = to_expected_case_type(&name.to_smol_str()).map(|new_name| {
|
||||
Replacement { current_name: name.clone(), suggested_text: new_name, expected_case }
|
||||
}) else {
|
||||
let Some(replacement) =
|
||||
to_expected_case_type(&name.display(self.db.upcast()).to_smolstr()).map(|new_name| {
|
||||
Replacement { current_name: name.clone(), suggested_text: new_name, expected_case }
|
||||
})
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
|
@ -8,7 +8,7 @@ use either::Either;
|
||||
use hir_def::lang_item::LangItem;
|
||||
use hir_def::{resolver::HasResolver, AdtId, AssocItemId, DefWithBodyId, HasModule};
|
||||
use hir_def::{ItemContainerId, Lookup};
|
||||
use hir_expand::name;
|
||||
use intern::sym;
|
||||
use itertools::Itertools;
|
||||
use rustc_hash::FxHashSet;
|
||||
use rustc_pattern_analysis::constructor::Constructor;
|
||||
@ -423,7 +423,9 @@ impl FilterMapNextChecker {
|
||||
ItemContainerId::TraitId(iterator_trait_id) => {
|
||||
let iterator_trait_items = &db.trait_data(iterator_trait_id).items;
|
||||
iterator_trait_items.iter().find_map(|(name, it)| match it {
|
||||
&AssocItemId::FunctionId(id) if *name == name![filter_map] => Some(id),
|
||||
&AssocItemId::FunctionId(id) if *name == sym::filter_map.clone() => {
|
||||
Some(id)
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
|
@ -206,7 +206,7 @@ impl<'a> PatCtxt<'a> {
|
||||
&mut self,
|
||||
pats: &[PatId],
|
||||
expected_len: usize,
|
||||
ellipsis: Option<usize>,
|
||||
ellipsis: Option<u32>,
|
||||
) -> Vec<FieldPat> {
|
||||
if pats.len() > expected_len {
|
||||
self.errors.push(PatternError::ExtraFields);
|
||||
@ -214,7 +214,7 @@ impl<'a> PatCtxt<'a> {
|
||||
}
|
||||
|
||||
pats.iter()
|
||||
.enumerate_and_adjust(expected_len, ellipsis)
|
||||
.enumerate_and_adjust(expected_len, ellipsis.map(|it| it as usize))
|
||||
.map(|(i, &subpattern)| FieldPat {
|
||||
field: LocalFieldId::from_raw((i as u32).into()),
|
||||
pattern: self.lower_pattern(subpattern),
|
||||
|
@ -3,6 +3,7 @@
|
||||
use std::fmt;
|
||||
|
||||
use hir_def::{DefWithBodyId, EnumId, EnumVariantId, HasModule, LocalFieldId, ModuleId, VariantId};
|
||||
use intern::sym;
|
||||
use once_cell::unsync::Lazy;
|
||||
use rustc_pattern_analysis::{
|
||||
constructor::{Constructor, ConstructorSet, VariantVisibility},
|
||||
@ -74,9 +75,9 @@ pub(crate) struct MatchCheckCtx<'db> {
|
||||
impl<'db> MatchCheckCtx<'db> {
|
||||
pub(crate) fn new(module: ModuleId, body: DefWithBodyId, db: &'db dyn HirDatabase) -> Self {
|
||||
let def_map = db.crate_def_map(module.krate());
|
||||
let exhaustive_patterns = def_map.is_unstable_feature_enabled("exhaustive_patterns");
|
||||
let exhaustive_patterns = def_map.is_unstable_feature_enabled(&sym::exhaustive_patterns);
|
||||
let min_exhaustive_patterns =
|
||||
def_map.is_unstable_feature_enabled("min_exhaustive_patterns");
|
||||
def_map.is_unstable_feature_enabled(&sym::min_exhaustive_patterns);
|
||||
Self { module, body, db, exhaustive_patterns, min_exhaustive_patterns }
|
||||
}
|
||||
|
||||
@ -85,6 +86,15 @@ impl<'db> MatchCheckCtx<'db> {
|
||||
arms: &[MatchArm<'db>],
|
||||
scrut_ty: Ty,
|
||||
) -> Result<UsefulnessReport<'db, Self>, ()> {
|
||||
if scrut_ty.contains_unknown() {
|
||||
return Err(());
|
||||
}
|
||||
for arm in arms {
|
||||
if arm.pat.ty().contains_unknown() {
|
||||
return Err(());
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: Determine place validity correctly. For now, err on the safe side.
|
||||
let place_validity = PlaceValidity::MaybeInvalid;
|
||||
// Measured to take ~100ms on modern hardware.
|
||||
@ -99,7 +109,7 @@ impl<'db> MatchCheckCtx<'db> {
|
||||
/// Returns whether the given ADT is from another crate declared `#[non_exhaustive]`.
|
||||
fn is_foreign_non_exhaustive(&self, adt: hir_def::AdtId) -> bool {
|
||||
let is_local = adt.krate(self.db.upcast()) == self.module.krate();
|
||||
!is_local && self.db.attrs(adt.into()).by_key("non_exhaustive").exists()
|
||||
!is_local && self.db.attrs(adt.into()).by_key(&sym::non_exhaustive).exists()
|
||||
}
|
||||
|
||||
fn variant_id_for_adt(
|
||||
|
@ -25,7 +25,7 @@ use hir_def::{
|
||||
ModuleId, TraitId,
|
||||
};
|
||||
use hir_expand::name::Name;
|
||||
use intern::{Internable, Interned};
|
||||
use intern::{sym, Internable, Interned};
|
||||
use itertools::Itertools;
|
||||
use la_arena::ArenaMap;
|
||||
use rustc_apfloat::{
|
||||
@ -1171,7 +1171,9 @@ impl HirDisplay for Ty {
|
||||
.lang_item(body.module(db.upcast()).krate(), LangItem::Future)
|
||||
.and_then(LangItemTarget::as_trait);
|
||||
let output = future_trait.and_then(|t| {
|
||||
db.trait_data(t).associated_type_by_name(&hir_expand::name!(Output))
|
||||
db.trait_data(t).associated_type_by_name(&Name::new_symbol_root(
|
||||
sym::Output.clone(),
|
||||
))
|
||||
});
|
||||
write!(f, "impl ")?;
|
||||
if let Some(t) = future_trait {
|
||||
@ -1933,7 +1935,7 @@ impl HirDisplay for TypeRef {
|
||||
}
|
||||
if let Some(abi) = abi {
|
||||
f.write_str("extern \"")?;
|
||||
f.write_str(abi)?;
|
||||
f.write_str(abi.as_str())?;
|
||||
f.write_str("\" ")?;
|
||||
}
|
||||
write!(f, "fn(")?;
|
||||
@ -2042,7 +2044,7 @@ impl HirDisplay for Path {
|
||||
.display_name
|
||||
.as_ref()
|
||||
.map(|name| name.canonical_name())
|
||||
.unwrap_or("$crate");
|
||||
.unwrap_or(&sym::dollar_crate);
|
||||
write!(f, "{name}")?
|
||||
}
|
||||
}
|
||||
|
@ -46,8 +46,9 @@ use hir_def::{
|
||||
AdtId, AssocItemId, DefWithBodyId, FieldId, FunctionId, ItemContainerId, Lookup, TraitId,
|
||||
TupleFieldId, TupleId, TypeAliasId, VariantId,
|
||||
};
|
||||
use hir_expand::name::{name, Name};
|
||||
use hir_expand::name::Name;
|
||||
use indexmap::IndexSet;
|
||||
use intern::sym;
|
||||
use la_arena::{ArenaMap, Entry};
|
||||
use once_cell::unsync::OnceCell;
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
@ -811,7 +812,7 @@ impl<'a> InferenceContext<'a> {
|
||||
None => self.err_ty(),
|
||||
};
|
||||
|
||||
param_tys.push(va_list_ty)
|
||||
param_tys.push(va_list_ty);
|
||||
}
|
||||
let mut param_tys = param_tys.into_iter().chain(iter::repeat(self.table.new_type_var()));
|
||||
if let Some(self_param) = self.body.self_param {
|
||||
@ -1424,7 +1425,9 @@ impl<'a> InferenceContext<'a> {
|
||||
}
|
||||
|
||||
fn resolve_output_on(&self, trait_: TraitId) -> Option<TypeAliasId> {
|
||||
self.db.trait_data(trait_).associated_type_by_name(&name![Output])
|
||||
self.db
|
||||
.trait_data(trait_)
|
||||
.associated_type_by_name(&Name::new_symbol_root(sym::Output.clone()))
|
||||
}
|
||||
|
||||
fn resolve_lang_trait(&self, lang: LangItem) -> Option<TraitId> {
|
||||
|
@ -15,7 +15,8 @@ use hir_def::{
|
||||
resolver::{resolver_for_expr, ResolveValueResult, ValueNs},
|
||||
DefWithBodyId, FieldId, HasModule, TupleFieldId, TupleId, VariantId,
|
||||
};
|
||||
use hir_expand::name;
|
||||
use hir_expand::name::Name;
|
||||
use intern::sym;
|
||||
use rustc_hash::FxHashMap;
|
||||
use smallvec::SmallVec;
|
||||
use stdx::never;
|
||||
@ -268,9 +269,7 @@ impl CapturedItem {
|
||||
}
|
||||
let variant_data = f.parent.variant_data(db.upcast());
|
||||
let field = match &*variant_data {
|
||||
VariantData::Record(fields) => {
|
||||
fields[f.local_id].name.as_str().unwrap_or("[missing field]").to_owned()
|
||||
}
|
||||
VariantData::Record(fields) => fields[f.local_id].name.as_str().to_owned(),
|
||||
VariantData::Tuple(fields) => fields
|
||||
.iter()
|
||||
.position(|it| it.0 == f.local_id)
|
||||
@ -621,8 +620,10 @@ impl InferenceContext<'_> {
|
||||
if let Some(deref_trait) =
|
||||
self.resolve_lang_item(LangItem::DerefMut).and_then(|it| it.as_trait())
|
||||
{
|
||||
if let Some(deref_fn) =
|
||||
self.db.trait_data(deref_trait).method_by_name(&name![deref_mut])
|
||||
if let Some(deref_fn) = self
|
||||
.db
|
||||
.trait_data(deref_trait)
|
||||
.method_by_name(&Name::new_symbol_root(sym::deref_mut.clone()))
|
||||
{
|
||||
break 'b deref_fn == f;
|
||||
}
|
||||
@ -888,7 +889,7 @@ impl InferenceContext<'_> {
|
||||
match &self.body[pat] {
|
||||
Pat::Missing | Pat::Wild => (),
|
||||
Pat::Tuple { args, ellipsis } => {
|
||||
let (al, ar) = args.split_at(ellipsis.unwrap_or(args.len()));
|
||||
let (al, ar) = args.split_at(ellipsis.map_or(args.len(), |it| it as usize));
|
||||
let field_count = match self.result[pat].kind(Interner) {
|
||||
TyKind::Tuple(_, s) => s.len(Interner),
|
||||
_ => return,
|
||||
@ -963,7 +964,7 @@ impl InferenceContext<'_> {
|
||||
}
|
||||
VariantId::StructId(s) => {
|
||||
let vd = &*self.db.struct_data(s).variant_data;
|
||||
let (al, ar) = args.split_at(ellipsis.unwrap_or(args.len()));
|
||||
let (al, ar) = args.split_at(ellipsis.map_or(args.len(), |it| it as usize));
|
||||
let fields = vd.fields().iter();
|
||||
let it =
|
||||
al.iter().zip(fields.clone()).chain(ar.iter().rev().zip(fields.rev()));
|
||||
|
@ -12,10 +12,11 @@ use hir_def::{
|
||||
ArithOp, Array, BinaryOp, ClosureKind, Expr, ExprId, LabelId, Literal, Statement, UnaryOp,
|
||||
},
|
||||
lang_item::{LangItem, LangItemTarget},
|
||||
path::{GenericArgs, Path},
|
||||
path::{GenericArg, GenericArgs, Path},
|
||||
BlockId, FieldId, GenericDefId, GenericParamId, ItemContainerId, Lookup, TupleFieldId, TupleId,
|
||||
};
|
||||
use hir_expand::name::{name, Name};
|
||||
use hir_expand::name::Name;
|
||||
use intern::sym;
|
||||
use stdx::always;
|
||||
use syntax::ast::RangeOp;
|
||||
|
||||
@ -646,8 +647,10 @@ impl InferenceContext<'_> {
|
||||
match op {
|
||||
UnaryOp::Deref => {
|
||||
if let Some(deref_trait) = self.resolve_lang_trait(LangItem::Deref) {
|
||||
if let Some(deref_fn) =
|
||||
self.db.trait_data(deref_trait).method_by_name(&name![deref])
|
||||
if let Some(deref_fn) = self
|
||||
.db
|
||||
.trait_data(deref_trait)
|
||||
.method_by_name(&Name::new_symbol_root(sym::deref.clone()))
|
||||
{
|
||||
// FIXME: this is wrong in multiple ways, subst is empty, and we emit it even for builtin deref (note that
|
||||
// the mutability is not wrong, and will be fixed in `self.infer_mut`).
|
||||
@ -785,8 +788,10 @@ impl InferenceContext<'_> {
|
||||
// mutability will be fixed up in `InferenceContext::infer_mut`;
|
||||
adj.push(Adjustment::borrow(Mutability::Not, self_ty.clone()));
|
||||
self.write_expr_adj(*base, adj);
|
||||
if let Some(func) =
|
||||
self.db.trait_data(index_trait).method_by_name(&name!(index))
|
||||
if let Some(func) = self
|
||||
.db
|
||||
.trait_data(index_trait)
|
||||
.method_by_name(&Name::new_symbol_root(sym::index.clone()))
|
||||
{
|
||||
let substs = TyBuilder::subst_for_def(self.db, index_trait, None)
|
||||
.push(self_ty.clone())
|
||||
@ -1165,7 +1170,7 @@ impl InferenceContext<'_> {
|
||||
Expr::Tuple { exprs, .. } => {
|
||||
// We don't consider multiple ellipses. This is analogous to
|
||||
// `hir_def::body::lower::ExprCollector::collect_tuple_pat()`.
|
||||
let ellipsis = exprs.iter().position(|e| is_rest_expr(*e));
|
||||
let ellipsis = exprs.iter().position(|e| is_rest_expr(*e)).map(|it| it as u32);
|
||||
let exprs: Vec<_> = exprs.iter().filter(|e| !is_rest_expr(**e)).copied().collect();
|
||||
|
||||
self.infer_tuple_pat_like(&rhs_ty, (), ellipsis, &exprs)
|
||||
@ -1179,7 +1184,7 @@ impl InferenceContext<'_> {
|
||||
|
||||
// We don't consider multiple ellipses. This is analogous to
|
||||
// `hir_def::body::lower::ExprCollector::collect_tuple_pat()`.
|
||||
let ellipsis = args.iter().position(|e| is_rest_expr(*e));
|
||||
let ellipsis = args.iter().position(|e| is_rest_expr(*e)).map(|it| it as u32);
|
||||
let args: Vec<_> = args.iter().filter(|e| !is_rest_expr(**e)).copied().collect();
|
||||
|
||||
self.infer_tuple_struct_pat_like(path, &rhs_ty, (), lhs, ellipsis, &args)
|
||||
@ -1846,29 +1851,45 @@ impl InferenceContext<'_> {
|
||||
if let Some(generic_args) = generic_args {
|
||||
// if args are provided, it should be all of them, but we can't rely on that
|
||||
let self_params = type_params + const_params + lifetime_params;
|
||||
for (arg, kind_id) in
|
||||
generic_args.args.iter().zip(def_generics.iter_self_id()).take(self_params)
|
||||
{
|
||||
let arg = generic_arg_to_chalk(
|
||||
self.db,
|
||||
kind_id,
|
||||
arg,
|
||||
self,
|
||||
|this, type_ref| this.make_ty(type_ref),
|
||||
|this, c, ty| {
|
||||
const_or_path_to_chalk(
|
||||
this.db,
|
||||
&this.resolver,
|
||||
this.owner.into(),
|
||||
ty,
|
||||
c,
|
||||
ParamLoweringMode::Placeholder,
|
||||
|| this.generics(),
|
||||
DebruijnIndex::INNERMOST,
|
||||
)
|
||||
},
|
||||
|this, lt_ref| this.make_lifetime(lt_ref),
|
||||
);
|
||||
|
||||
let mut args = generic_args.args.iter().peekable();
|
||||
for kind_id in def_generics.iter_self_id().take(self_params) {
|
||||
let arg = args.peek();
|
||||
let arg = match (kind_id, arg) {
|
||||
// Lifetimes can be elided.
|
||||
// Once we have implemented lifetime elision correctly,
|
||||
// this should be handled in a proper way.
|
||||
(
|
||||
GenericParamId::LifetimeParamId(_),
|
||||
None | Some(GenericArg::Type(_) | GenericArg::Const(_)),
|
||||
) => error_lifetime().cast(Interner),
|
||||
|
||||
// If we run out of `generic_args`, stop pushing substs
|
||||
(_, None) => break,
|
||||
|
||||
// Normal cases
|
||||
(_, Some(_)) => generic_arg_to_chalk(
|
||||
self.db,
|
||||
kind_id,
|
||||
args.next().unwrap(), // `peek()` is `Some(_)`, so guaranteed no panic
|
||||
self,
|
||||
|this, type_ref| this.make_ty(type_ref),
|
||||
|this, c, ty| {
|
||||
const_or_path_to_chalk(
|
||||
this.db,
|
||||
&this.resolver,
|
||||
this.owner.into(),
|
||||
ty,
|
||||
c,
|
||||
ParamLoweringMode::Placeholder,
|
||||
|| this.generics(),
|
||||
DebruijnIndex::INNERMOST,
|
||||
)
|
||||
},
|
||||
|this, lt_ref| this.make_lifetime(lt_ref),
|
||||
),
|
||||
};
|
||||
|
||||
substs.push(arg);
|
||||
}
|
||||
};
|
||||
@ -1945,25 +1966,25 @@ impl InferenceContext<'_> {
|
||||
};
|
||||
|
||||
let data = self.db.function_data(func);
|
||||
if data.legacy_const_generics_indices.is_empty() {
|
||||
let Some(legacy_const_generics_indices) = &data.legacy_const_generics_indices else {
|
||||
return Default::default();
|
||||
}
|
||||
};
|
||||
|
||||
// only use legacy const generics if the param count matches with them
|
||||
if data.params.len() + data.legacy_const_generics_indices.len() != args.len() {
|
||||
if data.params.len() + legacy_const_generics_indices.len() != args.len() {
|
||||
if args.len() <= data.params.len() {
|
||||
return Default::default();
|
||||
} else {
|
||||
// there are more parameters than there should be without legacy
|
||||
// const params; use them
|
||||
let mut indices = data.legacy_const_generics_indices.clone();
|
||||
let mut indices = legacy_const_generics_indices.as_ref().clone();
|
||||
indices.sort();
|
||||
return indices;
|
||||
}
|
||||
}
|
||||
|
||||
// check legacy const parameters
|
||||
for (subst_idx, arg_idx) in data.legacy_const_generics_indices.iter().copied().enumerate() {
|
||||
for (subst_idx, arg_idx) in legacy_const_generics_indices.iter().copied().enumerate() {
|
||||
let arg = match subst.at(Interner, subst_idx).constant(Interner) {
|
||||
Some(c) => c,
|
||||
None => continue, // not a const parameter?
|
||||
@ -1976,7 +1997,7 @@ impl InferenceContext<'_> {
|
||||
self.infer_expr(args[arg_idx as usize], &expected);
|
||||
// FIXME: evaluate and unify with the const
|
||||
}
|
||||
let mut indices = data.legacy_const_generics_indices.clone();
|
||||
let mut indices = legacy_const_generics_indices.as_ref().clone();
|
||||
indices.sort();
|
||||
indices
|
||||
}
|
||||
|
@ -1,14 +1,18 @@
|
||||
//! Finds if an expression is an immutable context or a mutable context, which is used in selecting
|
||||
//! between `Deref` and `DerefMut` or `Index` and `IndexMut` or similar.
|
||||
|
||||
use chalk_ir::Mutability;
|
||||
use chalk_ir::{cast::Cast, Mutability};
|
||||
use hir_def::{
|
||||
hir::{Array, BinaryOp, BindingAnnotation, Expr, ExprId, PatId, Statement, UnaryOp},
|
||||
lang_item::LangItem,
|
||||
};
|
||||
use hir_expand::name;
|
||||
use hir_expand::name::Name;
|
||||
use intern::sym;
|
||||
|
||||
use crate::{lower::lower_to_chalk_mutability, Adjust, Adjustment, AutoBorrow, OverloadedDeref};
|
||||
use crate::{
|
||||
infer::Expectation, lower::lower_to_chalk_mutability, Adjust, Adjustment, AutoBorrow, Interner,
|
||||
OverloadedDeref, TyBuilder, TyKind,
|
||||
};
|
||||
|
||||
use super::InferenceContext;
|
||||
|
||||
@ -100,7 +104,7 @@ impl InferenceContext<'_> {
|
||||
Expr::RecordLit { path: _, fields, spread, ellipsis: _, is_assignee_expr: _ } => {
|
||||
self.infer_mut_not_expr_iter(fields.iter().map(|it| it.expr).chain(*spread))
|
||||
}
|
||||
&Expr::Index { base, index, is_assignee_expr: _ } => {
|
||||
&Expr::Index { base, index, is_assignee_expr } => {
|
||||
if mutability == Mutability::Mut {
|
||||
if let Some((f, _)) = self.result.method_resolutions.get_mut(&tgt_expr) {
|
||||
if let Some(index_trait) = self
|
||||
@ -108,10 +112,13 @@ impl InferenceContext<'_> {
|
||||
.lang_item(self.table.trait_env.krate, LangItem::IndexMut)
|
||||
.and_then(|l| l.as_trait())
|
||||
{
|
||||
if let Some(index_fn) =
|
||||
self.db.trait_data(index_trait).method_by_name(&name![index_mut])
|
||||
if let Some(index_fn) = self
|
||||
.db
|
||||
.trait_data(index_trait)
|
||||
.method_by_name(&Name::new_symbol_root(sym::index_mut.clone()))
|
||||
{
|
||||
*f = index_fn;
|
||||
let mut base_ty = None;
|
||||
let base_adjustments = self
|
||||
.result
|
||||
.expr_adjustments
|
||||
@ -119,11 +126,32 @@ impl InferenceContext<'_> {
|
||||
.and_then(|it| it.last_mut());
|
||||
if let Some(Adjustment {
|
||||
kind: Adjust::Borrow(AutoBorrow::Ref(mutability)),
|
||||
..
|
||||
target,
|
||||
}) = base_adjustments
|
||||
{
|
||||
// For assignee exprs `IndexMut` obiligations are already applied
|
||||
if !is_assignee_expr {
|
||||
if let TyKind::Ref(_, _, ty) = target.kind(Interner) {
|
||||
base_ty = Some(ty.clone());
|
||||
}
|
||||
}
|
||||
*mutability = Mutability::Mut;
|
||||
}
|
||||
|
||||
// Apply `IndexMut` obligation for non-assignee expr
|
||||
if let Some(base_ty) = base_ty {
|
||||
let index_ty =
|
||||
if let Some(ty) = self.result.type_of_expr.get(index) {
|
||||
ty.clone()
|
||||
} else {
|
||||
self.infer_expr(index, &Expectation::none())
|
||||
};
|
||||
let trait_ref = TyBuilder::trait_ref(self.db, index_trait)
|
||||
.push(base_ty)
|
||||
.fill(|_| index_ty.clone().cast(Interner))
|
||||
.build();
|
||||
self.push_obligation(trait_ref.cast(Interner));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -139,8 +167,10 @@ impl InferenceContext<'_> {
|
||||
.lang_item(self.table.trait_env.krate, LangItem::DerefMut)
|
||||
.and_then(|l| l.as_trait())
|
||||
{
|
||||
if let Some(deref_fn) =
|
||||
self.db.trait_data(deref_trait).method_by_name(&name![deref_mut])
|
||||
if let Some(deref_fn) = self
|
||||
.db
|
||||
.trait_data(deref_trait)
|
||||
.method_by_name(&Name::new_symbol_root(sym::deref_mut.clone()))
|
||||
{
|
||||
*f = deref_fn;
|
||||
}
|
||||
|
@ -68,7 +68,7 @@ impl InferenceContext<'_> {
|
||||
expected: &Ty,
|
||||
default_bm: T::BindingMode,
|
||||
id: T,
|
||||
ellipsis: Option<usize>,
|
||||
ellipsis: Option<u32>,
|
||||
subs: &[T],
|
||||
) -> Ty {
|
||||
let (ty, def) = self.resolve_variant(path, true);
|
||||
@ -98,7 +98,7 @@ impl InferenceContext<'_> {
|
||||
let visibilities = self.db.field_visibilities(def);
|
||||
|
||||
let (pre, post) = match ellipsis {
|
||||
Some(idx) => subs.split_at(idx),
|
||||
Some(idx) => subs.split_at(idx as usize),
|
||||
None => (subs, &[][..]),
|
||||
};
|
||||
let post_idx_offset = field_types.iter().count().saturating_sub(post.len());
|
||||
@ -219,7 +219,7 @@ impl InferenceContext<'_> {
|
||||
&mut self,
|
||||
expected: &Ty,
|
||||
default_bm: T::BindingMode,
|
||||
ellipsis: Option<usize>,
|
||||
ellipsis: Option<u32>,
|
||||
subs: &[T],
|
||||
) -> Ty {
|
||||
let expected = self.resolve_ty_shallow(expected);
|
||||
@ -229,7 +229,9 @@ impl InferenceContext<'_> {
|
||||
};
|
||||
|
||||
let ((pre, post), n_uncovered_patterns) = match ellipsis {
|
||||
Some(idx) => (subs.split_at(idx), expectations.len().saturating_sub(subs.len())),
|
||||
Some(idx) => {
|
||||
(subs.split_at(idx as usize), expectations.len().saturating_sub(subs.len()))
|
||||
}
|
||||
None => ((subs, &[][..]), 0),
|
||||
};
|
||||
let mut expectations_iter = expectations
|
||||
|
@ -7,6 +7,7 @@ use hir_def::{
|
||||
AdtId, AssocItemId, GenericDefId, ItemContainerId, Lookup,
|
||||
};
|
||||
use hir_expand::name::Name;
|
||||
use intern::sym;
|
||||
use stdx::never;
|
||||
|
||||
use crate::{
|
||||
@ -227,7 +228,7 @@ impl InferenceContext<'_> {
|
||||
Path::LangItem(..) => (
|
||||
PathSegment {
|
||||
name: {
|
||||
_d = hir_expand::name::known::Unknown;
|
||||
_d = Name::new_symbol_root(sym::Unknown.clone());
|
||||
&_d
|
||||
},
|
||||
args_and_bindings: None,
|
||||
|
@ -9,7 +9,8 @@ use chalk_ir::{
|
||||
use chalk_solve::infer::ParameterEnaVariableExt;
|
||||
use either::Either;
|
||||
use ena::unify::UnifyKey;
|
||||
use hir_expand::name;
|
||||
use hir_expand::name::Name;
|
||||
use intern::sym;
|
||||
use rustc_hash::FxHashMap;
|
||||
use smallvec::SmallVec;
|
||||
use triomphe::Arc;
|
||||
@ -781,7 +782,8 @@ impl<'a> InferenceTable<'a> {
|
||||
let krate = self.trait_env.krate;
|
||||
let fn_once_trait = FnTrait::FnOnce.get_id(self.db, krate)?;
|
||||
let trait_data = self.db.trait_data(fn_once_trait);
|
||||
let output_assoc_type = trait_data.associated_type_by_name(&name![Output])?;
|
||||
let output_assoc_type =
|
||||
trait_data.associated_type_by_name(&Name::new_symbol_root(sym::Output.clone()))?;
|
||||
|
||||
let mut arg_tys = Vec::with_capacity(num_args);
|
||||
let arg_ty = TyBuilder::tuple(num_args)
|
||||
|
@ -6,6 +6,7 @@ use chalk_ir::{
|
||||
DebruijnIndex,
|
||||
};
|
||||
use hir_def::{visibility::Visibility, AdtId, EnumVariantId, HasModule, ModuleId, VariantId};
|
||||
use intern::sym;
|
||||
use rustc_hash::FxHashSet;
|
||||
|
||||
use crate::{
|
||||
@ -118,7 +119,7 @@ impl UninhabitedFrom<'_> {
|
||||
subst: &Substitution,
|
||||
) -> ControlFlow<VisiblyUninhabited> {
|
||||
let is_local = variant.krate(self.db.upcast()) == self.target_mod.krate();
|
||||
if !is_local && self.db.attrs(variant.into()).by_key("non_exhaustive").exists() {
|
||||
if !is_local && self.db.attrs(variant.into()).by_key(&sym::non_exhaustive).exists() {
|
||||
return CONTINUE_OPAQUELY_INHABITED;
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
use hir_def::{data::adt::StructFlags, lang_item::LangItem, AdtId};
|
||||
use hir_expand::name::Name;
|
||||
use intern::sym;
|
||||
|
||||
use crate::db::HirDatabase;
|
||||
|
||||
@ -16,48 +17,57 @@ pub fn is_unsafe_cell(db: &dyn HirDatabase, adt: AdtId) -> bool {
|
||||
}
|
||||
|
||||
pub fn lang_items_for_bin_op(op: syntax::ast::BinaryOp) -> Option<(Name, LangItem)> {
|
||||
use hir_expand::name;
|
||||
use syntax::ast::{ArithOp, BinaryOp, CmpOp, Ordering};
|
||||
Some(match op {
|
||||
BinaryOp::LogicOp(_) => return None,
|
||||
BinaryOp::ArithOp(aop) => match aop {
|
||||
ArithOp::Add => (name![add], LangItem::Add),
|
||||
ArithOp::Mul => (name![mul], LangItem::Mul),
|
||||
ArithOp::Sub => (name![sub], LangItem::Sub),
|
||||
ArithOp::Div => (name![div], LangItem::Div),
|
||||
ArithOp::Rem => (name![rem], LangItem::Rem),
|
||||
ArithOp::Shl => (name![shl], LangItem::Shl),
|
||||
ArithOp::Shr => (name![shr], LangItem::Shr),
|
||||
ArithOp::BitXor => (name![bitxor], LangItem::BitXor),
|
||||
ArithOp::BitOr => (name![bitor], LangItem::BitOr),
|
||||
ArithOp::BitAnd => (name![bitand], LangItem::BitAnd),
|
||||
ArithOp::Add => (Name::new_symbol_root(sym::add.clone()), LangItem::Add),
|
||||
ArithOp::Mul => (Name::new_symbol_root(sym::mul.clone()), LangItem::Mul),
|
||||
ArithOp::Sub => (Name::new_symbol_root(sym::sub.clone()), LangItem::Sub),
|
||||
ArithOp::Div => (Name::new_symbol_root(sym::div.clone()), LangItem::Div),
|
||||
ArithOp::Rem => (Name::new_symbol_root(sym::rem.clone()), LangItem::Rem),
|
||||
ArithOp::Shl => (Name::new_symbol_root(sym::shl.clone()), LangItem::Shl),
|
||||
ArithOp::Shr => (Name::new_symbol_root(sym::shr.clone()), LangItem::Shr),
|
||||
ArithOp::BitXor => (Name::new_symbol_root(sym::bitxor.clone()), LangItem::BitXor),
|
||||
ArithOp::BitOr => (Name::new_symbol_root(sym::bitor.clone()), LangItem::BitOr),
|
||||
ArithOp::BitAnd => (Name::new_symbol_root(sym::bitand.clone()), LangItem::BitAnd),
|
||||
},
|
||||
BinaryOp::Assignment { op: Some(aop) } => match aop {
|
||||
ArithOp::Add => (name![add_assign], LangItem::AddAssign),
|
||||
ArithOp::Mul => (name![mul_assign], LangItem::MulAssign),
|
||||
ArithOp::Sub => (name![sub_assign], LangItem::SubAssign),
|
||||
ArithOp::Div => (name![div_assign], LangItem::DivAssign),
|
||||
ArithOp::Rem => (name![rem_assign], LangItem::RemAssign),
|
||||
ArithOp::Shl => (name![shl_assign], LangItem::ShlAssign),
|
||||
ArithOp::Shr => (name![shr_assign], LangItem::ShrAssign),
|
||||
ArithOp::BitXor => (name![bitxor_assign], LangItem::BitXorAssign),
|
||||
ArithOp::BitOr => (name![bitor_assign], LangItem::BitOrAssign),
|
||||
ArithOp::BitAnd => (name![bitand_assign], LangItem::BitAndAssign),
|
||||
ArithOp::Add => (Name::new_symbol_root(sym::add_assign.clone()), LangItem::AddAssign),
|
||||
ArithOp::Mul => (Name::new_symbol_root(sym::mul_assign.clone()), LangItem::MulAssign),
|
||||
ArithOp::Sub => (Name::new_symbol_root(sym::sub_assign.clone()), LangItem::SubAssign),
|
||||
ArithOp::Div => (Name::new_symbol_root(sym::div_assign.clone()), LangItem::DivAssign),
|
||||
ArithOp::Rem => (Name::new_symbol_root(sym::rem_assign.clone()), LangItem::RemAssign),
|
||||
ArithOp::Shl => (Name::new_symbol_root(sym::shl_assign.clone()), LangItem::ShlAssign),
|
||||
ArithOp::Shr => (Name::new_symbol_root(sym::shr_assign.clone()), LangItem::ShrAssign),
|
||||
ArithOp::BitXor => {
|
||||
(Name::new_symbol_root(sym::bitxor_assign.clone()), LangItem::BitXorAssign)
|
||||
}
|
||||
ArithOp::BitOr => {
|
||||
(Name::new_symbol_root(sym::bitor_assign.clone()), LangItem::BitOrAssign)
|
||||
}
|
||||
ArithOp::BitAnd => {
|
||||
(Name::new_symbol_root(sym::bitand_assign.clone()), LangItem::BitAndAssign)
|
||||
}
|
||||
},
|
||||
BinaryOp::CmpOp(cop) => match cop {
|
||||
CmpOp::Eq { negated: false } => (name![eq], LangItem::PartialEq),
|
||||
CmpOp::Eq { negated: true } => (name![ne], LangItem::PartialEq),
|
||||
CmpOp::Eq { negated: false } => {
|
||||
(Name::new_symbol_root(sym::eq.clone()), LangItem::PartialEq)
|
||||
}
|
||||
CmpOp::Eq { negated: true } => {
|
||||
(Name::new_symbol_root(sym::ne.clone()), LangItem::PartialEq)
|
||||
}
|
||||
CmpOp::Ord { ordering: Ordering::Less, strict: false } => {
|
||||
(name![le], LangItem::PartialOrd)
|
||||
(Name::new_symbol_root(sym::le.clone()), LangItem::PartialOrd)
|
||||
}
|
||||
CmpOp::Ord { ordering: Ordering::Less, strict: true } => {
|
||||
(name![lt], LangItem::PartialOrd)
|
||||
(Name::new_symbol_root(sym::lt.clone()), LangItem::PartialOrd)
|
||||
}
|
||||
CmpOp::Ord { ordering: Ordering::Greater, strict: false } => {
|
||||
(name![ge], LangItem::PartialOrd)
|
||||
(Name::new_symbol_root(sym::ge.clone()), LangItem::PartialOrd)
|
||||
}
|
||||
CmpOp::Ord { ordering: Ordering::Greater, strict: true } => {
|
||||
(name![gt], LangItem::PartialOrd)
|
||||
(Name::new_symbol_root(sym::gt.clone()), LangItem::PartialOrd)
|
||||
}
|
||||
},
|
||||
BinaryOp::Assignment { op: None } => return None,
|
||||
|
@ -8,6 +8,7 @@ use hir_def::{
|
||||
layout::{Integer, LayoutCalculator, ReprOptions, TargetDataLayout},
|
||||
AdtId, VariantId,
|
||||
};
|
||||
use intern::sym;
|
||||
use rustc_index::IndexVec;
|
||||
use smallvec::SmallVec;
|
||||
use triomphe::Arc;
|
||||
@ -129,7 +130,10 @@ fn layout_scalar_valid_range(db: &dyn HirDatabase, def: AdtId) -> (Bound<u128>,
|
||||
}
|
||||
Bound::Unbounded
|
||||
};
|
||||
(get("rustc_layout_scalar_valid_range_start"), get("rustc_layout_scalar_valid_range_end"))
|
||||
(
|
||||
get(&sym::rustc_layout_scalar_valid_range_start),
|
||||
get(&sym::rustc_layout_scalar_valid_range_end),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn layout_of_adt_recover(
|
||||
|
@ -3,6 +3,7 @@ use either::Either;
|
||||
use hir_def::db::DefDatabase;
|
||||
use project_model::{target_data_layout::RustcDataLayoutConfig, Sysroot};
|
||||
use rustc_hash::FxHashMap;
|
||||
use syntax::ToSmolStr;
|
||||
use test_fixture::WithFixture;
|
||||
use triomphe::Arc;
|
||||
|
||||
@ -34,20 +35,26 @@ fn eval_goal(ra_fixture: &str, minicore: &str) -> Result<Arc<Layout>, LayoutErro
|
||||
let adt_or_type_alias_id = file_ids
|
||||
.into_iter()
|
||||
.find_map(|file_id| {
|
||||
let module_id = db.module_for_file(file_id);
|
||||
let module_id = db.module_for_file(file_id.file_id());
|
||||
let def_map = module_id.def_map(&db);
|
||||
let scope = &def_map[module_id.local_id].scope;
|
||||
let adt_or_type_alias_id = scope.declarations().find_map(|x| match x {
|
||||
hir_def::ModuleDefId::AdtId(x) => {
|
||||
let name = match x {
|
||||
hir_def::AdtId::StructId(x) => db.struct_data(x).name.to_smol_str(),
|
||||
hir_def::AdtId::UnionId(x) => db.union_data(x).name.to_smol_str(),
|
||||
hir_def::AdtId::EnumId(x) => db.enum_data(x).name.to_smol_str(),
|
||||
hir_def::AdtId::StructId(x) => {
|
||||
db.struct_data(x).name.display_no_db().to_smolstr()
|
||||
}
|
||||
hir_def::AdtId::UnionId(x) => {
|
||||
db.union_data(x).name.display_no_db().to_smolstr()
|
||||
}
|
||||
hir_def::AdtId::EnumId(x) => {
|
||||
db.enum_data(x).name.display_no_db().to_smolstr()
|
||||
}
|
||||
};
|
||||
(name == "Goal").then_some(Either::Left(x))
|
||||
}
|
||||
hir_def::ModuleDefId::TypeAliasId(x) => {
|
||||
let name = db.type_alias_data(x).name.to_smol_str();
|
||||
let name = db.type_alias_data(x).name.display_no_db().to_smolstr();
|
||||
(name == "Goal").then_some(Either::Right(x))
|
||||
}
|
||||
_ => None,
|
||||
@ -80,21 +87,26 @@ fn eval_expr(ra_fixture: &str, minicore: &str) -> Result<Arc<Layout>, LayoutErro
|
||||
);
|
||||
|
||||
let (db, file_id) = TestDB::with_single_file(&ra_fixture);
|
||||
let module_id = db.module_for_file(file_id);
|
||||
let module_id = db.module_for_file(file_id.file_id());
|
||||
let def_map = module_id.def_map(&db);
|
||||
let scope = &def_map[module_id.local_id].scope;
|
||||
let function_id = scope
|
||||
.declarations()
|
||||
.find_map(|x| match x {
|
||||
hir_def::ModuleDefId::FunctionId(x) => {
|
||||
let name = db.function_data(x).name.to_smol_str();
|
||||
let name = db.function_data(x).name.display_no_db().to_smolstr();
|
||||
(name == "main").then_some(x)
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
.unwrap();
|
||||
let hir_body = db.body(function_id.into());
|
||||
let b = hir_body.bindings.iter().find(|x| x.1.name.to_smol_str() == "goal").unwrap().0;
|
||||
let b = hir_body
|
||||
.bindings
|
||||
.iter()
|
||||
.find(|x| x.1.name.display_no_db().to_smolstr() == "goal")
|
||||
.unwrap()
|
||||
.0;
|
||||
let infer = db.infer(function_id.into());
|
||||
let goal_ty = infer.type_of_binding[b].clone();
|
||||
db.layout_of_ty(goal_ty, db.trait_environment(function_id.into()))
|
||||
|
@ -61,7 +61,8 @@ use chalk_ir::{
|
||||
};
|
||||
use either::Either;
|
||||
use hir_def::{hir::ExprId, type_ref::Rawness, CallableDefId, GeneralConstId, TypeOrConstParamId};
|
||||
use hir_expand::name;
|
||||
use hir_expand::name::Name;
|
||||
use intern::{sym, Symbol};
|
||||
use la_arena::{Arena, Idx};
|
||||
use mir::{MirEvalError, VTableMap};
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
@ -422,45 +423,45 @@ impl Hash for FnAbi {
|
||||
}
|
||||
|
||||
impl FnAbi {
|
||||
#[allow(clippy::should_implement_trait)]
|
||||
pub fn from_str(s: &str) -> FnAbi {
|
||||
#[rustfmt::skip]
|
||||
pub fn from_symbol(s: &Symbol) -> FnAbi {
|
||||
match s {
|
||||
"aapcs-unwind" => FnAbi::AapcsUnwind,
|
||||
"aapcs" => FnAbi::Aapcs,
|
||||
"avr-interrupt" => FnAbi::AvrInterrupt,
|
||||
"avr-non-blocking-interrupt" => FnAbi::AvrNonBlockingInterrupt,
|
||||
"C-cmse-nonsecure-call" => FnAbi::CCmseNonsecureCall,
|
||||
"C-unwind" => FnAbi::CUnwind,
|
||||
"C" => FnAbi::C,
|
||||
"cdecl-unwind" => FnAbi::CDeclUnwind,
|
||||
"cdecl" => FnAbi::CDecl,
|
||||
"efiapi" => FnAbi::Efiapi,
|
||||
"fastcall-unwind" => FnAbi::FastcallUnwind,
|
||||
"fastcall" => FnAbi::Fastcall,
|
||||
"msp430-interrupt" => FnAbi::Msp430Interrupt,
|
||||
"platform-intrinsic" => FnAbi::PlatformIntrinsic,
|
||||
"ptx-kernel" => FnAbi::PtxKernel,
|
||||
"riscv-interrupt-m" => FnAbi::RiscvInterruptM,
|
||||
"riscv-interrupt-s" => FnAbi::RiscvInterruptS,
|
||||
"rust-call" => FnAbi::RustCall,
|
||||
"rust-cold" => FnAbi::RustCold,
|
||||
"rust-intrinsic" => FnAbi::RustIntrinsic,
|
||||
"Rust" => FnAbi::Rust,
|
||||
"stdcall-unwind" => FnAbi::StdcallUnwind,
|
||||
"stdcall" => FnAbi::Stdcall,
|
||||
"system-unwind" => FnAbi::SystemUnwind,
|
||||
"system" => FnAbi::System,
|
||||
"sysv64-unwind" => FnAbi::Sysv64Unwind,
|
||||
"sysv64" => FnAbi::Sysv64,
|
||||
"thiscall-unwind" => FnAbi::ThiscallUnwind,
|
||||
"thiscall" => FnAbi::Thiscall,
|
||||
"unadjusted" => FnAbi::Unadjusted,
|
||||
"vectorcall-unwind" => FnAbi::VectorcallUnwind,
|
||||
"vectorcall" => FnAbi::Vectorcall,
|
||||
"wasm" => FnAbi::Wasm,
|
||||
"win64-unwind" => FnAbi::Win64Unwind,
|
||||
"win64" => FnAbi::Win64,
|
||||
"x86-interrupt" => FnAbi::X86Interrupt,
|
||||
s if *s == sym::aapcs_dash_unwind => FnAbi::AapcsUnwind,
|
||||
s if *s == sym::aapcs => FnAbi::Aapcs,
|
||||
s if *s == sym::avr_dash_interrupt => FnAbi::AvrInterrupt,
|
||||
s if *s == sym::avr_dash_non_dash_blocking_dash_interrupt => FnAbi::AvrNonBlockingInterrupt,
|
||||
s if *s == sym::C_dash_cmse_dash_nonsecure_dash_call => FnAbi::CCmseNonsecureCall,
|
||||
s if *s == sym::C_dash_unwind => FnAbi::CUnwind,
|
||||
s if *s == sym::C => FnAbi::C,
|
||||
s if *s == sym::cdecl_dash_unwind => FnAbi::CDeclUnwind,
|
||||
s if *s == sym::cdecl => FnAbi::CDecl,
|
||||
s if *s == sym::efiapi => FnAbi::Efiapi,
|
||||
s if *s == sym::fastcall_dash_unwind => FnAbi::FastcallUnwind,
|
||||
s if *s == sym::fastcall => FnAbi::Fastcall,
|
||||
s if *s == sym::msp430_dash_interrupt => FnAbi::Msp430Interrupt,
|
||||
s if *s == sym::platform_dash_intrinsic => FnAbi::PlatformIntrinsic,
|
||||
s if *s == sym::ptx_dash_kernel => FnAbi::PtxKernel,
|
||||
s if *s == sym::riscv_dash_interrupt_dash_m => FnAbi::RiscvInterruptM,
|
||||
s if *s == sym::riscv_dash_interrupt_dash_s => FnAbi::RiscvInterruptS,
|
||||
s if *s == sym::rust_dash_call => FnAbi::RustCall,
|
||||
s if *s == sym::rust_dash_cold => FnAbi::RustCold,
|
||||
s if *s == sym::rust_dash_intrinsic => FnAbi::RustIntrinsic,
|
||||
s if *s == sym::Rust => FnAbi::Rust,
|
||||
s if *s == sym::stdcall_dash_unwind => FnAbi::StdcallUnwind,
|
||||
s if *s == sym::stdcall => FnAbi::Stdcall,
|
||||
s if *s == sym::system_dash_unwind => FnAbi::SystemUnwind,
|
||||
s if *s == sym::system => FnAbi::System,
|
||||
s if *s == sym::sysv64_dash_unwind => FnAbi::Sysv64Unwind,
|
||||
s if *s == sym::sysv64 => FnAbi::Sysv64,
|
||||
s if *s == sym::thiscall_dash_unwind => FnAbi::ThiscallUnwind,
|
||||
s if *s == sym::thiscall => FnAbi::Thiscall,
|
||||
s if *s == sym::unadjusted => FnAbi::Unadjusted,
|
||||
s if *s == sym::vectorcall_dash_unwind => FnAbi::VectorcallUnwind,
|
||||
s if *s == sym::vectorcall => FnAbi::Vectorcall,
|
||||
s if *s == sym::wasm => FnAbi::Wasm,
|
||||
s if *s == sym::win64_dash_unwind => FnAbi::Win64Unwind,
|
||||
s if *s == sym::win64 => FnAbi::Win64,
|
||||
s if *s == sym::x86_dash_interrupt => FnAbi::X86Interrupt,
|
||||
_ => FnAbi::Unknown,
|
||||
}
|
||||
}
|
||||
@ -894,7 +895,9 @@ pub fn callable_sig_from_fn_trait(
|
||||
) -> Option<(FnTrait, CallableSig)> {
|
||||
let krate = trait_env.krate;
|
||||
let fn_once_trait = FnTrait::FnOnce.get_id(db, krate)?;
|
||||
let output_assoc_type = db.trait_data(fn_once_trait).associated_type_by_name(&name![Output])?;
|
||||
let output_assoc_type = db
|
||||
.trait_data(fn_once_trait)
|
||||
.associated_type_by_name(&Name::new_symbol_root(sym::Output.clone()))?;
|
||||
|
||||
let mut table = InferenceTable::new(db, trait_env.clone());
|
||||
let b = TyBuilder::trait_ref(db, fn_once_trait);
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user