Merge from rustc

This commit is contained in:
Ralf Jung 2024-08-14 07:43:52 +02:00
commit 2016e149f5
213 changed files with 3973 additions and 2614 deletions

View file

@ -30,7 +30,6 @@ jobs:
run: |
git config --global user.email "runner@gha.local"
git config --global user.name "GitHub Action"
# Remove r-a crates from the workspaces so we don't auto-publish them as well
sed -i 's/ "crates\/\*"//' ./Cargo.toml
sed -i 's/ "xtask\/"//' ./Cargo.toml
# Only publish the crates under lib/
sed -i 's|^members = .*$|members = ["lib/*"]|' Cargo.toml
cargo workspaces publish --yes --exact --from-git --no-git-commit --allow-dirty

370
Cargo.lock generated
View file

@ -52,16 +52,16 @@ checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
[[package]]
name = "backtrace"
version = "0.3.72"
version = "0.3.73"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17c6a35df3749d2e8bb1b7b21a976d82b15548788d2735b9d82f329268f71a11"
checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a"
dependencies = [
"addr2line",
"cc",
"cfg-if",
"libc",
"miniz_oxide",
"object 0.35.0",
"object 0.36.3",
"rustc-demangle",
]
@ -92,9 +92,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
version = "2.5.0"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
[[package]]
name = "byteorder"
@ -136,9 +136,9 @@ dependencies = [
[[package]]
name = "cc"
version = "1.0.98"
version = "1.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41c270e7540d725e65ac7f1b212ac8ce349719624d7bcff99f8e2e488e8cf03f"
checksum = "e9e8aabfac534be767c909e0690571677d49f41bd8465ae876fe043d52ba5292"
[[package]]
name = "cfg"
@ -148,10 +148,10 @@ dependencies = [
"derive_arbitrary",
"expect-test",
"intern",
"mbe",
"oorandom",
"rustc-hash",
"syntax",
"syntax-bridge",
"tt",
]
@ -185,7 +185,7 @@ version = "0.98.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d5f2eb1cd6054da221bd1ac0197fb2fe5e2caf3dcb93619398fc1433f8f09093"
dependencies = [
"bitflags 2.5.0",
"bitflags 2.6.0",
"chalk-derive",
]
@ -226,9 +226,9 @@ checksum = "7704b5fdd17b18ae31c4c1da5a2e0305a2bf17b5249300a9ee9ed7b72114c636"
[[package]]
name = "cov-mark"
version = "2.0.0-pre.1"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d48d8f76bd9331f19fe2aaf3821a9f9fb32c3963e1e3d6ce82a8c09cef7444a"
checksum = "0570650661aa447e7335f1d5e4f499d8e58796e617bedc9267d971e51c8b49d4"
[[package]]
name = "crc32fast"
@ -366,9 +366,9 @@ checksum = "9bda8e21c04aca2ae33ffc2fd8c23134f3cac46db123ba97bd9d3f3b8a4a85e1"
[[package]]
name = "either"
version = "1.12.0"
version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b"
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
[[package]]
name = "ena"
@ -397,14 +397,14 @@ dependencies = [
[[package]]
name = "filetime"
version = "0.2.23"
version = "0.2.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd"
checksum = "bf401df4a4e3872c4fe8151134cf483738e74b67fc934d6532c882b3d24a4550"
dependencies = [
"cfg-if",
"libc",
"redox_syscall 0.4.1",
"windows-sys 0.52.0",
"libredox",
"windows-sys 0.59.0",
]
[[package]]
@ -415,31 +415,14 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
[[package]]
name = "flate2"
version = "1.0.30"
version = "1.0.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae"
checksum = "7f211bbe8e69bbd0cfdea405084f128ae8b4aaa6b0b522fc8f2b009084797920"
dependencies = [
"crc32fast",
"miniz_oxide",
]
[[package]]
name = "flycheck"
version = "0.0.0"
dependencies = [
"cargo_metadata",
"crossbeam-channel",
"paths",
"process-wrap",
"project-model",
"rustc-hash",
"serde",
"serde_json",
"stdx",
"toolchain",
"tracing",
]
[[package]]
name = "form_urlencoded"
version = "1.2.1"
@ -529,7 +512,7 @@ version = "0.0.0"
dependencies = [
"arrayvec",
"base-db",
"bitflags 2.5.0",
"bitflags 2.6.0",
"cfg",
"cov-mark",
"dashmap",
@ -554,6 +537,7 @@ dependencies = [
"span",
"stdx",
"syntax",
"syntax-bridge",
"test-fixture",
"test-utils",
"tracing",
@ -582,6 +566,7 @@ dependencies = [
"span",
"stdx",
"syntax",
"syntax-bridge",
"tracing",
"triomphe",
"tt",
@ -593,7 +578,7 @@ version = "0.0.0"
dependencies = [
"arrayvec",
"base-db",
"bitflags 2.5.0",
"bitflags 2.6.0",
"chalk-derive",
"chalk-ir",
"chalk-recursive",
@ -722,7 +707,7 @@ version = "0.0.0"
dependencies = [
"arrayvec",
"base-db",
"bitflags 2.5.0",
"bitflags 2.6.0",
"cov-mark",
"crossbeam-channel",
"either",
@ -803,9 +788,9 @@ dependencies = [
[[package]]
name = "indexmap"
version = "2.2.6"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
checksum = "de3fc2e30ba82dd1b3911c8de1ffc143c74a914a14e99514d7637e3099df5ea0"
dependencies = [
"equivalent",
"hashbrown",
@ -895,9 +880,9 @@ checksum = "3752f229dcc5a481d60f385fa479ff46818033d881d2d801aa27dffcfb5e8306"
[[package]]
name = "lazy_static"
version = "1.4.0"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]]
name = "libc"
@ -907,19 +892,19 @@ checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
[[package]]
name = "libloading"
version = "0.8.3"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19"
checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4"
dependencies = [
"cfg-if",
"windows-targets 0.52.5",
"windows-targets 0.52.6",
]
[[package]]
name = "libmimalloc-sys"
version = "0.1.38"
version = "0.1.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e7bb23d733dfcc8af652a78b7bf232f0e967710d044732185e561e47c0336b6"
checksum = "23aa6811d3bd4deb8a84dde645f943476d13b248d818edcf8ce0b2f37f036b44"
dependencies = [
"cc",
"libc",
@ -931,8 +916,9 @@ version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
dependencies = [
"bitflags 2.5.0",
"bitflags 2.6.0",
"libc",
"redox_syscall",
]
[[package]]
@ -996,9 +982,9 @@ dependencies = [
[[package]]
name = "log"
version = "0.4.21"
version = "0.4.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
[[package]]
name = "lsp-server"
@ -1056,6 +1042,7 @@ dependencies = [
"span",
"stdx",
"syntax",
"syntax-bridge",
"test-utils",
"tracing",
"tt",
@ -1063,9 +1050,9 @@ dependencies = [
[[package]]
name = "memchr"
version = "2.7.2"
version = "2.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d"
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
name = "memmap2"
@ -1087,18 +1074,18 @@ dependencies = [
[[package]]
name = "mimalloc"
version = "0.1.42"
version = "0.1.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e9186d86b79b52f4a77af65604b51225e8db1d6ee7e3f41aec1e40829c71a176"
checksum = "68914350ae34959d83f732418d51e2427a794055d0b9529f48259ac07af65633"
dependencies = [
"libmimalloc-sys",
]
[[package]]
name = "miniz_oxide"
version = "0.7.3"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87dfd01fe195c66b572b37921ad8803d010623c0aca821bea2302239d155cdae"
checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08"
dependencies = [
"adler",
]
@ -1130,7 +1117,7 @@ version = "0.28.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4"
dependencies = [
"bitflags 2.5.0",
"bitflags 2.6.0",
"cfg-if",
"cfg_aliases",
"libc",
@ -1148,7 +1135,7 @@ version = "6.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d"
dependencies = [
"bitflags 2.5.0",
"bitflags 2.6.0",
"crossbeam-channel",
"filetime",
"fsevent-sys",
@ -1163,11 +1150,11 @@ dependencies = [
[[package]]
name = "nu-ansi-term"
version = "0.50.0"
version = "0.50.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd2800e1520bdc966782168a627aa5d1ad92e33b984bf7c7615d31280c83ff14"
checksum = "d4a28e057d01f97e61255210fcff094d74ed0466038633e95017f5beb68e4399"
dependencies = [
"windows-sys 0.48.0",
"windows-sys 0.52.0",
]
[[package]]
@ -1197,9 +1184,9 @@ dependencies = [
[[package]]
name = "object"
version = "0.35.0"
version = "0.36.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8ec7ab813848ba4522158d5517a6093db1ded27575b070f4177b8d12b41db5e"
checksum = "27b64972346851a39438c60b341ebc01bba47464ae329e55cf343eb93964efd9"
dependencies = [
"memchr",
]
@ -1212,9 +1199,9 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]]
name = "oorandom"
version = "11.1.3"
version = "11.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575"
checksum = "b410bbe7e14ab526a0e86877eb47c6996a2bd7746f027ba551028c925390e4e9"
[[package]]
name = "option-ext"
@ -1240,9 +1227,9 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
dependencies = [
"cfg-if",
"libc",
"redox_syscall 0.5.1",
"redox_syscall",
"smallvec",
"windows-targets 0.52.5",
"windows-targets 0.52.6",
]
[[package]]
@ -1268,6 +1255,7 @@ name = "paths"
version = "0.0.0"
dependencies = [
"camino",
"serde",
]
[[package]]
@ -1319,9 +1307,12 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
[[package]]
name = "ppv-lite86"
version = "0.2.17"
version = "0.2.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
dependencies = [
"zerocopy",
]
[[package]]
name = "proc-macro-api"
@ -1330,14 +1321,12 @@ dependencies = [
"base-db",
"indexmap",
"intern",
"la-arena 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"paths",
"rustc-hash",
"serde",
"serde_json",
"span",
"stdx",
"text-size",
"tracing",
"tt",
]
@ -1350,7 +1339,6 @@ dependencies = [
"expect-test",
"intern",
"libloading",
"mbe",
"memmap2",
"object 0.33.0",
"paths",
@ -1360,6 +1348,7 @@ dependencies = [
"snap",
"span",
"stdx",
"syntax-bridge",
"tt",
]
@ -1380,9 +1369,9 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.85"
version = "1.0.86"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23"
checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
dependencies = [
"unicode-ident",
]
@ -1460,7 +1449,7 @@ version = "0.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57206b407293d2bcd3af849ce869d52068623f19e1b5ff8e8778e3309439682b"
dependencies = [
"bitflags 2.5.0",
"bitflags 2.6.0",
"memchr",
"unicase",
]
@ -1485,20 +1474,20 @@ dependencies = [
[[package]]
name = "ra-ap-rustc_abi"
version = "0.53.0"
version = "0.63.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "80b1d613eee933486c0613a7bc26e515e46f43adf479d1edd5e537f983e9ce46"
checksum = "b011c39d409940a890414e3a7b239762ac16d88029ad71b050a8374831b93790"
dependencies = [
"bitflags 2.5.0",
"bitflags 2.6.0",
"ra-ap-rustc_index",
"tracing",
]
[[package]]
name = "ra-ap-rustc_index"
version = "0.53.0"
version = "0.63.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f072060ac77e9e1a02cc20028095993af7e72cc0804779c68bcbf47b16de49c9"
checksum = "9027acdee649b0b27eb10b7db5be833efee3362d394935c5eed8f0745a9d43ce"
dependencies = [
"arrayvec",
"ra-ap-rustc_index_macros",
@ -1507,21 +1496,20 @@ dependencies = [
[[package]]
name = "ra-ap-rustc_index_macros"
version = "0.53.0"
version = "0.63.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "82f3d6dcb30a66905388e14756b8f2216131d9f8004922c07f13335840e058d1"
checksum = "540b86dc0384141ac8e825fc2874cd44bffd4277d99d8ec63ee416f1a98d5997"
dependencies = [
"proc-macro2",
"quote",
"syn",
"synstructure",
]
[[package]]
name = "ra-ap-rustc_lexer"
version = "0.53.0"
version = "0.63.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbd8a2b0bdcba9892cbce0b25f6c953d31b0febc1f3420fc692884fce5a23ad8"
checksum = "3bdf98bb457b47b9ae4aeebf867d0ca440c86925e0b6381658c4a02589748c9d"
dependencies = [
"unicode-properties",
"unicode-xid",
@ -1529,9 +1517,9 @@ dependencies = [
[[package]]
name = "ra-ap-rustc_parse_format"
version = "0.53.0"
version = "0.63.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70dad7a491c2554590222e0c9212dcb7c2e7aceb668875075012a35ea780d135"
checksum = "e8fe3556ab6311bb775220563a300e2bf62ec56404521fe0c511a583937683d5"
dependencies = [
"ra-ap-rustc_index",
"ra-ap-rustc_lexer",
@ -1539,9 +1527,9 @@ dependencies = [
[[package]]
name = "ra-ap-rustc_pattern_analysis"
version = "0.53.0"
version = "0.63.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34768e1faf88c31f2e9ad57b48318a52b507dafac0cddbf01b5d63bfc0b0a365"
checksum = "1709080fdeb5db630e1c2644026c2962aaa32416cd92f0190c04b0c21e114b91"
dependencies = [
"ra-ap-rustc_index",
"rustc-hash",
@ -1602,20 +1590,11 @@ dependencies = [
[[package]]
name = "redox_syscall"
version = "0.4.1"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4"
dependencies = [
"bitflags 1.3.2",
]
[[package]]
name = "redox_syscall"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e"
dependencies = [
"bitflags 2.5.0",
"bitflags 2.6.0",
]
[[package]]
@ -1648,12 +1627,12 @@ version = "0.0.0"
dependencies = [
"always-assert",
"anyhow",
"cargo_metadata",
"cfg",
"crossbeam-channel",
"dirs",
"dissimilar",
"expect-test",
"flycheck",
"hir",
"hir-def",
"hir-ty",
@ -1665,7 +1644,6 @@ dependencies = [
"load-cargo",
"lsp-server 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)",
"lsp-types",
"mbe",
"memchr",
"mimalloc",
"nohash-hasher",
@ -1675,6 +1653,7 @@ dependencies = [
"parser",
"paths",
"proc-macro-api",
"process-wrap",
"profile",
"project-model",
"rayon",
@ -1685,6 +1664,7 @@ dependencies = [
"serde_json",
"stdx",
"syntax",
"syntax-bridge",
"test-fixture",
"test-utils",
"tikv-jemallocator",
@ -1716,9 +1696,9 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]]
name = "rustc_apfloat"
version = "0.2.0+llvm-462a31f5a5ab"
version = "0.2.1+llvm-462a31f5a5ab"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "465187772033a5ee566f69fe008df03628fce549a0899aae76f0a0c2e34696be"
checksum = "886d94c63c812a8037c4faca2607453a0fa4cf82f734665266876b022244543f"
dependencies = [
"bitflags 1.3.2",
"smallvec",
@ -1801,18 +1781,18 @@ dependencies = [
[[package]]
name = "serde"
version = "1.0.203"
version = "1.0.206"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094"
checksum = "5b3e4cd94123dd520a128bcd11e34d9e9e423e7e3e50425cb1b4b1e3549d0284"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.203"
version = "1.0.206"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba"
checksum = "fabfb6138d2383ea8208cf98ccf69cdfb1aff4088460681d84189aa259762f97"
dependencies = [
"proc-macro2",
"quote",
@ -1821,12 +1801,13 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.117"
version = "1.0.124"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3"
checksum = "66ad62847a56b3dba58cc891acd13884b9c61138d330c0d7b6181713d4fce38d"
dependencies = [
"indexmap",
"itoa",
"memchr",
"ryu",
"serde",
]
@ -1844,9 +1825,9 @@ dependencies = [
[[package]]
name = "serde_spanned"
version = "0.6.6"
version = "0.6.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0"
checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d"
dependencies = [
"serde",
]
@ -1923,9 +1904,9 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.66"
version = "2.0.74"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5"
checksum = "1fceb41e3d546d0bd83421d3409b1460cc7444cd389341a4c880fe7a042cb3d7"
dependencies = [
"proc-macro2",
"quote",
@ -1967,6 +1948,21 @@ dependencies = [
"triomphe",
]
[[package]]
name = "syntax-bridge"
version = "0.0.0"
dependencies = [
"intern",
"parser",
"rustc-hash",
"span",
"stdx",
"syntax",
"test-utils",
"tracing",
"tt",
]
[[package]]
name = "test-fixture"
version = "0.0.0"
@ -1987,6 +1983,7 @@ name = "test-utils"
version = "0.0.0"
dependencies = [
"dissimilar",
"paths",
"profile",
"rustc-hash",
"stdx",
@ -2010,18 +2007,18 @@ checksum = "f18aa187839b2bdb1ad2fa35ead8c4c2976b64e4363c386d45ac0f7ee85c9233"
[[package]]
name = "thiserror"
version = "1.0.61"
version = "1.0.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709"
checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.61"
version = "1.0.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533"
checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
dependencies = [
"proc-macro2",
"quote",
@ -2090,9 +2087,9 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
[[package]]
name = "tinyvec"
version = "1.6.0"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938"
dependencies = [
"tinyvec_macros",
]
@ -2105,9 +2102,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "toml"
version = "0.8.14"
version = "0.8.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f49eb2ab21d2f26bd6db7bf383edc527a7ebaee412d17af4d40fdccd442f335"
checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e"
dependencies = [
"serde",
"serde_spanned",
@ -2117,18 +2114,18 @@ dependencies = [
[[package]]
name = "toml_datetime"
version = "0.6.6"
version = "0.6.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf"
checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41"
dependencies = [
"serde",
]
[[package]]
name = "toml_edit"
version = "0.22.14"
version = "0.22.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f21c7aaf97f1bd9ca9d4f9e73b0a6c74bd5afef56f2bc931943a6e1c37e04e38"
checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d"
dependencies = [
"indexmap",
"serde",
@ -2214,9 +2211,9 @@ dependencies = [
[[package]]
name = "triomphe"
version = "0.1.12"
version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b2cb4fbb9995eeb36ac86fadf24031ccd58f99d6b4b2d7b911db70bddb80d90"
checksum = "e6631e42e10b40c0690bf92f404ebcfe6e1fdb480391d15f17cc8e96eeed5369"
dependencies = [
"serde",
"stable_deref_trait",
@ -2289,9 +2286,9 @@ checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
[[package]]
name = "url"
version = "2.5.0"
version = "2.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633"
checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c"
dependencies = [
"form_urlencoded",
"idna",
@ -2307,14 +2304,15 @@ checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
[[package]]
name = "version_check"
version = "0.9.4"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]]
name = "vfs"
version = "0.0.0"
dependencies = [
"crossbeam-channel",
"fst",
"indexmap",
"nohash-hasher",
@ -2331,6 +2329,8 @@ dependencies = [
"crossbeam-channel",
"notify",
"paths",
"rayon",
"rustc-hash",
"stdx",
"tracing",
"vfs",
@ -2355,11 +2355,11 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "winapi-util"
version = "0.1.8"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b"
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
dependencies = [
"windows-sys 0.52.0",
"windows-sys 0.59.0",
]
[[package]]
@ -2369,7 +2369,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1de69df01bdf1ead2f4ac895dc77c9351aefff65b2f3db429a343f9cbf05e132"
dependencies = [
"windows-core",
"windows-targets 0.52.5",
"windows-targets 0.52.6",
]
[[package]]
@ -2381,7 +2381,7 @@ dependencies = [
"windows-implement",
"windows-interface",
"windows-result",
"windows-targets 0.52.5",
"windows-targets 0.52.6",
]
[[package]]
@ -2408,11 +2408,11 @@ dependencies = [
[[package]]
name = "windows-result"
version = "0.1.1"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "749f0da9cc72d82e600d8d2e44cadd0b9eedb9038f71a1c58556ac1c5791813b"
checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8"
dependencies = [
"windows-targets 0.52.5",
"windows-targets 0.52.6",
]
[[package]]
@ -2430,7 +2430,16 @@ version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets 0.52.5",
"windows-targets 0.52.6",
]
[[package]]
name = "windows-sys"
version = "0.59.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
dependencies = [
"windows-targets 0.52.6",
]
[[package]]
@ -2450,18 +2459,18 @@ dependencies = [
[[package]]
name = "windows-targets"
version = "0.52.5"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm 0.52.5",
"windows_aarch64_msvc 0.52.5",
"windows_i686_gnu 0.52.5",
"windows_aarch64_gnullvm 0.52.6",
"windows_aarch64_msvc 0.52.6",
"windows_i686_gnu 0.52.6",
"windows_i686_gnullvm",
"windows_i686_msvc 0.52.5",
"windows_x86_64_gnu 0.52.5",
"windows_x86_64_gnullvm 0.52.5",
"windows_x86_64_msvc 0.52.5",
"windows_i686_msvc 0.52.6",
"windows_x86_64_gnu 0.52.6",
"windows_x86_64_gnullvm 0.52.6",
"windows_x86_64_msvc 0.52.6",
]
[[package]]
@ -2472,9 +2481,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.5"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_msvc"
@ -2484,9 +2493,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.5"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_i686_gnu"
@ -2496,15 +2505,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
[[package]]
name = "windows_i686_gnu"
version = "0.52.5"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.5"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_msvc"
@ -2514,9 +2523,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
[[package]]
name = "windows_i686_msvc"
version = "0.52.5"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_x86_64_gnu"
@ -2526,9 +2535,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.5"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnullvm"
@ -2538,9 +2547,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.5"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_msvc"
@ -2550,15 +2559,15 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.5"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "winnow"
version = "0.6.11"
version = "0.6.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56c52728401e1dc672a56e81e593e912aa54c78f40246869f78359a2bf24d29d"
checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f"
dependencies = [
"memchr",
]
@ -2618,6 +2627,27 @@ dependencies = [
"zip",
]
[[package]]
name = "zerocopy"
version = "0.7.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
dependencies = [
"byteorder",
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.7.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "zip"
version = "0.6.6"

View file

@ -4,10 +4,11 @@ exclude = ["crates/proc-macro-srv/proc-macro-test/imp"]
resolver = "2"
[workspace.package]
rust-version = "1.78"
rust-version = "1.80"
edition = "2021"
license = "MIT OR Apache-2.0"
authors = ["rust-analyzer team"]
repository = "https://github.com/rust-lang/rust-analyzer"
[profile.dev]
debug = 1
@ -51,7 +52,6 @@ debug = 2
# local crates
base-db = { path = "./crates/base-db", 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" }
hir-expand = { path = "./crates/hir-expand", version = "0.0.0" }
@ -77,17 +77,18 @@ salsa = { path = "./crates/salsa", version = "0.0.0" }
span = { path = "./crates/span", version = "0.0.0" }
stdx = { path = "./crates/stdx", version = "0.0.0" }
syntax = { path = "./crates/syntax", version = "0.0.0" }
syntax-bridge = { path = "./crates/syntax-bridge", version = "0.0.0" }
text-edit = { path = "./crates/text-edit", version = "0.0.0" }
toolchain = { path = "./crates/toolchain", version = "0.0.0" }
tt = { path = "./crates/tt", version = "0.0.0" }
vfs-notify = { path = "./crates/vfs-notify", version = "0.0.0" }
vfs = { path = "./crates/vfs", version = "0.0.0" }
ra-ap-rustc_lexer = { version = "0.53.0", default-features = false }
ra-ap-rustc_parse_format = { version = "0.53.0", default-features = false }
ra-ap-rustc_index = { version = "0.53.0", default-features = false }
ra-ap-rustc_abi = { version = "0.53.0", default-features = false }
ra-ap-rustc_pattern_analysis = { version = "0.53.0", default-features = false }
ra-ap-rustc_lexer = { version = "0.63.0", default-features = false }
ra-ap-rustc_parse_format = { version = "0.63.0", default-features = false }
ra-ap-rustc_index = { version = "0.63.0", default-features = false }
ra-ap-rustc_abi = { version = "0.63.0", default-features = false }
ra-ap-rustc_pattern_analysis = { version = "0.63.0", default-features = false }
# local crates that aren't published to crates.io. These should not have versions.
test-fixture = { path = "./crates/test-fixture" }
@ -124,11 +125,11 @@ memmap2 = "0.5.4"
nohash-hasher = "0.2.0"
oorandom = "11.1.3"
object = { version = "0.33.0", default-features = false, features = [
"std",
"read_core",
"elf",
"macho",
"pe",
"std",
"read_core",
"elf",
"macho",
"pe",
] }
process-wrap = { version = "8.0.2", features = ["std"] }
pulldown-cmark-to-cmark = "10.0.4"
@ -158,7 +159,6 @@ url = "2.3.1"
xshell = "0.2.5"
# We need to freeze the version of the crate, as the raw-api feature is considered unstable
dashmap = { version = "=5.5.3", features = ["raw-api"] }

View file

@ -1,7 +1,8 @@
[package]
name = "base-db"
version = "0.0.0"
description = "TBD"
repository.workspace = true
description = "Basic database traits for rust-analyzer. The concrete DB is defined by `ide` (aka `ra_ap_ide`)."
authors.workspace = true
edition.workspace = true

View file

@ -7,7 +7,7 @@ use salsa::Durability;
use triomphe::Arc;
use vfs::FileId;
use crate::{CrateGraph, SourceDatabaseExt, SourceDatabaseExt2, SourceRoot, SourceRootId};
use crate::{CrateGraph, SourceDatabaseFileInputExt, SourceRoot, SourceRootDatabase, SourceRootId};
/// Encapsulate a bunch of raw `.set` calls on the database.
#[derive(Default)]
@ -50,7 +50,7 @@ impl FileChange {
self.crate_graph = Some(graph);
}
pub fn apply(self, db: &mut dyn SourceDatabaseExt) {
pub fn apply(self, db: &mut dyn SourceRootDatabase) {
let _p = tracing::info_span!("FileChange::apply").entered();
if let Some(roots) = self.roots {
for (idx, root) in roots.into_iter().enumerate() {

View file

@ -690,6 +690,14 @@ impl Env {
pub fn extend_from_other(&mut self, other: &Env) {
self.entries.extend(other.entries.iter().map(|(x, y)| (x.to_owned(), y.to_owned())));
}
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
pub fn insert(&mut self, k: impl Into<String>, v: impl Into<String>) -> Option<String> {
self.entries.insert(k.into(), v.into())
}
}
impl From<Env> for Vec<(String, String)> {
@ -700,6 +708,15 @@ impl From<Env> for Vec<(String, String)> {
}
}
impl<'a> IntoIterator for &'a Env {
type Item = (&'a String, &'a String);
type IntoIter = std::collections::hash_map::Iter<'a, String, String>;
fn into_iter(self) -> Self::IntoIter {
self.entries.iter()
}
}
#[derive(Debug)]
pub struct CyclicDependenciesError {
path: Vec<(CrateId, Option<CrateDisplayName>)>,

View file

@ -1,5 +1,5 @@
//! base_db defines basic database traits. The concrete DB is defined by ide.
// FIXME: Rename this crate, base db is non descriptive
mod change;
mod input;
@ -47,8 +47,6 @@ pub const DEFAULT_PARSE_LRU_CAP: u16 = 128;
pub const DEFAULT_BORROWCK_LRU_CAP: u16 = 2024;
pub trait FileLoader {
/// Text of the file.
fn file_text(&self, file_id: FileId) -> Arc<str>;
fn resolve_path(&self, path: AnchoredPath<'_>) -> Option<FileId>;
/// Crates whose root's source root is the same as the source root of `file_id`
fn relevant_crates(&self, file_id: FileId) -> Arc<[CrateId]>;
@ -58,6 +56,13 @@ pub trait FileLoader {
/// model. Everything else in rust-analyzer is derived from these queries.
#[salsa::query_group(SourceDatabaseStorage)]
pub trait SourceDatabase: FileLoader + std::fmt::Debug {
#[salsa::input]
fn compressed_file_text(&self, file_id: FileId) -> Arc<[u8]>;
/// Text of the file.
#[salsa::lru]
fn file_text(&self, file_id: FileId) -> Arc<str>;
/// Parses the file into the syntax tree.
#[salsa::lru]
fn parse(&self, file_id: EditionedFileId) -> Parse<ast::SourceFile>;
@ -99,16 +104,18 @@ fn parse_errors(db: &dyn SourceDatabase, file_id: EditionedFileId) -> Option<Arc
}
}
fn file_text(db: &dyn SourceDatabase, file_id: FileId) -> Arc<str> {
let bytes = db.compressed_file_text(file_id);
let bytes =
lz4_flex::decompress_size_prepended(&bytes).expect("lz4 decompression should not fail");
let text = std::str::from_utf8(&bytes).expect("file contents should be valid UTF-8");
Arc::from(text)
}
/// We don't want to give HIR knowledge of source roots, hence we extract these
/// methods into a separate DB.
#[salsa::query_group(SourceDatabaseExtStorage)]
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>;
#[salsa::query_group(SourceRootDatabaseStorage)]
pub trait SourceRootDatabase: SourceDatabase {
/// Path to a file, relative to the root of its source root.
/// Source root of the file.
#[salsa::input]
@ -121,15 +128,7 @@ pub trait SourceDatabaseExt: SourceDatabase {
fn source_root_crates(&self, id: SourceRootId) -> Arc<[CrateId]>;
}
fn file_text(db: &dyn SourceDatabaseExt, file_id: FileId) -> Arc<str> {
let bytes = db.compressed_file_text(file_id);
let bytes =
lz4_flex::decompress_size_prepended(&bytes).expect("lz4 decompression should not fail");
let text = std::str::from_utf8(&bytes).expect("file contents should be valid UTF-8");
Arc::from(text)
}
pub trait SourceDatabaseExt2 {
pub trait SourceDatabaseFileInputExt {
fn set_file_text(&mut self, file_id: FileId, text: &str) {
self.set_file_text_with_durability(file_id, text, Durability::LOW);
}
@ -142,7 +141,7 @@ pub trait SourceDatabaseExt2 {
);
}
impl<Db: ?Sized + SourceDatabaseExt> SourceDatabaseExt2 for Db {
impl<Db: ?Sized + SourceRootDatabase> SourceDatabaseFileInputExt for Db {
fn set_file_text_with_durability(
&mut self,
file_id: FileId,
@ -159,7 +158,7 @@ impl<Db: ?Sized + SourceDatabaseExt> SourceDatabaseExt2 for Db {
}
}
fn source_root_crates(db: &dyn SourceDatabaseExt, id: SourceRootId) -> Arc<[CrateId]> {
fn source_root_crates(db: &dyn SourceRootDatabase, id: SourceRootId) -> Arc<[CrateId]> {
let graph = db.crate_graph();
let mut crates = graph
.iter()
@ -173,13 +172,12 @@ fn source_root_crates(db: &dyn SourceDatabaseExt, id: SourceRootId) -> Arc<[Crat
crates.into_iter().collect()
}
/// Silly workaround for cyclic deps between the traits
// FIXME: Would be nice to get rid of this somehow
/// Silly workaround for cyclic deps due to the SourceRootDatabase and SourceDatabase split
/// regarding FileLoader
pub struct FileLoaderDelegate<T>(pub T);
impl<T: SourceDatabaseExt> FileLoader for FileLoaderDelegate<&'_ T> {
fn file_text(&self, file_id: FileId) -> Arc<str> {
SourceDatabaseExt::file_text(self.0, file_id)
}
impl<T: SourceRootDatabase> FileLoader for FileLoaderDelegate<&'_ T> {
fn resolve_path(&self, path: AnchoredPath<'_>) -> Option<FileId> {
// FIXME: this *somehow* should be platform agnostic...
let source_root = self.0.file_source_root(path.anchor);

View file

@ -1,7 +1,8 @@
[package]
name = "cfg"
version = "0.0.0"
description = "TBD"
repository.workspace = true
description = "Conditional compiling options, `cfg` attribute parser and evaluator for rust-analyzer."
authors.workspace = true
edition.workspace = true
@ -28,7 +29,7 @@ arbitrary = "1.3.2"
derive_arbitrary = "1.3.2"
# local deps
mbe.workspace = true
syntax-bridge.workspace = true
syntax.workspace = true
[lints]

View file

@ -108,6 +108,14 @@ impl<'a> IntoIterator for &'a CfgOptions {
}
}
impl FromIterator<CfgAtom> for CfgOptions {
fn from_iter<T: IntoIterator<Item = CfgAtom>>(iter: T) -> Self {
let mut options = CfgOptions::default();
options.extend(iter);
options
}
}
#[derive(Default, Clone, Debug, PartialEq, Eq)]
pub struct CfgDiff {
// Invariants: No duplicates, no atom that's both in `enable` and `disable`.

View file

@ -1,8 +1,11 @@
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};
use syntax_bridge::{
dummy_test_span_utils::{DummyTestSpanMap, DUMMY},
syntax_node_to_token_tree, DocCommentDesugarMode,
};
use crate::{CfgAtom, CfgExpr, CfgOptions, DnfExpr};

View file

@ -1,30 +0,0 @@
[package]
name = "flycheck"
version = "0.0.0"
description = "TBD"
authors.workspace = true
edition.workspace = true
license.workspace = true
rust-version.workspace = true
[lib]
doctest = false
[dependencies]
cargo_metadata.workspace = true
crossbeam-channel.workspace = true
tracing.workspace = true
rustc-hash.workspace = true
serde_json.workspace = true
serde.workspace = true
process-wrap.workspace = true
# local deps
paths.workspace = true
stdx.workspace = true
toolchain.workspace = true
project-model.workspace = true
[lints]
workspace = true

View file

@ -1,7 +1,8 @@
[package]
name = "hir-def"
version = "0.0.0"
description = "TBD"
repository.workspace = true
description = "RPC Api for the `proc-macro-srv` crate of rust-analyzer."
authors.workspace = true
edition.workspace = true
@ -52,7 +53,7 @@ expect-test.workspace = true
# local deps
test-utils.workspace = true
test-fixture.workspace = true
syntax-bridge.workspace = true
[features]
in-rust-tree = ["hir-expand/in-rust-tree"]

View file

@ -657,9 +657,9 @@ mod tests {
use triomphe::Arc;
use hir_expand::span_map::{RealSpanMap, SpanMap};
use mbe::{syntax_node_to_token_tree, DocCommentDesugarMode};
use span::FileId;
use syntax::{ast, AstNode, TextRange};
use syntax_bridge::{syntax_node_to_token_tree, DocCommentDesugarMode};
use crate::attr::{DocAtom, DocExpr};

View file

@ -118,6 +118,7 @@ pub enum BodyDiagnostic {
MacroError { node: InFile<AstPtr<ast::MacroCall>>, err: ExpandError },
UnresolvedMacroCall { node: InFile<AstPtr<ast::MacroCall>>, path: ModPath },
UnreachableLabel { node: InFile<AstPtr<ast::Lifetime>>, name: Name },
AwaitOutsideOfAsync { node: InFile<AstPtr<ast::AwaitExpr>>, location: String },
UndeclaredLabel { node: InFile<AstPtr<ast::Lifetime>>, name: Name },
}
@ -157,7 +158,7 @@ impl Body {
}),
)
});
is_async_fn = data.has_async_kw();
is_async_fn = data.is_async();
src.map(|it| it.body().map(ast::Expr::from))
}
DefWithBodyId::ConstId(c) => {

View file

@ -72,6 +72,7 @@ pub(super) fn lower(
is_lowering_coroutine: false,
label_ribs: Vec::new(),
current_binding_owner: None,
awaitable_context: None,
}
.collect(params, body, is_async_fn)
}
@ -100,6 +101,8 @@ struct ExprCollector<'a> {
// resolution
label_ribs: Vec<LabelRib>,
current_binding_owner: Option<ExprId>,
awaitable_context: Option<Awaitable>,
}
#[derive(Clone, Debug)]
@ -135,6 +138,11 @@ impl RibKind {
}
}
enum Awaitable {
Yes,
No(&'static str),
}
#[derive(Debug, Default)]
struct BindingList {
map: FxHashMap<Name, BindingId>,
@ -180,6 +188,18 @@ impl ExprCollector<'_> {
body: Option<ast::Expr>,
is_async_fn: bool,
) -> (Body, BodySourceMap) {
self.awaitable_context.replace(if is_async_fn {
Awaitable::Yes
} else {
match self.owner {
DefWithBodyId::FunctionId(..) => Awaitable::No("non-async function"),
DefWithBodyId::StaticId(..) => Awaitable::No("static"),
DefWithBodyId::ConstId(..) | DefWithBodyId::InTypeConstId(..) => {
Awaitable::No("constant")
}
DefWithBodyId::VariantId(..) => Awaitable::No("enum variant"),
}
});
if let Some((param_list, mut attr_enabled)) = param_list {
let mut params = vec![];
if let Some(self_param) =
@ -280,31 +300,40 @@ impl ExprCollector<'_> {
}
Some(ast::BlockModifier::Async(_)) => {
self.with_label_rib(RibKind::Closure, |this| {
this.collect_block_(e, |id, statements, tail| Expr::Async {
id,
statements,
tail,
this.with_awaitable_block(Awaitable::Yes, |this| {
this.collect_block_(e, |id, statements, tail| Expr::Async {
id,
statements,
tail,
})
})
})
}
Some(ast::BlockModifier::Const(_)) => {
self.with_label_rib(RibKind::Constant, |this| {
let (result_expr_id, prev_binding_owner) =
this.initialize_binding_owner(syntax_ptr);
let inner_expr = this.collect_block(e);
let it = this.db.intern_anonymous_const(ConstBlockLoc {
parent: this.owner,
root: inner_expr,
});
this.body.exprs[result_expr_id] = Expr::Const(it);
this.current_binding_owner = prev_binding_owner;
result_expr_id
this.with_awaitable_block(Awaitable::No("constant block"), |this| {
let (result_expr_id, prev_binding_owner) =
this.initialize_binding_owner(syntax_ptr);
let inner_expr = this.collect_block(e);
let it = this.db.intern_anonymous_const(ConstBlockLoc {
parent: this.owner,
root: inner_expr,
});
this.body.exprs[result_expr_id] = Expr::Const(it);
this.current_binding_owner = prev_binding_owner;
result_expr_id
})
})
}
// FIXME
Some(ast::BlockModifier::AsyncGen(_)) | Some(ast::BlockModifier::Gen(_)) | None => {
self.collect_block(e)
Some(ast::BlockModifier::AsyncGen(_)) => {
self.with_awaitable_block(Awaitable::Yes, |this| this.collect_block(e))
}
Some(ast::BlockModifier::Gen(_)) => self
.with_awaitable_block(Awaitable::No("non-async gen block"), |this| {
this.collect_block(e)
}),
None => self.collect_block(e),
},
ast::Expr::LoopExpr(e) => {
let label = e.label().map(|label| self.collect_label(label));
@ -469,6 +498,12 @@ impl ExprCollector<'_> {
}
ast::Expr::AwaitExpr(e) => {
let expr = self.collect_expr_opt(e.expr());
if let Awaitable::No(location) = self.is_lowering_awaitable_block() {
self.source_map.diagnostics.push(BodyDiagnostic::AwaitOutsideOfAsync {
node: InFile::new(self.expander.current_file_id(), AstPtr::new(&e)),
location: location.to_string(),
});
}
self.alloc_expr(Expr::Await { expr }, syntax_ptr)
}
ast::Expr::TryExpr(e) => self.collect_try_operator(syntax_ptr, e),
@ -527,7 +562,13 @@ impl ExprCollector<'_> {
let prev_is_lowering_coroutine = mem::take(&mut this.is_lowering_coroutine);
let prev_try_block_label = this.current_try_block_label.take();
let body = this.collect_expr_opt(e.body());
let awaitable = if e.async_token().is_some() {
Awaitable::Yes
} else {
Awaitable::No("non-async closure")
};
let body =
this.with_awaitable_block(awaitable, |this| this.collect_expr_opt(e.body()));
let closure_kind = if this.is_lowering_coroutine {
let movability = if e.static_token().is_some() {
@ -2082,6 +2123,21 @@ impl ExprCollector<'_> {
fn alloc_label_desugared(&mut self, label: Label) -> LabelId {
self.body.labels.alloc(label)
}
fn is_lowering_awaitable_block(&self) -> &Awaitable {
self.awaitable_context.as_ref().unwrap_or(&Awaitable::No("unknown"))
}
fn with_awaitable_block<T>(
&mut self,
awaitable: Awaitable,
f: impl FnOnce(&mut Self) -> T,
) -> T {
let orig = self.awaitable_context.replace(awaitable);
let res = f(self);
self.awaitable_context = orig;
res
}
}
fn comma_follows_token(t: Option<syntax::SyntaxToken>) -> bool {

View file

@ -94,6 +94,12 @@ impl FunctionData {
.filter(|it| !it.is_empty())
.map(Box::new);
let rustc_allow_incoherent_impl = attrs.by_key(&sym::rustc_allow_incoherent_impl).exists();
if flags.contains(FnFlags::HAS_UNSAFE_KW)
&& !crate_graph[krate].edition.at_least_2024()
&& attrs.by_key(&sym::rustc_deprecated_safe_2024).exists()
{
flags.remove(FnFlags::HAS_UNSAFE_KW);
}
Arc::new(FunctionData {
name: func.name.clone(),
@ -126,19 +132,19 @@ impl FunctionData {
self.flags.contains(FnFlags::HAS_SELF_PARAM)
}
pub fn has_default_kw(&self) -> bool {
pub fn is_default(&self) -> bool {
self.flags.contains(FnFlags::HAS_DEFAULT_KW)
}
pub fn has_const_kw(&self) -> bool {
pub fn is_const(&self) -> bool {
self.flags.contains(FnFlags::HAS_CONST_KW)
}
pub fn has_async_kw(&self) -> bool {
pub fn is_async(&self) -> bool {
self.flags.contains(FnFlags::HAS_ASYNC_KW)
}
pub fn has_unsafe_kw(&self) -> bool {
pub fn is_unsafe(&self) -> bool {
self.flags.contains(FnFlags::HAS_UNSAFE_KW)
}

View file

@ -160,7 +160,7 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast<dyn ExpandDataba
fn const_data(&self, konst: ConstId) -> Arc<ConstData>;
#[salsa::invoke(StaticData::static_data_query)]
fn static_data(&self, konst: StaticId) -> Arc<StaticData>;
fn static_data(&self, statik: StaticId) -> Arc<StaticData>;
#[salsa::invoke(Macro2Data::macro2_data_query)]
fn macro2_data(&self, makro: Macro2Id) -> Arc<Macro2Data>;
@ -240,14 +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, EditionedFileId)>;
fn include_macro_invoc(&self, crate_id: CrateId) -> Arc<[(MacroCallId, EditionedFileId)]>;
}
// return: macro call id and include file id
fn include_macro_invoc(
db: &dyn DefDatabase,
krate: CrateId,
) -> Vec<(MacroCallId, EditionedFileId)> {
) -> Arc<[(MacroCallId, EditionedFileId)]> {
db.crate_def_map(krate)
.modules
.values()

View file

@ -50,13 +50,13 @@ pub fn find_path(
prefix: prefix_kind,
cfg,
ignore_local_imports,
is_std_item: db.crate_graph()[item_module.krate()].origin.is_lang(),
from,
from_def_map: &from.def_map(db),
fuel: Cell::new(FIND_PATH_FUEL),
},
item,
MAX_PATH_LEN,
db.crate_graph()[item_module.krate()].origin.is_lang(),
)
}
@ -98,20 +98,16 @@ struct FindPathCtx<'db> {
prefix: PrefixKind,
cfg: ImportPathConfig,
ignore_local_imports: bool,
is_std_item: bool,
from: ModuleId,
from_def_map: &'db DefMap,
fuel: Cell<usize>,
}
/// 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,
is_std_item: bool,
) -> Option<ModPath> {
fn find_path_inner(ctx: &FindPathCtx<'_>, item: ItemInNs, max_len: usize) -> Option<ModPath> {
// - if the item is a module, jump straight to module search
if !is_std_item {
if !ctx.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);
@ -138,12 +134,9 @@ fn find_path_inner(
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,
is_std_item,
) {
if let Some(mut path) =
find_path_inner(ctx, ItemInNs::Types(variant.lookup(ctx.db).parent.into()), max_len)
{
path.push_segment(ctx.db.enum_variant_data(variant).name.clone());
return Some(path);
}
@ -152,16 +145,6 @@ fn find_path_inner(
// variant somewhere
}
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);
}
}
let mut best_choice = None;
calculate_best_path(ctx, &mut FxHashSet::default(), item, max_len, &mut best_choice);
best_choice.map(|choice| choice.path)
@ -366,6 +349,12 @@ fn calculate_best_path(
// Item was defined in the same crate that wants to import it. It cannot be found in any
// dependency in this case.
calculate_best_path_local(ctx, visited_modules, item, max_len, best_choice)
} else if ctx.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.
find_in_sysroot(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
@ -382,10 +371,10 @@ fn find_in_sysroot(
visited_modules: &mut FxHashSet<(ItemInNs, ModuleId)>,
item: ItemInNs,
max_len: usize,
) -> Option<Choice> {
best_choice: &mut 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 {
@ -397,29 +386,31 @@ fn find_in_sysroot(
}
};
if ctx.cfg.prefer_no_std {
search(LangCrateOrigin::Core, &mut best_choice);
search(LangCrateOrigin::Core, best_choice);
if matches!(best_choice, Some(Choice { stability: Stable, .. })) {
return best_choice;
return;
}
search(LangCrateOrigin::Std, &mut best_choice);
search(LangCrateOrigin::Std, best_choice);
if matches!(best_choice, Some(Choice { stability: Stable, .. })) {
return best_choice;
return;
}
} else {
search(LangCrateOrigin::Std, &mut best_choice);
search(LangCrateOrigin::Std, best_choice);
if matches!(best_choice, Some(Choice { stability: Stable, .. })) {
return best_choice;
return;
}
search(LangCrateOrigin::Core, &mut best_choice);
search(LangCrateOrigin::Core, best_choice);
if matches!(best_choice, Some(Choice { stability: Stable, .. })) {
return best_choice;
return;
}
}
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
dependencies
.iter()
.filter(|it| it.is_sysroot())
.chain(dependencies.iter().filter(|it| !it.is_sysroot()))
.for_each(|dep| {
find_in_dep(ctx, visited_modules, item, max_len, best_choice, dep.crate_id);
});
}
fn find_in_dep(
@ -491,6 +482,7 @@ fn calculate_best_path_local(
);
}
#[derive(Debug)]
struct Choice {
path: ModPath,
/// The length in characters of the path
@ -676,6 +668,7 @@ mod tests {
path: &str,
prefer_prelude: bool,
prefer_absolute: bool,
prefer_no_std: bool,
expect: Expect,
) {
let (db, pos) = TestDB::with_position(ra_fixture);
@ -717,7 +710,7 @@ mod tests {
module,
prefix,
ignore_local_imports,
ImportPathConfig { prefer_no_std: false, prefer_prelude, prefer_absolute },
ImportPathConfig { prefer_no_std, prefer_prelude, prefer_absolute },
);
format_to!(
res,
@ -732,15 +725,19 @@ mod tests {
}
fn check_found_path(ra_fixture: &str, path: &str, expect: Expect) {
check_found_path_(ra_fixture, path, false, false, expect);
check_found_path_(ra_fixture, path, false, false, false, expect);
}
fn check_found_path_prelude(ra_fixture: &str, path: &str, expect: Expect) {
check_found_path_(ra_fixture, path, true, false, expect);
check_found_path_(ra_fixture, path, true, false, false, expect);
}
fn check_found_path_absolute(ra_fixture: &str, path: &str, expect: Expect) {
check_found_path_(ra_fixture, path, false, true, expect);
check_found_path_(ra_fixture, path, false, true, false, expect);
}
fn check_found_path_prefer_no_std(ra_fixture: &str, path: &str, expect: Expect) {
check_found_path_(ra_fixture, path, false, false, true, expect);
}
#[test]
@ -1361,9 +1358,66 @@ pub mod sync {
"#]],
);
}
#[test]
fn prefer_core_paths_over_std_for_mod_reexport() {
check_found_path_prefer_no_std(
r#"
//- /main.rs crate:main deps:core,std
$0
//- /stdlib.rs crate:std deps:core
pub use core::pin;
//- /corelib.rs crate:core
pub mod pin {
pub struct Pin;
}
"#,
"std::pin::Pin",
expect![[r#"
Plain (imports ): core::pin::Pin
Plain (imports ): core::pin::Pin
ByCrate(imports ): core::pin::Pin
ByCrate(imports ): core::pin::Pin
BySelf (imports ): core::pin::Pin
BySelf (imports ): core::pin::Pin
"#]],
);
}
#[test]
fn prefer_core_paths_over_std() {
check_found_path_prefer_no_std(
r#"
//- /main.rs crate:main deps:core,std
$0
//- /std.rs crate:std deps:core
pub mod fmt {
pub use core::fmt::Error;
}
//- /zzz.rs crate:core
pub mod fmt {
pub struct Error;
}
"#,
"core::fmt::Error",
expect![[r#"
Plain (imports ): core::fmt::Error
Plain (imports ): core::fmt::Error
ByCrate(imports ): core::fmt::Error
ByCrate(imports ): core::fmt::Error
BySelf (imports ): core::fmt::Error
BySelf (imports ): core::fmt::Error
"#]],
);
check_found_path(
r#"
//- /main.rs crate:main deps:core,std
@ -1878,10 +1932,9 @@ pub mod ops {
#[test]
fn respect_unstable_modules() {
check_found_path(
check_found_path_prefer_no_std(
r#"
//- /main.rs crate:main deps:std,core
#![no_std]
extern crate std;
$0
//- /longer.rs crate:std deps:core

View file

@ -105,7 +105,7 @@ use crate::{
type FxIndexMap<K, V> =
indexmap::IndexMap<K, V, std::hash::BuildHasherDefault<rustc_hash::FxHasher>>;
/// A wrapper around two booleans, [`ImportPathConfig::prefer_no_std`] and [`ImportPathConfig::prefer_prelude`].
/// A wrapper around three booleans
#[derive(Debug, Clone, PartialEq, Eq, Hash, Copy)]
pub struct ImportPathConfig {
/// If true, prefer to unconditionally use imports of the `core` and `alloc` crate

View file

@ -1201,7 +1201,6 @@ macro_rules! m {
#[test]
fn test_meta_doc_comments() {
cov_mark::check!(test_meta_doc_comments);
check(
r#"
macro_rules! m {

View file

@ -317,9 +317,9 @@ impl ProcMacroExpander for IdentityWhenValidProcMacroExpander {
_: Span,
_: Span,
) -> Result<Subtree, ProcMacroExpansionError> {
let (parse, _) = ::mbe::token_tree_to_syntax_node(
let (parse, _) = syntax_bridge::token_tree_to_syntax_node(
subtree,
::mbe::TopEntryPoint::MacroItems,
syntax_bridge::TopEntryPoint::MacroItems,
span::Edition::CURRENT,
);
if parse.errors().is_empty() {

View file

@ -56,7 +56,6 @@ use crate::{
};
static GLOB_RECURSION_LIMIT: Limit = Limit::new(100);
static EXPANSION_DEPTH_LIMIT: Limit = Limit::new(128);
static FIXED_POINT_LIMIT: Limit = Limit::new(8192);
pub(super) fn collect_defs(db: &dyn DefDatabase, def_map: DefMap, tree_id: TreeId) -> DefMap {
@ -1440,7 +1439,14 @@ impl DefCollector<'_> {
depth: usize,
container: ItemContainerId,
) {
if EXPANSION_DEPTH_LIMIT.check(depth).is_err() {
let recursion_limit = self.def_map.recursion_limit() as usize;
let recursion_limit = Limit::new(if cfg!(test) {
// Without this, `body::tests::your_stack_belongs_to_me` stack-overflows in debug
std::cmp::min(32, recursion_limit)
} else {
recursion_limit
});
if recursion_limit.check(depth).is_err() {
cov_mark::hit!(macro_expansion_overflow);
tracing::warn!("macro expansion is too deep");
return;
@ -2003,7 +2009,7 @@ impl ModCollector<'_, '_> {
Err(cfg) => {
self.emit_unconfigured_diagnostic(
self.tree_id,
AttrOwner::TopLevel,
AttrOwner::ModItem(module_id.into()),
&cfg,
);
}

View file

@ -1,4 +1,4 @@
use base_db::{SourceDatabase, SourceDatabaseExt2 as _};
use base_db::{SourceDatabase, SourceDatabaseFileInputExt as _};
use test_fixture::WithFixture;
use crate::{db::DefDatabase, nameres::tests::TestDB, AdtId, ModuleDefId};

View file

@ -194,6 +194,11 @@ pub(super) fn lower_generic_args(
match generic_arg {
ast::GenericArg::TypeArg(type_arg) => {
let type_ref = TypeRef::from_ast_opt(lower_ctx, type_arg.ty());
type_ref.walk(&mut |tr| {
if let TypeRef::ImplTrait(bounds) = tr {
lower_ctx.update_impl_traits_bounds(bounds.clone());
}
});
args.push(GenericArg::Type(type_ref));
}
ast::GenericArg::AssocTypeArg(assoc_type_arg) => {

View file

@ -19,7 +19,7 @@ use crate::{
};
#[salsa::database(
base_db::SourceDatabaseExtStorage,
base_db::SourceRootDatabaseStorage,
base_db::SourceDatabaseStorage,
hir_expand::db::ExpandDatabaseStorage,
crate::db::InternDatabaseStorage,
@ -69,9 +69,6 @@ impl fmt::Debug for TestDB {
impl panic::RefUnwindSafe for TestDB {}
impl FileLoader for TestDB {
fn file_text(&self, file_id: FileId) -> Arc<str> {
FileLoaderDelegate(self).file_text(file_id)
}
fn resolve_path(&self, path: AnchoredPath<'_>) -> Option<FileId> {
FileLoaderDelegate(self).resolve_path(path)
}

View file

@ -1,7 +1,8 @@
[package]
name = "hir-expand"
version = "0.0.0"
description = "TBD"
repository.workspace = true
description = "Macro expansion for rust-analyzer."
authors.workspace = true
edition.workspace = true
@ -33,6 +34,7 @@ mbe.workspace = true
limit.workspace = true
span.workspace = true
parser.workspace = true
syntax-bridge.workspace = true
[dev-dependencies]
expect-test = "1.4.0"

View file

@ -6,14 +6,12 @@ use cfg::CfgExpr;
use either::Either;
use intern::{sym, Interned, Symbol};
use mbe::{
desugar_doc_comment_text, syntax_node_to_token_tree, DelimiterKind, DocCommentDesugarMode,
Punct,
};
use mbe::{DelimiterKind, Punct};
use smallvec::{smallvec, SmallVec};
use span::{Span, SyntaxContextId};
use syntax::unescape;
use syntax::{ast, match_ast, AstNode, AstToken, SyntaxNode};
use syntax_bridge::{desugar_doc_comment_text, syntax_node_to_token_tree, DocCommentDesugarMode};
use triomphe::ThinArc;
use crate::name::Name;

View file

@ -2,10 +2,10 @@
use intern::sym;
use itertools::izip;
use mbe::DocCommentDesugarMode;
use rustc_hash::FxHashSet;
use span::{MacroCallId, Span};
use stdx::never;
use syntax_bridge::DocCommentDesugarMode;
use tracing::debug;
use crate::{
@ -209,9 +209,9 @@ struct BasicAdtInfo {
}
fn parse_adt(tt: &tt::Subtree, call_site: Span) -> Result<BasicAdtInfo, ExpandError> {
let (parsed, tm) = &mbe::token_tree_to_syntax_node(
let (parsed, tm) = &syntax_bridge::token_tree_to_syntax_node(
tt,
mbe::TopEntryPoint::MacroItems,
syntax_bridge::TopEntryPoint::MacroItems,
parser::Edition::CURRENT_FIXME,
);
let macro_items = ast::MacroItems::cast(parsed.syntax_node())
@ -268,7 +268,7 @@ fn parse_adt(tt: &tt::Subtree, call_site: Span) -> Result<BasicAdtInfo, ExpandEr
match this {
Some(it) => {
param_type_set.insert(it.as_name());
mbe::syntax_node_to_token_tree(
syntax_bridge::syntax_node_to_token_tree(
it.syntax(),
tm,
call_site,
@ -282,7 +282,7 @@ fn parse_adt(tt: &tt::Subtree, call_site: Span) -> Result<BasicAdtInfo, ExpandEr
};
let bounds = match &param {
ast::TypeOrConstParam::Type(it) => it.type_bound_list().map(|it| {
mbe::syntax_node_to_token_tree(
syntax_bridge::syntax_node_to_token_tree(
it.syntax(),
tm,
call_site,
@ -295,7 +295,7 @@ fn parse_adt(tt: &tt::Subtree, call_site: Span) -> Result<BasicAdtInfo, ExpandEr
let ty = param
.ty()
.map(|ty| {
mbe::syntax_node_to_token_tree(
syntax_bridge::syntax_node_to_token_tree(
ty.syntax(),
tm,
call_site,
@ -316,7 +316,7 @@ fn parse_adt(tt: &tt::Subtree, call_site: Span) -> Result<BasicAdtInfo, ExpandEr
let where_clause = if let Some(w) = where_clause {
w.predicates()
.map(|it| {
mbe::syntax_node_to_token_tree(
syntax_bridge::syntax_node_to_token_tree(
it.syntax(),
tm,
call_site,
@ -353,7 +353,7 @@ fn parse_adt(tt: &tt::Subtree, call_site: Span) -> Result<BasicAdtInfo, ExpandEr
param_type_set.contains(&name).then_some(p)
})
.map(|it| {
mbe::syntax_node_to_token_tree(
syntax_bridge::syntax_node_to_token_tree(
it.syntax(),
tm,
call_site,

View file

@ -4,13 +4,14 @@ use base_db::AnchoredPath;
use cfg::CfgExpr;
use either::Either;
use intern::{sym, Symbol};
use mbe::{parse_exprs_with_sep, parse_to_token_tree, DelimiterKind};
use mbe::{expect_fragment, 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 syntax_bridge::parse_to_token_tree;
use crate::{
builtin::quote::{dollar_crate, quote},
@ -228,20 +229,22 @@ 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_FIXME);
let mut iter = ::tt::iter::TtIter::new(tt);
let cond = expect_fragment(
&mut iter,
parser::PrefixEntryPoint::Expr,
db.crate_graph()[id.lookup(db).krate].edition,
tt::DelimSpan { open: tt.delimiter.open, close: tt.delimiter.close },
);
_ = iter.expect_char(',');
let rest = iter.as_slice();
let dollar_crate = dollar_crate(span);
let expanded = match &*args {
[cond, panic_args @ ..] => {
let comma = tt::Subtree {
delimiter: tt::Delimiter::invisible_spanned(call_site_span),
token_trees: Box::new([tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct {
char: ',',
spacing: tt::Spacing::Alone,
span: call_site_span,
}))]),
};
let cond = cond.clone();
let panic_args = itertools::Itertools::intersperse(panic_args.iter().cloned(), comma);
let expanded = match cond.value {
Some(cond) => {
let panic_args = rest.iter().cloned();
let mac = if use_panic_2021(db, span) {
quote! {call_site_span => #dollar_crate::panic::panic_2021!(##panic_args) }
} else {
@ -253,10 +256,13 @@ fn assert_expand(
}
}}
}
[] => quote! {call_site_span =>{}},
None => quote! {call_site_span =>{}},
};
ExpandResult::ok(expanded)
match cond.err {
Some(err) => ExpandResult::new(expanded, err.into()),
None => ExpandResult::ok(expanded),
}
}
fn file_expand(

View file

@ -1,7 +1,7 @@
//! Defines a unit of change that can applied to the database to get the next
//! state. Changes are transactional.
use base_db::{
salsa::Durability, CrateGraph, CrateId, FileChange, SourceDatabaseExt, SourceRoot,
salsa::Durability, CrateGraph, CrateId, FileChange, SourceRoot, SourceRootDatabase,
TargetLayoutLoadResult, Version,
};
use la_arena::RawIdx;
@ -23,7 +23,7 @@ impl ChangeWithProcMacros {
Self::default()
}
pub fn apply(self, db: &mut (impl ExpandDatabase + SourceDatabaseExt)) {
pub fn apply(self, db: &mut (impl ExpandDatabase + SourceRootDatabase)) {
self.source_change.apply(db);
if let Some(proc_macros) = self.proc_macros {
db.set_proc_macros_with_durability(Arc::new(proc_macros), Durability::HIGH);

View file

@ -3,10 +3,11 @@
use base_db::{salsa, CrateId, SourceDatabase};
use either::Either;
use limit::Limit;
use mbe::{syntax_node_to_token_tree, DocCommentDesugarMode, MatchedArmIndex};
use mbe::MatchedArmIndex;
use rustc_hash::FxHashSet;
use span::{AstIdMap, EditionedFileId, Span, SyntaxContextData, SyntaxContextId};
use syntax::{ast, AstNode, Parse, SyntaxElement, SyntaxError, SyntaxNode, SyntaxToken, T};
use syntax_bridge::{syntax_node_to_token_tree, DocCommentDesugarMode};
use triomphe::Arc;
use crate::{
@ -165,7 +166,7 @@ pub fn expand_speculative(
// Build the subtree and token mapping for the speculative args
let (mut tt, undo_info) = match loc.kind {
MacroCallKind::FnLike { .. } => (
mbe::syntax_node_to_token_tree(
syntax_bridge::syntax_node_to_token_tree(
speculative_args,
span_map,
span,
@ -178,7 +179,7 @@ pub fn expand_speculative(
SyntaxFixupUndoInfo::NONE,
),
MacroCallKind::Attr { .. } if loc.def.is_attribute_derive() => (
mbe::syntax_node_to_token_tree(
syntax_bridge::syntax_node_to_token_tree(
speculative_args,
span_map,
span,
@ -213,7 +214,7 @@ pub fn expand_speculative(
fixups.remove.extend(censor_cfg);
(
mbe::syntax_node_to_token_tree_modified(
syntax_bridge::syntax_node_to_token_tree_modified(
speculative_args,
span_map,
fixups.append,
@ -459,7 +460,7 @@ fn macro_arg(db: &dyn ExpandDatabase, id: MacroCallId) -> MacroArgResult {
return dummy_tt(kind);
}
let mut tt = mbe::syntax_node_to_token_tree(
let mut tt = syntax_bridge::syntax_node_to_token_tree(
tt.syntax(),
map.as_ref(),
span,
@ -515,7 +516,7 @@ fn macro_arg(db: &dyn ExpandDatabase, id: MacroCallId) -> MacroArgResult {
fixups.remove.extend(censor_cfg);
(
mbe::syntax_node_to_token_tree_modified(
syntax_bridge::syntax_node_to_token_tree_modified(
syntax,
map,
fixups.append,
@ -720,13 +721,13 @@ fn token_tree_to_syntax_node(
edition: parser::Edition,
) -> (Parse<SyntaxNode>, ExpansionSpanMap) {
let entry_point = match expand_to {
ExpandTo::Statements => mbe::TopEntryPoint::MacroStmts,
ExpandTo::Items => mbe::TopEntryPoint::MacroItems,
ExpandTo::Pattern => mbe::TopEntryPoint::Pattern,
ExpandTo::Type => mbe::TopEntryPoint::Type,
ExpandTo::Expr => mbe::TopEntryPoint::Expr,
ExpandTo::Statements => syntax_bridge::TopEntryPoint::MacroStmts,
ExpandTo::Items => syntax_bridge::TopEntryPoint::MacroItems,
ExpandTo::Pattern => syntax_bridge::TopEntryPoint::Pattern,
ExpandTo::Type => syntax_bridge::TopEntryPoint::Type,
ExpandTo::Expr => syntax_bridge::TopEntryPoint::Expr,
};
mbe::token_tree_to_syntax_node(tt, entry_point, edition)
syntax_bridge::token_tree_to_syntax_node(tt, entry_point, edition)
}
fn check_tt_count(tt: &tt::Subtree) -> Result<(), ExpandResult<()>> {

View file

@ -2,10 +2,10 @@
use base_db::CrateId;
use intern::sym;
use mbe::DocCommentDesugarMode;
use span::{Edition, MacroCallId, Span, SyntaxContextId};
use stdx::TupleExt;
use syntax::{ast, AstNode};
use syntax_bridge::DocCommentDesugarMode;
use triomphe::Arc;
use crate::{
@ -112,7 +112,7 @@ impl DeclarativeMacroExpander {
ast::Macro::MacroRules(macro_rules) => (
match macro_rules.token_tree() {
Some(arg) => {
let tt = mbe::syntax_node_to_token_tree(
let tt = syntax_bridge::syntax_node_to_token_tree(
arg.syntax(),
map.as_ref(),
map.span_for_range(
@ -135,14 +135,14 @@ impl DeclarativeMacroExpander {
let span =
map.span_for_range(macro_def.macro_token().unwrap().text_range());
let args = macro_def.args().map(|args| {
mbe::syntax_node_to_token_tree(
syntax_bridge::syntax_node_to_token_tree(
args.syntax(),
map.as_ref(),
span,
DocCommentDesugarMode::Mbe,
)
});
let body = mbe::syntax_node_to_token_tree(
let body = syntax_bridge::syntax_node_to_token_tree(
body.syntax(),
map.as_ref(),
span,

View file

@ -19,9 +19,9 @@
//!
//! See the full discussion : <https://rust-lang.zulipchat.com/#narrow/stream/131828-t-compiler/topic/Eager.20expansion.20of.20built-in.20macros>
use base_db::CrateId;
use mbe::DocCommentDesugarMode;
use span::SyntaxContextId;
use syntax::{ted, Parse, SyntaxElement, SyntaxNode, TextSize, WalkEvent};
use syntax_bridge::DocCommentDesugarMode;
use triomphe::Arc;
use crate::{
@ -82,7 +82,7 @@ pub fn expand_eager_macro_input(
return ExpandResult { value: None, err };
};
let mut subtree = mbe::syntax_node_to_token_tree(
let mut subtree = syntax_bridge::syntax_node_to_token_tree(
&expanded_eager_input,
arg_map,
span,

View file

@ -2,7 +2,6 @@
//! 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;
use span::{
@ -14,6 +13,7 @@ use syntax::{
ast::{self, AstNode, HasLoopBody},
match_ast, SyntaxElement, SyntaxKind, SyntaxNode, TextRange, TextSize,
};
use syntax_bridge::DocCommentDesugarMode;
use triomphe::Arc;
use tt::Spacing;
@ -76,7 +76,8 @@ pub(crate) fn fixup_syntax(
if can_handle_error(&node) && has_error_to_handle(&node) {
remove.insert(node.clone().into());
// the node contains an error node, we have to completely replace it by something valid
let original_tree = mbe::syntax_node_to_token_tree(&node, span_map, call_site, mode);
let original_tree =
syntax_bridge::syntax_node_to_token_tree(&node, span_map, call_site, mode);
let idx = original.len() as u32;
original.push(original_tree);
let span = span_map.span_for_range(node_range);
@ -434,9 +435,9 @@ fn reverse_fixups_(tt: &mut Subtree, undo_info: &[Subtree]) {
#[cfg(test)]
mod tests {
use expect_test::{expect, Expect};
use mbe::DocCommentDesugarMode;
use span::{Edition, EditionedFileId, FileId};
use syntax::TextRange;
use syntax_bridge::DocCommentDesugarMode;
use triomphe::Arc;
use crate::{
@ -483,7 +484,7 @@ mod tests {
span_map.span_for_range(TextRange::empty(0.into())),
DocCommentDesugarMode::Mbe,
);
let mut tt = mbe::syntax_node_to_token_tree_modified(
let mut tt = syntax_bridge::syntax_node_to_token_tree_modified(
&parsed.syntax_node(),
span_map.as_ref(),
fixups.append,
@ -498,9 +499,9 @@ mod tests {
expect.assert_eq(&actual);
// the fixed-up tree should be syntactically valid
let (parse, _) = mbe::token_tree_to_syntax_node(
let (parse, _) = syntax_bridge::token_tree_to_syntax_node(
&tt,
::mbe::TopEntryPoint::MacroItems,
syntax_bridge::TopEntryPoint::MacroItems,
parser::Edition::CURRENT,
);
assert!(
@ -513,7 +514,7 @@ mod tests {
// the fixed-up + reversed version should be equivalent to the original input
// modulo token IDs and `Punct`s' spacing.
let original_as_tt = mbe::syntax_node_to_token_tree(
let original_as_tt = syntax_bridge::syntax_node_to_token_tree(
&parsed.syntax_node(),
span_map.as_ref(),
span_map.span_for_range(TextRange::empty(0.into())),

View file

@ -176,7 +176,12 @@ impl ExpandErrorKind {
&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),
None => (
format!(
"internal error: proc-macro map is missing error entry for crate {def_crate:?}"
),
true,
),
}
}
ExpandErrorKind::MacroDefinition => {

View file

@ -28,13 +28,13 @@ pub enum SpanMapRef<'a> {
RealSpanMap(&'a RealSpanMap),
}
impl mbe::SpanMapper<Span> for SpanMap {
impl syntax_bridge::SpanMapper<Span> for SpanMap {
fn span_for(&self, range: TextRange) -> Span {
self.span_for_range(range)
}
}
impl mbe::SpanMapper<Span> for SpanMapRef<'_> {
impl syntax_bridge::SpanMapper<Span> for SpanMapRef<'_> {
fn span_for(&self, range: TextRange) -> Span {
self.span_for_range(range)
}

View file

@ -1,7 +1,8 @@
[package]
name = "hir-ty"
version = "0.0.0"
description = "TBD"
repository.workspace = true
description = "The type system for rust-analyzer."
authors.workspace = true
edition.workspace = true

View file

@ -275,7 +275,7 @@ impl chalk_solve::RustIrDatabase<Interner> for ChalkContext<'_> {
};
chalk_ir::Binders::new(binders, bound)
}
crate::ImplTraitId::AssociatedTypeImplTrait(alias, idx) => {
crate::ImplTraitId::TypeAliasImplTrait(alias, idx) => {
let datas = self
.db
.type_alias_impl_traits(alias)

View file

@ -276,7 +276,7 @@ impl TyExt for Ty {
data.substitute(Interner, &subst).into_value_and_skipped_binders().0
})
}
ImplTraitId::AssociatedTypeImplTrait(alias, idx) => {
ImplTraitId::TypeAliasImplTrait(alias, idx) => {
db.type_alias_impl_traits(alias).map(|it| {
let data =
(*it).as_ref().map(|rpit| rpit.impl_traits[idx].bounds.clone());
@ -295,7 +295,7 @@ impl TyExt for Ty {
data.substitute(Interner, &opaque_ty.substitution)
})
}
ImplTraitId::AssociatedTypeImplTrait(alias, idx) => {
ImplTraitId::TypeAliasImplTrait(alias, idx) => {
db.type_alias_impl_traits(alias).map(|it| {
let data =
(*it).as_ref().map(|rpit| rpit.impl_traits[idx].bounds.clone());

View file

@ -4,20 +4,25 @@
use std::fmt;
use chalk_solve::rust_ir::AdtKind;
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_def::{
lang_item::LangItem,
resolver::{HasResolver, ValueNs},
AdtId, AssocItemId, DefWithBodyId, HasModule, ItemContainerId, Lookup,
};
use intern::sym;
use itertools::Itertools;
use rustc_hash::FxHashSet;
use rustc_pattern_analysis::constructor::Constructor;
use syntax::{ast, AstNode};
use syntax::{
ast::{self, UnaryOp},
AstNode,
};
use tracing::debug;
use triomphe::Arc;
use typed_arena::Arena;
use crate::Interner;
use crate::{
db::HirDatabase,
diagnostics::match_check::{
@ -25,7 +30,7 @@ use crate::{
pat_analysis::{self, DeconstructedPat, MatchCheckCtx, WitnessPat},
},
display::HirDisplay,
InferenceResult, Ty, TyExt,
Adjust, InferenceResult, Interner, Ty, TyExt, TyKind,
};
pub(crate) use hir_def::{
@ -117,7 +122,7 @@ impl ExprValidator {
Expr::If { .. } => {
self.check_for_unnecessary_else(id, expr, db);
}
Expr::Block { .. } => {
Expr::Block { .. } | Expr::Async { .. } | Expr::Unsafe { .. } => {
self.validate_block(db, expr);
}
_ => {}
@ -236,7 +241,12 @@ impl ExprValidator {
return;
}
let report = match cx.compute_match_usefulness(m_arms.as_slice(), scrut_ty.clone()) {
let known_valid_scrutinee = Some(self.is_known_valid_scrutinee(scrutinee_expr, db));
let report = match cx.compute_match_usefulness(
m_arms.as_slice(),
scrut_ty.clone(),
known_valid_scrutinee,
) {
Ok(report) => report,
Err(()) => return,
};
@ -253,8 +263,52 @@ impl ExprValidator {
}
}
// [rustc's `is_known_valid_scrutinee`](https://github.com/rust-lang/rust/blob/c9bd03cb724e13cca96ad320733046cbdb16fbbe/compiler/rustc_mir_build/src/thir/pattern/check_match.rs#L288)
//
// While the above function in rustc uses thir exprs, r-a doesn't have them.
// So, the logic here is getting same result as "hir lowering + match with lowered thir"
// with "hir only"
fn is_known_valid_scrutinee(&self, scrutinee_expr: ExprId, db: &dyn HirDatabase) -> bool {
if self
.infer
.expr_adjustments
.get(&scrutinee_expr)
.is_some_and(|adjusts| adjusts.iter().any(|a| matches!(a.kind, Adjust::Deref(..))))
{
return false;
}
match &self.body[scrutinee_expr] {
Expr::UnaryOp { op: UnaryOp::Deref, .. } => false,
Expr::Path(path) => {
let value_or_partial = self
.owner
.resolver(db.upcast())
.resolve_path_in_value_ns_fully(db.upcast(), path);
value_or_partial.map_or(true, |v| !matches!(v, ValueNs::StaticId(_)))
}
Expr::Field { expr, .. } => match self.infer.type_of_expr[*expr].kind(Interner) {
TyKind::Adt(adt, ..)
if db.adt_datum(self.owner.krate(db.upcast()), *adt).kind == AdtKind::Union =>
{
false
}
_ => self.is_known_valid_scrutinee(*expr, db),
},
Expr::Index { base, .. } => self.is_known_valid_scrutinee(*base, db),
Expr::Cast { expr, .. } => self.is_known_valid_scrutinee(*expr, db),
Expr::Missing => false,
_ => true,
}
}
fn validate_block(&mut self, db: &dyn HirDatabase, expr: &Expr) {
let Expr::Block { statements, .. } = expr else { return };
let (Expr::Block { statements, .. }
| Expr::Async { statements, .. }
| Expr::Unsafe { statements, .. }) = expr
else {
return;
};
let pattern_arena = Arena::new();
let cx = MatchCheckCtx::new(self.owner.module(db.upcast()), self.owner, db);
for stmt in &**statements {
@ -280,7 +334,7 @@ impl ExprValidator {
has_guard: false,
arm_data: (),
};
let report = match cx.compute_match_usefulness(&[match_arm], ty.clone()) {
let report = match cx.compute_match_usefulness(&[match_arm], ty.clone(), None) {
Ok(v) => v,
Err(e) => {
debug!(?e, "match usefulness error");

View file

@ -69,22 +69,20 @@ pub(crate) struct MatchCheckCtx<'db> {
body: DefWithBodyId,
pub(crate) db: &'db dyn HirDatabase,
exhaustive_patterns: bool,
min_exhaustive_patterns: bool,
}
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(&sym::exhaustive_patterns);
let min_exhaustive_patterns =
def_map.is_unstable_feature_enabled(&sym::min_exhaustive_patterns);
Self { module, body, db, exhaustive_patterns, min_exhaustive_patterns }
Self { module, body, db, exhaustive_patterns }
}
pub(crate) fn compute_match_usefulness(
&self,
arms: &[MatchArm<'db>],
scrut_ty: Ty,
known_valid_scrutinee: Option<bool>,
) -> Result<UsefulnessReport<'db, Self>, ()> {
if scrut_ty.contains_unknown() {
return Err(());
@ -95,8 +93,7 @@ impl<'db> MatchCheckCtx<'db> {
}
}
// FIXME: Determine place validity correctly. For now, err on the safe side.
let place_validity = PlaceValidity::MaybeInvalid;
let place_validity = PlaceValidity::from_bool(known_valid_scrutinee.unwrap_or(true));
// Measured to take ~100ms on modern hardware.
let complexity_limit = Some(500000);
compute_match_usefulness(self, arms, scrut_ty, place_validity, complexity_limit)
@ -307,7 +304,8 @@ impl<'db> MatchCheckCtx<'db> {
&Str(void) => match void {},
Wildcard | NonExhaustive | Hidden | PrivateUninhabited => PatKind::Wild,
Never => PatKind::Never,
Missing | F32Range(..) | F64Range(..) | Opaque(..) | Or => {
Missing | F16Range(..) | F32Range(..) | F64Range(..) | F128Range(..) | Opaque(..)
| Or => {
never!("can't convert to pattern: {:?}", pat.ctor());
PatKind::Wild
}
@ -327,9 +325,6 @@ impl<'db> PatCx for MatchCheckCtx<'db> {
fn is_exhaustive_patterns_feature_on(&self) -> bool {
self.exhaustive_patterns
}
fn is_min_exhaustive_patterns_feature_on(&self) -> bool {
self.min_exhaustive_patterns
}
fn ctor_arity(
&self,
@ -356,8 +351,9 @@ impl<'db> PatCx for MatchCheckCtx<'db> {
},
Ref => 1,
Slice(..) => unimplemented!(),
Never | Bool(..) | IntRange(..) | F32Range(..) | F64Range(..) | Str(..)
| Opaque(..) | NonExhaustive | PrivateUninhabited | Hidden | Missing | Wildcard => 0,
Never | Bool(..) | IntRange(..) | F16Range(..) | F32Range(..) | F64Range(..)
| F128Range(..) | Str(..) | Opaque(..) | NonExhaustive | PrivateUninhabited
| Hidden | Missing | Wildcard => 0,
Or => {
never!("The `Or` constructor doesn't have a fixed arity");
0
@ -419,8 +415,9 @@ impl<'db> PatCx for MatchCheckCtx<'db> {
}
},
Slice(_) => unreachable!("Found a `Slice` constructor in match checking"),
Never | Bool(..) | IntRange(..) | F32Range(..) | F64Range(..) | Str(..)
| Opaque(..) | NonExhaustive | PrivateUninhabited | Hidden | Missing | Wildcard => {
Never | Bool(..) | IntRange(..) | F16Range(..) | F32Range(..) | F64Range(..)
| F128Range(..) | Str(..) | Opaque(..) | NonExhaustive | PrivateUninhabited
| Hidden | Missing | Wildcard => {
smallvec![]
}
Or => {

View file

@ -17,7 +17,7 @@ pub fn missing_unsafe(db: &dyn HirDatabase, def: DefWithBodyId) -> Vec<ExprId> {
let mut res = Vec::new();
let is_unsafe = match def {
DefWithBodyId::FunctionId(it) => db.function_data(it).has_unsafe_kw(),
DefWithBodyId::FunctionId(it) => db.function_data(it).is_unsafe(),
DefWithBodyId::StaticId(_)
| DefWithBodyId::ConstId(_)
| DefWithBodyId::VariantId(_)

View file

@ -1022,16 +1022,16 @@ impl HirDisplay for Ty {
// We print all params except implicit impl Trait params. Still a bit weird; should we leave out parent and self?
if parameters.len() - impl_ > 0 {
// `parameters` are in the order of fn's params (including impl traits), fn's lifetimes
let without_impl = self_param as usize + type_ + const_ + lifetime;
// parent's params (those from enclosing impl or trait, if any).
let (fn_params, other) =
parameters.split_at(self_param as usize + type_ + const_ + lifetime);
let (_impl, parent_params) = other.split_at(impl_);
let (fn_params, parent_params) = parameters.split_at(without_impl + impl_);
debug_assert_eq!(parent_params.len(), parent_len);
let parent_params =
generic_args_sans_defaults(f, Some(generic_def_id), parent_params);
let fn_params =
generic_args_sans_defaults(f, Some(generic_def_id), fn_params);
&generic_args_sans_defaults(f, Some(generic_def_id), fn_params)
[0..without_impl];
write!(f, "<")?;
hir_fmt_generic_arguments(f, parent_params, None)?;
@ -1069,6 +1069,7 @@ impl HirDisplay for Ty {
module_id,
PrefixKind::Plain,
false,
// FIXME: no_std Cfg?
ImportPathConfig {
prefer_no_std: false,
prefer_prelude: true,
@ -1151,11 +1152,10 @@ impl HirDisplay for Ty {
)?;
// FIXME: it would maybe be good to distinguish this from the alias type (when debug printing), and to show the substitution
}
ImplTraitId::AssociatedTypeImplTrait(alias, idx) => {
ImplTraitId::TypeAliasImplTrait(alias, idx) => {
let datas =
db.type_alias_impl_traits(alias).expect("impl trait id without data");
let data =
(*datas).as_ref().map(|rpit| rpit.impl_traits[idx].bounds.clone());
let data = (*datas).as_ref().map(|it| it.impl_traits[idx].bounds.clone());
let bounds = data.substitute(Interner, &parameters);
let krate = alias.krate(db.upcast());
write_bounds_like_dyn_trait_with_prefix(
@ -1338,7 +1338,7 @@ impl HirDisplay for Ty {
SizedByDefault::Sized { anchor: krate },
)?;
}
ImplTraitId::AssociatedTypeImplTrait(alias, idx) => {
ImplTraitId::TypeAliasImplTrait(alias, idx) => {
let datas =
db.type_alias_impl_traits(alias).expect("impl trait id without data");
let data =

View file

@ -36,15 +36,14 @@ use hir_def::{
body::Body,
builtin_type::{BuiltinInt, BuiltinType, BuiltinUint},
data::{ConstData, StaticData},
hir::LabelId,
hir::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, PatId},
hir::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, LabelId, PatId},
lang_item::{LangItem, LangItemTarget},
layout::Integer,
path::{ModPath, Path},
resolver::{HasResolver, ResolveValueResult, Resolver, TypeNs, ValueNs},
type_ref::{LifetimeRef, TypeRef},
AdtId, AssocItemId, DefWithBodyId, FieldId, FunctionId, ItemContainerId, Lookup, TraitId,
TupleFieldId, TupleId, TypeAliasId, VariantId,
AdtId, AssocItemId, DefWithBodyId, FieldId, FunctionId, ImplId, ItemContainerId, Lookup,
TraitId, TupleFieldId, TupleId, TypeAliasId, VariantId,
};
use hir_expand::name::Name;
use indexmap::IndexSet;
@ -785,14 +784,19 @@ impl<'a> InferenceContext<'a> {
fn collect_const(&mut self, data: &ConstData) {
let return_ty = self.make_ty(&data.type_ref);
// Constants might be associated items that define ATPITs.
self.insert_atpit_coercion_table(iter::once(&return_ty));
// Constants might be defining usage sites of TAITs.
self.make_tait_coercion_table(iter::once(&return_ty));
self.return_ty = return_ty;
}
fn collect_static(&mut self, data: &StaticData) {
self.return_ty = self.make_ty(&data.type_ref);
let return_ty = self.make_ty(&data.type_ref);
// Statics might be defining usage sites of TAITs.
self.make_tait_coercion_table(iter::once(&return_ty));
self.return_ty = return_ty;
}
fn collect_fn(&mut self, func: FunctionId) {
@ -857,11 +861,11 @@ impl<'a> InferenceContext<'a> {
self.return_ty = self.normalize_associated_types_in(return_ty);
self.return_coercion = Some(CoerceMany::new(self.return_ty.clone()));
// Functions might be associated items that define ATPITs.
// To define an ATPITs, that ATPIT must appear in the function's signatures.
// Functions might be defining usage sites of TAITs.
// To define an TAITs, that TAIT must appear in the function's signatures.
// So, it suffices to check for params and return types.
params_and_ret_tys.push(self.return_ty.clone());
self.insert_atpit_coercion_table(params_and_ret_tys.iter());
self.make_tait_coercion_table(params_and_ret_tys.iter());
}
fn insert_inference_vars_for_impl_trait<T>(&mut self, t: T, placeholders: Substitution) -> T
@ -880,7 +884,7 @@ impl<'a> InferenceContext<'a> {
ImplTraitId::ReturnTypeImplTrait(def, idx) => {
(self.db.return_type_impl_traits(def), idx)
}
ImplTraitId::AssociatedTypeImplTrait(def, idx) => {
ImplTraitId::TypeAliasImplTrait(def, idx) => {
(self.db.type_alias_impl_traits(def), idx)
}
_ => unreachable!(),
@ -909,23 +913,25 @@ impl<'a> InferenceContext<'a> {
}
/// The coercion of a non-inference var into an opaque type should fail,
/// but not in the defining sites of the ATPITs.
/// In such cases, we insert an proxy inference var for each ATPIT,
/// and coerce into it instead of ATPIT itself.
/// but not in the defining sites of the TAITs.
/// In such cases, we insert an proxy inference var for each TAIT,
/// and coerce into it instead of TAIT itself.
///
/// The inference var stretagy is effective because;
///
/// - It can still unify types that coerced into ATPIT
/// - It can still unify types that coerced into TAITs
/// - We are pushing `impl Trait` bounds into it
///
/// This function inserts a map that maps the opaque type to that proxy inference var.
fn insert_atpit_coercion_table<'b>(&mut self, tys: impl Iterator<Item = &'b Ty>) {
struct OpaqueTyCollector<'a, 'b> {
fn make_tait_coercion_table<'b>(&mut self, tait_candidates: impl Iterator<Item = &'b Ty>) {
struct TypeAliasImplTraitCollector<'a, 'b> {
db: &'b dyn HirDatabase,
table: &'b mut InferenceTable<'a>,
opaque_tys: FxHashMap<OpaqueTyId, Ty>,
assocs: FxHashMap<OpaqueTyId, (ImplId, Ty)>,
non_assocs: FxHashMap<OpaqueTyId, Ty>,
}
impl<'a, 'b> TypeVisitor<Interner> for OpaqueTyCollector<'a, 'b> {
impl<'a, 'b> TypeVisitor<Interner> for TypeAliasImplTraitCollector<'a, 'b> {
type BreakTy = ();
fn as_dyn(&mut self) -> &mut dyn TypeVisitor<Interner, BreakTy = Self::BreakTy> {
@ -944,59 +950,105 @@ impl<'a> InferenceContext<'a> {
let ty = self.table.resolve_ty_shallow(ty);
if let TyKind::OpaqueType(id, _) = ty.kind(Interner) {
self.opaque_tys.insert(*id, ty.clone());
if let ImplTraitId::TypeAliasImplTrait(alias_id, _) =
self.db.lookup_intern_impl_trait_id((*id).into())
{
let loc = self.db.lookup_intern_type_alias(alias_id);
match loc.container {
ItemContainerId::ImplId(impl_id) => {
self.assocs.insert(*id, (impl_id, ty.clone()));
}
ItemContainerId::ModuleId(..) | ItemContainerId::ExternBlockId(..) => {
self.non_assocs.insert(*id, ty.clone());
}
_ => {}
}
}
}
ty.super_visit_with(self, outer_binder)
}
}
// Early return if this is not happening inside the impl block
let impl_id = if let Some(impl_id) = self.resolver.impl_def() {
impl_id
} else {
return;
let mut collector = TypeAliasImplTraitCollector {
db: self.db,
table: &mut self.table,
assocs: FxHashMap::default(),
non_assocs: FxHashMap::default(),
};
let assoc_tys: FxHashSet<_> = self
.db
.impl_data(impl_id)
.items
.iter()
.filter_map(|item| match item {
AssocItemId::TypeAliasId(alias) => Some(*alias),
_ => None,
})
.collect();
if assoc_tys.is_empty() {
return;
}
let mut collector =
OpaqueTyCollector { table: &mut self.table, opaque_tys: FxHashMap::default() };
for ty in tys {
for ty in tait_candidates {
ty.visit_with(collector.as_dyn(), DebruijnIndex::INNERMOST);
}
let atpit_coercion_table: FxHashMap<_, _> = collector
.opaque_tys
.into_iter()
.filter_map(|(opaque_ty_id, ty)| {
if let ImplTraitId::AssociatedTypeImplTrait(alias_id, _) =
self.db.lookup_intern_impl_trait_id(opaque_ty_id.into())
{
if assoc_tys.contains(&alias_id) {
let alias_placeholders = TyBuilder::placeholder_subst(self.db, alias_id);
let ty = self.insert_inference_vars_for_impl_trait(ty, alias_placeholders);
return Some((opaque_ty_id, ty));
}
}
None
// Non-assoc TAITs can be define-used everywhere as long as they are
// in function signatures or const types, etc
let mut taits = collector.non_assocs;
// assoc TAITs(ATPITs) can be only define-used inside their impl block.
// They cannot be define-used in inner items like in the following;
//
// ```
// impl Trait for Struct {
// type Assoc = impl Default;
//
// fn assoc_fn() -> Self::Assoc {
// let foo: Self::Assoc = true; // Allowed here
//
// fn inner() -> Self::Assoc {
// false // Not allowed here
// }
//
// foo
// }
// }
// ```
let impl_id = match self.owner {
DefWithBodyId::FunctionId(it) => {
let loc = self.db.lookup_intern_function(it);
if let ItemContainerId::ImplId(impl_id) = loc.container {
Some(impl_id)
} else {
None
}
}
DefWithBodyId::ConstId(it) => {
let loc = self.db.lookup_intern_const(it);
if let ItemContainerId::ImplId(impl_id) = loc.container {
Some(impl_id)
} else {
None
}
}
_ => None,
};
if let Some(impl_id) = impl_id {
taits.extend(collector.assocs.into_iter().filter_map(|(id, (impl_, ty))| {
if impl_ == impl_id {
Some((id, ty))
} else {
None
}
}));
}
let tait_coercion_table: FxHashMap<_, _> = taits
.into_iter()
.filter_map(|(id, ty)| {
if let ImplTraitId::TypeAliasImplTrait(alias_id, _) =
self.db.lookup_intern_impl_trait_id(id.into())
{
let subst = TyBuilder::placeholder_subst(self.db, alias_id);
let ty = self.insert_inference_vars_for_impl_trait(ty, subst);
Some((id, ty))
} else {
None
}
})
.collect();
if !atpit_coercion_table.is_empty() {
self.table.atpit_coercion_table = Some(atpit_coercion_table);
if !tait_coercion_table.is_empty() {
self.table.tait_coercion_table = Some(tait_coercion_table);
}
}

View file

@ -276,16 +276,16 @@ impl InferenceTable<'_> {
return success(simple(Adjust::NeverToAny)(to_ty.clone()), to_ty.clone(), vec![]);
}
// If we are coercing into an ATPIT, coerce into its proxy inference var, instead.
// If we are coercing into a TAIT, coerce into its proxy inference var, instead.
let mut to_ty = to_ty;
let _to;
if let Some(atpit_table) = &self.atpit_coercion_table {
if let Some(tait_table) = &self.tait_coercion_table {
if let TyKind::OpaqueType(opaque_ty_id, _) = to_ty.kind(Interner) {
if !matches!(
from_ty.kind(Interner),
TyKind::InferenceVar(..) | TyKind::OpaqueType(..)
) {
if let Some(ty) = atpit_table.get(opaque_ty_id) {
if let Some(ty) = tait_table.get(opaque_ty_id) {
_to = ty.clone();
to_ty = &_to;
}

View file

@ -1759,13 +1759,14 @@ impl InferenceContext<'_> {
skip_indices: &[u32],
is_varargs: bool,
) {
if args.len() != param_tys.len() + skip_indices.len() && !is_varargs {
let arg_count_mismatch = args.len() != param_tys.len() + skip_indices.len() && !is_varargs;
if arg_count_mismatch {
self.push_diagnostic(InferenceDiagnostic::MismatchedArgCount {
call_expr: expr,
expected: param_tys.len() + skip_indices.len(),
found: args.len(),
});
}
};
// Quoting https://github.com/rust-lang/rust/blob/6ef275e6c3cb1384ec78128eceeb4963ff788dca/src/librustc_typeck/check/mod.rs#L3325 --
// We do this in a pretty awful way: first we type-check any arguments
@ -1819,7 +1820,7 @@ impl InferenceContext<'_> {
// The function signature may contain some unknown types, so we need to insert
// type vars here to avoid type mismatch false positive.
let coercion_target = self.insert_type_vars(coercion_target);
if self.coerce(Some(arg), &ty, &coercion_target).is_err() {
if self.coerce(Some(arg), &ty, &coercion_target).is_err() && !arg_count_mismatch {
self.result.type_mismatches.insert(
arg.into(),
TypeMismatch { expected: coercion_target, actual: ty.clone() },

View file

@ -224,7 +224,7 @@ type ChalkInferenceTable = chalk_solve::infer::InferenceTable<Interner>;
pub(crate) struct InferenceTable<'a> {
pub(crate) db: &'a dyn HirDatabase,
pub(crate) trait_env: Arc<TraitEnvironment>,
pub(crate) atpit_coercion_table: Option<FxHashMap<OpaqueTyId, Ty>>,
pub(crate) tait_coercion_table: Option<FxHashMap<OpaqueTyId, Ty>>,
var_unification_table: ChalkInferenceTable,
type_variable_table: SmallVec<[TypeVariableFlags; 16]>,
pending_obligations: Vec<Canonicalized<InEnvironment<Goal>>>,
@ -244,7 +244,7 @@ impl<'a> InferenceTable<'a> {
InferenceTable {
db,
trait_env,
atpit_coercion_table: None,
tait_coercion_table: None,
var_unification_table: ChalkInferenceTable::new(),
type_variable_table: SmallVec::new(),
pending_obligations: Vec::new(),

View file

@ -391,7 +391,7 @@ pub fn layout_of_ty_query(
let infer = db.infer(func.into());
return db.layout_of_ty(infer.type_of_rpit[idx].clone(), trait_env);
}
crate::ImplTraitId::AssociatedTypeImplTrait(..) => {
crate::ImplTraitId::TypeAliasImplTrait(..) => {
return Err(LayoutError::NotImplemented);
}
crate::ImplTraitId::AsyncBlockTypeImplTrait(_, _) => {

View file

@ -595,7 +595,7 @@ impl TypeFoldable<Interner> for CallableSig {
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
pub enum ImplTraitId {
ReturnTypeImplTrait(hir_def::FunctionId, ImplTraitIdx),
AssociatedTypeImplTrait(hir_def::TypeAliasId, ImplTraitIdx),
TypeAliasImplTrait(hir_def::TypeAliasId, ImplTraitIdx),
AsyncBlockTypeImplTrait(hir_def::DefWithBodyId, ExprId),
}
impl InternValueTrivial for ImplTraitId {}

View file

@ -341,7 +341,7 @@ impl<'a> TyLoweringContext<'a> {
let impl_trait_id = origin.either(
|f| ImplTraitId::ReturnTypeImplTrait(f, idx),
|a| ImplTraitId::AssociatedTypeImplTrait(a, idx),
|a| ImplTraitId::TypeAliasImplTrait(a, idx),
);
let opaque_ty_id = self.db.intern_impl_trait_id(impl_trait_id).into();
let generics =
@ -1857,7 +1857,7 @@ fn fn_sig_for_fn(db: &dyn HirDatabase, def: FunctionId) -> PolyFnSig {
params,
ret,
data.is_varargs(),
if data.has_unsafe_kw() { Safety::Unsafe } else { Safety::Safe },
if data.is_unsafe() { Safety::Unsafe } else { Safety::Safe },
data.abi.as_ref().map_or(FnAbi::Rust, FnAbi::from_symbol),
);
make_binders(db, &generics, sig)
@ -2131,7 +2131,6 @@ pub(crate) fn type_alias_impl_traits(
if let Some(type_ref) = &data.type_ref {
let _ty = ctx.lower_ty(type_ref);
}
let generics = generics(db.upcast(), def.into());
let type_alias_impl_traits = ImplTraits {
impl_traits: match ctx.impl_trait_mode {
ImplTraitLoweringState::Opaque(x) => x.into_inner(),
@ -2141,6 +2140,7 @@ pub(crate) fn type_alias_impl_traits(
if type_alias_impl_traits.impl_traits.is_empty() {
None
} else {
let generics = generics(db.upcast(), def.into());
Some(Arc::new(make_binders(db, &generics, type_alias_impl_traits)))
}
}

View file

@ -82,8 +82,8 @@ impl FallibleTypeFolder<Interner> for Filler<'_> {
};
filler.try_fold_ty(infer.type_of_rpit[idx].clone(), outer_binder)
}
crate::ImplTraitId::AssociatedTypeImplTrait(..) => {
not_supported!("associated type impl trait");
crate::ImplTraitId::TypeAliasImplTrait(..) => {
not_supported!("type alias impl trait");
}
crate::ImplTraitId::AsyncBlockTypeImplTrait(_, _) => {
not_supported!("async block impl trait");

View file

@ -15,7 +15,7 @@ use test_utils::extract_annotations;
use triomphe::Arc;
#[salsa::database(
base_db::SourceDatabaseExtStorage,
base_db::SourceRootDatabaseStorage,
base_db::SourceDatabaseStorage,
hir_expand::db::ExpandDatabaseStorage,
hir_def::db::InternDatabaseStorage,
@ -75,9 +75,6 @@ impl salsa::ParallelDatabase for TestDB {
impl panic::RefUnwindSafe for TestDB {}
impl FileLoader for TestDB {
fn file_text(&self, file_id: FileId) -> Arc<str> {
FileLoaderDelegate(self).file_text(file_id)
}
fn resolve_path(&self, path: AnchoredPath<'_>) -> Option<FileId> {
FileLoaderDelegate(self).resolve_path(path)
}

View file

@ -9,10 +9,11 @@ mod patterns;
mod regression;
mod simple;
mod traits;
mod type_alias_impl_traits;
use std::env;
use base_db::SourceDatabaseExt2 as _;
use base_db::SourceDatabaseFileInputExt as _;
use expect_test::Expect;
use hir_def::{
body::{Body, BodySourceMap, SyntheticSyntax},

View file

@ -1,4 +1,4 @@
use base_db::SourceDatabaseExt2 as _;
use base_db::SourceDatabaseFileInputExt as _;
use test_fixture::WithFixture;
use crate::{db::HirDatabase, test_db::TestDB};

View file

@ -2122,3 +2122,22 @@ fn test() {
"#,
)
}
#[test]
fn issue_17191() {
check_types(
r#"
trait A {
type Item;
}
trait B<T> {}
fn foo<T: B<impl A>>() {}
fn test() {
let f = foo;
//^ fn foo<{unknown}>()
}"#,
);
}

View file

@ -4691,119 +4691,6 @@ fn f<T: Send, U>() {
);
}
#[test]
fn associated_type_impl_trait() {
check_types(
r#"
trait Foo {}
struct S1;
impl Foo for S1 {}
trait Bar {
type Item;
fn bar(&self) -> Self::Item;
}
struct S2;
impl Bar for S2 {
type Item = impl Foo;
fn bar(&self) -> Self::Item {
S1
}
}
fn test() {
let x = S2.bar();
//^ impl Foo + ?Sized
}
"#,
);
}
#[test]
fn associated_type_impl_traits_complex() {
check_types(
r#"
struct Unary<T>(T);
struct Binary<T, U>(T, U);
trait Foo {}
struct S1;
impl Foo for S1 {}
trait Bar {
type Item;
fn bar(&self) -> Unary<Self::Item>;
}
struct S2;
impl Bar for S2 {
type Item = Unary<impl Foo>;
fn bar(&self) -> Unary<<Self as Bar>::Item> {
Unary(Unary(S1))
}
}
trait Baz {
type Target1;
type Target2;
fn baz(&self) -> Binary<Self::Target1, Self::Target2>;
}
struct S3;
impl Baz for S3 {
type Target1 = impl Foo;
type Target2 = Unary<impl Bar>;
fn baz(&self) -> Binary<Self::Target1, Self::Target2> {
Binary(S1, Unary(S2))
}
}
fn test() {
let x = S3.baz();
//^ Binary<impl Foo + ?Sized, Unary<impl Bar + ?Sized>>
let y = x.1.0.bar();
//^ Unary<Bar::Item<impl Bar + ?Sized>>
}
"#,
);
}
#[test]
fn associated_type_with_impl_trait_in_tuple() {
check_no_mismatches(
r#"
pub trait Iterator {
type Item;
}
pub trait Value {}
fn bar<I: Iterator<Item = (usize, impl Value)>>() {}
fn foo() {
bar();
}
"#,
);
}
#[test]
fn associated_type_with_impl_trait_in_nested_tuple() {
check_no_mismatches(
r#"
pub trait Iterator {
type Item;
}
pub trait Value {}
fn bar<I: Iterator<Item = ((impl Value, usize), u32)>>() {}
fn foo() {
bar();
}
"#,
);
}
#[test]
fn dyn_trait_with_lifetime_in_rpit() {
check_types(

View file

@ -0,0 +1,161 @@
use expect_test::expect;
use super::{check_infer_with_mismatches, check_no_mismatches, check_types};
#[test]
fn associated_type_impl_trait() {
check_types(
r#"
trait Foo {}
struct S1;
impl Foo for S1 {}
trait Bar {
type Item;
fn bar(&self) -> Self::Item;
}
struct S2;
impl Bar for S2 {
type Item = impl Foo;
fn bar(&self) -> Self::Item {
S1
}
}
fn test() {
let x = S2.bar();
//^ impl Foo + ?Sized
}
"#,
);
}
#[test]
fn associated_type_impl_traits_complex() {
check_types(
r#"
struct Unary<T>(T);
struct Binary<T, U>(T, U);
trait Foo {}
struct S1;
impl Foo for S1 {}
trait Bar {
type Item;
fn bar(&self) -> Unary<Self::Item>;
}
struct S2;
impl Bar for S2 {
type Item = Unary<impl Foo>;
fn bar(&self) -> Unary<<Self as Bar>::Item> {
Unary(Unary(S1))
}
}
trait Baz {
type Target1;
type Target2;
fn baz(&self) -> Binary<Self::Target1, Self::Target2>;
}
struct S3;
impl Baz for S3 {
type Target1 = impl Foo;
type Target2 = Unary<impl Bar>;
fn baz(&self) -> Binary<Self::Target1, Self::Target2> {
Binary(S1, Unary(S2))
}
}
fn test() {
let x = S3.baz();
//^ Binary<impl Foo + ?Sized, Unary<impl Bar + ?Sized>>
let y = x.1.0.bar();
//^ Unary<Bar::Item<impl Bar + ?Sized>>
}
"#,
);
}
#[test]
fn associated_type_with_impl_trait_in_tuple() {
check_no_mismatches(
r#"
pub trait Iterator {
type Item;
}
pub trait Value {}
fn bar<I: Iterator<Item = (usize, impl Value)>>() {}
fn foo() {
bar();
}
"#,
);
}
#[test]
fn associated_type_with_impl_trait_in_nested_tuple() {
check_no_mismatches(
r#"
pub trait Iterator {
type Item;
}
pub trait Value {}
fn bar<I: Iterator<Item = ((impl Value, usize), u32)>>() {}
fn foo() {
bar();
}
"#,
);
}
#[test]
fn type_alias_impl_trait_simple() {
check_no_mismatches(
r#"
trait Trait {}
struct Struct;
impl Trait for Struct {}
type AliasTy = impl Trait;
static ALIAS: AliasTy = {
let res: AliasTy = Struct;
res
};
"#,
);
check_infer_with_mismatches(
r#"
trait Trait {}
struct Struct;
impl Trait for Struct {}
type AliasTy = impl Trait;
static ALIAS: i32 = {
// TATIs cannot be define-used if not in signature or type annotations
let _a: AliasTy = Struct;
5
};
"#,
expect![[r#"
106..220 '{ ... 5 }': i32
191..193 '_a': impl Trait + ?Sized
205..211 'Struct': Struct
217..218 '5': i32
205..211: expected impl Trait + ?Sized, got Struct
"#]],
)
}

View file

@ -253,12 +253,7 @@ impl<'a> ClosureSubst<'a> {
pub fn is_fn_unsafe_to_call(db: &dyn HirDatabase, func: FunctionId) -> bool {
let data = db.function_data(func);
if data.has_unsafe_kw() {
// Functions that are `#[rustc_deprecated_safe_2024]` are safe to call before 2024.
if db.attrs(func.into()).by_key(&sym::rustc_deprecated_safe_2024).exists() {
// FIXME: Properly check the caller span and mark it as unsafe after 2024.
return false;
}
if data.is_unsafe() {
return true;
}

View file

@ -1,7 +1,8 @@
[package]
name = "hir"
version = "0.0.0"
description = "TBD"
repository.workspace = true
description = "A high-level object-oriented access to Rust code for rust-analyzer."
authors.workspace = true
edition.workspace = true

View file

@ -48,6 +48,7 @@ macro_rules! diagnostics {
// ]
diagnostics![
AwaitOutsideOfAsync,
BreakOutsideOfLoop,
ExpectedFunction,
InactiveCode,
@ -135,6 +136,12 @@ pub struct UnreachableLabel {
pub name: Name,
}
#[derive(Debug)]
pub struct AwaitOutsideOfAsync {
pub node: InFile<AstPtr<ast::AwaitExpr>>,
pub location: String,
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct UndeclaredLabel {
pub node: InFile<AstPtr<ast::Lifetime>>,

View file

@ -69,13 +69,13 @@ impl HirDisplay for Function {
write_visibility(module_id, self.visibility(db), f)?;
if data.has_default_kw() {
if data.is_default() {
f.write_str("default ")?;
}
if data.has_const_kw() {
if data.is_const() {
f.write_str("const ")?;
}
if data.has_async_kw() {
if data.is_async() {
f.write_str("async ")?;
}
if self.is_unsafe_to_call(db) {
@ -125,7 +125,7 @@ impl HirDisplay for Function {
// `FunctionData::ret_type` will be `::core::future::Future<Output = ...>` for async fns.
// Use ugly pattern match to strip the Future trait.
// Better way?
let ret_type = if !data.has_async_kw() {
let ret_type = if !data.is_async() {
&data.ret_type
} else {
match &*data.ret_type {

View file

@ -1,4 +1,4 @@
//! HIR (previously known as descriptors) provides a high-level object oriented
//! HIR (previously known as descriptors) provides a high-level object-oriented
//! access to Rust code.
//!
//! The principal difference between HIR and syntax trees is that HIR is bound
@ -1828,6 +1828,9 @@ impl DefWithBody {
is_bang: true,
}
.into(),
BodyDiagnostic::AwaitOutsideOfAsync { node, location } => {
AwaitOutsideOfAsync { node: *node, location: location.clone() }.into()
}
BodyDiagnostic::UnreachableLabel { node, name } => {
UnreachableLabel { node: *node, name: name.clone() }.into()
}
@ -2186,11 +2189,11 @@ impl Function {
}
pub fn is_const(self, db: &dyn HirDatabase) -> bool {
db.function_data(self.id).has_const_kw()
db.function_data(self.id).is_const()
}
pub fn is_async(self, db: &dyn HirDatabase) -> bool {
db.function_data(self.id).has_async_kw()
db.function_data(self.id).is_async()
}
/// Does this function have `#[test]` attribute?

View file

@ -28,7 +28,7 @@ use hir_expand::{
use itertools::Itertools;
use rustc_hash::{FxHashMap, FxHashSet};
use smallvec::{smallvec, SmallVec};
use span::{EditionedFileId, FileId, Span, SyntaxContextId, ROOT_ERASED_FILE_AST_ID};
use span::{EditionedFileId, FileId};
use stdx::TupleExt;
use syntax::{
algo::skip_trivia_token,
@ -757,81 +757,9 @@ impl<'db> SemanticsImpl<'db> {
res
}
// return:
// SourceAnalyzer(file_id that original call include!)
// macro file id
// token in include! macro mapped from token in params
// span for the mapped token
fn is_from_include_file(
&self,
token: SyntaxToken,
) -> Option<(SourceAnalyzer, HirFileId, SyntaxToken, Span)> {
let parent = token.parent()?;
let file_id = self.find_file(&parent).file_id.file_id()?;
// iterate related crates and find all include! invocations that include_file_id matches
for (invoc, _) in self
.db
.relevant_crates(file_id.file_id())
.iter()
.flat_map(|krate| self.db.include_macro_invoc(*krate))
.filter(|&(_, include_file_id)| include_file_id == file_id)
{
let macro_file = invoc.as_macro_file();
let expansion_info = {
self.with_ctx(|ctx| {
ctx.cache
.expansion_info_cache
.entry(macro_file)
.or_insert_with(|| {
let exp_info = macro_file.expansion_info(self.db.upcast());
let InMacroFile { file_id, value } = exp_info.expanded();
if let InFile { file_id, value: Some(value) } = exp_info.arg() {
self.cache(value.ancestors().last().unwrap(), file_id);
}
self.cache(value, file_id.into());
exp_info
})
.clone()
})
};
// FIXME: uncached parse
// Create the source analyzer for the macro call scope
let Some(sa) = expansion_info
.arg()
.value
.and_then(|it| self.analyze_no_infer(&it.ancestors().last().unwrap()))
else {
continue;
};
// get mapped token in the include! macro file
let span = span::Span {
range: token.text_range(),
anchor: span::SpanAnchor { file_id, ast_id: ROOT_ERASED_FILE_AST_ID },
ctx: SyntaxContextId::ROOT,
};
let Some(InMacroFile { file_id, value: mut mapped_tokens }) =
expansion_info.map_range_down_exact(span)
else {
continue;
};
// if we find one, then return
if let Some(t) = mapped_tokens.next() {
return Some((sa, file_id.into(), t, span));
}
}
None
}
fn descend_into_macros_impl(
&self,
mut token: SyntaxToken,
token: SyntaxToken,
f: &mut dyn FnMut(InFile<SyntaxToken>) -> ControlFlow<()>,
) {
let _p = tracing::info_span!("descend_into_macros_impl").entered();
@ -848,17 +776,7 @@ impl<'db> SemanticsImpl<'db> {
return;
}
},
None => {
// if we cannot find a source analyzer for this token, then we try to find out
// whether this file is an included file and treat that as the include input
let Some((it, macro_file_id, mapped_token, s)) =
self.is_from_include_file(token)
else {
return;
};
token = mapped_token;
(it, s, macro_file_id)
}
None => return,
};
let mut m_cache = self.macro_call_cache.borrow_mut();

View file

@ -94,8 +94,9 @@ use hir_def::{
},
hir::{BindingId, LabelId},
AdtId, BlockId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId, ExternCrateId,
FieldId, FunctionId, GenericDefId, GenericParamId, ImplId, LifetimeParamId, MacroId, ModuleId,
StaticId, StructId, TraitAliasId, TraitId, TypeAliasId, TypeParamId, UnionId, UseId, VariantId,
FieldId, FunctionId, GenericDefId, GenericParamId, ImplId, LifetimeParamId, Lookup, MacroId,
ModuleId, StaticId, StructId, TraitAliasId, TraitId, TypeAliasId, TypeParamId, UnionId, UseId,
VariantId,
};
use hir_expand::{
attrs::AttrId, name::AsName, ExpansionInfo, HirFileId, HirFileIdExt, MacroCallId,
@ -131,11 +132,30 @@ impl SourceToDefCtx<'_, '_> {
for &crate_id in self.db.relevant_crates(file).iter() {
// Note: `mod` declarations in block modules cannot be supported here
let crate_def_map = self.db.crate_def_map(crate_id);
mods.extend(
let n_mods = mods.len();
let modules = |file| {
crate_def_map
.modules_for_file(file)
.map(|local_id| crate_def_map.module_id(local_id)),
)
.map(|local_id| crate_def_map.module_id(local_id))
};
mods.extend(modules(file));
if mods.len() == n_mods {
mods.extend(
self.db
.include_macro_invoc(crate_id)
.iter()
.filter(|&&(_, file_id)| file_id == file)
.flat_map(|(call, _)| {
modules(
call.lookup(self.db.upcast())
.kind
.file_id()
.original_file(self.db.upcast())
.file_id(),
)
}),
);
}
}
if mods.is_empty() {
// FIXME: detached file

View file

@ -1,7 +1,8 @@
[package]
name = "ide-assists"
version = "0.0.0"
description = "TBD"
repository.workspace = true
description = "Code assists for rust-analyzer."
authors.workspace = true
edition.workspace = true

View file

@ -2280,4 +2280,29 @@ impl b::LocalTrait for B {
"#,
)
}
#[test]
fn impl_with_type_param_with_former_param_as_default() {
check_assist(
add_missing_impl_members,
r#"
pub trait Test<'a, T, U = T> {
fn test(item: &'a T) -> U;
}
impl<'a> Test<'a, i32> for bool {
$0
}
"#,
r#"
pub trait Test<'a, T, U = T> {
fn test(item: &'a T) -> U;
}
impl<'a> Test<'a, i32> for bool {
fn test(item: &'a i32) -> i32 {
${0:todo!()}
}
}
"#,
);
}
}

View file

@ -43,7 +43,7 @@ pub(crate) fn bind_unused_param(acc: &mut Assists, ctx: &AssistContext<'_>) -> O
acc.add(
AssistId("bind_unused_param", AssistKind::QuickFix),
&format!("Bind as `let _ = {ident_pat};`"),
format!("Bind as `let _ = {ident_pat};`"),
param.syntax().text_range(),
|builder| {
let line_index = ctx.db().line_index(ctx.file_id().into());

View file

@ -2,7 +2,7 @@ use either::Either;
use ide_db::defs::{Definition, NameRefClass};
use syntax::{
ast::{self, AstNode, HasGenericParams, HasVisibility},
match_ast, SyntaxNode,
match_ast, SyntaxKind, SyntaxNode,
};
use crate::{assist_context::SourceChangeBuilder, AssistContext, AssistId, AssistKind, Assists};
@ -100,7 +100,9 @@ fn edit_struct_def(
ast::make::tokens::single_newline().text(),
);
edit.insert(tuple_fields_text_range.start(), w.syntax().text());
edit.insert(tuple_fields_text_range.start(), ",");
if !w.syntax().last_token().is_some_and(|t| t.kind() == SyntaxKind::COMMA) {
edit.insert(tuple_fields_text_range.start(), ",");
}
edit.insert(
tuple_fields_text_range.start(),
ast::make::tokens::single_newline().text(),
@ -879,6 +881,29 @@ use crate::{A::Variant, Inner};
fn f() {
let a = Variant { field1: Inner };
}
"#,
);
}
#[test]
fn where_clause_with_trailing_comma() {
check_assist(
convert_tuple_struct_to_named_struct,
r#"
trait Foo {}
struct Bar$0<T>(pub T)
where
T: Foo,;
"#,
r#"
trait Foo {}
struct Bar<T>
where
T: Foo,
{ pub field1: T }
"#,
);
}

View file

@ -3,7 +3,7 @@ mod generated;
use expect_test::expect;
use hir::{FileRange, Semantics};
use ide_db::{
base_db::SourceDatabaseExt,
base_db::{SourceDatabase, SourceRootDatabase},
imports::insert_use::{ImportGranularity, InsertUseConfig},
source_change::FileSystemEdit,
EditionedFileId, RootDatabase, SnippetCap,

View file

@ -1,7 +1,8 @@
[package]
name = "ide-completion"
version = "0.0.0"
description = "TBD"
repository.workspace = true
description = "Utilities for generating completions of user input for rust-analyzer."
authors.workspace = true
edition.workspace = true

View file

@ -24,7 +24,7 @@ pub(crate) mod vis;
use std::iter;
use hir::{sym, HasAttrs, ImportPathConfig, Name, ScopeDef, Variant};
use hir::{sym, HasAttrs, Name, ScopeDef, Variant};
use ide_db::{imports::import_assets::LocatedImport, RootDatabase, SymbolKind};
use syntax::{ast, SmolStr, ToSmolStr};
@ -645,11 +645,7 @@ fn enum_variants_with_paths(
if let Some(path) = ctx.module.find_path(
ctx.db,
hir::ModuleDef::from(variant),
ImportPathConfig {
prefer_no_std: ctx.config.prefer_no_std,
prefer_prelude: ctx.config.prefer_prelude,
prefer_absolute: ctx.config.prefer_absolute,
},
ctx.config.import_path_config(),
) {
// Variants with trivial paths are already added by the existing completion logic,
// so we should avoid adding these twice

View file

@ -1,6 +1,6 @@
//! Completion of names from the current scope in expression position.
use hir::{sym, ImportPathConfig, Name, ScopeDef};
use hir::{sym, Name, ScopeDef};
use syntax::ast;
use crate::{
@ -174,11 +174,7 @@ pub(crate) fn complete_expr_path(
.find_path(
ctx.db,
hir::ModuleDef::from(strukt),
ImportPathConfig {
prefer_no_std: ctx.config.prefer_no_std,
prefer_prelude: ctx.config.prefer_prelude,
prefer_absolute: ctx.config.prefer_absolute,
},
ctx.config.import_path_config(),
)
.filter(|it| it.len() > 1);
@ -200,11 +196,7 @@ pub(crate) fn complete_expr_path(
.find_path(
ctx.db,
hir::ModuleDef::from(un),
ImportPathConfig {
prefer_no_std: ctx.config.prefer_no_std,
prefer_prelude: ctx.config.prefer_prelude,
prefer_absolute: ctx.config.prefer_absolute,
},
ctx.config.import_path_config(),
)
.filter(|it| it.len() > 1);

View file

@ -1,5 +1,5 @@
//! See [`import_on_the_fly`].
use hir::{ImportPathConfig, ItemInNs, ModuleDef};
use hir::{ItemInNs, ModuleDef};
use ide_db::imports::{
import_assets::{ImportAssets, LocatedImport},
insert_use::ImportScope,
@ -256,11 +256,7 @@ fn import_on_the_fly(
};
let user_input_lowercased = potential_import_name.to_lowercase();
let import_cfg = ImportPathConfig {
prefer_no_std: ctx.config.prefer_no_std,
prefer_prelude: ctx.config.prefer_prelude,
prefer_absolute: ctx.config.prefer_absolute,
};
let import_cfg = ctx.config.import_path_config();
import_assets
.search_for_imports(&ctx.sema, import_cfg, ctx.config.insert_use.prefix_kind)
@ -306,12 +302,7 @@ fn import_on_the_fly_pat_(
ItemInNs::Values(def) => matches!(def, hir::ModuleDef::Const(_)),
};
let user_input_lowercased = potential_import_name.to_lowercase();
let cfg = ImportPathConfig {
prefer_no_std: ctx.config.prefer_no_std,
prefer_prelude: ctx.config.prefer_prelude,
prefer_absolute: ctx.config.prefer_absolute,
};
let cfg = ctx.config.import_path_config();
import_assets
.search_for_imports(&ctx.sema, cfg, ctx.config.insert_use.prefix_kind)
@ -353,11 +344,7 @@ fn import_on_the_fly_method(
let user_input_lowercased = potential_import_name.to_lowercase();
let cfg = ImportPathConfig {
prefer_no_std: ctx.config.prefer_no_std,
prefer_prelude: ctx.config.prefer_prelude,
prefer_absolute: ctx.config.prefer_absolute,
};
let cfg = ctx.config.import_path_config();
import_assets
.search_for_imports(&ctx.sema, cfg, ctx.config.insert_use.prefix_kind)

View file

@ -4,7 +4,7 @@ use std::iter;
use hir::{HirFileIdExt, Module};
use ide_db::{
base_db::{SourceDatabaseExt, VfsPath},
base_db::{SourceRootDatabase, VfsPath},
FxHashSet, RootDatabase, SymbolKind,
};
use stdx::IsNoneOr;

View file

@ -2,7 +2,7 @@
mod format_like;
use hir::{ImportPathConfig, ItemInNs};
use hir::ItemInNs;
use ide_db::{
documentation::{Documentation, HasDocs},
imports::insert_use::ImportScope,
@ -60,11 +60,7 @@ pub(crate) fn complete_postfix(
None => return,
};
let cfg = ImportPathConfig {
prefer_no_std: ctx.config.prefer_no_std,
prefer_prelude: ctx.config.prefer_prelude,
prefer_absolute: ctx.config.prefer_absolute,
};
let cfg = ctx.config.import_path_config();
if let Some(drop_trait) = ctx.famous_defs().core_ops_Drop() {
if receiver_ty.impls_trait(ctx.db, drop_trait, &[]) {

View file

@ -4,6 +4,7 @@
//! module, and we use to statically check that we only produce snippet
//! completions if we are allowed to.
use hir::ImportPathConfig;
use ide_db::{imports::insert_use::InsertUseConfig, SnippetCap};
use crate::snippet::Snippet;
@ -45,4 +46,12 @@ impl CompletionConfig {
.iter()
.flat_map(|snip| snip.prefix_triggers.iter().map(move |trigger| (&**trigger, snip)))
}
pub fn import_path_config(&self) -> ImportPathConfig {
ImportPathConfig {
prefer_no_std: self.prefer_no_std,
prefer_prelude: self.prefer_prelude,
prefer_absolute: self.prefer_absolute,
}
}
}

View file

@ -10,7 +10,6 @@ mod snippet;
#[cfg(test)]
mod tests;
use hir::ImportPathConfig;
use ide_db::{
helpers::mod_path_to_ast,
imports::{
@ -249,11 +248,7 @@ pub fn resolve_completion_edits(
let new_ast = scope.clone_for_update();
let mut import_insert = TextEdit::builder();
let cfg = ImportPathConfig {
prefer_no_std: config.prefer_no_std,
prefer_prelude: config.prefer_prelude,
prefer_absolute: config.prefer_absolute,
};
let cfg = config.import_path_config();
imports.into_iter().for_each(|(full_import_path, imported_name)| {
let items_with_name = items_locator::items_with_name(

View file

@ -10,7 +10,7 @@ pub(crate) mod type_alias;
pub(crate) mod union_literal;
pub(crate) mod variant;
use hir::{sym, AsAssocItem, HasAttrs, HirDisplay, ImportPathConfig, ModuleDef, ScopeDef, Type};
use hir::{sym, AsAssocItem, HasAttrs, HirDisplay, ModuleDef, ScopeDef, Type};
use ide_db::{
documentation::{Documentation, HasDocs},
helpers::item_name,
@ -294,11 +294,7 @@ pub(crate) fn render_expr(
.unwrap_or_else(|| String::from("..."))
};
let cfg = ImportPathConfig {
prefer_no_std: ctx.config.prefer_no_std,
prefer_prelude: ctx.config.prefer_prelude,
prefer_absolute: ctx.config.prefer_absolute,
};
let cfg = ctx.config.import_path_config();
let label = expr.gen_source_code(&ctx.scope, &mut label_formatter, cfg).ok()?;

View file

@ -100,7 +100,6 @@
// }
// ----
use hir::ImportPathConfig;
use ide_db::imports::import_assets::LocatedImport;
use itertools::Itertools;
use syntax::{ast, AstNode, GreenNode, SyntaxNode};
@ -169,11 +168,7 @@ impl Snippet {
}
fn import_edits(ctx: &CompletionContext<'_>, requires: &[GreenNode]) -> Option<Vec<LocatedImport>> {
let import_cfg = ImportPathConfig {
prefer_no_std: ctx.config.prefer_no_std,
prefer_prelude: ctx.config.prefer_prelude,
prefer_absolute: ctx.config.prefer_absolute,
};
let import_cfg = ctx.config.import_path_config();
let resolve = |import: &GreenNode| {
let path = ast::Path::cast(SyntaxNode::new_root(import.clone()))?;

View file

@ -23,10 +23,10 @@ mod type_pos;
mod use_tree;
mod visibility;
use base_db::SourceDatabase;
use expect_test::Expect;
use hir::PrefixKind;
use ide_db::{
base_db::FileLoader,
imports::insert_use::{ImportGranularity, InsertUseConfig},
FilePosition, RootDatabase, SnippetCap,
};

View file

@ -1,7 +1,8 @@
[package]
name = "ide-db"
version = "0.0.0"
description = "TBD"
repository.workspace = true
description = "Core data structure representing IDE state for rust-analyzer."
authors.workspace = true
edition.workspace = true
@ -13,7 +14,7 @@ doctest = false
[dependencies]
cov-mark = "2.0.0-pre.1"
crossbeam-channel = "0.5.5"
crossbeam-channel.workspace = true
tracing.workspace = true
rayon.workspace = true
fst = { version = "0.4.7", default-features = false }

View file

@ -2,7 +2,7 @@
use std::collections::VecDeque;
use base_db::SourceDatabaseExt;
use base_db::SourceRootDatabase;
use hir::{Crate, DescendPreference, ItemInNs, ModuleDef, Name, Semantics};
use span::FileId;
use syntax::{

View file

@ -1,4 +1,4 @@
//! This crate defines the core datastructure representing IDE state -- `RootDatabase`.
//! This crate defines the core data structure representing IDE state -- `RootDatabase`.
//!
//! It is mainly a `HirDatabase` for semantic analysis, plus a `SymbolsDatabase`, for fuzzy search.
@ -74,7 +74,7 @@ pub type FilePosition = FilePositionWrapper<FileId>;
pub type FileRange = FileRangeWrapper<FileId>;
#[salsa::database(
base_db::SourceDatabaseExtStorage,
base_db::SourceRootDatabaseStorage,
base_db::SourceDatabaseStorage,
hir::db::ExpandDatabaseStorage,
hir::db::DefDatabaseStorage,
@ -125,9 +125,6 @@ impl Upcast<dyn HirDatabase> for RootDatabase {
}
impl FileLoader for RootDatabase {
fn file_text(&self, file_id: FileId) -> Arc<str> {
FileLoaderDelegate(self).file_text(file_id)
}
fn resolve_path(&self, path: AnchoredPath<'_>) -> Option<FileId> {
FileLoaderDelegate(self).resolve_path(path)
}

View file

@ -7,7 +7,7 @@ use itertools::Itertools;
use rustc_hash::FxHashMap;
use syntax::{
ast::{self, make, AstNode, HasGenericArgs},
ted, SyntaxNode,
ted, NodeOrToken, SyntaxNode,
};
#[derive(Default)]
@ -328,10 +328,26 @@ impl Ctx<'_> {
let qualified = make::path_from_segments(std::iter::once(segment), false);
ted::replace(path.syntax(), qualified.clone_for_update().syntax());
} else if let Some(path_ty) = ast::PathType::cast(parent) {
ted::replace(
path_ty.syntax(),
subst.clone_subtree().clone_for_update().syntax(),
);
let old = path_ty.syntax();
if old.parent().is_some() {
ted::replace(old, subst.clone_subtree().clone_for_update().syntax());
} else {
// Some `path_ty` has no parent, especially ones made for default value
// of type parameters.
// In this case, `ted` cannot replace `path_ty` with `subst` directly.
// So, just replace its children as long as the `subst` is the same type.
let new = subst.clone_subtree().clone_for_update();
if !matches!(new, ast::Type::PathType(..)) {
return None;
}
let start = path_ty.syntax().first_child().map(NodeOrToken::Node)?;
let end = path_ty.syntax().last_child().map(NodeOrToken::Node)?;
ted::replace_all(
start..=end,
new.syntax().children().map(NodeOrToken::Node).collect::<Vec<_>>(),
);
}
} else {
ted::replace(
path.syntax(),

View file

@ -11,7 +11,7 @@ use hir::db::DefDatabase;
use crate::{
base_db::{
salsa::{Database, ParallelDatabase, Snapshot},
Cancelled, CrateId, SourceDatabase, SourceDatabaseExt,
Cancelled, CrateId, SourceDatabase, SourceRootDatabase,
},
FxIndexMap, RootDatabase,
};

View file

@ -6,7 +6,7 @@
use std::mem;
use base_db::{salsa::Database, SourceDatabase, SourceDatabaseExt};
use base_db::{salsa::Database, SourceDatabase, SourceRootDatabase};
use hir::{
sym, AsAssocItem, DefWithBody, DescendPreference, FileRange, HasAttrs, HasSource, HirFileIdExt,
InFile, InRealFile, ModuleSource, PathResolution, Semantics, Visibility,
@ -663,9 +663,16 @@ impl<'a> FindUsages<'a> {
name_ref: &ast::NameRef,
sink: &mut dyn FnMut(EditionedFileId, FileReference) -> bool,
) -> bool {
// See https://github.com/rust-lang/rust-analyzer/pull/15864/files/e0276dc5ddc38c65240edb408522bb869f15afb4#r1389848845
let ty_eq = |ty: hir::Type| match (ty.as_adt(), self_ty.as_adt()) {
(Some(ty), Some(self_ty)) => ty == self_ty,
(None, None) => ty == *self_ty,
_ => false,
};
match NameRefClass::classify(self.sema, name_ref) {
Some(NameRefClass::Definition(Definition::SelfType(impl_)))
if impl_.self_ty(self.sema.db).as_adt() == self_ty.as_adt() =>
if ty_eq(impl_.self_ty(self.sema.db)) =>
{
let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax());
let reference = FileReference {

View file

@ -29,7 +29,7 @@ use std::{
use base_db::{
salsa::{self, ParallelDatabase},
SourceDatabaseExt, SourceRootId, Upcast,
SourceRootDatabase, SourceRootId, Upcast,
};
use fst::{raw::IndexedValue, Automaton, Streamer};
use hir::{
@ -100,7 +100,7 @@ impl Query {
}
#[salsa::query_group(SymbolsDatabaseStorage)]
pub trait SymbolsDatabase: HirDatabase + SourceDatabaseExt + Upcast<dyn HirDatabase> {
pub trait SymbolsDatabase: HirDatabase + SourceRootDatabase + Upcast<dyn HirDatabase> {
/// The symbol index for a given module. These modules should only be in source roots that
/// are inside local_roots.
fn module_symbols(&self, module: Module) -> Arc<SymbolIndex>;

View file

@ -1,7 +1,8 @@
[package]
name = "ide-diagnostics"
version = "0.0.0"
description = "TBD"
repository.workspace = true
description = "Diagnostics rendering and fixits for rust-analyzer."
authors.workspace = true
edition.workspace = true

View file

@ -0,0 +1,101 @@
use crate::{adjusted_display_range, Diagnostic, DiagnosticsContext};
// Diagnostic: await-outside-of-async
//
// This diagnostic is triggered if the `await` keyword is used outside of an async function or block
pub(crate) fn await_outside_of_async(
ctx: &DiagnosticsContext<'_>,
d: &hir::AwaitOutsideOfAsync,
) -> Diagnostic {
let display_range =
adjusted_display_range(ctx, d.node, &|node| Some(node.await_token()?.text_range()));
Diagnostic::new(
crate::DiagnosticCode::RustcHardError("E0728"),
format!("`await` is used inside {}, which is not an `async` context", d.location),
display_range,
)
}
#[cfg(test)]
mod tests {
use crate::tests::check_diagnostics;
#[test]
fn await_inside_non_async_fn() {
check_diagnostics(
r#"
async fn foo() {}
fn bar() {
foo().await;
//^^^^^ error: `await` is used inside non-async function, which is not an `async` context
}
"#,
);
}
#[test]
fn await_inside_async_fn() {
check_diagnostics(
r#"
async fn foo() {}
async fn bar() {
foo().await;
}
"#,
);
}
#[test]
fn await_inside_closure() {
check_diagnostics(
r#"
async fn foo() {}
async fn bar() {
let _a = || { foo().await };
//^^^^^ error: `await` is used inside non-async closure, which is not an `async` context
}
"#,
);
}
#[test]
fn await_inside_async_block() {
check_diagnostics(
r#"
async fn foo() {}
fn bar() {
let _a = async { foo().await };
}
"#,
);
}
#[test]
fn await_in_complex_context() {
check_diagnostics(
r#"
async fn foo() {}
fn bar() {
async fn baz() {
let a = foo().await;
}
let x = || {
let y = async {
baz().await;
let z = || {
baz().await;
//^^^^^ error: `await` is used inside non-async closure, which is not an `async` context
};
};
};
}
"#,
);
}
}

View file

@ -42,7 +42,10 @@ mod tests {
use crate::{tests::check_diagnostics_with_config, DiagnosticsConfig};
pub(crate) fn check(ra_fixture: &str) {
let config = DiagnosticsConfig::test_sample();
let config = DiagnosticsConfig {
disabled: std::iter::once("unlinked-file".to_owned()).collect(),
..DiagnosticsConfig::test_sample()
};
check_diagnostics_with_config(config, ra_fixture)
}
@ -168,6 +171,27 @@ union FooBar {
#[cfg(a)] baz: u32,
//^^^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: a is disabled
}
"#,
);
}
#[test]
fn modules() {
check(
r#"
//- /main.rs
#[cfg(outline)] mod outline;
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: outline is disabled
mod outline_inner;
//^^^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: outline_inner is disabled
#[cfg(inline)] mod inline {}
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: inline is disabled
//- /outline_inner.rs
#![cfg(outline_inner)]
//- /outline.rs
"#,
);
}

View file

@ -273,11 +273,7 @@ fn f() {
#[test]
fn include_does_not_break_diagnostics() {
let mut config = DiagnosticsConfig::test_sample();
config.disabled.insert("inactive-code".to_owned());
config.disabled.insert("unlinked-file".to_owned());
check_diagnostics_with_config(
config,
check_diagnostics(
r#"
//- minicore: include
//- /lib.rs crate:lib

View file

@ -472,4 +472,18 @@ fn f(
"#,
)
}
#[test]
fn no_type_mismatches_when_arg_count_mismatch() {
check_diagnostics(
r#"
fn foo((): (), (): ()) {
foo(1, 2, 3);
// ^^ error: expected 2 arguments, found 3
foo(1);
// ^ error: expected 2 arguments, found 1
}
"#,
);
}
}

View file

@ -1032,6 +1032,44 @@ fn f() {
check_diagnostics_no_bails(&code);
}
#[test]
fn min_exhaustive() {
check_diagnostics(
r#"
//- minicore: result
fn test(x: Result<i32, !>) {
match x {
Ok(_y) => {}
}
}
"#,
);
check_diagnostics(
r#"
//- minicore: result
fn test(ptr: *const Result<i32, !>) {
unsafe {
match *ptr {
//^^^^ error: missing match arm: `Err(!)` not covered
Ok(_x) => {}
}
}
}
"#,
);
check_diagnostics(
r#"
//- minicore: result
fn test(x: Result<i32, &'static !>) {
match x {
//^ error: missing match arm: `Err(_)` not covered
Ok(_y) => {}
}
}
"#,
);
}
mod rust_unstable {
use super::*;

View file

@ -483,6 +483,30 @@ unsafe fn foo() -> u8 {
fn main() {
let x = format!("foo: {}", foo$0());
}
"#,
)
}
#[test]
fn rustc_deprecated_safe_2024() {
check_diagnostics(
r#"
//- /ed2021.rs crate:ed2021 edition:2021
#[rustc_deprecated_safe_2024]
unsafe fn safe() -> u8 {
0
}
//- /ed2024.rs crate:ed2024 edition:2024
#[rustc_deprecated_safe_2024]
unsafe fn not_safe() -> u8 {
0
}
//- /main.rs crate:main deps:ed2021,ed2024
fn main() {
ed2021::safe();
ed2024::not_safe();
//^^^^^^^^^^^^^^^^^^💡 error: this operation is unsafe and requires an unsafe function or block
}
"#,
)

View file

@ -41,6 +41,67 @@ fn main() {
fn main() {
let Some(_) | None = Some(5);
}
"#,
);
}
#[test]
fn option_nonexhaustive_inside_blocks() {
check_diagnostics(
r#"
//- minicore: option
fn main() {
'_a: {
let None = Some(5);
//^^^^ error: non-exhaustive pattern: `Some(_)` not covered
}
}
"#,
);
check_diagnostics(
r#"
//- minicore: future, option
fn main() {
let _ = async {
let None = Some(5);
//^^^^ error: non-exhaustive pattern: `Some(_)` not covered
};
}
"#,
);
check_diagnostics(
r#"
//- minicore: option
fn main() {
unsafe {
let None = Some(5);
//^^^^ error: non-exhaustive pattern: `Some(_)` not covered
}
}
"#,
);
}
#[test]
fn min_exhaustive() {
check_diagnostics(
r#"
//- minicore: result
fn test(x: Result<i32, !>) {
let Ok(_y) = x;
}
"#,
);
check_diagnostics(
r#"
//- minicore: result
fn test(x: Result<i32, &'static !>) {
let Ok(_y) = x;
//^^^^^^ error: non-exhaustive pattern: `Err(_)` not covered
}
"#,
);
}

View file

@ -2,8 +2,12 @@ use either::Either;
use hir::{db::ExpandDatabase, ClosureStyle, HirDisplay, HirFileIdExt, InFile, Type};
use ide_db::{famous_defs::FamousDefs, source_change::SourceChange};
use syntax::{
ast::{self, BlockExpr, ExprStmt},
AstNode, AstPtr,
ast::{
self,
edit::{AstNodeEdit, IndentLevel},
BlockExpr, Expr, ExprStmt,
},
AstNode, AstPtr, TextSize,
};
use text_edit::TextEdit;
@ -119,6 +123,38 @@ fn add_missing_ok_or_some(
return None;
}
if d.actual.is_unit() {
if let Expr::BlockExpr(block) = &expr {
if block.tail_expr().is_none() {
let mut builder = TextEdit::builder();
let block_indent = block.indent_level();
if block.statements().count() == 0 {
// Empty block
let indent = block_indent + 1;
builder.insert(
block.syntax().text_range().start() + TextSize::from(1),
format!("\n{indent}{variant_name}(())\n{block_indent}"),
);
} else {
let indent = IndentLevel::from(1);
builder.insert(
block.syntax().text_range().end() - TextSize::from(1),
format!("{indent}{variant_name}(())\n{block_indent}"),
);
}
let source_change = SourceChange::from_text_edit(
expr_ptr.file_id.original_file(ctx.sema.db),
builder.finish(),
);
let name = format!("Insert {variant_name}(()) as the tail of this block");
acc.push(fix("insert_wrapped_unit", &name, source_change, expr_range));
}
return Some(());
}
}
let mut builder = TextEdit::builder();
builder.insert(expr.syntax().text_range().start(), format!("{variant_name}("));
builder.insert(expr.syntax().text_range().end(), ")".to_owned());
@ -533,6 +569,36 @@ fn div(x: i32, y: i32) -> MyResult<i32> {
);
}
#[test]
fn test_wrapped_unit_as_block_tail_expr() {
check_fix(
r#"
//- minicore: result
fn foo() -> Result<(), ()> {
foo();
}$0
"#,
r#"
fn foo() -> Result<(), ()> {
foo();
Ok(())
}
"#,
);
check_fix(
r#"
//- minicore: result
fn foo() -> Result<(), ()> {}$0
"#,
r#"
fn foo() -> Result<(), ()> {
Ok(())
}
"#,
);
}
#[test]
fn test_in_const_and_static() {
check_fix(

View file

@ -4,7 +4,7 @@ use std::iter;
use hir::{db::DefDatabase, DefMap, InFile, ModuleSource};
use ide_db::{
base_db::{FileLoader, SourceDatabaseExt},
base_db::{FileLoader, SourceDatabase, SourceRootDatabase},
source_change::SourceChange,
FileId, FileRange, LineIndexDatabase,
};
@ -47,7 +47,7 @@ pub(crate) fn unlinked_file(
//
// Only show this diagnostic on the first three characters of
// the file, to avoid overwhelming the user during startup.
range = FileLoader::file_text(ctx.sema.db, file_id)
range = SourceDatabase::file_text(ctx.sema.db, file_id)
.char_indices()
.take(3)
.last()
@ -499,6 +499,18 @@ $0
mod bar {
mod foo;
}
"#,
);
}
#[test]
fn include_macro_works() {
check_diagnostics(
r#"
//- minicore: include
//- /main.rs
include!("bar/foo/mod.rs");
//- /bar/foo/mod.rs
"#,
);
}

View file

@ -24,6 +24,7 @@
//! don't yet have a great pattern for how to do them properly.
mod handlers {
pub(crate) mod await_outside_of_async;
pub(crate) mod break_outside_of_loop;
pub(crate) mod expected_function;
pub(crate) mod inactive_code;
@ -96,6 +97,7 @@ use syntax::{
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum DiagnosticCode {
RustcHardError(&'static str),
SyntaxError,
RustcLint(&'static str),
Clippy(&'static str),
Ra(&'static str, Severity),
@ -107,6 +109,9 @@ impl DiagnosticCode {
DiagnosticCode::RustcHardError(e) => {
format!("https://doc.rust-lang.org/stable/error_codes/{e}.html")
}
DiagnosticCode::SyntaxError => {
String::from("https://doc.rust-lang.org/stable/reference/")
}
DiagnosticCode::RustcLint(e) => {
format!("https://doc.rust-lang.org/rustc/?search={e}")
}
@ -125,6 +130,7 @@ impl DiagnosticCode {
| DiagnosticCode::RustcLint(r)
| DiagnosticCode::Clippy(r)
| DiagnosticCode::Ra(r, _) => r,
DiagnosticCode::SyntaxError => "syntax-error",
}
}
}
@ -154,7 +160,7 @@ impl Diagnostic {
message,
range: range.into(),
severity: match code {
DiagnosticCode::RustcHardError(_) => Severity::Error,
DiagnosticCode::RustcHardError(_) | DiagnosticCode::SyntaxError => Severity::Error,
// FIXME: Rustc lints are not always warning, but the ones that are currently implemented are all warnings.
DiagnosticCode::RustcLint(_) => Severity::Warning,
// FIXME: We can make this configurable, and if the user uses `cargo clippy` on flycheck, we can
@ -297,31 +303,54 @@ impl DiagnosticsContext<'_> {
}
}
/// Request diagnostics for the given [`FileId`]. The produced diagnostics may point to other files
/// Request parser level diagnostics for the given [`FileId`].
pub fn syntax_diagnostics(
db: &RootDatabase,
config: &DiagnosticsConfig,
file_id: FileId,
) -> Vec<Diagnostic> {
let _p = tracing::info_span!("syntax_diagnostics").entered();
if config.disabled.contains("syntax-error") {
return Vec::new();
}
let sema = Semantics::new(db);
let file_id = sema
.attach_first_edition(file_id)
.unwrap_or_else(|| EditionedFileId::current_edition(file_id));
// [#3434] Only take first 128 errors to prevent slowing down editor/ide, the number 128 is chosen arbitrarily.
db.parse_errors(file_id)
.as_deref()
.into_iter()
.flatten()
.take(128)
.map(|err| {
Diagnostic::new(
DiagnosticCode::SyntaxError,
format!("Syntax Error: {err}"),
FileRange { file_id: file_id.into(), range: err.range() },
)
})
.collect()
}
/// Request semantic diagnostics for the given [`FileId`]. The produced diagnostics may point to other files
/// due to macros.
pub fn diagnostics(
pub fn semantic_diagnostics(
db: &RootDatabase,
config: &DiagnosticsConfig,
resolve: &AssistResolveStrategy,
file_id: FileId,
) -> Vec<Diagnostic> {
let _p = tracing::info_span!("diagnostics").entered();
let _p = tracing::info_span!("semantic_diagnostics").entered();
let sema = Semantics::new(db);
let file_id = sema
.attach_first_edition(file_id)
.unwrap_or_else(|| EditionedFileId::current_edition(file_id));
let mut res = Vec::new();
// [#34344] Only take first 128 errors to prevent slowing down editor/ide, the number 128 is chosen arbitrarily.
res.extend(db.parse_errors(file_id).as_deref().into_iter().flatten().take(128).map(|err| {
Diagnostic::new(
DiagnosticCode::RustcHardError("syntax-error"),
format!("Syntax Error: {err}"),
FileRange { file_id: file_id.into(), range: err.range() },
)
}));
let parse_errors = res.len();
let parse = sema.parse(file_id);
// FIXME: This iterates the entire file which is a rather expensive operation.
@ -341,13 +370,17 @@ pub fn diagnostics(
match module {
// A bunch of parse errors in a file indicate some bigger structural parse changes in the
// file, so we skip semantic diagnostics so we can show these faster.
Some(m) if parse_errors < 16 => m.diagnostics(db, &mut diags, config.style_lints),
Some(_) => (),
Some(m) => {
if !db.parse_errors(file_id).as_deref().is_some_and(|es| es.len() >= 16) {
m.diagnostics(db, &mut diags, config.style_lints);
}
}
None => handlers::unlinked_file::unlinked_file(&ctx, &mut res, file_id.file_id()),
}
for diag in diags {
let d = match diag {
AnyDiagnostic::AwaitOutsideOfAsync(d) => handlers::await_outside_of_async::await_outside_of_async(&ctx, &d),
AnyDiagnostic::ExpectedFunction(d) => handlers::expected_function::expected_function(&ctx, &d),
AnyDiagnostic::InactiveCode(d) => match handlers::inactive_code::inactive_code(&ctx, &d) {
Some(it) => it,
@ -363,7 +396,7 @@ pub fn diagnostics(
res.extend(d.errors.iter().take(16).map(|err| {
{
Diagnostic::new(
DiagnosticCode::RustcHardError("syntax-error"),
DiagnosticCode::SyntaxError,
format!("Syntax Error in Expansion: {err}"),
ctx.resolve_precise_location(&d.node.clone(), d.precise_location),
)
@ -464,6 +497,19 @@ pub fn diagnostics(
res
}
/// Request both syntax and semantic diagnostics for the given [`FileId`].
pub fn full_diagnostics(
db: &RootDatabase,
config: &DiagnosticsConfig,
resolve: &AssistResolveStrategy,
file_id: FileId,
) -> Vec<Diagnostic> {
let mut res = syntax_diagnostics(db, config, file_id);
let sema = semantic_diagnostics(db, config, resolve, file_id);
res.extend(sema);
res
}
// `__RA_EVERY_LINT` is a fake lint group to allow every lint in proc macros
static RUSTC_LINT_GROUPS_DICT: Lazy<FxHashMap<&str, Vec<&str>>> =

View file

@ -1,7 +1,7 @@
#![allow(clippy::print_stderr)]
use ide_db::{
assists::AssistResolveStrategy, base_db::SourceDatabaseExt, LineIndexDatabase, RootDatabase,
assists::AssistResolveStrategy, base_db::SourceDatabase, LineIndexDatabase, RootDatabase,
};
use itertools::Itertools;
use stdx::trim_indent;
@ -59,10 +59,14 @@ fn check_nth_fix_with_config(
let after = trim_indent(ra_fixture_after);
let (db, file_position) = RootDatabase::with_position(ra_fixture_before);
let diagnostic =
super::diagnostics(&db, &config, &AssistResolveStrategy::All, file_position.file_id.into())
.pop()
.expect("no diagnostics");
let diagnostic = super::full_diagnostics(
&db,
&config,
&AssistResolveStrategy::All,
file_position.file_id.into(),
)
.pop()
.expect("no diagnostics");
let fix = &diagnostic
.fixes
.unwrap_or_else(|| panic!("{:?} diagnostic misses fixes", diagnostic.code))[nth];
@ -102,37 +106,39 @@ pub(crate) fn check_has_fix(ra_fixture_before: &str, ra_fixture_after: &str) {
let (db, file_position) = RootDatabase::with_position(ra_fixture_before);
let mut conf = DiagnosticsConfig::test_sample();
conf.expr_fill_default = ExprFillDefaultMode::Default;
let fix =
super::diagnostics(&db, &conf, &AssistResolveStrategy::All, file_position.file_id.into())
.into_iter()
.find(|d| {
d.fixes
.as_ref()
.and_then(|fixes| {
fixes.iter().find(|fix| {
if !fix.target.contains_inclusive(file_position.offset) {
return false;
}
let actual = {
let source_change = fix.source_change.as_ref().unwrap();
let file_id =
*source_change.source_file_edits.keys().next().unwrap();
let mut actual = db.file_text(file_id).to_string();
let fix = super::full_diagnostics(
&db,
&conf,
&AssistResolveStrategy::All,
file_position.file_id.into(),
)
.into_iter()
.find(|d| {
d.fixes
.as_ref()
.and_then(|fixes| {
fixes.iter().find(|fix| {
if !fix.target.contains_inclusive(file_position.offset) {
return false;
}
let actual = {
let source_change = fix.source_change.as_ref().unwrap();
let file_id = *source_change.source_file_edits.keys().next().unwrap();
let mut actual = db.file_text(file_id).to_string();
for (edit, snippet_edit) in source_change.source_file_edits.values()
{
edit.apply(&mut actual);
if let Some(snippet_edit) = snippet_edit {
snippet_edit.apply(&mut actual);
}
}
actual
};
after == actual
})
})
.is_some()
});
for (edit, snippet_edit) in source_change.source_file_edits.values() {
edit.apply(&mut actual);
if let Some(snippet_edit) = snippet_edit {
snippet_edit.apply(&mut actual);
}
}
actual
};
after == actual
})
})
.is_some()
});
assert!(fix.is_some(), "no diagnostic with desired fix");
}
@ -144,38 +150,40 @@ pub(crate) fn check_has_single_fix(ra_fixture_before: &str, ra_fixture_after: &s
let mut conf = DiagnosticsConfig::test_sample();
conf.expr_fill_default = ExprFillDefaultMode::Default;
let mut n_fixes = 0;
let fix =
super::diagnostics(&db, &conf, &AssistResolveStrategy::All, file_position.file_id.into())
.into_iter()
.find(|d| {
d.fixes
.as_ref()
.and_then(|fixes| {
n_fixes += fixes.len();
fixes.iter().find(|fix| {
if !fix.target.contains_inclusive(file_position.offset) {
return false;
}
let actual = {
let source_change = fix.source_change.as_ref().unwrap();
let file_id =
*source_change.source_file_edits.keys().next().unwrap();
let mut actual = db.file_text(file_id).to_string();
let fix = super::full_diagnostics(
&db,
&conf,
&AssistResolveStrategy::All,
file_position.file_id.into(),
)
.into_iter()
.find(|d| {
d.fixes
.as_ref()
.and_then(|fixes| {
n_fixes += fixes.len();
fixes.iter().find(|fix| {
if !fix.target.contains_inclusive(file_position.offset) {
return false;
}
let actual = {
let source_change = fix.source_change.as_ref().unwrap();
let file_id = *source_change.source_file_edits.keys().next().unwrap();
let mut actual = db.file_text(file_id).to_string();
for (edit, snippet_edit) in source_change.source_file_edits.values()
{
edit.apply(&mut actual);
if let Some(snippet_edit) = snippet_edit {
snippet_edit.apply(&mut actual);
}
}
actual
};
after == actual
})
})
.is_some()
});
for (edit, snippet_edit) in source_change.source_file_edits.values() {
edit.apply(&mut actual);
if let Some(snippet_edit) = snippet_edit {
snippet_edit.apply(&mut actual);
}
}
actual
};
after == actual
})
})
.is_some()
});
assert!(fix.is_some(), "no diagnostic with desired fix");
assert!(n_fixes == 1, "Too many fixes suggested");
}
@ -183,7 +191,7 @@ pub(crate) fn check_has_single_fix(ra_fixture_before: &str, ra_fixture_after: &s
/// Checks that there's a diagnostic *without* fix at `$0`.
pub(crate) fn check_no_fix(ra_fixture: &str) {
let (db, file_position) = RootDatabase::with_position(ra_fixture);
let diagnostic = super::diagnostics(
let diagnostic = super::full_diagnostics(
&db,
&DiagnosticsConfig::test_sample(),
&AssistResolveStrategy::All,
@ -215,7 +223,7 @@ pub(crate) fn check_diagnostics_with_config(config: DiagnosticsConfig, ra_fixtur
.iter()
.copied()
.flat_map(|file_id| {
super::diagnostics(&db, &config, &AssistResolveStrategy::All, file_id.into())
super::full_diagnostics(&db, &config, &AssistResolveStrategy::All, file_id.into())
.into_iter()
.map(|d| {
let mut annotation = String::new();
@ -243,6 +251,12 @@ pub(crate) fn check_diagnostics_with_config(config: DiagnosticsConfig, ra_fixtur
let mut actual = annotations.remove(&file_id).unwrap_or_default();
let expected = extract_annotations(&db.file_text(file_id));
actual.sort_by_key(|(range, _)| range.start());
// FIXME: We should panic on duplicates instead, but includes currently cause us to report
// diagnostics twice for the calling module when both files are queried.
actual.dedup();
// actual.iter().duplicates().for_each(|(range, msg)| {
// panic!("duplicate diagnostic at {:?}: {msg:?}", line_index.line_col(range.start()))
// });
if expected.is_empty() {
// makes minicore smoke test debuggable
for (e, _) in &actual {
@ -277,10 +291,10 @@ fn test_disabled_diagnostics() {
let (db, file_id) = RootDatabase::with_single_file(r#"mod foo;"#);
let file_id = file_id.into();
let diagnostics = super::diagnostics(&db, &config, &AssistResolveStrategy::All, file_id);
let diagnostics = super::full_diagnostics(&db, &config, &AssistResolveStrategy::All, file_id);
assert!(diagnostics.is_empty());
let diagnostics = super::diagnostics(
let diagnostics = super::full_diagnostics(
&db,
&DiagnosticsConfig::test_sample(),
&AssistResolveStrategy::All,

View file

@ -1,8 +1,8 @@
[package]
name = "ide-ssr"
version = "0.0.0"
description = "Structural search and replace of Rust code"
repository = "https://github.com/rust-lang/rust-analyzer"
repository.workspace = true
description = "Structural search and replace of Rust code for rust-analyzer."
authors.workspace = true
edition.workspace = true

Some files were not shown because too many files have changed in this diff Show more