Auto merge of #128490 - lnicola:sync-from-ra, r=lnicola

Subtree update of `rust-analyzer`

r? `@ghost`
This commit is contained in:
bors 2024-08-01 13:13:38 +00:00
commit e60ebb2f2c
955 changed files with 15292 additions and 7940 deletions

View File

@ -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:

View File

@ -14,6 +14,8 @@ extend-ignore-re = [
"\\w*\\.{3,4}\\w*",
'"flate2"',
"raison d'être",
"inout",
"optin"
]
[default.extend-words]

View File

@ -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"

View File

@ -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",
]

View File

@ -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

View File

@ -27,6 +27,7 @@ stdx.workspace = true
syntax.workspace = true
vfs.workspace = true
span.workspace = true
intern.workspace = true
[lints]
workspace = true

View File

@ -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 {

View File

@ -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.

View File

@ -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"

View File

@ -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)?),
})
}
}

View File

@ -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,

View File

@ -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,
})
}

View File

@ -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);
}
}

View File

@ -24,6 +24,7 @@ process-wrap.workspace = true
paths.workspace = true
stdx.workspace = true
toolchain.workspace = true
project-model.workspace = true
[lints]
workspace = true

View File

@ -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(

View 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 { .. }));
}

View File

@ -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");

View File

@ -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()),
);
}
}

View File

@ -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();
}
}

View File

@ -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);
}

View File

@ -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);

View File

@ -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());

View File

@ -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]

View File

@ -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(|&param| 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);

View File

@ -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(

View File

@ -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
}
_ => {}

View File

@ -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>()?;

View File

@ -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(&current.stability)
.then_with(|| other.prefer_due_to_prelude.cmp(&current.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
"#]],
);
}
}

View File

@ -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),

View File

@ -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() {

View File

@ -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))
}
}
}

View File

@ -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));

View File

@ -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,

View File

@ -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,
}

View File

@ -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(&param);
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(), &param, 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(), &param, 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(),
}
}

View File

@ -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, ",");
}
});

View File

@ -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

View File

@ -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);

View File

@ -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(

View File

@ -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"; }
"#]],
);
}

View File

@ -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() {
;
}
"#]],
);
}

View File

@ -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"";
}
"#]],

View File

@ -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 {

View File

@ -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(),
}
}

View File

@ -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;
}
}

View File

@ -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,

View File

@ -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

View File

@ -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,
));
}
}
}

View File

@ -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,

View File

@ -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

View File

@ -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
"#]],
);
}

View File

@ -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(|| {

View File

@ -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(

View File

@ -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()),
}
}
}

View File

@ -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(),

View File

@ -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(")?;

View File

@ -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) {

View File

@ -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)
}
}

View File

@ -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() {

View File

@ -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()
}
}

View File

@ -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

View 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,
},
};

View File

@ -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,
}
}

View File

@ -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);

View File

@ -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()
}
}

View File

@ -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]

View File

@ -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]

View File

@ -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 {

View File

@ -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(())

View File

@ -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(),

View File

@ -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;
}

View File

@ -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()
}

View File

@ -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(),

View File

@ -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),
// ),

View File

@ -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);
}
}

View File

@ -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()),)*
])
});
}

View File

@ -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;

View File

@ -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()),
),
),
},
}

View File

@ -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());

View File

@ -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);

View File

@ -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 Symbols value as variable is void: AsyncBlock<T>:
//

View File

@ -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

View File

@ -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)]

View File

@ -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;
};

View File

@ -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,
})
}

View File

@ -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),

View File

@ -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(

View File

@ -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}")?
}
}

View File

@ -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> {

View File

@ -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()));

View File

@ -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
}

View File

@ -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;
}

View File

@ -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

View File

@ -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,

View File

@ -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)

View File

@ -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;
}

View File

@ -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,

View File

@ -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(

View File

@ -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()))

View File

@ -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