mirror of
https://github.com/rust-lang/rust-analyzer
synced 2025-01-10 12:18:49 +00:00
Rollup merge of #120916 - lnicola:sync-from-ra, r=lnicola
Subtree update of ` rust-analyzer` r? `@ghost`
This commit is contained in:
commit
7c663dc6a6
378 changed files with 14720 additions and 3111 deletions
6
.github/workflows/ci.yaml
vendored
6
.github/workflows/ci.yaml
vendored
|
@ -28,7 +28,7 @@ jobs:
|
||||||
proc_macros: ${{ steps.filter.outputs.proc_macros }}
|
proc_macros: ${{ steps.filter.outputs.proc_macros }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- uses: dorny/paths-filter@4067d885736b84de7c414f582ac45897079b0a78
|
- uses: dorny/paths-filter@1441771bbfdd59dcd748680ee64ebd8faab1a242
|
||||||
id: filter
|
id: filter
|
||||||
with:
|
with:
|
||||||
filters: |
|
filters: |
|
||||||
|
@ -104,8 +104,8 @@ jobs:
|
||||||
run: target/${{ matrix.target }}/debug/rust-analyzer analysis-stats --with-deps $(rustc --print sysroot)/lib/rustlib/src/rust/library/std
|
run: target/${{ matrix.target }}/debug/rust-analyzer analysis-stats --with-deps $(rustc --print sysroot)/lib/rustlib/src/rust/library/std
|
||||||
|
|
||||||
- name: clippy
|
- name: clippy
|
||||||
if: matrix.os == 'ubuntu-latest'
|
if: matrix.os == 'windows-latest'
|
||||||
run: cargo clippy --all-targets
|
run: cargo clippy --all-targets -- -D clippy::disallowed_macros -D clippy::dbg_macro -D clippy::todo -D clippy::print_stdout -D clippy::print_stderr
|
||||||
|
|
||||||
# Weird targets to catch non-portable code
|
# Weird targets to catch non-portable code
|
||||||
rust-cross:
|
rust-cross:
|
||||||
|
|
228
Cargo.lock
generated
228
Cargo.lock
generated
|
@ -72,8 +72,8 @@ dependencies = [
|
||||||
"cfg",
|
"cfg",
|
||||||
"la-arena 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"la-arena 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"profile",
|
"profile",
|
||||||
"rust-analyzer-salsa",
|
|
||||||
"rustc-hash",
|
"rustc-hash",
|
||||||
|
"salsa",
|
||||||
"semver",
|
"semver",
|
||||||
"span",
|
"span",
|
||||||
"stdx",
|
"stdx",
|
||||||
|
@ -167,7 +167,7 @@ checksum = "5676cea088c32290fe65c82895be9d06dd21e0fa49bb97ca840529e9417ab71a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.39",
|
"syn",
|
||||||
"synstructure",
|
"synstructure",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -313,17 +313,6 @@ dependencies = [
|
||||||
"parking_lot_core",
|
"parking_lot_core",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "derivative"
|
|
||||||
version = "2.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 1.0.109",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "derive_arbitrary"
|
name = "derive_arbitrary"
|
||||||
version = "1.3.2"
|
version = "1.3.2"
|
||||||
|
@ -332,7 +321,7 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.39",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -368,6 +357,15 @@ dependencies = [
|
||||||
"log",
|
"log",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "env_logger"
|
||||||
|
version = "0.10.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "equivalent"
|
name = "equivalent"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
|
@ -452,6 +450,17 @@ version = "0.4.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7ab85b9b05e3978cc9a9cf8fea7f01b494e1a09ed3037e16ba39edc7a29eb61a"
|
checksum = "7ab85b9b05e3978cc9a9cf8fea7f01b494e1a09ed3037e16ba39edc7a29eb61a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "getrandom"
|
||||||
|
version = "0.2.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"wasi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gimli"
|
name = "gimli"
|
||||||
version = "0.27.3"
|
version = "0.27.3"
|
||||||
|
@ -929,6 +938,12 @@ dependencies = [
|
||||||
"text-size",
|
"text-size",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "linked-hash-map"
|
||||||
|
version = "0.5.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "load-cargo"
|
name = "load-cargo"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
|
@ -1272,6 +1287,12 @@ version = "0.2.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
|
checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ppv-lite86"
|
||||||
|
version = "0.2.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro-api"
|
name = "proc-macro-api"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
|
@ -1283,6 +1304,7 @@ dependencies = [
|
||||||
"object 0.32.0",
|
"object 0.32.0",
|
||||||
"paths",
|
"paths",
|
||||||
"profile",
|
"profile",
|
||||||
|
"rustc-hash",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"snap",
|
"snap",
|
||||||
|
@ -1435,17 +1457,6 @@ dependencies = [
|
||||||
"tracing",
|
"tracing",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "ra-ap-rustc_index"
|
|
||||||
version = "0.33.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5e5313d7f243b63ef9e58d94355b11aa8499f1328055f1f58adf0a5ea7d2faca"
|
|
||||||
dependencies = [
|
|
||||||
"arrayvec",
|
|
||||||
"ra-ap-rustc_index_macros 0.33.0",
|
|
||||||
"smallvec",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ra-ap-rustc_index"
|
name = "ra-ap-rustc_index"
|
||||||
version = "0.35.0"
|
version = "0.35.0"
|
||||||
|
@ -1458,15 +1469,14 @@ dependencies = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ra-ap-rustc_index_macros"
|
name = "ra-ap-rustc_index"
|
||||||
version = "0.33.0"
|
version = "0.36.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a83108ebf3e73dde205b9c25706209bcd7736480820f90ded28eabaf8b469f25"
|
checksum = "f8a41dee58608b1fc93779ea365edaa70ac9927e3335ae914b675be0fa063cd7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"arrayvec",
|
||||||
"quote",
|
"ra-ap-rustc_index_macros 0.36.0",
|
||||||
"syn 2.0.39",
|
"smallvec",
|
||||||
"synstructure",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1477,7 +1487,19 @@ checksum = "054e25eac52f0506c1309ca4317c11ad4925d7b99eb897f71aa7c3cbafb46c2b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.39",
|
"syn",
|
||||||
|
"synstructure",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ra-ap-rustc_index_macros"
|
||||||
|
version = "0.36.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fbfe98def54c4337a2f7d8233850bd5d5349972b185fe8a0db2b979164b30ed8"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
"synstructure",
|
"synstructure",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -1503,18 +1525,47 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ra-ap-rustc_pattern_analysis"
|
name = "ra-ap-rustc_pattern_analysis"
|
||||||
version = "0.33.0"
|
version = "0.36.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6c4085e0c771fd4b883930b599ef42966b855762bbe4052c17673b3253421a6d"
|
checksum = "b5529bffec7530b4a3425640bfdfd9b95d87c4c620f740266c0de6572561aab4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"derivative",
|
"ra-ap-rustc_index 0.36.0",
|
||||||
"ra-ap-rustc_index 0.33.0",
|
|
||||||
"rustc-hash",
|
"rustc-hash",
|
||||||
"rustc_apfloat",
|
"rustc_apfloat",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"tracing",
|
"tracing",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand"
|
||||||
|
version = "0.8.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"rand_chacha",
|
||||||
|
"rand_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_chacha"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||||
|
dependencies = [
|
||||||
|
"ppv-lite86",
|
||||||
|
"rand_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.6.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rayon"
|
name = "rayon"
|
||||||
version = "1.8.0"
|
version = "1.8.0"
|
||||||
|
@ -1622,35 +1673,6 @@ dependencies = [
|
||||||
"xshell",
|
"xshell",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rust-analyzer-salsa"
|
|
||||||
version = "0.17.0-pre.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "719825638c59fd26a55412a24561c7c5bcf54364c88b9a7a04ba08a6eafaba8d"
|
|
||||||
dependencies = [
|
|
||||||
"indexmap",
|
|
||||||
"lock_api",
|
|
||||||
"oorandom",
|
|
||||||
"parking_lot",
|
|
||||||
"rust-analyzer-salsa-macros",
|
|
||||||
"rustc-hash",
|
|
||||||
"smallvec",
|
|
||||||
"tracing",
|
|
||||||
"triomphe",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rust-analyzer-salsa-macros"
|
|
||||||
version = "0.17.0-pre.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "4d96498e9684848c6676c399032ebc37c52da95ecbefa83d71ccc53b9f8a4a8e"
|
|
||||||
dependencies = [
|
|
||||||
"heck",
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 2.0.39",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc-demangle"
|
name = "rustc-demangle"
|
||||||
version = "0.1.23"
|
version = "0.1.23"
|
||||||
|
@ -1679,6 +1701,36 @@ version = "1.0.13"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041"
|
checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "salsa"
|
||||||
|
version = "0.0.0"
|
||||||
|
dependencies = [
|
||||||
|
"dissimilar",
|
||||||
|
"expect-test",
|
||||||
|
"indexmap",
|
||||||
|
"linked-hash-map",
|
||||||
|
"lock_api",
|
||||||
|
"oorandom",
|
||||||
|
"parking_lot",
|
||||||
|
"rand",
|
||||||
|
"rustc-hash",
|
||||||
|
"salsa-macros",
|
||||||
|
"smallvec",
|
||||||
|
"test-log",
|
||||||
|
"tracing",
|
||||||
|
"triomphe",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "salsa-macros"
|
||||||
|
version = "0.0.0"
|
||||||
|
dependencies = [
|
||||||
|
"heck",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "same-file"
|
name = "same-file"
|
||||||
version = "1.0.6"
|
version = "1.0.6"
|
||||||
|
@ -1735,7 +1787,7 @@ checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.39",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1758,7 +1810,7 @@ checksum = "bcec881020c684085e55a25f7fd888954d56609ef363479dc5a1305eb0d40cab"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.39",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1803,7 +1855,7 @@ name = "span"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"la-arena 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"la-arena 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rust-analyzer-salsa",
|
"salsa",
|
||||||
"stdx",
|
"stdx",
|
||||||
"syntax",
|
"syntax",
|
||||||
"vfs",
|
"vfs",
|
||||||
|
@ -1835,17 +1887,6 @@ dependencies = [
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "syn"
|
|
||||||
version = "1.0.109"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"unicode-ident",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.39"
|
version = "2.0.39"
|
||||||
|
@ -1865,7 +1906,7 @@ checksum = "285ba80e733fac80aa4270fbcdf83772a79b80aa35c97075320abfee4a915b06"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.39",
|
"syn",
|
||||||
"unicode-xid",
|
"unicode-xid",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -1911,6 +1952,27 @@ dependencies = [
|
||||||
"tt",
|
"tt",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "test-log"
|
||||||
|
version = "0.2.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6159ab4116165c99fc88cce31f99fa2c9dbe08d3691cb38da02fc3b45f357d2b"
|
||||||
|
dependencies = [
|
||||||
|
"env_logger",
|
||||||
|
"test-log-macros",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "test-log-macros"
|
||||||
|
version = "0.2.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7ba277e77219e9eea169e8508942db1bf5d8a41ff2db9b20aab5a5aadc9fa25d"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "test-utils"
|
name = "test-utils"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
|
@ -1954,7 +2016,7 @@ checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.39",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -2055,7 +2117,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.39",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
21
Cargo.toml
21
Cargo.toml
|
@ -70,6 +70,7 @@ proc-macro-srv = { path = "./crates/proc-macro-srv", version = "0.0.0" }
|
||||||
proc-macro-srv-cli = { path = "./crates/proc-macro-srv-cli", version = "0.0.0" }
|
proc-macro-srv-cli = { path = "./crates/proc-macro-srv-cli", version = "0.0.0" }
|
||||||
profile = { path = "./crates/profile", version = "0.0.0" }
|
profile = { path = "./crates/profile", version = "0.0.0" }
|
||||||
project-model = { path = "./crates/project-model", version = "0.0.0" }
|
project-model = { path = "./crates/project-model", version = "0.0.0" }
|
||||||
|
salsa = { path = "./crates/salsa", version = "0.0.0" }
|
||||||
span = { path = "./crates/span", version = "0.0.0" }
|
span = { path = "./crates/span", version = "0.0.0" }
|
||||||
stdx = { path = "./crates/stdx", version = "0.0.0" }
|
stdx = { path = "./crates/stdx", version = "0.0.0" }
|
||||||
syntax = { path = "./crates/syntax", version = "0.0.0" }
|
syntax = { path = "./crates/syntax", version = "0.0.0" }
|
||||||
|
@ -83,7 +84,7 @@ ra-ap-rustc_lexer = { version = "0.35.0", default-features = false }
|
||||||
ra-ap-rustc_parse_format = { version = "0.35.0", default-features = false }
|
ra-ap-rustc_parse_format = { version = "0.35.0", default-features = false }
|
||||||
ra-ap-rustc_index = { version = "0.35.0", default-features = false }
|
ra-ap-rustc_index = { version = "0.35.0", default-features = false }
|
||||||
ra-ap-rustc_abi = { version = "0.35.0", default-features = false }
|
ra-ap-rustc_abi = { version = "0.35.0", default-features = false }
|
||||||
ra-ap-rustc_pattern_analysis = { version = "0.33.0", default-features = false }
|
ra-ap-rustc_pattern_analysis = { version = "0.36.0", default-features = false }
|
||||||
|
|
||||||
# local crates that aren't published to crates.io. These should not have versions.
|
# local crates that aren't published to crates.io. These should not have versions.
|
||||||
sourcegen = { path = "./crates/sourcegen" }
|
sourcegen = { path = "./crates/sourcegen" }
|
||||||
|
@ -113,7 +114,6 @@ itertools = "0.12.0"
|
||||||
libc = "0.2.150"
|
libc = "0.2.150"
|
||||||
nohash-hasher = "0.2.0"
|
nohash-hasher = "0.2.0"
|
||||||
rayon = "1.8.0"
|
rayon = "1.8.0"
|
||||||
rust-analyzer-salsa = "0.17.0-pre.6"
|
|
||||||
rustc-hash = "1.1.0"
|
rustc-hash = "1.1.0"
|
||||||
semver = "1.0.14"
|
semver = "1.0.14"
|
||||||
serde = { version = "1.0.192", features = ["derive"] }
|
serde = { version = "1.0.192", features = ["derive"] }
|
||||||
|
@ -164,23 +164,20 @@ len_without_is_empty = "allow"
|
||||||
enum_variant_names = "allow"
|
enum_variant_names = "allow"
|
||||||
# Builder pattern disagrees
|
# Builder pattern disagrees
|
||||||
new_ret_no_self = "allow"
|
new_ret_no_self = "allow"
|
||||||
|
# Has a bunch of false positives
|
||||||
|
useless_asref = "allow"
|
||||||
|
|
||||||
## Following lints should be tackled at some point
|
## Following lints should be tackled at some point
|
||||||
borrowed_box = "allow"
|
|
||||||
derived_hash_with_manual_eq = "allow"
|
|
||||||
forget_non_drop = "allow"
|
|
||||||
needless_doctest_main = "allow"
|
|
||||||
non_canonical_clone_impl = "allow"
|
|
||||||
non_canonical_partial_ord_impl = "allow"
|
|
||||||
self_named_constructors = "allow"
|
|
||||||
too_many_arguments = "allow"
|
too_many_arguments = "allow"
|
||||||
type_complexity = "allow"
|
type_complexity = "allow"
|
||||||
wrong_self_convention = "allow"
|
wrong_self_convention = "allow"
|
||||||
|
|
||||||
## warn at following lints
|
## warn at following lints
|
||||||
|
# CI raises these to deny
|
||||||
dbg_macro = "warn"
|
dbg_macro = "warn"
|
||||||
todo = "warn"
|
todo = "warn"
|
||||||
unimplemented = "allow"
|
print_stdout = "warn"
|
||||||
|
print_stderr = "warn"
|
||||||
|
|
||||||
rc_buffer = "warn"
|
rc_buffer = "warn"
|
||||||
# FIXME enable this, we use this pattern a lot so its annoying work ...
|
str_to_string = "warn"
|
||||||
# str_to_string = "warn"
|
|
||||||
|
|
5
clippy.toml
Normal file
5
clippy.toml
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
disallowed-types = [
|
||||||
|
{ path = "std::collections::HashMap", reason = "use FxHashMap" },
|
||||||
|
{ path = "std::collections::HashSet", reason = "use FxHashSet" },
|
||||||
|
{ path = "std::collections::hash_map::RandomState", reason = "use BuildHasherDefault<FxHasher>"}
|
||||||
|
]
|
|
@ -13,7 +13,7 @@ doctest = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
la-arena.workspace = true
|
la-arena.workspace = true
|
||||||
rust-analyzer-salsa.workspace = true
|
salsa.workspace = true
|
||||||
rustc-hash.workspace = true
|
rustc-hash.workspace = true
|
||||||
triomphe.workspace = true
|
triomphe.workspace = true
|
||||||
semver.workspace = true
|
semver.workspace = true
|
||||||
|
|
|
@ -782,7 +782,7 @@ impl FromStr for Edition {
|
||||||
"2018" => Edition::Edition2018,
|
"2018" => Edition::Edition2018,
|
||||||
"2021" => Edition::Edition2021,
|
"2021" => Edition::Edition2021,
|
||||||
"2024" => Edition::Edition2024,
|
"2024" => Edition::Edition2024,
|
||||||
_ => return Err(ParseEditionError { invalid_input: s.to_string() }),
|
_ => return Err(ParseEditionError { invalid_input: s.to_owned() }),
|
||||||
};
|
};
|
||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
|
|
|
@ -100,9 +100,14 @@ impl FlycheckHandle {
|
||||||
FlycheckHandle { id, sender, _thread: thread }
|
FlycheckHandle { id, sender, _thread: thread }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Schedule a re-start of the cargo check worker.
|
/// Schedule a re-start of the cargo check worker to do a workspace wide check.
|
||||||
pub fn restart(&self) {
|
pub fn restart_workspace(&self) {
|
||||||
self.sender.send(StateChange::Restart).unwrap();
|
self.sender.send(StateChange::Restart(None)).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Schedule a re-start of the cargo check worker to do a package wide check.
|
||||||
|
pub fn restart_for_package(&self, package: String) {
|
||||||
|
self.sender.send(StateChange::Restart(Some(package))).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Stop this cargo check worker.
|
/// Stop this cargo check worker.
|
||||||
|
@ -153,7 +158,7 @@ pub enum Progress {
|
||||||
}
|
}
|
||||||
|
|
||||||
enum StateChange {
|
enum StateChange {
|
||||||
Restart,
|
Restart(Option<String>),
|
||||||
Cancel,
|
Cancel,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -213,7 +218,7 @@ impl FlycheckActor {
|
||||||
tracing::debug!(flycheck_id = self.id, "flycheck cancelled");
|
tracing::debug!(flycheck_id = self.id, "flycheck cancelled");
|
||||||
self.cancel_check_process();
|
self.cancel_check_process();
|
||||||
}
|
}
|
||||||
Event::RequestStateChange(StateChange::Restart) => {
|
Event::RequestStateChange(StateChange::Restart(package)) => {
|
||||||
// Cancel the previously spawned process
|
// Cancel the previously spawned process
|
||||||
self.cancel_check_process();
|
self.cancel_check_process();
|
||||||
while let Ok(restart) = inbox.recv_timeout(Duration::from_millis(50)) {
|
while let Ok(restart) = inbox.recv_timeout(Duration::from_millis(50)) {
|
||||||
|
@ -223,7 +228,7 @@ impl FlycheckActor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let command = self.check_command();
|
let command = self.check_command(package.as_deref());
|
||||||
let formatted_command = format!("{:?}", command);
|
let formatted_command = format!("{:?}", command);
|
||||||
|
|
||||||
tracing::debug!(?command, "will restart flycheck");
|
tracing::debug!(?command, "will restart flycheck");
|
||||||
|
@ -297,7 +302,7 @@ impl FlycheckActor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_command(&self) -> Command {
|
fn check_command(&self, package: Option<&str>) -> Command {
|
||||||
let (mut cmd, args) = match &self.config {
|
let (mut cmd, args) = match &self.config {
|
||||||
FlycheckConfig::CargoCommand {
|
FlycheckConfig::CargoCommand {
|
||||||
command,
|
command,
|
||||||
|
@ -314,7 +319,11 @@ impl FlycheckActor {
|
||||||
let mut cmd = Command::new(toolchain::cargo());
|
let mut cmd = Command::new(toolchain::cargo());
|
||||||
cmd.arg(command);
|
cmd.arg(command);
|
||||||
cmd.current_dir(&self.root);
|
cmd.current_dir(&self.root);
|
||||||
cmd.arg("--workspace");
|
|
||||||
|
match package {
|
||||||
|
Some(pkg) => cmd.arg("-p").arg(pkg),
|
||||||
|
None => cmd.arg("--workspace"),
|
||||||
|
};
|
||||||
|
|
||||||
cmd.arg(if *ansi_color_output {
|
cmd.arg(if *ansi_color_output {
|
||||||
"--message-format=json-diagnostic-rendered-ansi"
|
"--message-format=json-diagnostic-rendered-ansi"
|
||||||
|
@ -493,9 +502,7 @@ impl CargoActor {
|
||||||
// Skip certain kinds of messages to only spend time on what's useful
|
// Skip certain kinds of messages to only spend time on what's useful
|
||||||
JsonMessage::Cargo(message) => match message {
|
JsonMessage::Cargo(message) => match message {
|
||||||
cargo_metadata::Message::CompilerArtifact(artifact) if !artifact.fresh => {
|
cargo_metadata::Message::CompilerArtifact(artifact) if !artifact.fresh => {
|
||||||
self.sender
|
self.sender.send(CargoMessage::CompilerArtifact(artifact)).unwrap();
|
||||||
.send(CargoMessage::CompilerArtifact(Box::new(artifact)))
|
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
cargo_metadata::Message::CompilerMessage(msg) => {
|
cargo_metadata::Message::CompilerMessage(msg) => {
|
||||||
self.sender.send(CargoMessage::Diagnostic(msg.message)).unwrap();
|
self.sender.send(CargoMessage::Diagnostic(msg.message)).unwrap();
|
||||||
|
@ -539,8 +546,9 @@ impl CargoActor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::large_enum_variant)]
|
||||||
enum CargoMessage {
|
enum CargoMessage {
|
||||||
CompilerArtifact(Box<cargo_metadata::Artifact>),
|
CompilerArtifact(cargo_metadata::Artifact),
|
||||||
Diagnostic(Diagnostic),
|
Diagnostic(Diagnostic),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,11 +24,11 @@ use triomphe::Arc;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
db::DefDatabase,
|
db::DefDatabase,
|
||||||
item_tree::{AttrOwner, Fields, ItemTreeId, ItemTreeModItemNode},
|
item_tree::{AttrOwner, Fields, ItemTreeNode},
|
||||||
lang_item::LangItem,
|
lang_item::LangItem,
|
||||||
nameres::{ModuleOrigin, ModuleSource},
|
nameres::{ModuleOrigin, ModuleSource},
|
||||||
src::{HasChildSource, HasSource},
|
src::{HasChildSource, HasSource},
|
||||||
AdtId, AssocItemLoc, AttrDefId, GenericParamId, ItemLoc, LocalFieldId, Lookup, MacroId,
|
AdtId, AttrDefId, GenericParamId, HasModule, ItemTreeLoc, LocalFieldId, Lookup, MacroId,
|
||||||
VariantId,
|
VariantId,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -317,7 +317,7 @@ fn parse_comma_sep<S>(subtree: &tt::Subtree<S>) -> Vec<SmolStr> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AttrsWithOwner {
|
impl AttrsWithOwner {
|
||||||
pub fn attrs_with_owner(db: &dyn DefDatabase, owner: AttrDefId) -> Self {
|
pub fn new(db: &dyn DefDatabase, owner: AttrDefId) -> Self {
|
||||||
Self { attrs: db.attrs(owner), owner }
|
Self { attrs: db.attrs(owner), owner }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -356,12 +356,7 @@ impl AttrsWithOwner {
|
||||||
AttrDefId::FieldId(it) => {
|
AttrDefId::FieldId(it) => {
|
||||||
return db.fields_attrs(it.parent)[it.local_id].clone();
|
return db.fields_attrs(it.parent)[it.local_id].clone();
|
||||||
}
|
}
|
||||||
// FIXME: DRY this up
|
AttrDefId::EnumVariantId(it) => attrs_from_item_tree_loc(db, it),
|
||||||
AttrDefId::EnumVariantId(it) => {
|
|
||||||
let id = it.lookup(db).id;
|
|
||||||
let tree = id.item_tree(db);
|
|
||||||
tree.raw_attrs(id.value.into()).clone()
|
|
||||||
}
|
|
||||||
AttrDefId::AdtId(it) => match it {
|
AttrDefId::AdtId(it) => match it {
|
||||||
AdtId::StructId(it) => attrs_from_item_tree_loc(db, it),
|
AdtId::StructId(it) => attrs_from_item_tree_loc(db, it),
|
||||||
AdtId::EnumId(it) => attrs_from_item_tree_loc(db, it),
|
AdtId::EnumId(it) => attrs_from_item_tree_loc(db, it),
|
||||||
|
@ -370,15 +365,15 @@ impl AttrsWithOwner {
|
||||||
AttrDefId::TraitId(it) => attrs_from_item_tree_loc(db, it),
|
AttrDefId::TraitId(it) => attrs_from_item_tree_loc(db, it),
|
||||||
AttrDefId::TraitAliasId(it) => attrs_from_item_tree_loc(db, it),
|
AttrDefId::TraitAliasId(it) => attrs_from_item_tree_loc(db, it),
|
||||||
AttrDefId::MacroId(it) => match it {
|
AttrDefId::MacroId(it) => match it {
|
||||||
MacroId::Macro2Id(it) => attrs_from_item_tree(db, it.lookup(db).id),
|
MacroId::Macro2Id(it) => attrs_from_item_tree_loc(db, it),
|
||||||
MacroId::MacroRulesId(it) => attrs_from_item_tree(db, it.lookup(db).id),
|
MacroId::MacroRulesId(it) => attrs_from_item_tree_loc(db, it),
|
||||||
MacroId::ProcMacroId(it) => attrs_from_item_tree(db, it.lookup(db).id),
|
MacroId::ProcMacroId(it) => attrs_from_item_tree_loc(db, it),
|
||||||
},
|
},
|
||||||
AttrDefId::ImplId(it) => attrs_from_item_tree_loc(db, it),
|
AttrDefId::ImplId(it) => attrs_from_item_tree_loc(db, it),
|
||||||
AttrDefId::ConstId(it) => attrs_from_item_tree_assoc(db, it),
|
AttrDefId::ConstId(it) => attrs_from_item_tree_loc(db, it),
|
||||||
AttrDefId::StaticId(it) => attrs_from_item_tree_assoc(db, it),
|
AttrDefId::StaticId(it) => attrs_from_item_tree_loc(db, it),
|
||||||
AttrDefId::FunctionId(it) => attrs_from_item_tree_assoc(db, it),
|
AttrDefId::FunctionId(it) => attrs_from_item_tree_loc(db, it),
|
||||||
AttrDefId::TypeAliasId(it) => attrs_from_item_tree_assoc(db, it),
|
AttrDefId::TypeAliasId(it) => attrs_from_item_tree_loc(db, it),
|
||||||
AttrDefId::GenericParamId(it) => match it {
|
AttrDefId::GenericParamId(it) => match it {
|
||||||
GenericParamId::ConstParamId(it) => {
|
GenericParamId::ConstParamId(it) => {
|
||||||
let src = it.parent().child_source(db);
|
let src = it.parent().child_source(db);
|
||||||
|
@ -603,29 +598,14 @@ fn any_has_attrs<'db>(
|
||||||
id.lookup(db).source(db).map(ast::AnyHasAttrs::new)
|
id.lookup(db).source(db).map(ast::AnyHasAttrs::new)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn attrs_from_item_tree<N: ItemTreeModItemNode>(
|
fn attrs_from_item_tree_loc<'db, N: ItemTreeNode>(
|
||||||
db: &dyn DefDatabase,
|
db: &(dyn DefDatabase + 'db),
|
||||||
id: ItemTreeId<N>,
|
lookup: impl Lookup<Database<'db> = dyn DefDatabase + 'db, Data = impl ItemTreeLoc<Id = N>>,
|
||||||
) -> RawAttrs {
|
) -> RawAttrs {
|
||||||
|
let id = lookup.lookup(db).item_tree_id();
|
||||||
let tree = id.item_tree(db);
|
let tree = id.item_tree(db);
|
||||||
let mod_item = N::id_to_mod_item(id.value);
|
let attr_owner = N::attr_owner(id.value);
|
||||||
tree.raw_attrs(mod_item.into()).clone()
|
tree.raw_attrs(attr_owner).clone()
|
||||||
}
|
|
||||||
|
|
||||||
fn attrs_from_item_tree_loc<'db, N: ItemTreeModItemNode>(
|
|
||||||
db: &(dyn DefDatabase + 'db),
|
|
||||||
lookup: impl Lookup<Database<'db> = dyn DefDatabase + 'db, Data = ItemLoc<N>>,
|
|
||||||
) -> RawAttrs {
|
|
||||||
let id = lookup.lookup(db).id;
|
|
||||||
attrs_from_item_tree(db, id)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn attrs_from_item_tree_assoc<'db, N: ItemTreeModItemNode>(
|
|
||||||
db: &(dyn DefDatabase + 'db),
|
|
||||||
lookup: impl Lookup<Database<'db> = dyn DefDatabase + 'db, Data = AssocItemLoc<N>>,
|
|
||||||
) -> RawAttrs {
|
|
||||||
let id = lookup.lookup(db).id;
|
|
||||||
attrs_from_item_tree(db, id)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn fields_attrs_source_map(
|
pub(crate) fn fields_attrs_source_map(
|
||||||
|
|
|
@ -283,9 +283,6 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
|
||||||
experimental!(optimize),
|
experimental!(optimize),
|
||||||
),
|
),
|
||||||
|
|
||||||
gated!(
|
|
||||||
ffi_returns_twice, Normal, template!(Word), WarnFollowing, experimental!(ffi_returns_twice)
|
|
||||||
),
|
|
||||||
gated!(ffi_pure, Normal, template!(Word), WarnFollowing, experimental!(ffi_pure)),
|
gated!(ffi_pure, Normal, template!(Word), WarnFollowing, experimental!(ffi_pure)),
|
||||||
gated!(ffi_const, Normal, template!(Word), WarnFollowing, experimental!(ffi_const)),
|
gated!(ffi_const, Normal, template!(Word), WarnFollowing, experimental!(ffi_const)),
|
||||||
gated!(
|
gated!(
|
||||||
|
|
|
@ -1980,10 +1980,7 @@ fn pat_literal_to_hir(lit: &ast::LiteralPat) -> Option<(Literal, ast::Literal)>
|
||||||
let ast_lit = lit.literal()?;
|
let ast_lit = lit.literal()?;
|
||||||
let mut hir_lit: Literal = ast_lit.kind().into();
|
let mut hir_lit: Literal = ast_lit.kind().into();
|
||||||
if lit.minus_token().is_some() {
|
if lit.minus_token().is_some() {
|
||||||
let Some(h) = hir_lit.negate() else {
|
hir_lit = hir_lit.negate()?;
|
||||||
return None;
|
|
||||||
};
|
|
||||||
hir_lit = h;
|
|
||||||
}
|
}
|
||||||
Some((hir_lit, ast_lit))
|
Some((hir_lit, ast_lit))
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,11 +29,11 @@ pub(super) fn print_body_hir(db: &dyn DefDatabase, body: &Body, owner: DefWithBo
|
||||||
"const {} = ",
|
"const {} = ",
|
||||||
match &it.name {
|
match &it.name {
|
||||||
Some(name) => name.display(db.upcast()).to_string(),
|
Some(name) => name.display(db.upcast()).to_string(),
|
||||||
None => "_".to_string(),
|
None => "_".to_owned(),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
DefWithBodyId::InTypeConstId(_) => "In type const = ".to_string(),
|
DefWithBodyId::InTypeConstId(_) => "In type const = ".to_owned(),
|
||||||
DefWithBodyId::VariantId(it) => {
|
DefWithBodyId::VariantId(it) => {
|
||||||
let loc = it.lookup(db);
|
let loc = it.lookup(db);
|
||||||
let enum_loc = loc.parent.lookup(db);
|
let enum_loc = loc.parent.lookup(db);
|
||||||
|
@ -123,7 +123,7 @@ impl Printer<'_> {
|
||||||
wln!(self);
|
wln!(self);
|
||||||
f(self);
|
f(self);
|
||||||
self.indent_level -= 1;
|
self.indent_level -= 1;
|
||||||
self.buf = self.buf.trim_end_matches('\n').to_string();
|
self.buf = self.buf.trim_end_matches('\n').to_owned();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn whitespace(&mut self) {
|
fn whitespace(&mut self) {
|
||||||
|
|
|
@ -6,15 +6,21 @@
|
||||||
|
|
||||||
use either::Either;
|
use either::Either;
|
||||||
use hir_expand::{attrs::collect_attrs, HirFileId};
|
use hir_expand::{attrs::collect_attrs, HirFileId};
|
||||||
|
use syntax::ast;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
db::DefDatabase,
|
db::DefDatabase,
|
||||||
dyn_map::{keys, DynMap},
|
dyn_map::{
|
||||||
|
keys::{self, Key},
|
||||||
|
DynMap,
|
||||||
|
},
|
||||||
item_scope::ItemScope,
|
item_scope::ItemScope,
|
||||||
|
item_tree::ItemTreeNode,
|
||||||
nameres::DefMap,
|
nameres::DefMap,
|
||||||
src::{HasChildSource, HasSource},
|
src::{HasChildSource, HasSource},
|
||||||
AdtId, AssocItemId, DefWithBodyId, EnumId, ExternCrateId, FieldId, ImplId, Lookup, MacroId,
|
AdtId, AssocItemId, DefWithBodyId, EnumId, FieldId, GenericDefId, ImplId, ItemTreeLoc,
|
||||||
ModuleDefId, ModuleId, TraitId, UseId, VariantId,
|
LifetimeParamId, Lookup, MacroId, ModuleDefId, ModuleId, TraitId, TypeOrConstParamId,
|
||||||
|
VariantId,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub trait ChildBySource {
|
pub trait ChildBySource {
|
||||||
|
@ -55,29 +61,6 @@ impl ChildBySource for ImplId {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_assoc_item(db: &dyn DefDatabase, res: &mut DynMap, file_id: HirFileId, item: AssocItemId) {
|
|
||||||
match item {
|
|
||||||
AssocItemId::FunctionId(func) => {
|
|
||||||
let loc = func.lookup(db);
|
|
||||||
if loc.id.file_id() == file_id {
|
|
||||||
res[keys::FUNCTION].insert(loc.source(db).value, func)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
AssocItemId::ConstId(konst) => {
|
|
||||||
let loc = konst.lookup(db);
|
|
||||||
if loc.id.file_id() == file_id {
|
|
||||||
res[keys::CONST].insert(loc.source(db).value, konst)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
AssocItemId::TypeAliasId(ty) => {
|
|
||||||
let loc = ty.lookup(db);
|
|
||||||
if loc.id.file_id() == file_id {
|
|
||||||
res[keys::TYPE_ALIAS].insert(loc.source(db).value, ty)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ChildBySource for ModuleId {
|
impl ChildBySource for ModuleId {
|
||||||
fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap, file_id: HirFileId) {
|
fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap, file_id: HirFileId) {
|
||||||
let def_map = self.def_map(db);
|
let def_map = self.def_map(db);
|
||||||
|
@ -89,15 +72,12 @@ impl ChildBySource for ModuleId {
|
||||||
impl ChildBySource for ItemScope {
|
impl ChildBySource for ItemScope {
|
||||||
fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap, file_id: HirFileId) {
|
fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap, file_id: HirFileId) {
|
||||||
self.declarations().for_each(|item| add_module_def(db, res, file_id, item));
|
self.declarations().for_each(|item| add_module_def(db, res, file_id, item));
|
||||||
self.impls().for_each(|imp| add_impl(db, res, file_id, imp));
|
self.impls().for_each(|imp| insert_item_loc(db, res, file_id, imp, keys::IMPL));
|
||||||
self.extern_crate_decls().for_each(|ext| add_extern_crate(db, res, file_id, ext));
|
self.extern_crate_decls()
|
||||||
self.use_decls().for_each(|ext| add_use(db, res, file_id, ext));
|
.for_each(|ext| insert_item_loc(db, res, file_id, ext, keys::EXTERN_CRATE));
|
||||||
self.unnamed_consts(db).for_each(|konst| {
|
self.use_decls().for_each(|ext| insert_item_loc(db, res, file_id, ext, keys::USE));
|
||||||
let loc = konst.lookup(db);
|
self.unnamed_consts(db)
|
||||||
if loc.id.file_id() == file_id {
|
.for_each(|konst| insert_item_loc(db, res, file_id, konst, keys::CONST));
|
||||||
res[keys::CONST].insert(loc.source(db).value, konst);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
self.attr_macro_invocs().filter(|(id, _)| id.file_id == file_id).for_each(
|
self.attr_macro_invocs().filter(|(id, _)| id.file_id == file_id).for_each(
|
||||||
|(ast_id, call_id)| {
|
|(ast_id, call_id)| {
|
||||||
res[keys::ATTR_MACRO_CALL].insert(ast_id.to_node(db.upcast()), call_id);
|
res[keys::ATTR_MACRO_CALL].insert(ast_id.to_node(db.upcast()), call_id);
|
||||||
|
@ -132,59 +112,38 @@ impl ChildBySource for ItemScope {
|
||||||
file_id: HirFileId,
|
file_id: HirFileId,
|
||||||
item: ModuleDefId,
|
item: ModuleDefId,
|
||||||
) {
|
) {
|
||||||
macro_rules! insert {
|
|
||||||
($map:ident[$key:path].$insert:ident($id:ident)) => {{
|
|
||||||
let loc = $id.lookup(db);
|
|
||||||
if loc.id.file_id() == file_id {
|
|
||||||
$map[$key].$insert(loc.source(db).value, $id)
|
|
||||||
}
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
match item {
|
match item {
|
||||||
ModuleDefId::FunctionId(id) => insert!(map[keys::FUNCTION].insert(id)),
|
ModuleDefId::FunctionId(id) => {
|
||||||
ModuleDefId::ConstId(id) => insert!(map[keys::CONST].insert(id)),
|
insert_item_loc(db, map, file_id, id, keys::FUNCTION)
|
||||||
ModuleDefId::StaticId(id) => insert!(map[keys::STATIC].insert(id)),
|
}
|
||||||
ModuleDefId::TypeAliasId(id) => insert!(map[keys::TYPE_ALIAS].insert(id)),
|
ModuleDefId::ConstId(id) => insert_item_loc(db, map, file_id, id, keys::CONST),
|
||||||
ModuleDefId::TraitId(id) => insert!(map[keys::TRAIT].insert(id)),
|
ModuleDefId::TypeAliasId(id) => {
|
||||||
ModuleDefId::TraitAliasId(id) => insert!(map[keys::TRAIT_ALIAS].insert(id)),
|
insert_item_loc(db, map, file_id, id, keys::TYPE_ALIAS)
|
||||||
|
}
|
||||||
|
ModuleDefId::StaticId(id) => insert_item_loc(db, map, file_id, id, keys::STATIC),
|
||||||
|
ModuleDefId::TraitId(id) => insert_item_loc(db, map, file_id, id, keys::TRAIT),
|
||||||
|
ModuleDefId::TraitAliasId(id) => {
|
||||||
|
insert_item_loc(db, map, file_id, id, keys::TRAIT_ALIAS)
|
||||||
|
}
|
||||||
ModuleDefId::AdtId(adt) => match adt {
|
ModuleDefId::AdtId(adt) => match adt {
|
||||||
AdtId::StructId(id) => insert!(map[keys::STRUCT].insert(id)),
|
AdtId::StructId(id) => insert_item_loc(db, map, file_id, id, keys::STRUCT),
|
||||||
AdtId::UnionId(id) => insert!(map[keys::UNION].insert(id)),
|
AdtId::UnionId(id) => insert_item_loc(db, map, file_id, id, keys::UNION),
|
||||||
AdtId::EnumId(id) => insert!(map[keys::ENUM].insert(id)),
|
AdtId::EnumId(id) => insert_item_loc(db, map, file_id, id, keys::ENUM),
|
||||||
},
|
},
|
||||||
ModuleDefId::MacroId(id) => match id {
|
ModuleDefId::MacroId(id) => match id {
|
||||||
MacroId::Macro2Id(id) => insert!(map[keys::MACRO2].insert(id)),
|
MacroId::Macro2Id(id) => insert_item_loc(db, map, file_id, id, keys::MACRO2),
|
||||||
MacroId::MacroRulesId(id) => insert!(map[keys::MACRO_RULES].insert(id)),
|
MacroId::MacroRulesId(id) => {
|
||||||
MacroId::ProcMacroId(id) => insert!(map[keys::PROC_MACRO].insert(id)),
|
insert_item_loc(db, map, file_id, id, keys::MACRO_RULES)
|
||||||
|
}
|
||||||
|
MacroId::ProcMacroId(id) => {
|
||||||
|
insert_item_loc(db, map, file_id, id, keys::PROC_MACRO)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
ModuleDefId::ModuleId(_)
|
ModuleDefId::ModuleId(_)
|
||||||
| ModuleDefId::EnumVariantId(_)
|
| ModuleDefId::EnumVariantId(_)
|
||||||
| ModuleDefId::BuiltinType(_) => (),
|
| ModuleDefId::BuiltinType(_) => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn add_impl(db: &dyn DefDatabase, map: &mut DynMap, file_id: HirFileId, imp: ImplId) {
|
|
||||||
let loc = imp.lookup(db);
|
|
||||||
if loc.id.file_id() == file_id {
|
|
||||||
map[keys::IMPL].insert(loc.source(db).value, imp)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn add_extern_crate(
|
|
||||||
db: &dyn DefDatabase,
|
|
||||||
map: &mut DynMap,
|
|
||||||
file_id: HirFileId,
|
|
||||||
ext: ExternCrateId,
|
|
||||||
) {
|
|
||||||
let loc = ext.lookup(db);
|
|
||||||
if loc.id.file_id() == file_id {
|
|
||||||
map[keys::EXTERN_CRATE].insert(loc.source(db).value, ext)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn add_use(db: &dyn DefDatabase, map: &mut DynMap, file_id: HirFileId, ext: UseId) {
|
|
||||||
let loc = ext.lookup(db);
|
|
||||||
if loc.id.file_id() == file_id {
|
|
||||||
map[keys::USE].insert(loc.source(db).value, ext)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -237,3 +196,63 @@ impl ChildBySource for DefWithBodyId {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ChildBySource for GenericDefId {
|
||||||
|
fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap, file_id: HirFileId) {
|
||||||
|
let (gfile_id, generic_params_list) = self.file_id_and_params_of(db);
|
||||||
|
if gfile_id != file_id {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let generic_params = db.generic_params(*self);
|
||||||
|
let mut toc_idx_iter = generic_params.type_or_consts.iter().map(|(idx, _)| idx);
|
||||||
|
let lts_idx_iter = generic_params.lifetimes.iter().map(|(idx, _)| idx);
|
||||||
|
|
||||||
|
// For traits the first type index is `Self`, skip it.
|
||||||
|
if let GenericDefId::TraitId(_) = *self {
|
||||||
|
toc_idx_iter.next().unwrap(); // advance_by(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(generic_params_list) = generic_params_list {
|
||||||
|
for (local_id, ast_param) in
|
||||||
|
toc_idx_iter.zip(generic_params_list.type_or_const_params())
|
||||||
|
{
|
||||||
|
let id = TypeOrConstParamId { parent: *self, local_id };
|
||||||
|
match ast_param {
|
||||||
|
ast::TypeOrConstParam::Type(a) => res[keys::TYPE_PARAM].insert(a, id),
|
||||||
|
ast::TypeOrConstParam::Const(a) => res[keys::CONST_PARAM].insert(a, id),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (local_id, ast_param) in lts_idx_iter.zip(generic_params_list.lifetime_params()) {
|
||||||
|
let id = LifetimeParamId { parent: *self, local_id };
|
||||||
|
res[keys::LIFETIME_PARAM].insert(ast_param, id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn insert_item_loc<ID, N, Data>(
|
||||||
|
db: &dyn DefDatabase,
|
||||||
|
res: &mut DynMap,
|
||||||
|
file_id: HirFileId,
|
||||||
|
id: ID,
|
||||||
|
key: Key<N::Source, ID>,
|
||||||
|
) where
|
||||||
|
ID: for<'db> Lookup<Database<'db> = dyn DefDatabase + 'db, Data = Data> + 'static,
|
||||||
|
Data: ItemTreeLoc<Id = N>,
|
||||||
|
N: ItemTreeNode,
|
||||||
|
N::Source: 'static,
|
||||||
|
{
|
||||||
|
let loc = id.lookup(db);
|
||||||
|
if loc.item_tree_id().file_id() == file_id {
|
||||||
|
res[key].insert(loc.source(db).value, id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_assoc_item(db: &dyn DefDatabase, res: &mut DynMap, file_id: HirFileId, item: AssocItemId) {
|
||||||
|
match item {
|
||||||
|
AssocItemId::FunctionId(func) => insert_item_loc(db, res, file_id, func, keys::FUNCTION),
|
||||||
|
AssocItemId::ConstId(konst) => insert_item_loc(db, res, file_id, konst, keys::CONST),
|
||||||
|
AssocItemId::TypeAliasId(ty) => insert_item_loc(db, res, file_id, ty, keys::TYPE_ALIAS),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ use hir_expand::{
|
||||||
HirFileId, InFile,
|
HirFileId, InFile,
|
||||||
};
|
};
|
||||||
use intern::Interned;
|
use intern::Interned;
|
||||||
use la_arena::{Arena, ArenaMap};
|
use la_arena::Arena;
|
||||||
use rustc_abi::{Align, Integer, IntegerType, ReprFlags, ReprOptions};
|
use rustc_abi::{Align, Integer, IntegerType, ReprFlags, ReprOptions};
|
||||||
use syntax::ast::{self, HasName, HasVisibility};
|
use syntax::ast::{self, HasName, HasVisibility};
|
||||||
use triomphe::Arc;
|
use triomphe::Arc;
|
||||||
|
@ -22,13 +22,11 @@ use crate::{
|
||||||
lang_item::LangItem,
|
lang_item::LangItem,
|
||||||
lower::LowerCtx,
|
lower::LowerCtx,
|
||||||
nameres::diagnostics::{DefDiagnostic, DefDiagnostics},
|
nameres::diagnostics::{DefDiagnostic, DefDiagnostics},
|
||||||
src::HasChildSource,
|
|
||||||
src::HasSource,
|
|
||||||
trace::Trace,
|
trace::Trace,
|
||||||
tt::{Delimiter, DelimiterKind, Leaf, Subtree, TokenTree},
|
tt::{Delimiter, DelimiterKind, Leaf, Subtree, TokenTree},
|
||||||
type_ref::TypeRef,
|
type_ref::TypeRef,
|
||||||
visibility::RawVisibility,
|
visibility::RawVisibility,
|
||||||
EnumId, EnumVariantId, LocalFieldId, LocalModuleId, Lookup, StructId, UnionId, VariantId,
|
EnumId, EnumVariantId, LocalFieldId, LocalModuleId, Lookup, StructId, UnionId,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Note that we use `StructData` for unions as well!
|
/// Note that we use `StructData` for unions as well!
|
||||||
|
@ -387,46 +385,6 @@ impl VariantData {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HasChildSource<LocalFieldId> for VariantId {
|
|
||||||
type Value = Either<ast::TupleField, ast::RecordField>;
|
|
||||||
|
|
||||||
fn child_source(&self, db: &dyn DefDatabase) -> InFile<ArenaMap<LocalFieldId, Self::Value>> {
|
|
||||||
let item_tree;
|
|
||||||
let (src, fields, container) = match *self {
|
|
||||||
VariantId::EnumVariantId(it) => {
|
|
||||||
let lookup = it.lookup(db);
|
|
||||||
item_tree = lookup.id.item_tree(db);
|
|
||||||
(
|
|
||||||
lookup.source(db).map(|it| it.kind()),
|
|
||||||
&item_tree[lookup.id.value].fields,
|
|
||||||
lookup.parent.lookup(db).container,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
VariantId::StructId(it) => {
|
|
||||||
let lookup = it.lookup(db);
|
|
||||||
item_tree = lookup.id.item_tree(db);
|
|
||||||
(
|
|
||||||
lookup.source(db).map(|it| it.kind()),
|
|
||||||
&item_tree[lookup.id.value].fields,
|
|
||||||
lookup.container,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
VariantId::UnionId(it) => {
|
|
||||||
let lookup = it.lookup(db);
|
|
||||||
item_tree = lookup.id.item_tree(db);
|
|
||||||
(
|
|
||||||
lookup.source(db).map(|it| it.kind()),
|
|
||||||
&item_tree[lookup.id.value].fields,
|
|
||||||
lookup.container,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let mut trace = Trace::new_for_map();
|
|
||||||
lower_struct(db, &mut trace, &src, container.krate, &item_tree, fields);
|
|
||||||
src.with_value(trace.into_map())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
pub enum StructKind {
|
pub enum StructKind {
|
||||||
Tuple,
|
Tuple,
|
||||||
|
@ -434,7 +392,7 @@ pub enum StructKind {
|
||||||
Unit,
|
Unit,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lower_struct(
|
pub(crate) fn lower_struct(
|
||||||
db: &dyn DefDatabase,
|
db: &dyn DefDatabase,
|
||||||
trace: &mut Trace<FieldData, Either<ast::TupleField, ast::RecordField>>,
|
trace: &mut Trace<FieldData, Either<ast::TupleField, ast::RecordField>>,
|
||||||
ast: &InFile<ast::StructKind>,
|
ast: &InFile<ast::StructKind>,
|
||||||
|
|
|
@ -171,6 +171,7 @@ fn find_path_inner(ctx: FindPathCtx<'_>, item: ItemInNs, from: ModuleId) -> Opti
|
||||||
.map(|(item, _)| item)
|
.map(|(item, _)| item)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(skip_all)]
|
||||||
fn find_path_for_module(
|
fn find_path_for_module(
|
||||||
ctx: FindPathCtx<'_>,
|
ctx: FindPathCtx<'_>,
|
||||||
def_map: &DefMap,
|
def_map: &DefMap,
|
||||||
|
@ -312,6 +313,7 @@ fn find_self_super(def_map: &DefMap, item: ModuleId, from: ModuleId) -> Option<M
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(skip_all)]
|
||||||
fn calculate_best_path(
|
fn calculate_best_path(
|
||||||
ctx: FindPathCtx<'_>,
|
ctx: FindPathCtx<'_>,
|
||||||
def_map: &DefMap,
|
def_map: &DefMap,
|
||||||
|
|
|
@ -3,31 +3,27 @@
|
||||||
//! generic parameters. See also the `Generics` type and the `generics_of` query
|
//! generic parameters. See also the `Generics` type and the `generics_of` query
|
||||||
//! in rustc.
|
//! in rustc.
|
||||||
|
|
||||||
use base_db::FileId;
|
|
||||||
use either::Either;
|
use either::Either;
|
||||||
use hir_expand::{
|
use hir_expand::{
|
||||||
name::{AsName, Name},
|
name::{AsName, Name},
|
||||||
ExpandResult, HirFileId, InFile,
|
ExpandResult,
|
||||||
};
|
};
|
||||||
use intern::Interned;
|
use intern::Interned;
|
||||||
use la_arena::{Arena, ArenaMap, Idx};
|
use la_arena::{Arena, Idx};
|
||||||
use once_cell::unsync::Lazy;
|
use once_cell::unsync::Lazy;
|
||||||
use stdx::impl_from;
|
use stdx::impl_from;
|
||||||
use syntax::ast::{self, HasGenericParams, HasName, HasTypeBounds};
|
use syntax::ast::{self, HasGenericParams, HasName, HasTypeBounds};
|
||||||
use triomphe::Arc;
|
use triomphe::Arc;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
child_by_source::ChildBySource,
|
|
||||||
db::DefDatabase,
|
db::DefDatabase,
|
||||||
dyn_map::{keys, DynMap},
|
|
||||||
expander::Expander,
|
expander::Expander,
|
||||||
item_tree::ItemTree,
|
item_tree::{GenericsItemTreeNode, ItemTree},
|
||||||
lower::LowerCtx,
|
lower::LowerCtx,
|
||||||
nameres::{DefMap, MacroSubNs},
|
nameres::{DefMap, MacroSubNs},
|
||||||
src::{HasChildSource, HasSource},
|
|
||||||
type_ref::{ConstRef, LifetimeRef, TypeBound, TypeRef},
|
type_ref::{ConstRef, LifetimeRef, TypeBound, TypeRef},
|
||||||
AdtId, ConstParamId, GenericDefId, HasModule, LifetimeParamId, LocalLifetimeParamId,
|
AdtId, ConstParamId, GenericDefId, HasModule, ItemTreeLoc, LocalTypeOrConstParamId, Lookup,
|
||||||
LocalTypeOrConstParamId, Lookup, TypeOrConstParamId, TypeParamId,
|
TypeOrConstParamId, TypeParamId,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Data about a generic type parameter (to a function, struct, impl, ...).
|
/// Data about a generic type parameter (to a function, struct, impl, ...).
|
||||||
|
@ -264,7 +260,7 @@ impl GenericParamsCollector {
|
||||||
self.add_where_predicate_from_bound(
|
self.add_where_predicate_from_bound(
|
||||||
lower_ctx,
|
lower_ctx,
|
||||||
bound,
|
bound,
|
||||||
lifetimes.as_ref(),
|
lifetimes.as_deref(),
|
||||||
target.clone(),
|
target.clone(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -275,14 +271,14 @@ impl GenericParamsCollector {
|
||||||
&mut self,
|
&mut self,
|
||||||
lower_ctx: &LowerCtx<'_>,
|
lower_ctx: &LowerCtx<'_>,
|
||||||
bound: ast::TypeBound,
|
bound: ast::TypeBound,
|
||||||
hrtb_lifetimes: Option<&Box<[Name]>>,
|
hrtb_lifetimes: Option<&[Name]>,
|
||||||
target: Either<TypeRef, LifetimeRef>,
|
target: Either<TypeRef, LifetimeRef>,
|
||||||
) {
|
) {
|
||||||
let bound = TypeBound::from_ast(lower_ctx, bound);
|
let bound = TypeBound::from_ast(lower_ctx, bound);
|
||||||
let predicate = match (target, bound) {
|
let predicate = match (target, bound) {
|
||||||
(Either::Left(type_ref), bound) => match hrtb_lifetimes {
|
(Either::Left(type_ref), bound) => match hrtb_lifetimes {
|
||||||
Some(hrtb_lifetimes) => WherePredicate::ForLifetime {
|
Some(hrtb_lifetimes) => WherePredicate::ForLifetime {
|
||||||
lifetimes: hrtb_lifetimes.clone(),
|
lifetimes: hrtb_lifetimes.to_vec().into_boxed_slice(),
|
||||||
target: WherePredicateTypeTarget::TypeRef(Interned::new(type_ref)),
|
target: WherePredicateTypeTarget::TypeRef(Interned::new(type_ref)),
|
||||||
bound: Interned::new(bound),
|
bound: Interned::new(bound),
|
||||||
},
|
},
|
||||||
|
@ -418,13 +414,18 @@ impl GenericParams {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
macro_rules! id_to_generics {
|
fn id_to_generics<Id: GenericsItemTreeNode>(
|
||||||
($id:ident) => {{
|
db: &dyn DefDatabase,
|
||||||
let id = $id.lookup(db).id;
|
id: impl for<'db> Lookup<
|
||||||
|
Database<'db> = dyn DefDatabase + 'db,
|
||||||
|
Data = impl ItemTreeLoc<Id = Id>,
|
||||||
|
>,
|
||||||
|
enabled_params: impl Fn(&Interned<GenericParams>, &ItemTree) -> Interned<GenericParams>,
|
||||||
|
) -> Interned<GenericParams> {
|
||||||
|
let id = id.lookup(db).item_tree_id();
|
||||||
let tree = id.item_tree(db);
|
let tree = id.item_tree(db);
|
||||||
let item = &tree[id.value];
|
let item = &tree[id.value];
|
||||||
enabled_params(&item.generic_params, &tree)
|
enabled_params(item.generic_params(), &tree)
|
||||||
}};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
match def {
|
match def {
|
||||||
|
@ -457,13 +458,13 @@ impl GenericParams {
|
||||||
Interned::new(generic_params.finish())
|
Interned::new(generic_params.finish())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
GenericDefId::AdtId(AdtId::StructId(id)) => id_to_generics!(id),
|
GenericDefId::AdtId(AdtId::StructId(id)) => id_to_generics(db, id, enabled_params),
|
||||||
GenericDefId::AdtId(AdtId::EnumId(id)) => id_to_generics!(id),
|
GenericDefId::AdtId(AdtId::EnumId(id)) => id_to_generics(db, id, enabled_params),
|
||||||
GenericDefId::AdtId(AdtId::UnionId(id)) => id_to_generics!(id),
|
GenericDefId::AdtId(AdtId::UnionId(id)) => id_to_generics(db, id, enabled_params),
|
||||||
GenericDefId::TraitId(id) => id_to_generics!(id),
|
GenericDefId::TraitId(id) => id_to_generics(db, id, enabled_params),
|
||||||
GenericDefId::TraitAliasId(id) => id_to_generics!(id),
|
GenericDefId::TraitAliasId(id) => id_to_generics(db, id, enabled_params),
|
||||||
GenericDefId::TypeAliasId(id) => id_to_generics!(id),
|
GenericDefId::TypeAliasId(id) => id_to_generics(db, id, enabled_params),
|
||||||
GenericDefId::ImplId(id) => id_to_generics!(id),
|
GenericDefId::ImplId(id) => id_to_generics(db, id, enabled_params),
|
||||||
GenericDefId::EnumVariantId(_) | GenericDefId::ConstId(_) => {
|
GenericDefId::EnumVariantId(_) | GenericDefId::ConstId(_) => {
|
||||||
Interned::new(GenericParams {
|
Interned::new(GenericParams {
|
||||||
type_or_consts: Default::default(),
|
type_or_consts: Default::default(),
|
||||||
|
@ -507,141 +508,3 @@ impl GenericParams {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn file_id_and_params_of(
|
|
||||||
def: GenericDefId,
|
|
||||||
db: &dyn DefDatabase,
|
|
||||||
) -> (HirFileId, Option<ast::GenericParamList>) {
|
|
||||||
match def {
|
|
||||||
GenericDefId::FunctionId(it) => {
|
|
||||||
let src = it.lookup(db).source(db);
|
|
||||||
(src.file_id, src.value.generic_param_list())
|
|
||||||
}
|
|
||||||
GenericDefId::AdtId(AdtId::StructId(it)) => {
|
|
||||||
let src = it.lookup(db).source(db);
|
|
||||||
(src.file_id, src.value.generic_param_list())
|
|
||||||
}
|
|
||||||
GenericDefId::AdtId(AdtId::UnionId(it)) => {
|
|
||||||
let src = it.lookup(db).source(db);
|
|
||||||
(src.file_id, src.value.generic_param_list())
|
|
||||||
}
|
|
||||||
GenericDefId::AdtId(AdtId::EnumId(it)) => {
|
|
||||||
let src = it.lookup(db).source(db);
|
|
||||||
(src.file_id, src.value.generic_param_list())
|
|
||||||
}
|
|
||||||
GenericDefId::TraitId(it) => {
|
|
||||||
let src = it.lookup(db).source(db);
|
|
||||||
(src.file_id, src.value.generic_param_list())
|
|
||||||
}
|
|
||||||
GenericDefId::TraitAliasId(it) => {
|
|
||||||
let src = it.lookup(db).source(db);
|
|
||||||
(src.file_id, src.value.generic_param_list())
|
|
||||||
}
|
|
||||||
GenericDefId::TypeAliasId(it) => {
|
|
||||||
let src = it.lookup(db).source(db);
|
|
||||||
(src.file_id, src.value.generic_param_list())
|
|
||||||
}
|
|
||||||
GenericDefId::ImplId(it) => {
|
|
||||||
let src = it.lookup(db).source(db);
|
|
||||||
(src.file_id, src.value.generic_param_list())
|
|
||||||
}
|
|
||||||
// We won't be using this ID anyway
|
|
||||||
GenericDefId::EnumVariantId(_) | GenericDefId::ConstId(_) => (FileId::BOGUS.into(), None),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl HasChildSource<LocalTypeOrConstParamId> for GenericDefId {
|
|
||||||
type Value = Either<ast::TypeOrConstParam, ast::TraitOrAlias>;
|
|
||||||
fn child_source(
|
|
||||||
&self,
|
|
||||||
db: &dyn DefDatabase,
|
|
||||||
) -> InFile<ArenaMap<LocalTypeOrConstParamId, Self::Value>> {
|
|
||||||
let generic_params = db.generic_params(*self);
|
|
||||||
let mut idx_iter = generic_params.type_or_consts.iter().map(|(idx, _)| idx);
|
|
||||||
|
|
||||||
let (file_id, generic_params_list) = file_id_and_params_of(*self, db);
|
|
||||||
|
|
||||||
let mut params = ArenaMap::default();
|
|
||||||
|
|
||||||
// For traits and trait aliases the first type index is `Self`, we need to add it before
|
|
||||||
// the other params.
|
|
||||||
match *self {
|
|
||||||
GenericDefId::TraitId(id) => {
|
|
||||||
let trait_ref = id.lookup(db).source(db).value;
|
|
||||||
let idx = idx_iter.next().unwrap();
|
|
||||||
params.insert(idx, Either::Right(ast::TraitOrAlias::Trait(trait_ref)));
|
|
||||||
}
|
|
||||||
GenericDefId::TraitAliasId(id) => {
|
|
||||||
let alias = id.lookup(db).source(db).value;
|
|
||||||
let idx = idx_iter.next().unwrap();
|
|
||||||
params.insert(idx, Either::Right(ast::TraitOrAlias::TraitAlias(alias)));
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(generic_params_list) = generic_params_list {
|
|
||||||
for (idx, ast_param) in idx_iter.zip(generic_params_list.type_or_const_params()) {
|
|
||||||
params.insert(idx, Either::Left(ast_param));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
InFile::new(file_id, params)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl HasChildSource<LocalLifetimeParamId> for GenericDefId {
|
|
||||||
type Value = ast::LifetimeParam;
|
|
||||||
fn child_source(
|
|
||||||
&self,
|
|
||||||
db: &dyn DefDatabase,
|
|
||||||
) -> InFile<ArenaMap<LocalLifetimeParamId, Self::Value>> {
|
|
||||||
let generic_params = db.generic_params(*self);
|
|
||||||
let idx_iter = generic_params.lifetimes.iter().map(|(idx, _)| idx);
|
|
||||||
|
|
||||||
let (file_id, generic_params_list) = file_id_and_params_of(*self, db);
|
|
||||||
|
|
||||||
let mut params = ArenaMap::default();
|
|
||||||
|
|
||||||
if let Some(generic_params_list) = generic_params_list {
|
|
||||||
for (idx, ast_param) in idx_iter.zip(generic_params_list.lifetime_params()) {
|
|
||||||
params.insert(idx, ast_param);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
InFile::new(file_id, params)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ChildBySource for GenericDefId {
|
|
||||||
fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap, file_id: HirFileId) {
|
|
||||||
let (gfile_id, generic_params_list) = file_id_and_params_of(*self, db);
|
|
||||||
if gfile_id != file_id {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let generic_params = db.generic_params(*self);
|
|
||||||
let mut toc_idx_iter = generic_params.type_or_consts.iter().map(|(idx, _)| idx);
|
|
||||||
let lts_idx_iter = generic_params.lifetimes.iter().map(|(idx, _)| idx);
|
|
||||||
|
|
||||||
// For traits the first type index is `Self`, skip it.
|
|
||||||
if let GenericDefId::TraitId(_) = *self {
|
|
||||||
toc_idx_iter.next().unwrap(); // advance_by(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(generic_params_list) = generic_params_list {
|
|
||||||
for (local_id, ast_param) in
|
|
||||||
toc_idx_iter.zip(generic_params_list.type_or_const_params())
|
|
||||||
{
|
|
||||||
let id = TypeOrConstParamId { parent: *self, local_id };
|
|
||||||
match ast_param {
|
|
||||||
ast::TypeOrConstParam::Type(a) => res[keys::TYPE_PARAM].insert(a, id),
|
|
||||||
ast::TypeOrConstParam::Const(a) => res[keys::CONST_PARAM].insert(a, id),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (local_id, ast_param) in lts_idx_iter.zip(generic_params_list.lifetime_params()) {
|
|
||||||
let id = LifetimeParamId { parent: *self, local_id };
|
|
||||||
res[keys::LIFETIME_PARAM].insert(ast_param, id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -859,7 +859,7 @@ mod tests {
|
||||||
check_search(
|
check_search(
|
||||||
ra_fixture,
|
ra_fixture,
|
||||||
"main",
|
"main",
|
||||||
Query::new("fmt".to_string()).fuzzy(),
|
Query::new("fmt".to_owned()).fuzzy(),
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
dep::fmt (t)
|
dep::fmt (t)
|
||||||
dep::fmt::Display::FMT_CONST (a)
|
dep::fmt::Display::FMT_CONST (a)
|
||||||
|
@ -888,9 +888,7 @@ mod tests {
|
||||||
check_search(
|
check_search(
|
||||||
ra_fixture,
|
ra_fixture,
|
||||||
"main",
|
"main",
|
||||||
Query::new("fmt".to_string())
|
Query::new("fmt".to_owned()).fuzzy().assoc_search_mode(AssocSearchMode::AssocItemsOnly),
|
||||||
.fuzzy()
|
|
||||||
.assoc_search_mode(AssocSearchMode::AssocItemsOnly),
|
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
dep::fmt::Display::FMT_CONST (a)
|
dep::fmt::Display::FMT_CONST (a)
|
||||||
dep::fmt::Display::format_function (a)
|
dep::fmt::Display::format_function (a)
|
||||||
|
@ -901,7 +899,7 @@ mod tests {
|
||||||
check_search(
|
check_search(
|
||||||
ra_fixture,
|
ra_fixture,
|
||||||
"main",
|
"main",
|
||||||
Query::new("fmt".to_string()).fuzzy().assoc_search_mode(AssocSearchMode::Exclude),
|
Query::new("fmt".to_owned()).fuzzy().assoc_search_mode(AssocSearchMode::Exclude),
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
dep::fmt (t)
|
dep::fmt (t)
|
||||||
"#]],
|
"#]],
|
||||||
|
@ -937,7 +935,7 @@ pub mod fmt {
|
||||||
check_search(
|
check_search(
|
||||||
ra_fixture,
|
ra_fixture,
|
||||||
"main",
|
"main",
|
||||||
Query::new("fmt".to_string()).fuzzy(),
|
Query::new("fmt".to_owned()).fuzzy(),
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
dep::Fmt (m)
|
dep::Fmt (m)
|
||||||
dep::Fmt (t)
|
dep::Fmt (t)
|
||||||
|
@ -951,7 +949,7 @@ pub mod fmt {
|
||||||
check_search(
|
check_search(
|
||||||
ra_fixture,
|
ra_fixture,
|
||||||
"main",
|
"main",
|
||||||
Query::new("fmt".to_string()),
|
Query::new("fmt".to_owned()),
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
dep::Fmt (m)
|
dep::Fmt (m)
|
||||||
dep::Fmt (t)
|
dep::Fmt (t)
|
||||||
|
@ -991,7 +989,7 @@ pub mod fmt {
|
||||||
check_search(
|
check_search(
|
||||||
ra_fixture,
|
ra_fixture,
|
||||||
"main",
|
"main",
|
||||||
Query::new("fmt".to_string()),
|
Query::new("fmt".to_owned()),
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
dep::Fmt (m)
|
dep::Fmt (m)
|
||||||
dep::Fmt (t)
|
dep::Fmt (t)
|
||||||
|
@ -1015,7 +1013,7 @@ pub mod fmt {
|
||||||
check_search(
|
check_search(
|
||||||
ra_fixture,
|
ra_fixture,
|
||||||
"main",
|
"main",
|
||||||
Query::new("FMT".to_string()),
|
Query::new("FMT".to_owned()),
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
dep::FMT (t)
|
dep::FMT (t)
|
||||||
dep::FMT (v)
|
dep::FMT (v)
|
||||||
|
@ -1027,7 +1025,7 @@ pub mod fmt {
|
||||||
check_search(
|
check_search(
|
||||||
ra_fixture,
|
ra_fixture,
|
||||||
"main",
|
"main",
|
||||||
Query::new("FMT".to_string()).case_sensitive(),
|
Query::new("FMT".to_owned()).case_sensitive(),
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
dep::FMT (t)
|
dep::FMT (t)
|
||||||
dep::FMT (v)
|
dep::FMT (v)
|
||||||
|
|
|
@ -222,17 +222,15 @@ impl ItemScope {
|
||||||
self.declarations.iter().copied()
|
self.declarations.iter().copied()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn extern_crate_decls(
|
pub fn extern_crate_decls(&self) -> impl ExactSizeIterator<Item = ExternCrateId> + '_ {
|
||||||
&self,
|
|
||||||
) -> impl Iterator<Item = ExternCrateId> + ExactSizeIterator + '_ {
|
|
||||||
self.extern_crate_decls.iter().copied()
|
self.extern_crate_decls.iter().copied()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn use_decls(&self) -> impl Iterator<Item = UseId> + ExactSizeIterator + '_ {
|
pub fn use_decls(&self) -> impl ExactSizeIterator<Item = UseId> + '_ {
|
||||||
self.use_decls.iter().copied()
|
self.use_decls.iter().copied()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn impls(&self) -> impl Iterator<Item = ImplId> + ExactSizeIterator + '_ {
|
pub fn impls(&self) -> impl ExactSizeIterator<Item = ImplId> + '_ {
|
||||||
self.impls.iter().copied()
|
self.impls.iter().copied()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -674,7 +672,7 @@ impl ItemScope {
|
||||||
format_to!(
|
format_to!(
|
||||||
buf,
|
buf,
|
||||||
"{}:",
|
"{}:",
|
||||||
name.map_or("_".to_string(), |name| name.display(db).to_string())
|
name.map_or("_".to_owned(), |name| name.display(db).to_string())
|
||||||
);
|
);
|
||||||
|
|
||||||
if let Some((.., i)) = def.types {
|
if let Some((.., i)) = def.types {
|
||||||
|
|
|
@ -337,20 +337,18 @@ from_attrs!(
|
||||||
LifetimeParamData(Idx<LifetimeParamData>),
|
LifetimeParamData(Idx<LifetimeParamData>),
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Trait implemented by all item nodes in the item tree.
|
/// Trait implemented by all nodes in the item tree.
|
||||||
pub trait ItemTreeModItemNode: Clone {
|
pub trait ItemTreeNode: Clone {
|
||||||
type Source: AstIdNode + Into<ast::Item>;
|
type Source: AstIdNode;
|
||||||
|
|
||||||
fn ast_id(&self) -> FileAstId<Self::Source>;
|
fn ast_id(&self) -> FileAstId<Self::Source>;
|
||||||
|
|
||||||
/// Looks up an instance of `Self` in an item tree.
|
/// Looks up an instance of `Self` in an item tree.
|
||||||
fn lookup(tree: &ItemTree, index: Idx<Self>) -> &Self;
|
fn lookup(tree: &ItemTree, index: Idx<Self>) -> &Self;
|
||||||
|
fn attr_owner(id: FileItemTreeId<Self>) -> AttrOwner;
|
||||||
/// Downcasts a `ModItem` to a `FileItemTreeId` specific to this type.
|
}
|
||||||
fn id_from_mod_item(mod_item: ModItem) -> Option<FileItemTreeId<Self>>;
|
pub trait GenericsItemTreeNode: ItemTreeNode {
|
||||||
|
fn generic_params(&self) -> &Interned<GenericParams>;
|
||||||
/// Upcasts a `FileItemTreeId` to a generic `ModItem`.
|
|
||||||
fn id_to_mod_item(id: FileItemTreeId<Self>) -> ModItem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct FileItemTreeId<N>(Idx<N>);
|
pub struct FileItemTreeId<N>(Idx<N>);
|
||||||
|
@ -372,7 +370,7 @@ impl<N> FileItemTreeId<N> {
|
||||||
|
|
||||||
impl<N> Clone for FileItemTreeId<N> {
|
impl<N> Clone for FileItemTreeId<N> {
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
Self(self.0)
|
*self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<N> Copy for FileItemTreeId<N> {}
|
impl<N> Copy for FileItemTreeId<N> {}
|
||||||
|
@ -478,7 +476,7 @@ impl<N> Hash for ItemTreeId<N> {
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! mod_items {
|
macro_rules! mod_items {
|
||||||
( $( $typ:ident in $fld:ident -> $ast:ty ),+ $(,)? ) => {
|
( $( $typ:ident $(<$generic_params:ident>)? in $fld:ident -> $ast:ty ),+ $(,)? ) => {
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||||
pub enum ModItem {
|
pub enum ModItem {
|
||||||
$(
|
$(
|
||||||
|
@ -495,7 +493,7 @@ macro_rules! mod_items {
|
||||||
)+
|
)+
|
||||||
|
|
||||||
$(
|
$(
|
||||||
impl ItemTreeModItemNode for $typ {
|
impl ItemTreeNode for $typ {
|
||||||
type Source = $ast;
|
type Source = $ast;
|
||||||
|
|
||||||
fn ast_id(&self) -> FileAstId<Self::Source> {
|
fn ast_id(&self) -> FileAstId<Self::Source> {
|
||||||
|
@ -506,15 +504,8 @@ macro_rules! mod_items {
|
||||||
&tree.data().$fld[index]
|
&tree.data().$fld[index]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn id_from_mod_item(mod_item: ModItem) -> Option<FileItemTreeId<Self>> {
|
fn attr_owner(id: FileItemTreeId<Self>) -> AttrOwner {
|
||||||
match mod_item {
|
AttrOwner::ModItem(ModItem::$typ(id))
|
||||||
ModItem::$typ(id) => Some(id),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn id_to_mod_item(id: FileItemTreeId<Self>) -> ModItem {
|
|
||||||
ModItem::$typ(id)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -525,6 +516,14 @@ macro_rules! mod_items {
|
||||||
&self.data().$fld[index]
|
&self.data().$fld[index]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$(
|
||||||
|
impl GenericsItemTreeNode for $typ {
|
||||||
|
fn generic_params(&self) -> &Interned<GenericParams> {
|
||||||
|
&self.$generic_params
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)?
|
||||||
)+
|
)+
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -533,16 +532,16 @@ mod_items! {
|
||||||
Use in uses -> ast::Use,
|
Use in uses -> ast::Use,
|
||||||
ExternCrate in extern_crates -> ast::ExternCrate,
|
ExternCrate in extern_crates -> ast::ExternCrate,
|
||||||
ExternBlock in extern_blocks -> ast::ExternBlock,
|
ExternBlock in extern_blocks -> ast::ExternBlock,
|
||||||
Function in functions -> ast::Fn,
|
Function<explicit_generic_params> in functions -> ast::Fn,
|
||||||
Struct in structs -> ast::Struct,
|
Struct<generic_params> in structs -> ast::Struct,
|
||||||
Union in unions -> ast::Union,
|
Union<generic_params> in unions -> ast::Union,
|
||||||
Enum in enums -> ast::Enum,
|
Enum<generic_params> in enums -> ast::Enum,
|
||||||
Const in consts -> ast::Const,
|
Const in consts -> ast::Const,
|
||||||
Static in statics -> ast::Static,
|
Static in statics -> ast::Static,
|
||||||
Trait in traits -> ast::Trait,
|
Trait<generic_params> in traits -> ast::Trait,
|
||||||
TraitAlias in trait_aliases -> ast::TraitAlias,
|
TraitAlias<generic_params> in trait_aliases -> ast::TraitAlias,
|
||||||
Impl in impls -> ast::Impl,
|
Impl<generic_params> in impls -> ast::Impl,
|
||||||
TypeAlias in type_aliases -> ast::TypeAlias,
|
TypeAlias<generic_params> in type_aliases -> ast::TypeAlias,
|
||||||
Mod in mods -> ast::Module,
|
Mod in mods -> ast::Module,
|
||||||
MacroCall in macro_calls -> ast::MacroCall,
|
MacroCall in macro_calls -> ast::MacroCall,
|
||||||
MacroRules in macro_rules -> ast::MacroRules,
|
MacroRules in macro_rules -> ast::MacroRules,
|
||||||
|
@ -578,17 +577,26 @@ impl Index<RawVisibilityId> for ItemTree {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<N: ItemTreeModItemNode> Index<FileItemTreeId<N>> for ItemTree {
|
impl<N: ItemTreeNode> Index<FileItemTreeId<N>> for ItemTree {
|
||||||
type Output = N;
|
type Output = N;
|
||||||
fn index(&self, id: FileItemTreeId<N>) -> &N {
|
fn index(&self, id: FileItemTreeId<N>) -> &N {
|
||||||
N::lookup(self, id.index())
|
N::lookup(self, id.index())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Index<FileItemTreeId<Variant>> for ItemTree {
|
impl ItemTreeNode for Variant {
|
||||||
type Output = Variant;
|
type Source = ast::Variant;
|
||||||
fn index(&self, id: FileItemTreeId<Variant>) -> &Variant {
|
|
||||||
&self[id.index()]
|
fn ast_id(&self) -> FileAstId<Self::Source> {
|
||||||
|
self.ast_id
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lookup(tree: &ItemTree, index: Idx<Self>) -> &Self {
|
||||||
|
&tree.data().variants[index]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn attr_owner(id: FileItemTreeId<Self>) -> AttrOwner {
|
||||||
|
AttrOwner::Variant(id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1027,7 +1035,7 @@ impl AssocItem {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct Variant {
|
pub struct Variant {
|
||||||
pub name: Name,
|
pub name: Name,
|
||||||
pub fields: Fields,
|
pub fields: Fields,
|
||||||
|
|
|
@ -13,7 +13,7 @@ use crate::{
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
fn id<N: ItemTreeModItemNode>(index: Idx<N>) -> FileItemTreeId<N> {
|
fn id<N: ItemTreeNode>(index: Idx<N>) -> FileItemTreeId<N> {
|
||||||
FileItemTreeId(index)
|
FileItemTreeId(index)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -267,7 +267,7 @@ impl<'a> Ctx<'a> {
|
||||||
if let Some(data) = self.lower_variant(&variant) {
|
if let Some(data) = self.lower_variant(&variant) {
|
||||||
let idx = self.data().variants.alloc(data);
|
let idx = self.data().variants.alloc(data);
|
||||||
self.add_attrs(
|
self.add_attrs(
|
||||||
FileItemTreeId(idx).into(),
|
id(idx).into(),
|
||||||
RawAttrs::new(self.db.upcast(), &variant, self.span_map()),
|
RawAttrs::new(self.db.upcast(), &variant, self.span_map()),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -658,7 +658,7 @@ impl<'a> Ctx<'a> {
|
||||||
|
|
||||||
fn lower_visibility(&mut self, item: &dyn ast::HasVisibility) -> RawVisibilityId {
|
fn lower_visibility(&mut self, item: &dyn ast::HasVisibility) -> RawVisibilityId {
|
||||||
let vis =
|
let vis =
|
||||||
RawVisibility::from_ast_with_span_map(self.db, item.visibility(), self.span_map());
|
RawVisibility::from_opt_ast_with_span_map(self.db, item.visibility(), self.span_map());
|
||||||
self.data().vis.alloc(vis)
|
self.data().vis.alloc(vis)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,7 @@ pub(super) fn print_item_tree(db: &dyn DefDatabase, tree: &ItemTree) -> String {
|
||||||
p.print_mod_item(*item);
|
p.print_mod_item(*item);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut s = p.buf.trim_end_matches('\n').to_string();
|
let mut s = p.buf.trim_end_matches('\n').to_owned();
|
||||||
s.push('\n');
|
s.push('\n');
|
||||||
s
|
s
|
||||||
}
|
}
|
||||||
|
@ -58,7 +58,7 @@ impl Printer<'_> {
|
||||||
wln!(self);
|
wln!(self);
|
||||||
f(self);
|
f(self);
|
||||||
self.indent_level -= 1;
|
self.indent_level -= 1;
|
||||||
self.buf = self.buf.trim_end_matches('\n').to_string();
|
self.buf = self.buf.trim_end_matches('\n').to_owned();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ensures that a blank line is output before the next text.
|
/// Ensures that a blank line is output before the next text.
|
||||||
|
|
|
@ -70,7 +70,11 @@ use std::{
|
||||||
panic::{RefUnwindSafe, UnwindSafe},
|
panic::{RefUnwindSafe, UnwindSafe},
|
||||||
};
|
};
|
||||||
|
|
||||||
use base_db::{impl_intern_key, salsa, CrateId, Edition};
|
use base_db::{
|
||||||
|
impl_intern_key,
|
||||||
|
salsa::{self, impl_intern_value_trivial},
|
||||||
|
CrateId, Edition,
|
||||||
|
};
|
||||||
use hir_expand::{
|
use hir_expand::{
|
||||||
ast_id_map::{AstIdNode, FileAstId},
|
ast_id_map::{AstIdNode, FileAstId},
|
||||||
builtin_attr_macro::BuiltinAttrExpander,
|
builtin_attr_macro::BuiltinAttrExpander,
|
||||||
|
@ -87,7 +91,7 @@ use hir_expand::{
|
||||||
use item_tree::ExternBlock;
|
use item_tree::ExternBlock;
|
||||||
use la_arena::Idx;
|
use la_arena::Idx;
|
||||||
use nameres::DefMap;
|
use nameres::DefMap;
|
||||||
use span::Span;
|
use span::{FileId, Span};
|
||||||
use stdx::impl_from;
|
use stdx::impl_from;
|
||||||
use syntax::{ast, AstNode};
|
use syntax::{ast, AstNode};
|
||||||
|
|
||||||
|
@ -98,11 +102,268 @@ use crate::{
|
||||||
data::adt::VariantData,
|
data::adt::VariantData,
|
||||||
db::DefDatabase,
|
db::DefDatabase,
|
||||||
item_tree::{
|
item_tree::{
|
||||||
Const, Enum, ExternCrate, Function, Impl, ItemTreeId, ItemTreeModItemNode, Macro2,
|
Const, Enum, ExternCrate, Function, Impl, ItemTreeId, ItemTreeNode, Macro2, MacroRules,
|
||||||
MacroRules, Static, Struct, Trait, TraitAlias, TypeAlias, Union, Use, Variant,
|
Static, Struct, Trait, TraitAlias, TypeAlias, Union, Use, Variant,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ItemLoc<N: ItemTreeNode> {
|
||||||
|
pub container: ModuleId,
|
||||||
|
pub id: ItemTreeId<N>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<N: ItemTreeNode> Clone for ItemLoc<N> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
*self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<N: ItemTreeNode> Copy for ItemLoc<N> {}
|
||||||
|
|
||||||
|
impl<N: ItemTreeNode> PartialEq for ItemLoc<N> {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.container == other.container && self.id == other.id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<N: ItemTreeNode> Eq for ItemLoc<N> {}
|
||||||
|
|
||||||
|
impl<N: ItemTreeNode> Hash for ItemLoc<N> {
|
||||||
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
|
self.container.hash(state);
|
||||||
|
self.id.hash(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct AssocItemLoc<N: ItemTreeNode> {
|
||||||
|
pub container: ItemContainerId,
|
||||||
|
pub id: ItemTreeId<N>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<N: ItemTreeNode> Clone for AssocItemLoc<N> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
*self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<N: ItemTreeNode> Copy for AssocItemLoc<N> {}
|
||||||
|
|
||||||
|
impl<N: ItemTreeNode> PartialEq for AssocItemLoc<N> {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.container == other.container && self.id == other.id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<N: ItemTreeNode> Eq for AssocItemLoc<N> {}
|
||||||
|
|
||||||
|
impl<N: ItemTreeNode> Hash for AssocItemLoc<N> {
|
||||||
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
|
self.container.hash(state);
|
||||||
|
self.id.hash(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait ItemTreeLoc {
|
||||||
|
type Container;
|
||||||
|
type Id;
|
||||||
|
fn item_tree_id(&self) -> ItemTreeId<Self::Id>;
|
||||||
|
fn container(&self) -> Self::Container;
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! impl_intern {
|
||||||
|
($id:ident, $loc:ident, $intern:ident, $lookup:ident) => {
|
||||||
|
impl_intern_key!($id);
|
||||||
|
impl_intern_value_trivial!($loc);
|
||||||
|
impl_intern_lookup!(DefDatabase, $id, $loc, $intern, $lookup);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! impl_loc {
|
||||||
|
($loc:ident, $id:ident: $id_ty:ident, $container:ident: $container_type:ident) => {
|
||||||
|
impl ItemTreeLoc for $loc {
|
||||||
|
type Container = $container_type;
|
||||||
|
type Id = $id_ty;
|
||||||
|
fn item_tree_id(&self) -> ItemTreeId<Self::Id> {
|
||||||
|
self.$id
|
||||||
|
}
|
||||||
|
fn container(&self) -> Self::Container {
|
||||||
|
self.$container
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
pub struct FunctionId(salsa::InternId);
|
||||||
|
type FunctionLoc = AssocItemLoc<Function>;
|
||||||
|
impl_intern!(FunctionId, FunctionLoc, intern_function, lookup_intern_function);
|
||||||
|
impl_loc!(FunctionLoc, id: Function, container: ItemContainerId);
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||||
|
pub struct StructId(salsa::InternId);
|
||||||
|
type StructLoc = ItemLoc<Struct>;
|
||||||
|
impl_intern!(StructId, StructLoc, intern_struct, lookup_intern_struct);
|
||||||
|
impl_loc!(StructLoc, id: Struct, container: ModuleId);
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||||
|
pub struct UnionId(salsa::InternId);
|
||||||
|
pub type UnionLoc = ItemLoc<Union>;
|
||||||
|
impl_intern!(UnionId, UnionLoc, intern_union, lookup_intern_union);
|
||||||
|
impl_loc!(UnionLoc, id: Union, container: ModuleId);
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||||
|
pub struct EnumId(salsa::InternId);
|
||||||
|
pub type EnumLoc = ItemLoc<Enum>;
|
||||||
|
impl_intern!(EnumId, EnumLoc, intern_enum, lookup_intern_enum);
|
||||||
|
impl_loc!(EnumLoc, id: Enum, container: ModuleId);
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
pub struct ConstId(salsa::InternId);
|
||||||
|
type ConstLoc = AssocItemLoc<Const>;
|
||||||
|
impl_intern!(ConstId, ConstLoc, intern_const, lookup_intern_const);
|
||||||
|
impl_loc!(ConstLoc, id: Const, container: ItemContainerId);
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
pub struct StaticId(salsa::InternId);
|
||||||
|
pub type StaticLoc = AssocItemLoc<Static>;
|
||||||
|
impl_intern!(StaticId, StaticLoc, intern_static, lookup_intern_static);
|
||||||
|
impl_loc!(StaticLoc, id: Static, container: ItemContainerId);
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
pub struct TraitId(salsa::InternId);
|
||||||
|
pub type TraitLoc = ItemLoc<Trait>;
|
||||||
|
impl_intern!(TraitId, TraitLoc, intern_trait, lookup_intern_trait);
|
||||||
|
impl_loc!(TraitLoc, id: Trait, container: ModuleId);
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
pub struct TraitAliasId(salsa::InternId);
|
||||||
|
pub type TraitAliasLoc = ItemLoc<TraitAlias>;
|
||||||
|
impl_intern!(TraitAliasId, TraitAliasLoc, intern_trait_alias, lookup_intern_trait_alias);
|
||||||
|
impl_loc!(TraitAliasLoc, id: TraitAlias, container: ModuleId);
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
pub struct TypeAliasId(salsa::InternId);
|
||||||
|
type TypeAliasLoc = AssocItemLoc<TypeAlias>;
|
||||||
|
impl_intern!(TypeAliasId, TypeAliasLoc, intern_type_alias, lookup_intern_type_alias);
|
||||||
|
impl_loc!(TypeAliasLoc, id: TypeAlias, container: ItemContainerId);
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
|
||||||
|
pub struct ImplId(salsa::InternId);
|
||||||
|
type ImplLoc = ItemLoc<Impl>;
|
||||||
|
impl_intern!(ImplId, ImplLoc, intern_impl, lookup_intern_impl);
|
||||||
|
impl_loc!(ImplLoc, id: Impl, container: ModuleId);
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
|
||||||
|
pub struct UseId(salsa::InternId);
|
||||||
|
type UseLoc = ItemLoc<Use>;
|
||||||
|
impl_intern!(UseId, UseLoc, intern_use, lookup_intern_use);
|
||||||
|
impl_loc!(UseLoc, id: Use, container: ModuleId);
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
|
||||||
|
pub struct ExternCrateId(salsa::InternId);
|
||||||
|
type ExternCrateLoc = ItemLoc<ExternCrate>;
|
||||||
|
impl_intern!(ExternCrateId, ExternCrateLoc, intern_extern_crate, lookup_intern_extern_crate);
|
||||||
|
impl_loc!(ExternCrateLoc, id: ExternCrate, container: ModuleId);
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
|
||||||
|
pub struct ExternBlockId(salsa::InternId);
|
||||||
|
type ExternBlockLoc = ItemLoc<ExternBlock>;
|
||||||
|
impl_intern!(ExternBlockId, ExternBlockLoc, intern_extern_block, lookup_intern_extern_block);
|
||||||
|
impl_loc!(ExternBlockLoc, id: ExternBlock, container: ModuleId);
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
pub struct EnumVariantId(salsa::InternId);
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
pub struct EnumVariantLoc {
|
||||||
|
pub id: ItemTreeId<Variant>,
|
||||||
|
pub parent: EnumId,
|
||||||
|
pub index: u32,
|
||||||
|
}
|
||||||
|
impl_intern!(EnumVariantId, EnumVariantLoc, intern_enum_variant, lookup_intern_enum_variant);
|
||||||
|
impl_loc!(EnumVariantLoc, id: Variant, parent: EnumId);
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
|
||||||
|
pub struct Macro2Id(salsa::InternId);
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
pub struct Macro2Loc {
|
||||||
|
pub container: ModuleId,
|
||||||
|
pub id: ItemTreeId<Macro2>,
|
||||||
|
pub expander: MacroExpander,
|
||||||
|
pub allow_internal_unsafe: bool,
|
||||||
|
pub edition: Edition,
|
||||||
|
}
|
||||||
|
impl_intern!(Macro2Id, Macro2Loc, intern_macro2, lookup_intern_macro2);
|
||||||
|
impl_loc!(Macro2Loc, id: Macro2, container: ModuleId);
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
|
||||||
|
pub struct MacroRulesId(salsa::InternId);
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
pub struct MacroRulesLoc {
|
||||||
|
pub container: ModuleId,
|
||||||
|
pub id: ItemTreeId<MacroRules>,
|
||||||
|
pub expander: MacroExpander,
|
||||||
|
pub flags: MacroRulesLocFlags,
|
||||||
|
pub edition: Edition,
|
||||||
|
}
|
||||||
|
impl_intern!(MacroRulesId, MacroRulesLoc, intern_macro_rules, lookup_intern_macro_rules);
|
||||||
|
impl_loc!(MacroRulesLoc, id: MacroRules, container: ModuleId);
|
||||||
|
|
||||||
|
bitflags::bitflags! {
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
pub struct MacroRulesLocFlags: u8 {
|
||||||
|
const ALLOW_INTERNAL_UNSAFE = 1 << 0;
|
||||||
|
const LOCAL_INNER = 1 << 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
pub enum MacroExpander {
|
||||||
|
Declarative,
|
||||||
|
BuiltIn(BuiltinFnLikeExpander),
|
||||||
|
BuiltInAttr(BuiltinAttrExpander),
|
||||||
|
BuiltInDerive(BuiltinDeriveExpander),
|
||||||
|
BuiltInEager(EagerExpander),
|
||||||
|
}
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
|
||||||
|
pub struct ProcMacroId(salsa::InternId);
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
pub struct ProcMacroLoc {
|
||||||
|
pub container: CrateRootModuleId,
|
||||||
|
pub id: ItemTreeId<Function>,
|
||||||
|
pub expander: CustomProcMacroExpander,
|
||||||
|
pub kind: ProcMacroKind,
|
||||||
|
pub edition: Edition,
|
||||||
|
}
|
||||||
|
impl_intern!(ProcMacroId, ProcMacroLoc, intern_proc_macro, lookup_intern_proc_macro);
|
||||||
|
impl_loc!(ProcMacroLoc, id: Function, container: CrateRootModuleId);
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
|
||||||
|
pub struct BlockId(salsa::InternId);
|
||||||
|
#[derive(Debug, Hash, PartialEq, Eq, Clone)]
|
||||||
|
pub struct BlockLoc {
|
||||||
|
ast_id: AstId<ast::BlockExpr>,
|
||||||
|
/// The containing module.
|
||||||
|
module: ModuleId,
|
||||||
|
}
|
||||||
|
impl_intern!(BlockId, BlockLoc, intern_block, lookup_intern_block);
|
||||||
|
|
||||||
|
/// Id of the anonymous const block expression and patterns. This is very similar to `ClosureId` and
|
||||||
|
/// shouldn't be a `DefWithBodyId` since its type inference is dependent on its parent.
|
||||||
|
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
|
||||||
|
pub struct ConstBlockId(salsa::InternId);
|
||||||
|
impl_intern!(ConstBlockId, ConstBlockLoc, intern_anonymous_const, lookup_intern_anonymous_const);
|
||||||
|
|
||||||
|
#[derive(Debug, Hash, PartialEq, Eq, Clone)]
|
||||||
|
pub struct ConstBlockLoc {
|
||||||
|
/// The parent of the anonymous const block.
|
||||||
|
pub parent: DefWithBodyId,
|
||||||
|
/// The root expression of this const block in the parent body.
|
||||||
|
pub root: hir::ExprId,
|
||||||
|
}
|
||||||
|
|
||||||
/// A `ModuleId` that is always a crate's root module.
|
/// A `ModuleId` that is always a crate's root module.
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
pub struct CrateRootModuleId {
|
pub struct CrateRootModuleId {
|
||||||
|
@ -124,23 +385,6 @@ impl PartialEq<ModuleId> for CrateRootModuleId {
|
||||||
other.block.is_none() && other.local_id == DefMap::ROOT && self.krate == other.krate
|
other.block.is_none() && other.local_id == DefMap::ROOT && self.krate == other.krate
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl PartialEq<CrateRootModuleId> for ModuleId {
|
|
||||||
fn eq(&self, other: &CrateRootModuleId) -> bool {
|
|
||||||
other == self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<CrateRootModuleId> for ModuleId {
|
|
||||||
fn from(CrateRootModuleId { krate }: CrateRootModuleId) -> Self {
|
|
||||||
ModuleId { krate, block: None, local_id: DefMap::ROOT }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<CrateRootModuleId> for ModuleDefId {
|
|
||||||
fn from(value: CrateRootModuleId) -> Self {
|
|
||||||
ModuleDefId::ModuleId(value.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<CrateId> for CrateRootModuleId {
|
impl From<CrateId> for CrateRootModuleId {
|
||||||
fn from(krate: CrateId) -> Self {
|
fn from(krate: CrateId) -> Self {
|
||||||
|
@ -208,105 +452,27 @@ impl ModuleId {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PartialEq<CrateRootModuleId> for ModuleId {
|
||||||
|
fn eq(&self, other: &CrateRootModuleId) -> bool {
|
||||||
|
other == self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<CrateRootModuleId> for ModuleId {
|
||||||
|
fn from(CrateRootModuleId { krate }: CrateRootModuleId) -> Self {
|
||||||
|
ModuleId { krate, block: None, local_id: DefMap::ROOT }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<CrateRootModuleId> for ModuleDefId {
|
||||||
|
fn from(value: CrateRootModuleId) -> Self {
|
||||||
|
ModuleDefId::ModuleId(value.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// An ID of a module, **local** to a `DefMap`.
|
/// An ID of a module, **local** to a `DefMap`.
|
||||||
pub type LocalModuleId = Idx<nameres::ModuleData>;
|
pub type LocalModuleId = Idx<nameres::ModuleData>;
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct ItemLoc<N: ItemTreeModItemNode> {
|
|
||||||
pub container: ModuleId,
|
|
||||||
pub id: ItemTreeId<N>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<N: ItemTreeModItemNode> Clone for ItemLoc<N> {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
Self { container: self.container, id: self.id }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<N: ItemTreeModItemNode> Copy for ItemLoc<N> {}
|
|
||||||
|
|
||||||
impl<N: ItemTreeModItemNode> PartialEq for ItemLoc<N> {
|
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
self.container == other.container && self.id == other.id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<N: ItemTreeModItemNode> Eq for ItemLoc<N> {}
|
|
||||||
|
|
||||||
impl<N: ItemTreeModItemNode> Hash for ItemLoc<N> {
|
|
||||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
|
||||||
self.container.hash(state);
|
|
||||||
self.id.hash(state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct AssocItemLoc<N: ItemTreeModItemNode> {
|
|
||||||
pub container: ItemContainerId,
|
|
||||||
pub id: ItemTreeId<N>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<N: ItemTreeModItemNode> Clone for AssocItemLoc<N> {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
Self { container: self.container, id: self.id }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<N: ItemTreeModItemNode> Copy for AssocItemLoc<N> {}
|
|
||||||
|
|
||||||
impl<N: ItemTreeModItemNode> PartialEq for AssocItemLoc<N> {
|
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
self.container == other.container && self.id == other.id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<N: ItemTreeModItemNode> Eq for AssocItemLoc<N> {}
|
|
||||||
|
|
||||||
impl<N: ItemTreeModItemNode> Hash for AssocItemLoc<N> {
|
|
||||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
|
||||||
self.container.hash(state);
|
|
||||||
self.id.hash(state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! impl_intern {
|
|
||||||
($id:ident, $loc:ident, $intern:ident, $lookup:ident) => {
|
|
||||||
impl_intern_key!($id);
|
|
||||||
impl_intern_lookup!(DefDatabase, $id, $loc, $intern, $lookup);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
|
||||||
pub struct FunctionId(salsa::InternId);
|
|
||||||
type FunctionLoc = AssocItemLoc<Function>;
|
|
||||||
impl_intern!(FunctionId, FunctionLoc, intern_function, lookup_intern_function);
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
|
||||||
pub struct StructId(salsa::InternId);
|
|
||||||
type StructLoc = ItemLoc<Struct>;
|
|
||||||
impl_intern!(StructId, StructLoc, intern_struct, lookup_intern_struct);
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
|
||||||
pub struct UnionId(salsa::InternId);
|
|
||||||
pub type UnionLoc = ItemLoc<Union>;
|
|
||||||
impl_intern!(UnionId, UnionLoc, intern_union, lookup_intern_union);
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
|
||||||
pub struct EnumId(salsa::InternId);
|
|
||||||
pub type EnumLoc = ItemLoc<Enum>;
|
|
||||||
impl_intern!(EnumId, EnumLoc, intern_enum, lookup_intern_enum);
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
|
||||||
pub struct EnumVariantId(salsa::InternId);
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
|
||||||
pub struct EnumVariantLoc {
|
|
||||||
pub id: ItemTreeId<Variant>,
|
|
||||||
pub parent: EnumId,
|
|
||||||
pub index: u32,
|
|
||||||
}
|
|
||||||
impl_intern!(EnumVariantId, EnumVariantLoc, intern_enum_variant, lookup_intern_enum_variant);
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
pub struct FieldId {
|
pub struct FieldId {
|
||||||
pub parent: VariantId,
|
pub parent: VariantId,
|
||||||
|
@ -324,119 +490,12 @@ pub struct TupleFieldId {
|
||||||
pub index: u32,
|
pub index: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
|
||||||
pub struct ConstId(salsa::InternId);
|
|
||||||
type ConstLoc = AssocItemLoc<Const>;
|
|
||||||
impl_intern!(ConstId, ConstLoc, intern_const, lookup_intern_const);
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
|
||||||
pub struct StaticId(salsa::InternId);
|
|
||||||
pub type StaticLoc = AssocItemLoc<Static>;
|
|
||||||
impl_intern!(StaticId, StaticLoc, intern_static, lookup_intern_static);
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
|
||||||
pub struct TraitId(salsa::InternId);
|
|
||||||
pub type TraitLoc = ItemLoc<Trait>;
|
|
||||||
impl_intern!(TraitId, TraitLoc, intern_trait, lookup_intern_trait);
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
|
||||||
pub struct TraitAliasId(salsa::InternId);
|
|
||||||
pub type TraitAliasLoc = ItemLoc<TraitAlias>;
|
|
||||||
impl_intern!(TraitAliasId, TraitAliasLoc, intern_trait_alias, lookup_intern_trait_alias);
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
|
||||||
pub struct TypeAliasId(salsa::InternId);
|
|
||||||
type TypeAliasLoc = AssocItemLoc<TypeAlias>;
|
|
||||||
impl_intern!(TypeAliasId, TypeAliasLoc, intern_type_alias, lookup_intern_type_alias);
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
|
|
||||||
pub struct ImplId(salsa::InternId);
|
|
||||||
type ImplLoc = ItemLoc<Impl>;
|
|
||||||
impl_intern!(ImplId, ImplLoc, intern_impl, lookup_intern_impl);
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
|
|
||||||
pub struct UseId(salsa::InternId);
|
|
||||||
type UseLoc = ItemLoc<Use>;
|
|
||||||
impl_intern!(UseId, UseLoc, intern_use, lookup_intern_use);
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
|
|
||||||
pub struct ExternCrateId(salsa::InternId);
|
|
||||||
type ExternCrateLoc = ItemLoc<ExternCrate>;
|
|
||||||
impl_intern!(ExternCrateId, ExternCrateLoc, intern_extern_crate, lookup_intern_extern_crate);
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
|
|
||||||
pub struct ExternBlockId(salsa::InternId);
|
|
||||||
type ExternBlockLoc = ItemLoc<ExternBlock>;
|
|
||||||
impl_intern!(ExternBlockId, ExternBlockLoc, intern_extern_block, lookup_intern_extern_block);
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
|
||||||
pub enum MacroExpander {
|
|
||||||
Declarative,
|
|
||||||
BuiltIn(BuiltinFnLikeExpander),
|
|
||||||
BuiltInAttr(BuiltinAttrExpander),
|
|
||||||
BuiltInDerive(BuiltinDeriveExpander),
|
|
||||||
BuiltInEager(EagerExpander),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
|
|
||||||
pub struct Macro2Id(salsa::InternId);
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
|
||||||
pub struct Macro2Loc {
|
|
||||||
pub container: ModuleId,
|
|
||||||
pub id: ItemTreeId<Macro2>,
|
|
||||||
pub expander: MacroExpander,
|
|
||||||
pub allow_internal_unsafe: bool,
|
|
||||||
pub edition: Edition,
|
|
||||||
}
|
|
||||||
impl_intern!(Macro2Id, Macro2Loc, intern_macro2, lookup_intern_macro2);
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
|
|
||||||
pub struct MacroRulesId(salsa::InternId);
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
|
||||||
pub struct MacroRulesLoc {
|
|
||||||
pub container: ModuleId,
|
|
||||||
pub id: ItemTreeId<MacroRules>,
|
|
||||||
pub expander: MacroExpander,
|
|
||||||
pub flags: MacroRulesLocFlags,
|
|
||||||
pub edition: Edition,
|
|
||||||
}
|
|
||||||
impl_intern!(MacroRulesId, MacroRulesLoc, intern_macro_rules, lookup_intern_macro_rules);
|
|
||||||
|
|
||||||
bitflags::bitflags! {
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
|
||||||
pub struct MacroRulesLocFlags: u8 {
|
|
||||||
const ALLOW_INTERNAL_UNSAFE = 1 << 0;
|
|
||||||
const LOCAL_INNER = 1 << 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
|
|
||||||
pub struct ProcMacroId(salsa::InternId);
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
|
||||||
pub struct ProcMacroLoc {
|
|
||||||
pub container: CrateRootModuleId,
|
|
||||||
pub id: ItemTreeId<Function>,
|
|
||||||
pub expander: CustomProcMacroExpander,
|
|
||||||
pub kind: ProcMacroKind,
|
|
||||||
pub edition: Edition,
|
|
||||||
}
|
|
||||||
impl_intern!(ProcMacroId, ProcMacroLoc, intern_proc_macro, lookup_intern_proc_macro);
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
|
|
||||||
pub struct BlockId(salsa::InternId);
|
|
||||||
#[derive(Debug, Hash, PartialEq, Eq, Clone)]
|
|
||||||
pub struct BlockLoc {
|
|
||||||
ast_id: AstId<ast::BlockExpr>,
|
|
||||||
/// The containing module.
|
|
||||||
module: ModuleId,
|
|
||||||
}
|
|
||||||
impl_intern!(BlockId, BlockLoc, intern_block, lookup_intern_block);
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
pub struct TypeOrConstParamId {
|
pub struct TypeOrConstParamId {
|
||||||
pub parent: GenericDefId,
|
pub parent: GenericDefId,
|
||||||
pub local_id: LocalTypeOrConstParamId,
|
pub local_id: LocalTypeOrConstParamId,
|
||||||
}
|
}
|
||||||
|
impl_intern_value_trivial!(TypeOrConstParamId);
|
||||||
|
|
||||||
/// A TypeOrConstParamId with an invariant that it actually belongs to a type
|
/// A TypeOrConstParamId with an invariant that it actually belongs to a type
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
@ -498,6 +557,7 @@ pub struct LifetimeParamId {
|
||||||
pub local_id: LocalLifetimeParamId,
|
pub local_id: LocalLifetimeParamId,
|
||||||
}
|
}
|
||||||
pub type LocalLifetimeParamId = Idx<generics::LifetimeParamData>;
|
pub type LocalLifetimeParamId = Idx<generics::LifetimeParamData>;
|
||||||
|
impl_intern_value_trivial!(LifetimeParamId);
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
pub enum ItemContainerId {
|
pub enum ItemContainerId {
|
||||||
|
@ -572,20 +632,6 @@ impl_from!(
|
||||||
for ModuleDefId
|
for ModuleDefId
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Id of the anonymous const block expression and patterns. This is very similar to `ClosureId` and
|
|
||||||
/// shouldn't be a `DefWithBodyId` since its type inference is dependent on its parent.
|
|
||||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
|
|
||||||
pub struct ConstBlockId(salsa::InternId);
|
|
||||||
impl_intern!(ConstBlockId, ConstBlockLoc, intern_anonymous_const, lookup_intern_anonymous_const);
|
|
||||||
|
|
||||||
#[derive(Debug, Hash, PartialEq, Eq, Clone)]
|
|
||||||
pub struct ConstBlockLoc {
|
|
||||||
/// The parent of the anonymous const block.
|
|
||||||
pub parent: DefWithBodyId,
|
|
||||||
/// The root expression of this const block in the parent body.
|
|
||||||
pub root: hir::ExprId,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Something that holds types, required for the current const arg lowering implementation as they
|
/// Something that holds types, required for the current const arg lowering implementation as they
|
||||||
/// need to be able to query where they are defined.
|
/// need to be able to query where they are defined.
|
||||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
|
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
|
||||||
|
@ -721,6 +767,9 @@ impl Clone for Box<dyn OpaqueInternableThing> {
|
||||||
pub struct InTypeConstId(salsa::InternId);
|
pub struct InTypeConstId(salsa::InternId);
|
||||||
impl_intern!(InTypeConstId, InTypeConstLoc, intern_in_type_const, lookup_intern_in_type_const);
|
impl_intern!(InTypeConstId, InTypeConstLoc, intern_in_type_const, lookup_intern_in_type_const);
|
||||||
|
|
||||||
|
// We would like to set `derive(PartialEq)`
|
||||||
|
// but the compiler complains about that `.expected_ty` does not implement the `Copy` trait.
|
||||||
|
#[allow(clippy::derived_hash_with_manual_eq)]
|
||||||
#[derive(Debug, Hash, Eq, Clone)]
|
#[derive(Debug, Hash, Eq, Clone)]
|
||||||
pub struct InTypeConstLoc {
|
pub struct InTypeConstLoc {
|
||||||
pub id: AstId<ast::ConstArg>,
|
pub id: AstId<ast::ConstArg>,
|
||||||
|
@ -850,6 +899,39 @@ impl_from!(
|
||||||
for GenericDefId
|
for GenericDefId
|
||||||
);
|
);
|
||||||
|
|
||||||
|
impl GenericDefId {
|
||||||
|
fn file_id_and_params_of(
|
||||||
|
self,
|
||||||
|
db: &dyn DefDatabase,
|
||||||
|
) -> (HirFileId, Option<ast::GenericParamList>) {
|
||||||
|
fn file_id_and_params_of_item_loc<Loc>(
|
||||||
|
db: &dyn DefDatabase,
|
||||||
|
def: impl for<'db> Lookup<Database<'db> = dyn DefDatabase + 'db, Data = Loc>,
|
||||||
|
) -> (HirFileId, Option<ast::GenericParamList>)
|
||||||
|
where
|
||||||
|
Loc: src::HasSource,
|
||||||
|
Loc::Value: ast::HasGenericParams,
|
||||||
|
{
|
||||||
|
let src = def.lookup(db).source(db);
|
||||||
|
(src.file_id, ast::HasGenericParams::generic_param_list(&src.value))
|
||||||
|
}
|
||||||
|
|
||||||
|
match self {
|
||||||
|
GenericDefId::FunctionId(it) => file_id_and_params_of_item_loc(db, it),
|
||||||
|
GenericDefId::TypeAliasId(it) => file_id_and_params_of_item_loc(db, it),
|
||||||
|
GenericDefId::ConstId(_) => (FileId::BOGUS.into(), None),
|
||||||
|
GenericDefId::AdtId(AdtId::StructId(it)) => file_id_and_params_of_item_loc(db, it),
|
||||||
|
GenericDefId::AdtId(AdtId::UnionId(it)) => file_id_and_params_of_item_loc(db, it),
|
||||||
|
GenericDefId::AdtId(AdtId::EnumId(it)) => file_id_and_params_of_item_loc(db, it),
|
||||||
|
GenericDefId::TraitId(it) => file_id_and_params_of_item_loc(db, it),
|
||||||
|
GenericDefId::TraitAliasId(it) => file_id_and_params_of_item_loc(db, it),
|
||||||
|
GenericDefId::ImplId(it) => file_id_and_params_of_item_loc(db, it),
|
||||||
|
// We won't be using this ID anyway
|
||||||
|
GenericDefId::EnumVariantId(_) => (FileId::BOGUS.into(), None),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<AssocItemId> for GenericDefId {
|
impl From<AssocItemId> for GenericDefId {
|
||||||
fn from(item: AssocItemId) -> Self {
|
fn from(item: AssocItemId) -> Self {
|
||||||
match item {
|
match item {
|
||||||
|
@ -983,44 +1065,92 @@ impl VariantId {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait HasModule {
|
pub trait HasModule {
|
||||||
|
/// Returns the enclosing module this thing is defined within.
|
||||||
fn module(&self, db: &dyn DefDatabase) -> ModuleId;
|
fn module(&self, db: &dyn DefDatabase) -> ModuleId;
|
||||||
}
|
/// Returns the crate this thing is defined within.
|
||||||
|
|
||||||
impl HasModule for ItemContainerId {
|
|
||||||
fn module(&self, db: &dyn DefDatabase) -> ModuleId {
|
|
||||||
match *self {
|
|
||||||
ItemContainerId::ModuleId(it) => it,
|
|
||||||
ItemContainerId::ImplId(it) => it.lookup(db).container,
|
|
||||||
ItemContainerId::TraitId(it) => it.lookup(db).container,
|
|
||||||
ItemContainerId::ExternBlockId(it) => it.lookup(db).container,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<N: ItemTreeModItemNode> HasModule for AssocItemLoc<N> {
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn module(&self, db: &dyn DefDatabase) -> ModuleId {
|
#[doc(alias = "crate")]
|
||||||
self.container.module(db)
|
fn krate(&self, db: &dyn DefDatabase) -> CrateId {
|
||||||
|
self.module(db).krate
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HasModule for AdtId {
|
// In theory this impl should work out for us, but rustc thinks it collides with all the other
|
||||||
fn module(&self, db: &dyn DefDatabase) -> ModuleId {
|
// manual impls that do not have a ModuleId container...
|
||||||
match self {
|
// impl<N, ItemId, Data> HasModule for ItemId
|
||||||
AdtId::StructId(it) => it.lookup(db).container,
|
// where
|
||||||
AdtId::UnionId(it) => it.lookup(db).container,
|
// N: ItemTreeNode,
|
||||||
AdtId::EnumId(it) => it.lookup(db).container,
|
// ItemId: for<'db> Lookup<Database<'db> = dyn DefDatabase + 'db, Data = Data> + Copy,
|
||||||
}
|
// Data: ItemTreeLoc<Id = N, Container = ModuleId>,
|
||||||
}
|
// {
|
||||||
}
|
// #[inline]
|
||||||
|
// fn module(&self, db: &dyn DefDatabase) -> ModuleId {
|
||||||
|
// self.lookup(db).container()
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
impl HasModule for EnumId {
|
impl<N, ItemId> HasModule for ItemId
|
||||||
|
where
|
||||||
|
N: ItemTreeNode,
|
||||||
|
ItemId: for<'db> Lookup<Database<'db> = dyn DefDatabase + 'db, Data = ItemLoc<N>> + Copy,
|
||||||
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
fn module(&self, db: &dyn DefDatabase) -> ModuleId {
|
fn module(&self, db: &dyn DefDatabase) -> ModuleId {
|
||||||
self.lookup(db).container
|
self.lookup(db).container
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Technically this does not overlap with the above, but rustc currently forbids this, hence why we
|
||||||
|
// need to write the 3 impls manually instead
|
||||||
|
// impl<N, ItemId> HasModule for ItemId
|
||||||
|
// where
|
||||||
|
// N: ItemTreeModItemNode,
|
||||||
|
// ItemId: for<'db> Lookup<Database<'db> = dyn DefDatabase + 'db, Data = AssocItemLoc<N>> + Copy,
|
||||||
|
// {
|
||||||
|
// #[inline]
|
||||||
|
// fn module(&self, db: &dyn DefDatabase) -> ModuleId {
|
||||||
|
// self.lookup(db).container.module(db)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// region: manual-assoc-has-module-impls
|
||||||
|
#[inline]
|
||||||
|
fn module_for_assoc_item_loc<'db>(
|
||||||
|
db: &(dyn 'db + DefDatabase),
|
||||||
|
id: impl Lookup<Database<'db> = dyn DefDatabase + 'db, Data = AssocItemLoc<impl ItemTreeNode>>,
|
||||||
|
) -> ModuleId {
|
||||||
|
id.lookup(db).container.module(db)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HasModule for FunctionId {
|
||||||
|
#[inline]
|
||||||
|
fn module(&self, db: &dyn DefDatabase) -> ModuleId {
|
||||||
|
module_for_assoc_item_loc(db, *self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HasModule for ConstId {
|
||||||
|
#[inline]
|
||||||
|
fn module(&self, db: &dyn DefDatabase) -> ModuleId {
|
||||||
|
module_for_assoc_item_loc(db, *self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HasModule for StaticId {
|
||||||
|
#[inline]
|
||||||
|
fn module(&self, db: &dyn DefDatabase) -> ModuleId {
|
||||||
|
module_for_assoc_item_loc(db, *self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HasModule for TypeAliasId {
|
||||||
|
#[inline]
|
||||||
|
fn module(&self, db: &dyn DefDatabase) -> ModuleId {
|
||||||
|
module_for_assoc_item_loc(db, *self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// endregion: manual-assoc-has-module-impls
|
||||||
|
|
||||||
impl HasModule for EnumVariantId {
|
impl HasModule for EnumVariantId {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn module(&self, db: &dyn DefDatabase) -> ModuleId {
|
fn module(&self, db: &dyn DefDatabase) -> ModuleId {
|
||||||
|
@ -1028,46 +1158,81 @@ impl HasModule for EnumVariantId {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HasModule for ExternCrateId {
|
impl HasModule for MacroRulesId {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn module(&self, db: &dyn DefDatabase) -> ModuleId {
|
fn module(&self, db: &dyn DefDatabase) -> ModuleId {
|
||||||
self.lookup(db).container
|
self.lookup(db).container
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl HasModule for Macro2Id {
|
||||||
|
#[inline]
|
||||||
|
fn module(&self, db: &dyn DefDatabase) -> ModuleId {
|
||||||
|
self.lookup(db).container
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HasModule for ProcMacroId {
|
||||||
|
#[inline]
|
||||||
|
fn module(&self, db: &dyn DefDatabase) -> ModuleId {
|
||||||
|
self.lookup(db).container.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HasModule for ItemContainerId {
|
||||||
|
fn module(&self, db: &dyn DefDatabase) -> ModuleId {
|
||||||
|
match *self {
|
||||||
|
ItemContainerId::ModuleId(it) => it,
|
||||||
|
ItemContainerId::ImplId(it) => it.module(db),
|
||||||
|
ItemContainerId::TraitId(it) => it.module(db),
|
||||||
|
ItemContainerId::ExternBlockId(it) => it.module(db),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HasModule for AdtId {
|
||||||
|
fn module(&self, db: &dyn DefDatabase) -> ModuleId {
|
||||||
|
match *self {
|
||||||
|
AdtId::StructId(it) => it.module(db),
|
||||||
|
AdtId::UnionId(it) => it.module(db),
|
||||||
|
AdtId::EnumId(it) => it.module(db),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl HasModule for VariantId {
|
impl HasModule for VariantId {
|
||||||
fn module(&self, db: &dyn DefDatabase) -> ModuleId {
|
fn module(&self, db: &dyn DefDatabase) -> ModuleId {
|
||||||
match self {
|
match *self {
|
||||||
VariantId::EnumVariantId(it) => it.lookup(db).parent.module(db),
|
VariantId::EnumVariantId(it) => it.module(db),
|
||||||
VariantId::StructId(it) => it.lookup(db).container,
|
VariantId::StructId(it) => it.module(db),
|
||||||
VariantId::UnionId(it) => it.lookup(db).container,
|
VariantId::UnionId(it) => it.module(db),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HasModule for MacroId {
|
impl HasModule for MacroId {
|
||||||
fn module(&self, db: &dyn DefDatabase) -> ModuleId {
|
fn module(&self, db: &dyn DefDatabase) -> ModuleId {
|
||||||
match self {
|
match *self {
|
||||||
MacroId::MacroRulesId(it) => it.lookup(db).container,
|
MacroId::MacroRulesId(it) => it.module(db),
|
||||||
MacroId::Macro2Id(it) => it.lookup(db).container,
|
MacroId::Macro2Id(it) => it.module(db),
|
||||||
MacroId::ProcMacroId(it) => it.lookup(db).container.into(),
|
MacroId::ProcMacroId(it) => it.module(db),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HasModule for TypeOwnerId {
|
impl HasModule for TypeOwnerId {
|
||||||
fn module(&self, db: &dyn DefDatabase) -> ModuleId {
|
fn module(&self, db: &dyn DefDatabase) -> ModuleId {
|
||||||
match self {
|
match *self {
|
||||||
TypeOwnerId::FunctionId(it) => it.lookup(db).module(db),
|
TypeOwnerId::FunctionId(it) => it.module(db),
|
||||||
TypeOwnerId::StaticId(it) => it.lookup(db).module(db),
|
TypeOwnerId::StaticId(it) => it.module(db),
|
||||||
TypeOwnerId::ConstId(it) => it.lookup(db).module(db),
|
TypeOwnerId::ConstId(it) => it.module(db),
|
||||||
TypeOwnerId::InTypeConstId(it) => it.lookup(db).owner.module(db),
|
|
||||||
TypeOwnerId::AdtId(it) => it.module(db),
|
TypeOwnerId::AdtId(it) => it.module(db),
|
||||||
TypeOwnerId::TraitId(it) => it.lookup(db).container,
|
TypeOwnerId::TraitId(it) => it.module(db),
|
||||||
TypeOwnerId::TraitAliasId(it) => it.lookup(db).container,
|
TypeOwnerId::TraitAliasId(it) => it.module(db),
|
||||||
TypeOwnerId::TypeAliasId(it) => it.lookup(db).module(db),
|
TypeOwnerId::TypeAliasId(it) => it.module(db),
|
||||||
TypeOwnerId::ImplId(it) => it.lookup(db).container,
|
TypeOwnerId::ImplId(it) => it.module(db),
|
||||||
TypeOwnerId::EnumVariantId(it) => it.lookup(db).parent.module(db),
|
TypeOwnerId::EnumVariantId(it) => it.module(db),
|
||||||
|
TypeOwnerId::InTypeConstId(it) => it.lookup(db).owner.module(db),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1075,10 +1240,10 @@ impl HasModule for TypeOwnerId {
|
||||||
impl HasModule for DefWithBodyId {
|
impl HasModule for DefWithBodyId {
|
||||||
fn module(&self, db: &dyn DefDatabase) -> ModuleId {
|
fn module(&self, db: &dyn DefDatabase) -> ModuleId {
|
||||||
match self {
|
match self {
|
||||||
DefWithBodyId::FunctionId(it) => it.lookup(db).module(db),
|
DefWithBodyId::FunctionId(it) => it.module(db),
|
||||||
DefWithBodyId::StaticId(it) => it.lookup(db).module(db),
|
DefWithBodyId::StaticId(it) => it.module(db),
|
||||||
DefWithBodyId::ConstId(it) => it.lookup(db).module(db),
|
DefWithBodyId::ConstId(it) => it.module(db),
|
||||||
DefWithBodyId::VariantId(it) => it.lookup(db).parent.module(db),
|
DefWithBodyId::VariantId(it) => it.module(db),
|
||||||
DefWithBodyId::InTypeConstId(it) => it.lookup(db).owner.module(db),
|
DefWithBodyId::InTypeConstId(it) => it.lookup(db).owner.module(db),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1087,29 +1252,43 @@ impl HasModule for DefWithBodyId {
|
||||||
impl HasModule for GenericDefId {
|
impl HasModule for GenericDefId {
|
||||||
fn module(&self, db: &dyn DefDatabase) -> ModuleId {
|
fn module(&self, db: &dyn DefDatabase) -> ModuleId {
|
||||||
match self {
|
match self {
|
||||||
GenericDefId::FunctionId(it) => it.lookup(db).module(db),
|
GenericDefId::FunctionId(it) => it.module(db),
|
||||||
GenericDefId::AdtId(it) => it.module(db),
|
GenericDefId::AdtId(it) => it.module(db),
|
||||||
GenericDefId::TraitId(it) => it.lookup(db).container,
|
GenericDefId::TraitId(it) => it.module(db),
|
||||||
GenericDefId::TraitAliasId(it) => it.lookup(db).container,
|
GenericDefId::TraitAliasId(it) => it.module(db),
|
||||||
GenericDefId::TypeAliasId(it) => it.lookup(db).module(db),
|
GenericDefId::TypeAliasId(it) => it.module(db),
|
||||||
GenericDefId::ImplId(it) => it.lookup(db).container,
|
GenericDefId::ImplId(it) => it.module(db),
|
||||||
GenericDefId::EnumVariantId(it) => it.lookup(db).parent.lookup(db).container,
|
GenericDefId::EnumVariantId(it) => it.module(db),
|
||||||
GenericDefId::ConstId(it) => it.lookup(db).module(db),
|
GenericDefId::ConstId(it) => it.module(db),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HasModule for TypeAliasId {
|
impl HasModule for AttrDefId {
|
||||||
#[inline]
|
|
||||||
fn module(&self, db: &dyn DefDatabase) -> ModuleId {
|
fn module(&self, db: &dyn DefDatabase) -> ModuleId {
|
||||||
self.lookup(db).module(db)
|
match self {
|
||||||
|
AttrDefId::ModuleId(it) => *it,
|
||||||
|
AttrDefId::FieldId(it) => it.parent.module(db),
|
||||||
|
AttrDefId::AdtId(it) => it.module(db),
|
||||||
|
AttrDefId::FunctionId(it) => it.module(db),
|
||||||
|
AttrDefId::EnumVariantId(it) => it.module(db),
|
||||||
|
AttrDefId::StaticId(it) => it.module(db),
|
||||||
|
AttrDefId::ConstId(it) => it.module(db),
|
||||||
|
AttrDefId::TraitId(it) => it.module(db),
|
||||||
|
AttrDefId::TraitAliasId(it) => it.module(db),
|
||||||
|
AttrDefId::TypeAliasId(it) => it.module(db),
|
||||||
|
AttrDefId::ImplId(it) => it.module(db),
|
||||||
|
AttrDefId::ExternBlockId(it) => it.module(db),
|
||||||
|
AttrDefId::GenericParamId(it) => match it {
|
||||||
|
GenericParamId::TypeParamId(it) => it.parent(),
|
||||||
|
GenericParamId::ConstParamId(it) => it.parent(),
|
||||||
|
GenericParamId::LifetimeParamId(it) => it.parent,
|
||||||
|
}
|
||||||
|
.module(db),
|
||||||
|
AttrDefId::MacroId(it) => it.module(db),
|
||||||
|
AttrDefId::ExternCrateId(it) => it.module(db),
|
||||||
|
AttrDefId::UseId(it) => it.module(db),
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl HasModule for TraitId {
|
|
||||||
#[inline]
|
|
||||||
fn module(&self, db: &dyn DefDatabase) -> ModuleId {
|
|
||||||
self.lookup(db).container
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1120,51 +1299,20 @@ impl ModuleDefId {
|
||||||
pub fn module(&self, db: &dyn DefDatabase) -> Option<ModuleId> {
|
pub fn module(&self, db: &dyn DefDatabase) -> Option<ModuleId> {
|
||||||
Some(match self {
|
Some(match self {
|
||||||
ModuleDefId::ModuleId(id) => *id,
|
ModuleDefId::ModuleId(id) => *id,
|
||||||
ModuleDefId::FunctionId(id) => id.lookup(db).module(db),
|
ModuleDefId::FunctionId(id) => id.module(db),
|
||||||
ModuleDefId::AdtId(id) => id.module(db),
|
ModuleDefId::AdtId(id) => id.module(db),
|
||||||
ModuleDefId::EnumVariantId(id) => id.lookup(db).parent.module(db),
|
ModuleDefId::EnumVariantId(id) => id.module(db),
|
||||||
ModuleDefId::ConstId(id) => id.lookup(db).container.module(db),
|
ModuleDefId::ConstId(id) => id.module(db),
|
||||||
ModuleDefId::StaticId(id) => id.lookup(db).module(db),
|
ModuleDefId::StaticId(id) => id.module(db),
|
||||||
ModuleDefId::TraitId(id) => id.lookup(db).container,
|
ModuleDefId::TraitId(id) => id.module(db),
|
||||||
ModuleDefId::TraitAliasId(id) => id.lookup(db).container,
|
ModuleDefId::TraitAliasId(id) => id.module(db),
|
||||||
ModuleDefId::TypeAliasId(id) => id.lookup(db).module(db),
|
ModuleDefId::TypeAliasId(id) => id.module(db),
|
||||||
ModuleDefId::MacroId(id) => id.module(db),
|
ModuleDefId::MacroId(id) => id.module(db),
|
||||||
ModuleDefId::BuiltinType(_) => return None,
|
ModuleDefId::BuiltinType(_) => return None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AttrDefId {
|
|
||||||
pub fn krate(&self, db: &dyn DefDatabase) -> CrateId {
|
|
||||||
match self {
|
|
||||||
AttrDefId::ModuleId(it) => it.krate,
|
|
||||||
AttrDefId::FieldId(it) => it.parent.module(db).krate,
|
|
||||||
AttrDefId::AdtId(it) => it.module(db).krate,
|
|
||||||
AttrDefId::FunctionId(it) => it.lookup(db).module(db).krate,
|
|
||||||
AttrDefId::EnumVariantId(it) => it.lookup(db).parent.module(db).krate,
|
|
||||||
AttrDefId::StaticId(it) => it.lookup(db).module(db).krate,
|
|
||||||
AttrDefId::ConstId(it) => it.lookup(db).module(db).krate,
|
|
||||||
AttrDefId::TraitId(it) => it.lookup(db).container.krate,
|
|
||||||
AttrDefId::TraitAliasId(it) => it.lookup(db).container.krate,
|
|
||||||
AttrDefId::TypeAliasId(it) => it.lookup(db).module(db).krate,
|
|
||||||
AttrDefId::ImplId(it) => it.lookup(db).container.krate,
|
|
||||||
AttrDefId::ExternBlockId(it) => it.lookup(db).container.krate,
|
|
||||||
AttrDefId::GenericParamId(it) => {
|
|
||||||
match it {
|
|
||||||
GenericParamId::TypeParamId(it) => it.parent(),
|
|
||||||
GenericParamId::ConstParamId(it) => it.parent(),
|
|
||||||
GenericParamId::LifetimeParamId(it) => it.parent,
|
|
||||||
}
|
|
||||||
.module(db)
|
|
||||||
.krate
|
|
||||||
}
|
|
||||||
AttrDefId::MacroId(it) => it.module(db).krate,
|
|
||||||
AttrDefId::ExternCrateId(it) => it.lookup(db).container.krate,
|
|
||||||
AttrDefId::UseId(it) => it.lookup(db).container.krate,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A helper trait for converting to MacroCallId
|
/// A helper trait for converting to MacroCallId
|
||||||
pub trait AsMacroCall {
|
pub trait AsMacroCall {
|
||||||
fn as_call_id(
|
fn as_call_id(
|
||||||
|
|
|
@ -157,7 +157,7 @@ where
|
||||||
generic: Vec<T::InGenericArg>,
|
generic: Vec<T::InGenericArg>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl <T: $crate::clone::Clone, > $crate::clone::Clone for Foo<T, > where T: Trait, T::InFieldShorthand: $crate::clone::Clone, T::InGenericArg: $crate::clone::Clone, {
|
impl <T: $crate::clone::Clone, > $crate::clone::Clone for Foo<T, > where <T as Trait>::InWc: Marker, T: Trait, T::InFieldShorthand: $crate::clone::Clone, T::InGenericArg: $crate::clone::Clone, {
|
||||||
fn clone(&self ) -> Self {
|
fn clone(&self ) -> Self {
|
||||||
match self {
|
match self {
|
||||||
Foo {
|
Foo {
|
||||||
|
|
|
@ -35,9 +35,9 @@ macro_rules! f {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
struct#0:1@58..64#2# MyTraitMap2#0:2@31..42#0# {#0:1@72..73#2#
|
struct#0:1@58..64#1# MyTraitMap2#0:2@31..42#0# {#0:1@72..73#1#
|
||||||
map#0:1@86..89#2#:#0:1@89..90#2# #0:1@89..90#2#::#0:1@91..92#2#std#0:1@93..96#2#::#0:1@96..97#2#collections#0:1@98..109#2#::#0:1@109..110#2#HashSet#0:1@111..118#2#<#0:1@118..119#2#(#0:1@119..120#2#)#0:1@120..121#2#>#0:1@121..122#2#,#0:1@122..123#2#
|
map#0:1@86..89#1#:#0:1@89..90#1# #0:1@89..90#1#::#0:1@91..92#1#std#0:1@93..96#1#::#0:1@96..97#1#collections#0:1@98..109#1#::#0:1@109..110#1#HashSet#0:1@111..118#1#<#0:1@118..119#1#(#0:1@119..120#1#)#0:1@120..121#1#>#0:1@121..122#1#,#0:1@122..123#1#
|
||||||
}#0:1@132..133#2#
|
}#0:1@132..133#1#
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -171,7 +171,7 @@ fn main(foo: ()) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main(foo: ()) {
|
fn main(foo: ()) {
|
||||||
/* error: unresolved macro unresolved */"helloworld!"#0:3@207..323#6#;
|
/* error: unresolved macro unresolved */"helloworld!"#0:3@207..323#2#;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -197,7 +197,7 @@ macro_rules! mk_struct {
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod foo;
|
mod foo;
|
||||||
|
|
||||||
struct#1:1@59..65#2# Foo#0:2@32..35#0#(#1:1@70..71#2#u32#0:2@41..44#0#)#1:1@74..75#2#;#1:1@75..76#2#
|
struct#1:1@59..65#1# Foo#0:2@32..35#0#(#1:1@70..71#1#u32#0:2@41..44#0#)#1:1@74..75#1#;#1:1@75..76#1#
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -224,7 +224,7 @@ fn reindent(indent: IndentLevel, pp: String) -> String {
|
||||||
return pp;
|
return pp;
|
||||||
}
|
}
|
||||||
let mut lines = pp.split_inclusive('\n');
|
let mut lines = pp.split_inclusive('\n');
|
||||||
let mut res = lines.next().unwrap().to_string();
|
let mut res = lines.next().unwrap().to_owned();
|
||||||
for line in lines {
|
for line in lines {
|
||||||
if line.trim().is_empty() {
|
if line.trim().is_empty() {
|
||||||
res.push_str(line)
|
res.push_str(line)
|
||||||
|
|
|
@ -33,8 +33,8 @@ use crate::{
|
||||||
db::DefDatabase,
|
db::DefDatabase,
|
||||||
item_scope::{ImportId, ImportOrExternCrate, ImportType, PerNsGlobImports},
|
item_scope::{ImportId, ImportOrExternCrate, ImportType, PerNsGlobImports},
|
||||||
item_tree::{
|
item_tree::{
|
||||||
self, ExternCrate, Fields, FileItemTreeId, ImportKind, ItemTree, ItemTreeId,
|
self, ExternCrate, Fields, FileItemTreeId, ImportKind, ItemTree, ItemTreeId, ItemTreeNode,
|
||||||
ItemTreeModItemNode, Macro2, MacroCall, MacroRules, Mod, ModItem, ModKind, TreeId,
|
Macro2, MacroCall, MacroRules, Mod, ModItem, ModKind, TreeId,
|
||||||
},
|
},
|
||||||
macro_call_as_call_id, macro_call_as_call_id_with_eager,
|
macro_call_as_call_id, macro_call_as_call_id_with_eager,
|
||||||
nameres::{
|
nameres::{
|
||||||
|
@ -2125,7 +2125,7 @@ impl ModCollector<'_, '_> {
|
||||||
|
|
||||||
let is_export = export_attr.exists();
|
let is_export = export_attr.exists();
|
||||||
let local_inner = if is_export {
|
let local_inner = if is_export {
|
||||||
export_attr.tt_values().flat_map(|it| &it.token_trees).any(|it| match it {
|
export_attr.tt_values().flat_map(|it| it.token_trees.iter()).any(|it| match it {
|
||||||
tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) => {
|
tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) => {
|
||||||
ident.text.contains("local_inner_macros")
|
ident.text.contains("local_inner_macros")
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,9 +27,9 @@ use crate::{
|
||||||
visibility::{RawVisibility, Visibility},
|
visibility::{RawVisibility, Visibility},
|
||||||
AdtId, ConstId, ConstParamId, CrateRootModuleId, DefWithBodyId, EnumId, EnumVariantId,
|
AdtId, ConstId, ConstParamId, CrateRootModuleId, DefWithBodyId, EnumId, EnumVariantId,
|
||||||
ExternBlockId, ExternCrateId, FunctionId, GenericDefId, GenericParamId, HasModule, ImplId,
|
ExternBlockId, ExternCrateId, FunctionId, GenericDefId, GenericParamId, HasModule, ImplId,
|
||||||
ItemContainerId, LifetimeParamId, LocalModuleId, Lookup, Macro2Id, MacroId, MacroRulesId,
|
ItemContainerId, ItemTreeLoc, LifetimeParamId, LocalModuleId, Lookup, Macro2Id, MacroId,
|
||||||
ModuleDefId, ModuleId, ProcMacroId, StaticId, StructId, TraitAliasId, TraitId, TypeAliasId,
|
MacroRulesId, ModuleDefId, ModuleId, ProcMacroId, StaticId, StructId, TraitAliasId, TraitId,
|
||||||
TypeOrConstParamId, TypeOwnerId, TypeParamId, UseId, VariantId,
|
TypeAliasId, TypeOrConstParamId, TypeOwnerId, TypeParamId, UseId, VariantId,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
@ -248,6 +248,7 @@ impl Resolver {
|
||||||
RawVisibility::Public => Some(Visibility::Public),
|
RawVisibility::Public => Some(Visibility::Public),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn resolve_path_in_value_ns(
|
pub fn resolve_path_in_value_ns(
|
||||||
&self,
|
&self,
|
||||||
db: &dyn DefDatabase,
|
db: &dyn DefDatabase,
|
||||||
|
@ -1014,13 +1015,13 @@ impl HasResolver for CrateRootModuleId {
|
||||||
|
|
||||||
impl HasResolver for TraitId {
|
impl HasResolver for TraitId {
|
||||||
fn resolver(self, db: &dyn DefDatabase) -> Resolver {
|
fn resolver(self, db: &dyn DefDatabase) -> Resolver {
|
||||||
self.lookup(db).container.resolver(db).push_generic_params_scope(db, self.into())
|
lookup_resolver(db, self).push_generic_params_scope(db, self.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HasResolver for TraitAliasId {
|
impl HasResolver for TraitAliasId {
|
||||||
fn resolver(self, db: &dyn DefDatabase) -> Resolver {
|
fn resolver(self, db: &dyn DefDatabase) -> Resolver {
|
||||||
self.lookup(db).container.resolver(db).push_generic_params_scope(db, self.into())
|
lookup_resolver(db, self).push_generic_params_scope(db, self.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1036,25 +1037,25 @@ impl<T: Into<AdtId> + Copy> HasResolver for T {
|
||||||
|
|
||||||
impl HasResolver for FunctionId {
|
impl HasResolver for FunctionId {
|
||||||
fn resolver(self, db: &dyn DefDatabase) -> Resolver {
|
fn resolver(self, db: &dyn DefDatabase) -> Resolver {
|
||||||
self.lookup(db).container.resolver(db).push_generic_params_scope(db, self.into())
|
lookup_resolver(db, self).push_generic_params_scope(db, self.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HasResolver for ConstId {
|
impl HasResolver for ConstId {
|
||||||
fn resolver(self, db: &dyn DefDatabase) -> Resolver {
|
fn resolver(self, db: &dyn DefDatabase) -> Resolver {
|
||||||
self.lookup(db).container.resolver(db)
|
lookup_resolver(db, self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HasResolver for StaticId {
|
impl HasResolver for StaticId {
|
||||||
fn resolver(self, db: &dyn DefDatabase) -> Resolver {
|
fn resolver(self, db: &dyn DefDatabase) -> Resolver {
|
||||||
self.lookup(db).container.resolver(db)
|
lookup_resolver(db, self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HasResolver for TypeAliasId {
|
impl HasResolver for TypeAliasId {
|
||||||
fn resolver(self, db: &dyn DefDatabase) -> Resolver {
|
fn resolver(self, db: &dyn DefDatabase) -> Resolver {
|
||||||
self.lookup(db).container.resolver(db).push_generic_params_scope(db, self.into())
|
lookup_resolver(db, self).push_generic_params_scope(db, self.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1071,19 +1072,19 @@ impl HasResolver for ImplId {
|
||||||
impl HasResolver for ExternBlockId {
|
impl HasResolver for ExternBlockId {
|
||||||
fn resolver(self, db: &dyn DefDatabase) -> Resolver {
|
fn resolver(self, db: &dyn DefDatabase) -> Resolver {
|
||||||
// Same as parent's
|
// Same as parent's
|
||||||
self.lookup(db).container.resolver(db)
|
lookup_resolver(db, self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HasResolver for ExternCrateId {
|
impl HasResolver for ExternCrateId {
|
||||||
fn resolver(self, db: &dyn DefDatabase) -> Resolver {
|
fn resolver(self, db: &dyn DefDatabase) -> Resolver {
|
||||||
self.lookup(db).container.resolver(db)
|
lookup_resolver(db, self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HasResolver for UseId {
|
impl HasResolver for UseId {
|
||||||
fn resolver(self, db: &dyn DefDatabase) -> Resolver {
|
fn resolver(self, db: &dyn DefDatabase) -> Resolver {
|
||||||
self.lookup(db).container.resolver(db)
|
lookup_resolver(db, self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1170,18 +1171,28 @@ impl HasResolver for MacroId {
|
||||||
|
|
||||||
impl HasResolver for Macro2Id {
|
impl HasResolver for Macro2Id {
|
||||||
fn resolver(self, db: &dyn DefDatabase) -> Resolver {
|
fn resolver(self, db: &dyn DefDatabase) -> Resolver {
|
||||||
self.lookup(db).container.resolver(db)
|
lookup_resolver(db, self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HasResolver for ProcMacroId {
|
impl HasResolver for ProcMacroId {
|
||||||
fn resolver(self, db: &dyn DefDatabase) -> Resolver {
|
fn resolver(self, db: &dyn DefDatabase) -> Resolver {
|
||||||
self.lookup(db).container.resolver(db)
|
lookup_resolver(db, self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HasResolver for MacroRulesId {
|
impl HasResolver for MacroRulesId {
|
||||||
fn resolver(self, db: &dyn DefDatabase) -> Resolver {
|
fn resolver(self, db: &dyn DefDatabase) -> Resolver {
|
||||||
self.lookup(db).container.resolver(db)
|
lookup_resolver(db, self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn lookup_resolver<'db>(
|
||||||
|
db: &(dyn DefDatabase + 'db),
|
||||||
|
lookup: impl Lookup<
|
||||||
|
Database<'db> = dyn DefDatabase + 'db,
|
||||||
|
Data = impl ItemTreeLoc<Container = impl HasResolver>,
|
||||||
|
>,
|
||||||
|
) -> Resolver {
|
||||||
|
lookup.lookup(db).container().resolver(db)
|
||||||
|
}
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
//! Utilities for mapping between hir IDs and the surface syntax.
|
//! Utilities for mapping between hir IDs and the surface syntax.
|
||||||
|
|
||||||
|
use either::Either;
|
||||||
use hir_expand::InFile;
|
use hir_expand::InFile;
|
||||||
use la_arena::ArenaMap;
|
use la_arena::ArenaMap;
|
||||||
use syntax::ast;
|
use syntax::ast;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
db::DefDatabase, item_tree::ItemTreeModItemNode, AssocItemLoc, EnumVariantLoc, ItemLoc, Lookup,
|
data::adt::lower_struct, db::DefDatabase, item_tree::ItemTreeNode, trace::Trace, GenericDefId,
|
||||||
Macro2Loc, MacroRulesLoc, ProcMacroLoc, UseId,
|
ItemTreeLoc, LocalFieldId, LocalLifetimeParamId, LocalTypeOrConstParamId, Lookup, UseId,
|
||||||
|
VariantId,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub trait HasSource {
|
pub trait HasSource {
|
||||||
|
@ -14,81 +16,22 @@ pub trait HasSource {
|
||||||
fn source(&self, db: &dyn DefDatabase) -> InFile<Self::Value>;
|
fn source(&self, db: &dyn DefDatabase) -> InFile<Self::Value>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<N: ItemTreeModItemNode> HasSource for AssocItemLoc<N> {
|
impl<T> HasSource for T
|
||||||
type Value = N::Source;
|
where
|
||||||
|
T: ItemTreeLoc,
|
||||||
fn source(&self, db: &dyn DefDatabase) -> InFile<N::Source> {
|
T::Id: ItemTreeNode,
|
||||||
let tree = self.id.item_tree(db);
|
{
|
||||||
let ast_id_map = db.ast_id_map(self.id.file_id());
|
type Value = <T::Id as ItemTreeNode>::Source;
|
||||||
let root = db.parse_or_expand(self.id.file_id());
|
|
||||||
let node = &tree[self.id.value];
|
|
||||||
|
|
||||||
InFile::new(self.id.file_id(), ast_id_map.get(node.ast_id()).to_node(&root))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<N: ItemTreeModItemNode> HasSource for ItemLoc<N> {
|
|
||||||
type Value = N::Source;
|
|
||||||
|
|
||||||
fn source(&self, db: &dyn DefDatabase) -> InFile<N::Source> {
|
|
||||||
let tree = self.id.item_tree(db);
|
|
||||||
let ast_id_map = db.ast_id_map(self.id.file_id());
|
|
||||||
let root = db.parse_or_expand(self.id.file_id());
|
|
||||||
let node = &tree[self.id.value];
|
|
||||||
|
|
||||||
InFile::new(self.id.file_id(), ast_id_map.get(node.ast_id()).to_node(&root))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl HasSource for EnumVariantLoc {
|
|
||||||
type Value = ast::Variant;
|
|
||||||
|
|
||||||
fn source(&self, db: &dyn DefDatabase) -> InFile<ast::Variant> {
|
|
||||||
let tree = self.id.item_tree(db);
|
|
||||||
let ast_id_map = db.ast_id_map(self.id.file_id());
|
|
||||||
let root = db.parse_or_expand(self.id.file_id());
|
|
||||||
let node = &tree[self.id.value];
|
|
||||||
|
|
||||||
InFile::new(self.id.file_id(), ast_id_map.get(node.ast_id).to_node(&root))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl HasSource for Macro2Loc {
|
|
||||||
type Value = ast::MacroDef;
|
|
||||||
|
|
||||||
fn source(&self, db: &dyn DefDatabase) -> InFile<Self::Value> {
|
fn source(&self, db: &dyn DefDatabase) -> InFile<Self::Value> {
|
||||||
let tree = self.id.item_tree(db);
|
let id = self.item_tree_id();
|
||||||
let ast_id_map = db.ast_id_map(self.id.file_id());
|
let file_id = id.file_id();
|
||||||
let root = db.parse_or_expand(self.id.file_id());
|
let tree = id.item_tree(db);
|
||||||
let node = &tree[self.id.value];
|
let ast_id_map = db.ast_id_map(file_id);
|
||||||
|
let root = db.parse_or_expand(file_id);
|
||||||
|
let node = &tree[id.value];
|
||||||
|
|
||||||
InFile::new(self.id.file_id(), ast_id_map.get(node.ast_id()).to_node(&root))
|
InFile::new(file_id, ast_id_map.get(node.ast_id()).to_node(&root))
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl HasSource for MacroRulesLoc {
|
|
||||||
type Value = ast::MacroRules;
|
|
||||||
|
|
||||||
fn source(&self, db: &dyn DefDatabase) -> InFile<Self::Value> {
|
|
||||||
let tree = self.id.item_tree(db);
|
|
||||||
let ast_id_map = db.ast_id_map(self.id.file_id());
|
|
||||||
let root = db.parse_or_expand(self.id.file_id());
|
|
||||||
let node = &tree[self.id.value];
|
|
||||||
|
|
||||||
InFile::new(self.id.file_id(), ast_id_map.get(node.ast_id()).to_node(&root))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl HasSource for ProcMacroLoc {
|
|
||||||
type Value = ast::Fn;
|
|
||||||
|
|
||||||
fn source(&self, db: &dyn DefDatabase) -> InFile<Self::Value> {
|
|
||||||
let tree = self.id.item_tree(db);
|
|
||||||
let ast_id_map = db.ast_id_map(self.id.file_id());
|
|
||||||
let root = db.parse_or_expand(self.id.file_id());
|
|
||||||
let node = &tree[self.id.value];
|
|
||||||
|
|
||||||
InFile::new(self.id.file_id(), ast_id_map.get(node.ast_id()).to_node(&root))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -111,3 +54,105 @@ impl HasChildSource<la_arena::Idx<ast::UseTree>> for UseId {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl HasChildSource<LocalTypeOrConstParamId> for GenericDefId {
|
||||||
|
type Value = Either<ast::TypeOrConstParam, ast::TraitOrAlias>;
|
||||||
|
fn child_source(
|
||||||
|
&self,
|
||||||
|
db: &dyn DefDatabase,
|
||||||
|
) -> InFile<ArenaMap<LocalTypeOrConstParamId, Self::Value>> {
|
||||||
|
let generic_params = db.generic_params(*self);
|
||||||
|
let mut idx_iter = generic_params.type_or_consts.iter().map(|(idx, _)| idx);
|
||||||
|
|
||||||
|
let (file_id, generic_params_list) = self.file_id_and_params_of(db);
|
||||||
|
|
||||||
|
let mut params = ArenaMap::default();
|
||||||
|
|
||||||
|
// For traits and trait aliases the first type index is `Self`, we need to add it before
|
||||||
|
// the other params.
|
||||||
|
match *self {
|
||||||
|
GenericDefId::TraitId(id) => {
|
||||||
|
let trait_ref = id.lookup(db).source(db).value;
|
||||||
|
let idx = idx_iter.next().unwrap();
|
||||||
|
params.insert(idx, Either::Right(ast::TraitOrAlias::Trait(trait_ref)));
|
||||||
|
}
|
||||||
|
GenericDefId::TraitAliasId(id) => {
|
||||||
|
let alias = id.lookup(db).source(db).value;
|
||||||
|
let idx = idx_iter.next().unwrap();
|
||||||
|
params.insert(idx, Either::Right(ast::TraitOrAlias::TraitAlias(alias)));
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(generic_params_list) = generic_params_list {
|
||||||
|
for (idx, ast_param) in idx_iter.zip(generic_params_list.type_or_const_params()) {
|
||||||
|
params.insert(idx, Either::Left(ast_param));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
InFile::new(file_id, params)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HasChildSource<LocalLifetimeParamId> for GenericDefId {
|
||||||
|
type Value = ast::LifetimeParam;
|
||||||
|
fn child_source(
|
||||||
|
&self,
|
||||||
|
db: &dyn DefDatabase,
|
||||||
|
) -> InFile<ArenaMap<LocalLifetimeParamId, Self::Value>> {
|
||||||
|
let generic_params = db.generic_params(*self);
|
||||||
|
let idx_iter = generic_params.lifetimes.iter().map(|(idx, _)| idx);
|
||||||
|
|
||||||
|
let (file_id, generic_params_list) = self.file_id_and_params_of(db);
|
||||||
|
|
||||||
|
let mut params = ArenaMap::default();
|
||||||
|
|
||||||
|
if let Some(generic_params_list) = generic_params_list {
|
||||||
|
for (idx, ast_param) in idx_iter.zip(generic_params_list.lifetime_params()) {
|
||||||
|
params.insert(idx, ast_param);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
InFile::new(file_id, params)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HasChildSource<LocalFieldId> for VariantId {
|
||||||
|
type Value = Either<ast::TupleField, ast::RecordField>;
|
||||||
|
|
||||||
|
fn child_source(&self, db: &dyn DefDatabase) -> InFile<ArenaMap<LocalFieldId, Self::Value>> {
|
||||||
|
let item_tree;
|
||||||
|
let (src, fields, container) = match *self {
|
||||||
|
VariantId::EnumVariantId(it) => {
|
||||||
|
let lookup = it.lookup(db);
|
||||||
|
item_tree = lookup.id.item_tree(db);
|
||||||
|
(
|
||||||
|
lookup.source(db).map(|it| it.kind()),
|
||||||
|
&item_tree[lookup.id.value].fields,
|
||||||
|
lookup.parent.lookup(db).container,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
VariantId::StructId(it) => {
|
||||||
|
let lookup = it.lookup(db);
|
||||||
|
item_tree = lookup.id.item_tree(db);
|
||||||
|
(
|
||||||
|
lookup.source(db).map(|it| it.kind()),
|
||||||
|
&item_tree[lookup.id.value].fields,
|
||||||
|
lookup.container,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
VariantId::UnionId(it) => {
|
||||||
|
let lookup = it.lookup(db);
|
||||||
|
item_tree = lookup.id.item_tree(db);
|
||||||
|
(
|
||||||
|
lookup.source(db).map(|it| it.kind()),
|
||||||
|
&item_tree[lookup.id.value].fields,
|
||||||
|
lookup.container,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let mut trace = Trace::new_for_map();
|
||||||
|
lower_struct(db, &mut trace, &src, container.krate, &item_tree, fields);
|
||||||
|
src.with_value(trace.into_map())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -11,6 +11,8 @@
|
||||||
//! projections.
|
//! projections.
|
||||||
use la_arena::{Arena, ArenaMap, Idx, RawIdx};
|
use la_arena::{Arena, ArenaMap, Idx, RawIdx};
|
||||||
|
|
||||||
|
// FIXME: This isn't really used anymore, at least not in a way where it does anything useful.
|
||||||
|
// Check if we should get rid of this or make proper use of it instead.
|
||||||
pub(crate) struct Trace<T, V> {
|
pub(crate) struct Trace<T, V> {
|
||||||
arena: Option<Arena<T>>,
|
arena: Option<Arena<T>>,
|
||||||
map: Option<ArenaMap<Idx<T>, V>>,
|
map: Option<ArenaMap<Idx<T>, V>>,
|
||||||
|
|
|
@ -37,10 +37,14 @@ impl RawVisibility {
|
||||||
db: &dyn DefDatabase,
|
db: &dyn DefDatabase,
|
||||||
node: InFile<Option<ast::Visibility>>,
|
node: InFile<Option<ast::Visibility>>,
|
||||||
) -> RawVisibility {
|
) -> RawVisibility {
|
||||||
|
let node = match node.transpose() {
|
||||||
|
None => return RawVisibility::private(),
|
||||||
|
Some(node) => node,
|
||||||
|
};
|
||||||
Self::from_ast_with_span_map(db, node.value, db.span_map(node.file_id).as_ref())
|
Self::from_ast_with_span_map(db, node.value, db.span_map(node.file_id).as_ref())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn from_ast_with_span_map(
|
pub(crate) fn from_opt_ast_with_span_map(
|
||||||
db: &dyn DefDatabase,
|
db: &dyn DefDatabase,
|
||||||
node: Option<ast::Visibility>,
|
node: Option<ast::Visibility>,
|
||||||
span_map: SpanMapRef<'_>,
|
span_map: SpanMapRef<'_>,
|
||||||
|
@ -49,30 +53,29 @@ impl RawVisibility {
|
||||||
None => return RawVisibility::private(),
|
None => return RawVisibility::private(),
|
||||||
Some(node) => node,
|
Some(node) => node,
|
||||||
};
|
};
|
||||||
match node.kind() {
|
Self::from_ast_with_span_map(db, node, span_map)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_ast_with_span_map(
|
||||||
|
db: &dyn DefDatabase,
|
||||||
|
node: ast::Visibility,
|
||||||
|
span_map: SpanMapRef<'_>,
|
||||||
|
) -> RawVisibility {
|
||||||
|
let path = match node.kind() {
|
||||||
ast::VisibilityKind::In(path) => {
|
ast::VisibilityKind::In(path) => {
|
||||||
let path = ModPath::from_src(db.upcast(), path, span_map);
|
let path = ModPath::from_src(db.upcast(), path, span_map);
|
||||||
let path = match path {
|
match path {
|
||||||
None => return RawVisibility::private(),
|
None => return RawVisibility::private(),
|
||||||
Some(path) => path,
|
Some(path) => path,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ast::VisibilityKind::PubCrate => ModPath::from_kind(PathKind::Crate),
|
||||||
|
ast::VisibilityKind::PubSuper => ModPath::from_kind(PathKind::Super(1)),
|
||||||
|
ast::VisibilityKind::PubSelf => ModPath::from_kind(PathKind::Super(0)),
|
||||||
|
ast::VisibilityKind::Pub => return RawVisibility::Public,
|
||||||
};
|
};
|
||||||
RawVisibility::Module(path, VisibilityExplicitness::Explicit)
|
RawVisibility::Module(path, VisibilityExplicitness::Explicit)
|
||||||
}
|
}
|
||||||
ast::VisibilityKind::PubCrate => {
|
|
||||||
let path = ModPath::from_kind(PathKind::Crate);
|
|
||||||
RawVisibility::Module(path, VisibilityExplicitness::Explicit)
|
|
||||||
}
|
|
||||||
ast::VisibilityKind::PubSuper => {
|
|
||||||
let path = ModPath::from_kind(PathKind::Super(1));
|
|
||||||
RawVisibility::Module(path, VisibilityExplicitness::Explicit)
|
|
||||||
}
|
|
||||||
ast::VisibilityKind::PubSelf => {
|
|
||||||
let path = ModPath::from_kind(PathKind::Super(0));
|
|
||||||
RawVisibility::Module(path, VisibilityExplicitness::Explicit)
|
|
||||||
}
|
|
||||||
ast::VisibilityKind::Pub => RawVisibility::Public,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn resolve(
|
pub fn resolve(
|
||||||
&self,
|
&self,
|
||||||
|
@ -94,6 +97,11 @@ pub enum Visibility {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Visibility {
|
impl Visibility {
|
||||||
|
pub(crate) fn is_visible_from_other_crate(self) -> bool {
|
||||||
|
matches!(self, Visibility::Public)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(skip_all)]
|
||||||
pub fn is_visible_from(self, db: &dyn DefDatabase, from_module: ModuleId) -> bool {
|
pub fn is_visible_from(self, db: &dyn DefDatabase, from_module: ModuleId) -> bool {
|
||||||
let to_module = match self {
|
let to_module = match self {
|
||||||
Visibility::Module(m, _) => m,
|
Visibility::Module(m, _) => m,
|
||||||
|
@ -104,24 +112,33 @@ impl Visibility {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
let def_map = from_module.def_map(db);
|
let def_map = from_module.def_map(db);
|
||||||
self.is_visible_from_def_map(db, &def_map, from_module.local_id)
|
Self::is_visible_from_def_map_(db, &def_map, to_module, from_module.local_id)
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn is_visible_from_other_crate(self) -> bool {
|
|
||||||
matches!(self, Visibility::Public)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn is_visible_from_def_map(
|
pub(crate) fn is_visible_from_def_map(
|
||||||
self,
|
self,
|
||||||
db: &dyn DefDatabase,
|
db: &dyn DefDatabase,
|
||||||
def_map: &DefMap,
|
def_map: &DefMap,
|
||||||
mut from_module: LocalModuleId,
|
from_module: LocalModuleId,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let mut to_module = match self {
|
let to_module = match self {
|
||||||
Visibility::Module(m, _) => m,
|
Visibility::Module(m, _) => m,
|
||||||
Visibility::Public => return true,
|
Visibility::Public => return true,
|
||||||
};
|
};
|
||||||
|
// if they're not in the same crate, it can't be visible
|
||||||
|
if def_map.krate() != to_module.krate {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Self::is_visible_from_def_map_(db, def_map, to_module, from_module)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_visible_from_def_map_(
|
||||||
|
db: &dyn DefDatabase,
|
||||||
|
def_map: &DefMap,
|
||||||
|
mut to_module: ModuleId,
|
||||||
|
mut from_module: LocalModuleId,
|
||||||
|
) -> bool {
|
||||||
|
debug_assert_eq!(to_module.krate, def_map.krate());
|
||||||
// `to_module` might be the root module of a block expression. Those have the same
|
// `to_module` might be the root module of a block expression. Those have the same
|
||||||
// visibility as the containing module (even though no items are directly nameable from
|
// visibility as the containing module (even though no items are directly nameable from
|
||||||
// there, getting this right is important for method resolution).
|
// there, getting this right is important for method resolution).
|
||||||
|
@ -129,21 +146,26 @@ impl Visibility {
|
||||||
|
|
||||||
// Additional complication: `to_module` might be in `from_module`'s `DefMap`, which we're
|
// Additional complication: `to_module` might be in `from_module`'s `DefMap`, which we're
|
||||||
// currently computing, so we must not call the `def_map` query for it.
|
// currently computing, so we must not call the `def_map` query for it.
|
||||||
let mut arc;
|
let def_map_block = def_map.block_id();
|
||||||
loop {
|
loop {
|
||||||
let to_module_def_map =
|
match (to_module.block, def_map_block) {
|
||||||
if to_module.krate == def_map.krate() && to_module.block == def_map.block_id() {
|
// to_module is not a block, so there is no parent def map to use
|
||||||
|
(None, _) => (),
|
||||||
|
(Some(a), Some(b)) if a == b => {
|
||||||
cov_mark::hit!(is_visible_from_same_block_def_map);
|
cov_mark::hit!(is_visible_from_same_block_def_map);
|
||||||
def_map
|
if let Some(parent) = def_map.parent() {
|
||||||
} else {
|
to_module = parent;
|
||||||
arc = to_module.def_map(db);
|
|
||||||
&arc
|
|
||||||
};
|
|
||||||
match to_module_def_map.parent() {
|
|
||||||
Some(parent) => to_module = parent,
|
|
||||||
None => break,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
_ => {
|
||||||
|
if let Some(parent) = to_module.def_map(db).parent() {
|
||||||
|
to_module = parent;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
// from_module needs to be a descendant of to_module
|
// from_module needs to be a descendant of to_module
|
||||||
let mut def_map = def_map;
|
let mut def_map = def_map;
|
||||||
|
@ -175,30 +197,25 @@ impl Visibility {
|
||||||
/// visible in unrelated modules).
|
/// visible in unrelated modules).
|
||||||
pub(crate) fn max(self, other: Visibility, def_map: &DefMap) -> Option<Visibility> {
|
pub(crate) fn max(self, other: Visibility, def_map: &DefMap) -> Option<Visibility> {
|
||||||
match (self, other) {
|
match (self, other) {
|
||||||
(Visibility::Module(_, _) | Visibility::Public, Visibility::Public)
|
(_, Visibility::Public) | (Visibility::Public, _) => Some(Visibility::Public),
|
||||||
| (Visibility::Public, Visibility::Module(_, _)) => Some(Visibility::Public),
|
(Visibility::Module(mod_a, expl_a), Visibility::Module(mod_b, expl_b)) => {
|
||||||
(Visibility::Module(mod_a, vis_a), Visibility::Module(mod_b, vis_b)) => {
|
|
||||||
if mod_a.krate != mod_b.krate {
|
if mod_a.krate != mod_b.krate {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut a_ancestors = iter::successors(Some(mod_a.local_id), |&m| {
|
let mut a_ancestors =
|
||||||
let parent_id = def_map[m].parent?;
|
iter::successors(Some(mod_a.local_id), |&m| def_map[m].parent);
|
||||||
Some(parent_id)
|
let mut b_ancestors =
|
||||||
});
|
iter::successors(Some(mod_b.local_id), |&m| def_map[m].parent);
|
||||||
let mut b_ancestors = iter::successors(Some(mod_b.local_id), |&m| {
|
|
||||||
let parent_id = def_map[m].parent?;
|
|
||||||
Some(parent_id)
|
|
||||||
});
|
|
||||||
|
|
||||||
if a_ancestors.any(|m| m == mod_b.local_id) {
|
if a_ancestors.any(|m| m == mod_b.local_id) {
|
||||||
// B is above A
|
// B is above A
|
||||||
return Some(Visibility::Module(mod_b, vis_b));
|
return Some(Visibility::Module(mod_b, expl_b));
|
||||||
}
|
}
|
||||||
|
|
||||||
if b_ancestors.any(|m| m == mod_a.local_id) {
|
if b_ancestors.any(|m| m == mod_a.local_id) {
|
||||||
// A is above B
|
// A is above B
|
||||||
return Some(Visibility::Module(mod_a, vis_a));
|
return Some(Visibility::Module(mod_a, expl_a));
|
||||||
}
|
}
|
||||||
|
|
||||||
None
|
None
|
||||||
|
@ -207,7 +224,8 @@ impl Visibility {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether the item was imported through `pub(crate) use` or just `use`.
|
/// Whether the item was imported through an explicit `pub(crate) use` or just a `use` without
|
||||||
|
/// visibility.
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||||
pub enum VisibilityExplicitness {
|
pub enum VisibilityExplicitness {
|
||||||
Explicit,
|
Explicit,
|
||||||
|
|
|
@ -155,7 +155,7 @@ impl PartialEq for AstIdMap {
|
||||||
impl Eq for AstIdMap {}
|
impl Eq for AstIdMap {}
|
||||||
|
|
||||||
impl AstIdMap {
|
impl AstIdMap {
|
||||||
pub(crate) fn ast_id_map(
|
pub(crate) fn new(
|
||||||
db: &dyn ExpandDatabase,
|
db: &dyn ExpandDatabase,
|
||||||
file_id: span::HirFileId,
|
file_id: span::HirFileId,
|
||||||
) -> triomphe::Arc<AstIdMap> {
|
) -> triomphe::Arc<AstIdMap> {
|
||||||
|
|
|
@ -123,7 +123,7 @@ impl RawAttrs {
|
||||||
.filter_map(|(idx, attr)| Attr::from_tt(db, attr, index.with_cfg_attr(idx)));
|
.filter_map(|(idx, attr)| Attr::from_tt(db, attr, index.with_cfg_attr(idx)));
|
||||||
|
|
||||||
let cfg_options = &crate_graph[krate].cfg_options;
|
let cfg_options = &crate_graph[krate].cfg_options;
|
||||||
let cfg = Subtree { delimiter: subtree.delimiter, token_trees: cfg.to_vec() };
|
let cfg = Subtree { delimiter: subtree.delimiter, token_trees: Box::from(cfg) };
|
||||||
let cfg = CfgExpr::parse(&cfg);
|
let cfg = CfgExpr::parse(&cfg);
|
||||||
if cfg_options.check(&cfg) == Some(false) {
|
if cfg_options.check(&cfg) == Some(false) {
|
||||||
smallvec![]
|
smallvec![]
|
||||||
|
|
|
@ -137,5 +137,8 @@ pub fn pseudo_derive_attr_expansion(
|
||||||
token_trees.extend(tt.iter().cloned());
|
token_trees.extend(tt.iter().cloned());
|
||||||
token_trees.push(mk_leaf(']'));
|
token_trees.push(mk_leaf(']'));
|
||||||
}
|
}
|
||||||
ExpandResult::ok(tt::Subtree { delimiter: tt.delimiter, token_trees })
|
ExpandResult::ok(tt::Subtree {
|
||||||
|
delimiter: tt.delimiter,
|
||||||
|
token_trees: token_trees.into_boxed_slice(),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -194,6 +194,7 @@ struct BasicAdtInfo {
|
||||||
/// second field is `Some(ty)` if it's a const param of type `ty`, `None` if it's a type param.
|
/// second field is `Some(ty)` if it's a const param of type `ty`, `None` if it's a type param.
|
||||||
/// third fields is where bounds, if any
|
/// third fields is where bounds, if any
|
||||||
param_types: Vec<(tt::Subtree, Option<tt::Subtree>, Option<tt::Subtree>)>,
|
param_types: Vec<(tt::Subtree, Option<tt::Subtree>, Option<tt::Subtree>)>,
|
||||||
|
where_clause: Vec<tt::Subtree>,
|
||||||
associated_types: Vec<tt::Subtree>,
|
associated_types: Vec<tt::Subtree>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -202,10 +203,11 @@ fn parse_adt(
|
||||||
adt: &ast::Adt,
|
adt: &ast::Adt,
|
||||||
call_site: Span,
|
call_site: Span,
|
||||||
) -> Result<BasicAdtInfo, ExpandError> {
|
) -> Result<BasicAdtInfo, ExpandError> {
|
||||||
let (name, generic_param_list, shape) = match adt {
|
let (name, generic_param_list, where_clause, shape) = match adt {
|
||||||
ast::Adt::Struct(it) => (
|
ast::Adt::Struct(it) => (
|
||||||
it.name(),
|
it.name(),
|
||||||
it.generic_param_list(),
|
it.generic_param_list(),
|
||||||
|
it.where_clause(),
|
||||||
AdtShape::Struct(VariantShape::from(tm, it.field_list())?),
|
AdtShape::Struct(VariantShape::from(tm, it.field_list())?),
|
||||||
),
|
),
|
||||||
ast::Adt::Enum(it) => {
|
ast::Adt::Enum(it) => {
|
||||||
|
@ -217,6 +219,7 @@ fn parse_adt(
|
||||||
(
|
(
|
||||||
it.name(),
|
it.name(),
|
||||||
it.generic_param_list(),
|
it.generic_param_list(),
|
||||||
|
it.where_clause(),
|
||||||
AdtShape::Enum {
|
AdtShape::Enum {
|
||||||
default_variant,
|
default_variant,
|
||||||
variants: it
|
variants: it
|
||||||
|
@ -233,7 +236,9 @@ fn parse_adt(
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
ast::Adt::Union(it) => (it.name(), it.generic_param_list(), AdtShape::Union),
|
ast::Adt::Union(it) => {
|
||||||
|
(it.name(), it.generic_param_list(), it.where_clause(), AdtShape::Union)
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut param_type_set: FxHashSet<Name> = FxHashSet::default();
|
let mut param_type_set: FxHashSet<Name> = FxHashSet::default();
|
||||||
|
@ -274,6 +279,14 @@ fn parse_adt(
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
let where_clause = if let Some(w) = where_clause {
|
||||||
|
w.predicates()
|
||||||
|
.map(|it| mbe::syntax_node_to_token_tree(it.syntax(), tm, call_site))
|
||||||
|
.collect()
|
||||||
|
} else {
|
||||||
|
vec![]
|
||||||
|
};
|
||||||
|
|
||||||
// For a generic parameter `T`, when shorthand associated type `T::Assoc` appears in field
|
// For a generic parameter `T`, when shorthand associated type `T::Assoc` appears in field
|
||||||
// types (of any variant for enums), we generate trait bound for it. It sounds reasonable to
|
// types (of any variant for enums), we generate trait bound for it. It sounds reasonable to
|
||||||
// also generate trait bound for qualified associated type `<T as Trait>::Assoc`, but rustc
|
// also generate trait bound for qualified associated type `<T as Trait>::Assoc`, but rustc
|
||||||
|
@ -301,7 +314,7 @@ fn parse_adt(
|
||||||
.map(|it| mbe::syntax_node_to_token_tree(it.syntax(), tm, call_site))
|
.map(|it| mbe::syntax_node_to_token_tree(it.syntax(), tm, call_site))
|
||||||
.collect();
|
.collect();
|
||||||
let name_token = name_to_token(tm, name)?;
|
let name_token = name_to_token(tm, name)?;
|
||||||
Ok(BasicAdtInfo { name: name_token, shape, param_types, associated_types })
|
Ok(BasicAdtInfo { name: name_token, shape, param_types, where_clause, associated_types })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn name_to_token(
|
fn name_to_token(
|
||||||
|
@ -366,7 +379,8 @@ fn expand_simple_derive(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let trait_body = make_trait_body(&info);
|
let trait_body = make_trait_body(&info);
|
||||||
let mut where_block = vec![];
|
let mut where_block: Vec<_> =
|
||||||
|
info.where_clause.into_iter().map(|w| quote! {invoc_span => #w , }).collect();
|
||||||
let (params, args): (Vec<_>, Vec<_>) = info
|
let (params, args): (Vec<_>, Vec<_>) = info
|
||||||
.param_types
|
.param_types
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
|
|
@ -155,10 +155,10 @@ fn line_expand(
|
||||||
// not incremental
|
// not incremental
|
||||||
ExpandResult::ok(tt::Subtree {
|
ExpandResult::ok(tt::Subtree {
|
||||||
delimiter: tt::Delimiter::invisible_spanned(span),
|
delimiter: tt::Delimiter::invisible_spanned(span),
|
||||||
token_trees: vec![tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal {
|
token_trees: Box::new([tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal {
|
||||||
text: "0u32".into(),
|
text: "0u32".into(),
|
||||||
span,
|
span,
|
||||||
}))],
|
}))]),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -208,11 +208,11 @@ fn assert_expand(
|
||||||
[cond, panic_args @ ..] => {
|
[cond, panic_args @ ..] => {
|
||||||
let comma = tt::Subtree {
|
let comma = tt::Subtree {
|
||||||
delimiter: tt::Delimiter::invisible_spanned(call_site_span),
|
delimiter: tt::Delimiter::invisible_spanned(call_site_span),
|
||||||
token_trees: vec![tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct {
|
token_trees: Box::new([tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct {
|
||||||
char: ',',
|
char: ',',
|
||||||
spacing: tt::Spacing::Alone,
|
spacing: tt::Spacing::Alone,
|
||||||
span: call_site_span,
|
span: call_site_span,
|
||||||
}))],
|
}))]),
|
||||||
};
|
};
|
||||||
let cond = cond.clone();
|
let cond = cond.clone();
|
||||||
let panic_args = itertools::Itertools::intersperse(panic_args.iter().cloned(), comma);
|
let panic_args = itertools::Itertools::intersperse(panic_args.iter().cloned(), comma);
|
||||||
|
@ -359,7 +359,10 @@ fn panic_expand(
|
||||||
close: call_site_span,
|
close: call_site_span,
|
||||||
kind: tt::DelimiterKind::Parenthesis,
|
kind: tt::DelimiterKind::Parenthesis,
|
||||||
};
|
};
|
||||||
call.token_trees.push(tt::TokenTree::Subtree(subtree));
|
|
||||||
|
// FIXME(slow): quote! have a way to expand to builder to make this a vec!
|
||||||
|
call.push(tt::TokenTree::Subtree(subtree));
|
||||||
|
|
||||||
ExpandResult::ok(call)
|
ExpandResult::ok(call)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -388,7 +391,10 @@ fn unreachable_expand(
|
||||||
close: call_site_span,
|
close: call_site_span,
|
||||||
kind: tt::DelimiterKind::Parenthesis,
|
kind: tt::DelimiterKind::Parenthesis,
|
||||||
};
|
};
|
||||||
call.token_trees.push(tt::TokenTree::Subtree(subtree));
|
|
||||||
|
// FIXME(slow): quote! have a way to expand to builder to make this a vec!
|
||||||
|
call.push(tt::TokenTree::Subtree(subtree));
|
||||||
|
|
||||||
ExpandResult::ok(call)
|
ExpandResult::ok(call)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -509,7 +515,7 @@ fn concat_bytes_expand(
|
||||||
tt::TokenTree::Leaf(tt::Leaf::Literal(lit)) => {
|
tt::TokenTree::Leaf(tt::Leaf::Literal(lit)) => {
|
||||||
let token = ast::make::tokens::literal(&lit.to_string());
|
let token = ast::make::tokens::literal(&lit.to_string());
|
||||||
match token.kind() {
|
match token.kind() {
|
||||||
syntax::SyntaxKind::BYTE => bytes.push(token.text().to_string()),
|
syntax::SyntaxKind::BYTE => bytes.push(token.text().to_owned()),
|
||||||
syntax::SyntaxKind::BYTE_STRING => {
|
syntax::SyntaxKind::BYTE_STRING => {
|
||||||
let components = unquote_byte_string(lit).unwrap_or_default();
|
let components = unquote_byte_string(lit).unwrap_or_default();
|
||||||
components.into_iter().for_each(|it| bytes.push(it.to_string()));
|
components.into_iter().for_each(|it| bytes.push(it.to_string()));
|
||||||
|
@ -564,7 +570,7 @@ fn concat_bytes_expand_subtree(
|
||||||
let lit = ast::make::tokens::literal(&lit.to_string());
|
let lit = ast::make::tokens::literal(&lit.to_string());
|
||||||
match lit.kind() {
|
match lit.kind() {
|
||||||
syntax::SyntaxKind::BYTE | syntax::SyntaxKind::INT_NUMBER => {
|
syntax::SyntaxKind::BYTE | syntax::SyntaxKind::INT_NUMBER => {
|
||||||
bytes.push(lit.text().to_string())
|
bytes.push(lit.text().to_owned())
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
return Err(mbe::ExpandError::UnexpectedToken.into());
|
return Err(mbe::ExpandError::UnexpectedToken.into());
|
||||||
|
@ -675,10 +681,10 @@ fn include_bytes_expand(
|
||||||
// FIXME: actually read the file here if the user asked for macro expansion
|
// FIXME: actually read the file here if the user asked for macro expansion
|
||||||
let res = tt::Subtree {
|
let res = tt::Subtree {
|
||||||
delimiter: tt::Delimiter::invisible_spanned(span),
|
delimiter: tt::Delimiter::invisible_spanned(span),
|
||||||
token_trees: vec![tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal {
|
token_trees: Box::new([tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal {
|
||||||
text: r#"b"""#.into(),
|
text: r#"b"""#.into(),
|
||||||
span,
|
span,
|
||||||
}))],
|
}))]),
|
||||||
};
|
};
|
||||||
ExpandResult::ok(res)
|
ExpandResult::ok(res)
|
||||||
}
|
}
|
||||||
|
@ -743,7 +749,7 @@ fn env_expand(
|
||||||
// We cannot use an empty string here, because for
|
// We cannot use an empty string here, because for
|
||||||
// `include!(concat!(env!("OUT_DIR"), "/foo.rs"))` will become
|
// `include!(concat!(env!("OUT_DIR"), "/foo.rs"))` will become
|
||||||
// `include!("foo.rs"), which might go to infinite loop
|
// `include!("foo.rs"), which might go to infinite loop
|
||||||
"UNRESOLVED_ENV_VAR".to_string()
|
"UNRESOLVED_ENV_VAR".to_owned()
|
||||||
});
|
});
|
||||||
let expanded = quote! {span => #s };
|
let expanded = quote! {span => #s };
|
||||||
|
|
||||||
|
|
|
@ -61,7 +61,7 @@ pub trait ExpandDatabase: SourceDatabase {
|
||||||
#[salsa::input]
|
#[salsa::input]
|
||||||
fn proc_macros(&self) -> Arc<ProcMacros>;
|
fn proc_macros(&self) -> Arc<ProcMacros>;
|
||||||
|
|
||||||
#[salsa::invoke(AstIdMap::ast_id_map)]
|
#[salsa::invoke(AstIdMap::new)]
|
||||||
fn ast_id_map(&self, file_id: HirFileId) -> Arc<AstIdMap>;
|
fn ast_id_map(&self, file_id: HirFileId) -> Arc<AstIdMap>;
|
||||||
|
|
||||||
/// Main public API -- parses a hir file, not caring whether it's a real
|
/// Main public API -- parses a hir file, not caring whether it's a real
|
||||||
|
@ -524,7 +524,7 @@ fn macro_expand(
|
||||||
return ExpandResult {
|
return ExpandResult {
|
||||||
value: CowArc::Owned(tt::Subtree {
|
value: CowArc::Owned(tt::Subtree {
|
||||||
delimiter: tt::Delimiter::invisible_spanned(loc.call_site),
|
delimiter: tt::Delimiter::invisible_spanned(loc.call_site),
|
||||||
token_trees: Vec::new(),
|
token_trees: Box::new([]),
|
||||||
}),
|
}),
|
||||||
// FIXME: We should make sure to enforce an invariant that invalid macro
|
// FIXME: We should make sure to enforce an invariant that invalid macro
|
||||||
// calls do not reach this call path!
|
// calls do not reach this call path!
|
||||||
|
@ -586,7 +586,7 @@ fn macro_expand(
|
||||||
return value.map(|()| {
|
return value.map(|()| {
|
||||||
CowArc::Owned(tt::Subtree {
|
CowArc::Owned(tt::Subtree {
|
||||||
delimiter: tt::Delimiter::invisible_spanned(loc.call_site),
|
delimiter: tt::Delimiter::invisible_spanned(loc.call_site),
|
||||||
token_trees: vec![],
|
token_trees: Box::new([]),
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -601,7 +601,7 @@ fn expand_proc_macro(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandResult<A
|
||||||
return ExpandResult {
|
return ExpandResult {
|
||||||
value: Arc::new(tt::Subtree {
|
value: Arc::new(tt::Subtree {
|
||||||
delimiter: tt::Delimiter::invisible_spanned(loc.call_site),
|
delimiter: tt::Delimiter::invisible_spanned(loc.call_site),
|
||||||
token_trees: Vec::new(),
|
token_trees: Box::new([]),
|
||||||
}),
|
}),
|
||||||
// FIXME: We should make sure to enforce an invariant that invalid macro
|
// FIXME: We should make sure to enforce an invariant that invalid macro
|
||||||
// calls do not reach this call path!
|
// calls do not reach this call path!
|
||||||
|
@ -635,7 +635,7 @@ fn expand_proc_macro(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandResult<A
|
||||||
return value.map(|()| {
|
return value.map(|()| {
|
||||||
Arc::new(tt::Subtree {
|
Arc::new(tt::Subtree {
|
||||||
delimiter: tt::Delimiter::invisible_spanned(loc.call_site),
|
delimiter: tt::Delimiter::invisible_spanned(loc.call_site),
|
||||||
token_trees: vec![],
|
token_trees: Box::new([]),
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -312,7 +312,7 @@ pub(crate) fn reverse_fixups(tt: &mut Subtree, undo_info: &SyntaxFixupUndoInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reverse_fixups_(tt: &mut Subtree, undo_info: &[Subtree]) {
|
fn reverse_fixups_(tt: &mut Subtree, undo_info: &[Subtree]) {
|
||||||
let tts = std::mem::take(&mut tt.token_trees);
|
let tts = std::mem::take(&mut tt.token_trees).into_vec();
|
||||||
tt.token_trees = tts
|
tt.token_trees = tts
|
||||||
.into_iter()
|
.into_iter()
|
||||||
// delete all fake nodes
|
// delete all fake nodes
|
||||||
|
@ -343,7 +343,7 @@ fn reverse_fixups_(tt: &mut Subtree, undo_info: &[Subtree]) {
|
||||||
// we have a fake node here, we need to replace it again with the original
|
// we have a fake node here, we need to replace it again with the original
|
||||||
let original = undo_info[u32::from(leaf.span().range.start()) as usize].clone();
|
let original = undo_info[u32::from(leaf.span().range.start()) as usize].clone();
|
||||||
if original.delimiter.kind == tt::DelimiterKind::Invisible {
|
if original.delimiter.kind == tt::DelimiterKind::Invisible {
|
||||||
original.token_trees.into()
|
SmallVec::from(original.token_trees.into_vec())
|
||||||
} else {
|
} else {
|
||||||
SmallVec::from_const([original.into()])
|
SmallVec::from_const([original.into()])
|
||||||
}
|
}
|
||||||
|
@ -383,7 +383,7 @@ mod tests {
|
||||||
fn check_subtree_eq(a: &tt::Subtree, b: &tt::Subtree) -> bool {
|
fn check_subtree_eq(a: &tt::Subtree, b: &tt::Subtree) -> bool {
|
||||||
a.delimiter.kind == b.delimiter.kind
|
a.delimiter.kind == b.delimiter.kind
|
||||||
&& a.token_trees.len() == b.token_trees.len()
|
&& a.token_trees.len() == b.token_trees.len()
|
||||||
&& a.token_trees.iter().zip(&b.token_trees).all(|(a, b)| check_tt_eq(a, b))
|
&& a.token_trees.iter().zip(b.token_trees.iter()).all(|(a, b)| check_tt_eq(a, b))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_tt_eq(a: &tt::TokenTree, b: &tt::TokenTree) -> bool {
|
fn check_tt_eq(a: &tt::TokenTree, b: &tt::TokenTree) -> bool {
|
||||||
|
|
|
@ -7,9 +7,10 @@
|
||||||
|
|
||||||
use std::iter;
|
use std::iter;
|
||||||
|
|
||||||
|
use base_db::salsa::{self, InternValue};
|
||||||
use span::{MacroCallId, Span, SyntaxContextId};
|
use span::{MacroCallId, Span, SyntaxContextId};
|
||||||
|
|
||||||
use crate::db::ExpandDatabase;
|
use crate::db::{ExpandDatabase, InternSyntaxContextQuery};
|
||||||
|
|
||||||
#[derive(Copy, Clone, Hash, PartialEq, Eq)]
|
#[derive(Copy, Clone, Hash, PartialEq, Eq)]
|
||||||
pub struct SyntaxContextData {
|
pub struct SyntaxContextData {
|
||||||
|
@ -22,6 +23,14 @@ pub struct SyntaxContextData {
|
||||||
pub opaque_and_semitransparent: SyntaxContextId,
|
pub opaque_and_semitransparent: SyntaxContextId,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl InternValue for SyntaxContextData {
|
||||||
|
type Key = (SyntaxContextId, Option<MacroCallId>, Transparency);
|
||||||
|
|
||||||
|
fn into_key(&self) -> Self::Key {
|
||||||
|
(self.parent, self.outer_expn, self.outer_transparency)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl std::fmt::Debug for SyntaxContextData {
|
impl std::fmt::Debug for SyntaxContextData {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
f.debug_struct("SyntaxContextData")
|
f.debug_struct("SyntaxContextData")
|
||||||
|
@ -149,38 +158,36 @@ fn apply_mark_internal(
|
||||||
transparency: Transparency,
|
transparency: Transparency,
|
||||||
) -> SyntaxContextId {
|
) -> SyntaxContextId {
|
||||||
let syntax_context_data = db.lookup_intern_syntax_context(ctxt);
|
let syntax_context_data = db.lookup_intern_syntax_context(ctxt);
|
||||||
let mut opaque = handle_self_ref(ctxt, syntax_context_data.opaque);
|
let mut opaque = syntax_context_data.opaque;
|
||||||
let mut opaque_and_semitransparent =
|
let mut opaque_and_semitransparent = syntax_context_data.opaque_and_semitransparent;
|
||||||
handle_self_ref(ctxt, syntax_context_data.opaque_and_semitransparent);
|
|
||||||
|
|
||||||
if transparency >= Transparency::Opaque {
|
if transparency >= Transparency::Opaque {
|
||||||
let parent = opaque;
|
let parent = opaque;
|
||||||
// Unlike rustc, with salsa we can't prefetch the to be allocated ID to create cycles with
|
opaque = salsa::plumbing::get_query_table::<InternSyntaxContextQuery>(db).get_or_insert(
|
||||||
// salsa when interning, so we use a sentinel value that effectively means the current
|
(parent, call_id, transparency),
|
||||||
// syntax context.
|
|new_opaque| SyntaxContextData {
|
||||||
let new_opaque = SyntaxContextId::SELF_REF;
|
|
||||||
opaque = db.intern_syntax_context(SyntaxContextData {
|
|
||||||
outer_expn: call_id,
|
outer_expn: call_id,
|
||||||
outer_transparency: transparency,
|
outer_transparency: transparency,
|
||||||
parent,
|
parent,
|
||||||
opaque: new_opaque,
|
opaque: new_opaque,
|
||||||
opaque_and_semitransparent: new_opaque,
|
opaque_and_semitransparent: new_opaque,
|
||||||
});
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if transparency >= Transparency::SemiTransparent {
|
if transparency >= Transparency::SemiTransparent {
|
||||||
let parent = opaque_and_semitransparent;
|
let parent = opaque_and_semitransparent;
|
||||||
// Unlike rustc, with salsa we can't prefetch the to be allocated ID to create cycles with
|
opaque_and_semitransparent =
|
||||||
// salsa when interning, so we use a sentinel value that effectively means the current
|
salsa::plumbing::get_query_table::<InternSyntaxContextQuery>(db).get_or_insert(
|
||||||
// syntax context.
|
(parent, call_id, transparency),
|
||||||
let new_opaque_and_semitransparent = SyntaxContextId::SELF_REF;
|
|new_opaque_and_semitransparent| SyntaxContextData {
|
||||||
opaque_and_semitransparent = db.intern_syntax_context(SyntaxContextData {
|
|
||||||
outer_expn: call_id,
|
outer_expn: call_id,
|
||||||
outer_transparency: transparency,
|
outer_transparency: transparency,
|
||||||
parent,
|
parent,
|
||||||
opaque,
|
opaque,
|
||||||
opaque_and_semitransparent: new_opaque_and_semitransparent,
|
opaque_and_semitransparent: new_opaque_and_semitransparent,
|
||||||
});
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let parent = ctxt;
|
let parent = ctxt;
|
||||||
|
@ -201,20 +208,12 @@ pub trait SyntaxContextExt {
|
||||||
fn marks(self, db: &dyn ExpandDatabase) -> Vec<(Option<MacroCallId>, Transparency)>;
|
fn marks(self, db: &dyn ExpandDatabase) -> Vec<(Option<MacroCallId>, Transparency)>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn handle_self_ref(p: SyntaxContextId, n: SyntaxContextId) -> SyntaxContextId {
|
|
||||||
match n {
|
|
||||||
SyntaxContextId::SELF_REF => p,
|
|
||||||
_ => n,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SyntaxContextExt for SyntaxContextId {
|
impl SyntaxContextExt for SyntaxContextId {
|
||||||
fn normalize_to_macro_rules(self, db: &dyn ExpandDatabase) -> Self {
|
fn normalize_to_macro_rules(self, db: &dyn ExpandDatabase) -> Self {
|
||||||
handle_self_ref(self, db.lookup_intern_syntax_context(self).opaque_and_semitransparent)
|
db.lookup_intern_syntax_context(self).opaque_and_semitransparent
|
||||||
}
|
}
|
||||||
fn normalize_to_macros_2_0(self, db: &dyn ExpandDatabase) -> Self {
|
fn normalize_to_macros_2_0(self, db: &dyn ExpandDatabase) -> Self {
|
||||||
handle_self_ref(self, db.lookup_intern_syntax_context(self).opaque)
|
db.lookup_intern_syntax_context(self).opaque
|
||||||
}
|
}
|
||||||
fn parent_ctxt(self, db: &dyn ExpandDatabase) -> Self {
|
fn parent_ctxt(self, db: &dyn ExpandDatabase) -> Self {
|
||||||
db.lookup_intern_syntax_context(self).parent
|
db.lookup_intern_syntax_context(self).parent
|
||||||
|
|
|
@ -30,7 +30,7 @@ use triomphe::Arc;
|
||||||
|
|
||||||
use std::{fmt, hash::Hash};
|
use std::{fmt, hash::Hash};
|
||||||
|
|
||||||
use base_db::{CrateId, Edition, FileId};
|
use base_db::{salsa::impl_intern_value_trivial, CrateId, Edition, FileId};
|
||||||
use either::Either;
|
use either::Either;
|
||||||
use span::{FileRange, HirFileIdRepr, Span, SyntaxContextId};
|
use span::{FileRange, HirFileIdRepr, Span, SyntaxContextId};
|
||||||
use syntax::{
|
use syntax::{
|
||||||
|
@ -66,6 +66,7 @@ pub mod tt {
|
||||||
pub type Delimiter = ::tt::Delimiter<Span>;
|
pub type Delimiter = ::tt::Delimiter<Span>;
|
||||||
pub type DelimSpan = ::tt::DelimSpan<Span>;
|
pub type DelimSpan = ::tt::DelimSpan<Span>;
|
||||||
pub type Subtree = ::tt::Subtree<Span>;
|
pub type Subtree = ::tt::Subtree<Span>;
|
||||||
|
pub type SubtreeBuilder = ::tt::SubtreeBuilder<Span>;
|
||||||
pub type Leaf = ::tt::Leaf<Span>;
|
pub type Leaf = ::tt::Leaf<Span>;
|
||||||
pub type Literal = ::tt::Literal<Span>;
|
pub type Literal = ::tt::Literal<Span>;
|
||||||
pub type Punct = ::tt::Punct<Span>;
|
pub type Punct = ::tt::Punct<Span>;
|
||||||
|
@ -175,6 +176,7 @@ pub struct MacroCallLoc {
|
||||||
pub kind: MacroCallKind,
|
pub kind: MacroCallKind,
|
||||||
pub call_site: Span,
|
pub call_site: Span,
|
||||||
}
|
}
|
||||||
|
impl_intern_value_trivial!(MacroCallLoc);
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
pub struct MacroDefId {
|
pub struct MacroDefId {
|
||||||
|
@ -760,7 +762,7 @@ impl ExpansionInfo {
|
||||||
(
|
(
|
||||||
Arc::new(tt::Subtree {
|
Arc::new(tt::Subtree {
|
||||||
delimiter: tt::Delimiter::invisible_spanned(loc.call_site),
|
delimiter: tt::Delimiter::invisible_spanned(loc.call_site),
|
||||||
token_trees: Vec::new(),
|
token_trees: Box::new([]),
|
||||||
}),
|
}),
|
||||||
SyntaxFixupUndoInfo::NONE,
|
SyntaxFixupUndoInfo::NONE,
|
||||||
)
|
)
|
||||||
|
|
|
@ -31,7 +31,7 @@ macro_rules! __quote {
|
||||||
open: $span,
|
open: $span,
|
||||||
close: $span,
|
close: $span,
|
||||||
},
|
},
|
||||||
token_trees: $crate::quote::IntoTt::to_tokens(children),
|
token_trees: $crate::quote::IntoTt::to_tokens(children).into_boxed_slice(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -146,7 +146,7 @@ impl IntoTt for Vec<crate::tt::TokenTree> {
|
||||||
fn to_subtree(self, span: Span) -> crate::tt::Subtree {
|
fn to_subtree(self, span: Span) -> crate::tt::Subtree {
|
||||||
crate::tt::Subtree {
|
crate::tt::Subtree {
|
||||||
delimiter: crate::tt::Delimiter::invisible_spanned(span),
|
delimiter: crate::tt::Delimiter::invisible_spanned(span),
|
||||||
token_trees: self,
|
token_trees: self.into_boxed_slice(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -296,8 +296,9 @@ mod tests {
|
||||||
// }
|
// }
|
||||||
let struct_name = mk_ident("Foo");
|
let struct_name = mk_ident("Foo");
|
||||||
let fields = [mk_ident("name"), mk_ident("id")];
|
let fields = [mk_ident("name"), mk_ident("id")];
|
||||||
let fields =
|
let fields = fields
|
||||||
fields.iter().flat_map(|it| quote!(DUMMY =>#it: self.#it.clone(), ).token_trees);
|
.iter()
|
||||||
|
.flat_map(|it| quote!(DUMMY =>#it: self.#it.clone(), ).token_trees.into_vec());
|
||||||
|
|
||||||
let list = crate::tt::Subtree {
|
let list = crate::tt::Subtree {
|
||||||
delimiter: crate::tt::Delimiter {
|
delimiter: crate::tt::Delimiter {
|
||||||
|
|
|
@ -88,6 +88,7 @@ impl<'a, 'db> Autoderef<'a, 'db> {
|
||||||
impl Iterator for Autoderef<'_, '_> {
|
impl Iterator for Autoderef<'_, '_> {
|
||||||
type Item = (Ty, usize);
|
type Item = (Ty, usize);
|
||||||
|
|
||||||
|
#[tracing::instrument(skip_all)]
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
if self.at_start {
|
if self.at_start {
|
||||||
self.at_start = false;
|
self.at_start = false;
|
||||||
|
|
|
@ -125,6 +125,7 @@ impl<D> TyBuilder<D> {
|
||||||
this
|
this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(skip_all)]
|
||||||
pub(crate) fn fill_with_inference_vars(self, table: &mut InferenceTable<'_>) -> Self {
|
pub(crate) fn fill_with_inference_vars(self, table: &mut InferenceTable<'_>) -> Self {
|
||||||
self.fill(|x| match x {
|
self.fill(|x| match x {
|
||||||
ParamKind::Type => table.new_type_var().cast(Interner),
|
ParamKind::Type => table.new_type_var().cast(Interner),
|
||||||
|
@ -208,6 +209,7 @@ impl TyBuilder<()> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(skip_all)]
|
||||||
pub fn subst_for_def(
|
pub fn subst_for_def(
|
||||||
db: &dyn HirDatabase,
|
db: &dyn HirDatabase,
|
||||||
def: impl Into<GenericDefId>,
|
def: impl Into<GenericDefId>,
|
||||||
|
|
|
@ -17,7 +17,7 @@ use hir_def::{
|
||||||
use hir_expand::name::name;
|
use hir_expand::name::name;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
db::HirDatabase,
|
db::{HirDatabase, InternedCoroutine},
|
||||||
display::HirDisplay,
|
display::HirDisplay,
|
||||||
from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id, make_binders,
|
from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id, make_binders,
|
||||||
make_single_type_binders,
|
make_single_type_binders,
|
||||||
|
@ -428,7 +428,7 @@ impl chalk_solve::RustIrDatabase<Interner> for ChalkContext<'_> {
|
||||||
&self,
|
&self,
|
||||||
id: chalk_ir::CoroutineId<Interner>,
|
id: chalk_ir::CoroutineId<Interner>,
|
||||||
) -> Arc<chalk_solve::rust_ir::CoroutineDatum<Interner>> {
|
) -> Arc<chalk_solve::rust_ir::CoroutineDatum<Interner>> {
|
||||||
let (parent, expr) = self.db.lookup_intern_coroutine(id.into());
|
let InternedCoroutine(parent, expr) = self.db.lookup_intern_coroutine(id.into());
|
||||||
|
|
||||||
// We fill substitution with unknown type, because we only need to know whether the generic
|
// We fill substitution with unknown type, because we only need to know whether the generic
|
||||||
// params are types or consts to build `Binders` and those being filled up are for
|
// params are types or consts to build `Binders` and those being filled up are for
|
||||||
|
@ -473,7 +473,7 @@ impl chalk_solve::RustIrDatabase<Interner> for ChalkContext<'_> {
|
||||||
let inner_types =
|
let inner_types =
|
||||||
rust_ir::CoroutineWitnessExistential { types: wrap_empty_binders(vec![]) };
|
rust_ir::CoroutineWitnessExistential { types: wrap_empty_binders(vec![]) };
|
||||||
|
|
||||||
let (parent, _) = self.db.lookup_intern_coroutine(id.into());
|
let InternedCoroutine(parent, _) = self.db.lookup_intern_coroutine(id.into());
|
||||||
// See the comment in `coroutine_datum()` for unknown types.
|
// See the comment in `coroutine_datum()` for unknown types.
|
||||||
let subst = TyBuilder::subst_for_coroutine(self.db, parent).fill_with_unknown().build();
|
let subst = TyBuilder::subst_for_coroutine(self.db, parent).fill_with_unknown().build();
|
||||||
let it = subst
|
let it = subst
|
||||||
|
|
|
@ -133,7 +133,7 @@ fn bit_op() {
|
||||||
check_number(r#"const GOAL: i8 = 1 << 7"#, (1i8 << 7) as i128);
|
check_number(r#"const GOAL: i8 = 1 << 7"#, (1i8 << 7) as i128);
|
||||||
check_number(r#"const GOAL: i8 = -1 << 2"#, (-1i8 << 2) as i128);
|
check_number(r#"const GOAL: i8 = -1 << 2"#, (-1i8 << 2) as i128);
|
||||||
check_fail(r#"const GOAL: i8 = 1 << 8"#, |e| {
|
check_fail(r#"const GOAL: i8 = 1 << 8"#, |e| {
|
||||||
e == ConstEvalError::MirEvalError(MirEvalError::Panic("Overflow in Shl".to_string()))
|
e == ConstEvalError::MirEvalError(MirEvalError::Panic("Overflow in Shl".to_owned()))
|
||||||
});
|
});
|
||||||
check_number(r#"const GOAL: i32 = 100000000i32 << 11"#, (100000000i32 << 11) as i128);
|
check_number(r#"const GOAL: i32 = 100000000i32 << 11"#, (100000000i32 << 11) as i128);
|
||||||
}
|
}
|
||||||
|
@ -2756,7 +2756,7 @@ fn memory_limit() {
|
||||||
"#,
|
"#,
|
||||||
|e| {
|
|e| {
|
||||||
e == ConstEvalError::MirEvalError(MirEvalError::Panic(
|
e == ConstEvalError::MirEvalError(MirEvalError::Panic(
|
||||||
"Memory allocation of 30000000000 bytes failed".to_string(),
|
"Memory allocation of 30000000000 bytes failed".to_owned(),
|
||||||
))
|
))
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
|
@ -3,7 +3,11 @@
|
||||||
|
|
||||||
use std::sync;
|
use std::sync;
|
||||||
|
|
||||||
use base_db::{impl_intern_key, salsa, CrateId, Upcast};
|
use base_db::{
|
||||||
|
impl_intern_key,
|
||||||
|
salsa::{self, impl_intern_value_trivial},
|
||||||
|
CrateId, Upcast,
|
||||||
|
};
|
||||||
use hir_def::{
|
use hir_def::{
|
||||||
db::DefDatabase, hir::ExprId, layout::TargetDataLayout, AdtId, BlockId, ConstParamId,
|
db::DefDatabase, hir::ExprId, layout::TargetDataLayout, AdtId, BlockId, ConstParamId,
|
||||||
DefWithBodyId, EnumVariantId, FunctionId, GeneralConstId, GenericDefId, ImplId,
|
DefWithBodyId, EnumVariantId, FunctionId, GeneralConstId, GenericDefId, ImplId,
|
||||||
|
@ -199,9 +203,9 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
|
||||||
#[salsa::interned]
|
#[salsa::interned]
|
||||||
fn intern_impl_trait_id(&self, id: ImplTraitId) -> InternedOpaqueTyId;
|
fn intern_impl_trait_id(&self, id: ImplTraitId) -> InternedOpaqueTyId;
|
||||||
#[salsa::interned]
|
#[salsa::interned]
|
||||||
fn intern_closure(&self, id: (DefWithBodyId, ExprId)) -> InternedClosureId;
|
fn intern_closure(&self, id: InternedClosure) -> InternedClosureId;
|
||||||
#[salsa::interned]
|
#[salsa::interned]
|
||||||
fn intern_coroutine(&self, id: (DefWithBodyId, ExprId)) -> InternedCoroutineId;
|
fn intern_coroutine(&self, id: InternedCoroutine) -> InternedCoroutineId;
|
||||||
|
|
||||||
#[salsa::invoke(chalk_db::associated_ty_data_query)]
|
#[salsa::invoke(chalk_db::associated_ty_data_query)]
|
||||||
fn associated_ty_data(
|
fn associated_ty_data(
|
||||||
|
@ -337,10 +341,18 @@ impl_intern_key!(InternedOpaqueTyId);
|
||||||
pub struct InternedClosureId(salsa::InternId);
|
pub struct InternedClosureId(salsa::InternId);
|
||||||
impl_intern_key!(InternedClosureId);
|
impl_intern_key!(InternedClosureId);
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
pub struct InternedClosure(pub DefWithBodyId, pub ExprId);
|
||||||
|
impl_intern_value_trivial!(InternedClosure);
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
pub struct InternedCoroutineId(salsa::InternId);
|
pub struct InternedCoroutineId(salsa::InternId);
|
||||||
impl_intern_key!(InternedCoroutineId);
|
impl_intern_key!(InternedCoroutineId);
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
pub struct InternedCoroutine(pub DefWithBodyId, pub ExprId);
|
||||||
|
impl_intern_value_trivial!(InternedCoroutine);
|
||||||
|
|
||||||
/// This exists just for Chalk, because Chalk just has a single `FnDefId` where
|
/// This exists just for Chalk, because Chalk just has a single `FnDefId` where
|
||||||
/// we have different IDs for struct and enum variant constructors.
|
/// we have different IDs for struct and enum variant constructors.
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
|
||||||
|
|
|
@ -16,11 +16,9 @@ mod case_conv;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
use hir_def::{
|
use hir_def::{
|
||||||
data::adt::VariantData,
|
data::adt::VariantData, db::DefDatabase, hir::Pat, src::HasSource, AdtId, AttrDefId, ConstId,
|
||||||
hir::{Pat, PatId},
|
EnumId, FunctionId, ItemContainerId, Lookup, ModuleDefId, ModuleId, StaticId, StructId,
|
||||||
src::HasSource,
|
TraitId, TypeAliasId,
|
||||||
AdtId, AttrDefId, ConstId, EnumId, FunctionId, ItemContainerId, Lookup, ModuleDefId, ModuleId,
|
|
||||||
StaticId, StructId,
|
|
||||||
};
|
};
|
||||||
use hir_expand::{
|
use hir_expand::{
|
||||||
name::{AsName, Name},
|
name::{AsName, Name},
|
||||||
|
@ -79,12 +77,14 @@ pub enum IdentType {
|
||||||
Enum,
|
Enum,
|
||||||
Field,
|
Field,
|
||||||
Function,
|
Function,
|
||||||
|
Module,
|
||||||
Parameter,
|
Parameter,
|
||||||
StaticVariable,
|
StaticVariable,
|
||||||
Structure,
|
Structure,
|
||||||
|
Trait,
|
||||||
|
TypeAlias,
|
||||||
Variable,
|
Variable,
|
||||||
Variant,
|
Variant,
|
||||||
Module,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for IdentType {
|
impl fmt::Display for IdentType {
|
||||||
|
@ -94,12 +94,14 @@ impl fmt::Display for IdentType {
|
||||||
IdentType::Enum => "Enum",
|
IdentType::Enum => "Enum",
|
||||||
IdentType::Field => "Field",
|
IdentType::Field => "Field",
|
||||||
IdentType::Function => "Function",
|
IdentType::Function => "Function",
|
||||||
|
IdentType::Module => "Module",
|
||||||
IdentType::Parameter => "Parameter",
|
IdentType::Parameter => "Parameter",
|
||||||
IdentType::StaticVariable => "Static variable",
|
IdentType::StaticVariable => "Static variable",
|
||||||
IdentType::Structure => "Structure",
|
IdentType::Structure => "Structure",
|
||||||
|
IdentType::Trait => "Trait",
|
||||||
|
IdentType::TypeAlias => "Type alias",
|
||||||
IdentType::Variable => "Variable",
|
IdentType::Variable => "Variable",
|
||||||
IdentType::Variant => "Variant",
|
IdentType::Variant => "Variant",
|
||||||
IdentType::Module => "Module",
|
|
||||||
};
|
};
|
||||||
|
|
||||||
repr.fmt(f)
|
repr.fmt(f)
|
||||||
|
@ -136,10 +138,12 @@ impl<'a> DeclValidator<'a> {
|
||||||
pub(super) fn validate_item(&mut self, item: ModuleDefId) {
|
pub(super) fn validate_item(&mut self, item: ModuleDefId) {
|
||||||
match item {
|
match item {
|
||||||
ModuleDefId::ModuleId(module_id) => self.validate_module(module_id),
|
ModuleDefId::ModuleId(module_id) => self.validate_module(module_id),
|
||||||
|
ModuleDefId::TraitId(trait_id) => self.validate_trait(trait_id),
|
||||||
ModuleDefId::FunctionId(func) => self.validate_func(func),
|
ModuleDefId::FunctionId(func) => self.validate_func(func),
|
||||||
ModuleDefId::AdtId(adt) => self.validate_adt(adt),
|
ModuleDefId::AdtId(adt) => self.validate_adt(adt),
|
||||||
ModuleDefId::ConstId(const_id) => self.validate_const(const_id),
|
ModuleDefId::ConstId(const_id) => self.validate_const(const_id),
|
||||||
ModuleDefId::StaticId(static_id) => self.validate_static(static_id),
|
ModuleDefId::StaticId(static_id) => self.validate_static(static_id),
|
||||||
|
ModuleDefId::TypeAliasId(type_alias_id) => self.validate_type_alias(type_alias_id),
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -242,50 +246,46 @@ impl<'a> DeclValidator<'a> {
|
||||||
|
|
||||||
// Check the module name.
|
// Check the module name.
|
||||||
let Some(module_name) = module_id.name(self.db.upcast()) else { return };
|
let Some(module_name) = module_id.name(self.db.upcast()) else { return };
|
||||||
let module_name_replacement =
|
let Some(module_name_replacement) =
|
||||||
module_name.as_str().and_then(to_lower_snake_case).map(|new_name| Replacement {
|
module_name.as_str().and_then(to_lower_snake_case).map(|new_name| Replacement {
|
||||||
current_name: module_name,
|
current_name: module_name,
|
||||||
suggested_text: new_name,
|
suggested_text: new_name,
|
||||||
expected_case: CaseType::LowerSnakeCase,
|
expected_case: CaseType::LowerSnakeCase,
|
||||||
});
|
})
|
||||||
|
else {
|
||||||
if let Some(module_name_replacement) = module_name_replacement {
|
return;
|
||||||
|
};
|
||||||
let module_data = &module_id.def_map(self.db.upcast())[module_id.local_id];
|
let module_data = &module_id.def_map(self.db.upcast())[module_id.local_id];
|
||||||
let module_src = module_data.declaration_source(self.db.upcast());
|
let Some(module_src) = module_data.declaration_source(self.db.upcast()) else {
|
||||||
|
return;
|
||||||
if let Some(module_src) = module_src {
|
};
|
||||||
let ast_ptr = match module_src.value.name() {
|
self.create_incorrect_case_diagnostic_for_ast_node(
|
||||||
Some(name) => name,
|
|
||||||
None => {
|
|
||||||
never!(
|
|
||||||
"Replacement ({:?}) was generated for a module without a name: {:?}",
|
|
||||||
module_name_replacement,
|
module_name_replacement,
|
||||||
module_src
|
module_src.file_id,
|
||||||
|
&module_src.value,
|
||||||
|
IdentType::Module,
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn validate_trait(&mut self, trait_id: TraitId) {
|
||||||
|
// Check whether non-snake case identifiers are allowed for this trait.
|
||||||
|
if self.allowed(trait_id.into(), allow::NON_CAMEL_CASE_TYPES, false) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
let diagnostic = IncorrectCase {
|
// Check the trait name.
|
||||||
file: module_src.file_id,
|
let data = self.db.trait_data(trait_id);
|
||||||
ident_type: IdentType::Module,
|
self.create_incorrect_case_diagnostic_for_item_name(
|
||||||
ident: AstPtr::new(&ast_ptr),
|
trait_id,
|
||||||
expected_case: module_name_replacement.expected_case,
|
&data.name,
|
||||||
ident_text: module_name_replacement
|
CaseType::UpperCamelCase,
|
||||||
.current_name
|
IdentType::Trait,
|
||||||
.display(self.db.upcast())
|
);
|
||||||
.to_string(),
|
|
||||||
suggested_text: module_name_replacement.suggested_text,
|
|
||||||
};
|
|
||||||
|
|
||||||
self.sink.push(diagnostic);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn validate_func(&mut self, func: FunctionId) {
|
fn validate_func(&mut self, func: FunctionId) {
|
||||||
let data = self.db.function_data(func);
|
let container = func.lookup(self.db.upcast()).container;
|
||||||
if matches!(func.lookup(self.db.upcast()).container, ItemContainerId::ExternBlockId(_)) {
|
if matches!(container, ItemContainerId::ExternBlockId(_)) {
|
||||||
cov_mark::hit!(extern_func_incorrect_case_ignored);
|
cov_mark::hit!(extern_func_incorrect_case_ignored);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -296,270 +296,173 @@ impl<'a> DeclValidator<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check the function name.
|
// Check the function name.
|
||||||
let function_name = data.name.display(self.db.upcast()).to_string();
|
// Skipped if function is an associated item of a trait implementation.
|
||||||
let fn_name_replacement = to_lower_snake_case(&function_name).map(|new_name| Replacement {
|
if !self.is_trait_impl_container(container) {
|
||||||
current_name: data.name.clone(),
|
let data = self.db.function_data(func);
|
||||||
suggested_text: new_name,
|
self.create_incorrect_case_diagnostic_for_item_name(
|
||||||
expected_case: CaseType::LowerSnakeCase,
|
func,
|
||||||
});
|
&data.name,
|
||||||
|
CaseType::LowerSnakeCase,
|
||||||
let body = self.db.body(func.into());
|
IdentType::Function,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
cov_mark::hit!(trait_impl_assoc_func_name_incorrect_case_ignored);
|
||||||
|
}
|
||||||
|
|
||||||
// Check the patterns inside the function body.
|
// Check the patterns inside the function body.
|
||||||
// This includes function parameters.
|
self.validate_func_body(func);
|
||||||
let pats_replacements = body
|
}
|
||||||
|
|
||||||
|
/// Check incorrect names for patterns inside the function body.
|
||||||
|
/// This includes function parameters except for trait implementation associated functions.
|
||||||
|
fn validate_func_body(&mut self, func: FunctionId) {
|
||||||
|
let body = self.db.body(func.into());
|
||||||
|
let mut pats_replacements = body
|
||||||
.pats
|
.pats
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|(pat_id, pat)| match pat {
|
.filter_map(|(pat_id, pat)| match pat {
|
||||||
Pat::Bind { id, .. } => Some((pat_id, &body.bindings[*id].name)),
|
Pat::Bind { id, .. } => {
|
||||||
|
let bind_name = &body.bindings[*id].name;
|
||||||
|
let replacement = Replacement {
|
||||||
|
current_name: bind_name.clone(),
|
||||||
|
suggested_text: to_lower_snake_case(&bind_name.to_smol_str())?,
|
||||||
|
expected_case: CaseType::LowerSnakeCase,
|
||||||
|
};
|
||||||
|
Some((pat_id, replacement))
|
||||||
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
})
|
})
|
||||||
.filter_map(|(id, bind_name)| {
|
.peekable();
|
||||||
Some((
|
|
||||||
id,
|
|
||||||
Replacement {
|
|
||||||
current_name: bind_name.clone(),
|
|
||||||
suggested_text: to_lower_snake_case(
|
|
||||||
&bind_name.display(self.db.upcast()).to_string(),
|
|
||||||
)?,
|
|
||||||
expected_case: CaseType::LowerSnakeCase,
|
|
||||||
},
|
|
||||||
))
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
// If there is at least one element to spawn a warning on, go to the source map and generate a warning.
|
|
||||||
if let Some(fn_name_replacement) = fn_name_replacement {
|
|
||||||
self.create_incorrect_case_diagnostic_for_func(func, fn_name_replacement);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.create_incorrect_case_diagnostic_for_variables(func, pats_replacements);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Given the information about incorrect names in the function declaration, looks up into the source code
|
|
||||||
/// for exact locations and adds diagnostics into the sink.
|
|
||||||
fn create_incorrect_case_diagnostic_for_func(
|
|
||||||
&mut self,
|
|
||||||
func: FunctionId,
|
|
||||||
fn_name_replacement: Replacement,
|
|
||||||
) {
|
|
||||||
let fn_loc = func.lookup(self.db.upcast());
|
|
||||||
let fn_src = fn_loc.source(self.db.upcast());
|
|
||||||
|
|
||||||
// Diagnostic for function name.
|
|
||||||
let ast_ptr = match fn_src.value.name() {
|
|
||||||
Some(name) => name,
|
|
||||||
None => {
|
|
||||||
never!(
|
|
||||||
"Replacement ({:?}) was generated for a function without a name: {:?}",
|
|
||||||
fn_name_replacement,
|
|
||||||
fn_src
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let diagnostic = IncorrectCase {
|
|
||||||
file: fn_src.file_id,
|
|
||||||
ident_type: IdentType::Function,
|
|
||||||
ident: AstPtr::new(&ast_ptr),
|
|
||||||
expected_case: fn_name_replacement.expected_case,
|
|
||||||
ident_text: fn_name_replacement.current_name.display(self.db.upcast()).to_string(),
|
|
||||||
suggested_text: fn_name_replacement.suggested_text,
|
|
||||||
};
|
|
||||||
|
|
||||||
self.sink.push(diagnostic);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Given the information about incorrect variable names, looks up into the source code
|
|
||||||
/// for exact locations and adds diagnostics into the sink.
|
|
||||||
fn create_incorrect_case_diagnostic_for_variables(
|
|
||||||
&mut self,
|
|
||||||
func: FunctionId,
|
|
||||||
pats_replacements: Vec<(PatId, Replacement)>,
|
|
||||||
) {
|
|
||||||
// XXX: only look at source_map if we do have missing fields
|
// XXX: only look at source_map if we do have missing fields
|
||||||
if pats_replacements.is_empty() {
|
if pats_replacements.peek().is_none() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let (_, source_map) = self.db.body_with_source_map(func.into());
|
let (_, source_map) = self.db.body_with_source_map(func.into());
|
||||||
|
|
||||||
for (id, replacement) in pats_replacements {
|
for (id, replacement) in pats_replacements {
|
||||||
if let Ok(source_ptr) = source_map.pat_syntax(id) {
|
let Ok(source_ptr) = source_map.pat_syntax(id) else {
|
||||||
if let Some(ptr) = source_ptr.value.cast::<ast::IdentPat>() {
|
continue;
|
||||||
|
};
|
||||||
|
let Some(ptr) = source_ptr.value.cast::<ast::IdentPat>() else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
let root = source_ptr.file_syntax(self.db.upcast());
|
let root = source_ptr.file_syntax(self.db.upcast());
|
||||||
let ident_pat = ptr.to_node(&root);
|
let ident_pat = ptr.to_node(&root);
|
||||||
let parent = match ident_pat.syntax().parent() {
|
let Some(parent) = ident_pat.syntax().parent() else {
|
||||||
Some(parent) => parent,
|
continue;
|
||||||
None => continue,
|
|
||||||
};
|
|
||||||
let name_ast = match ident_pat.name() {
|
|
||||||
Some(name_ast) => name_ast,
|
|
||||||
None => continue,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let is_param = ast::Param::can_cast(parent.kind());
|
let is_param = ast::Param::can_cast(parent.kind());
|
||||||
|
|
||||||
// We have to check that it's either `let var = ...` or `var @ Variant(_)` statement,
|
// We have to check that it's either `let var = ...` or `var @ Variant(_)` statement,
|
||||||
// because e.g. match arms are patterns as well.
|
// because e.g. match arms are patterns as well.
|
||||||
// In other words, we check that it's a named variable binding.
|
// In other words, we check that it's a named variable binding.
|
||||||
let is_binding = ast::LetStmt::can_cast(parent.kind())
|
let is_binding = ast::LetStmt::can_cast(parent.kind())
|
||||||
|| (ast::MatchArm::can_cast(parent.kind())
|
|| (ast::MatchArm::can_cast(parent.kind()) && ident_pat.at_token().is_some());
|
||||||
&& ident_pat.at_token().is_some());
|
|
||||||
if !(is_param || is_binding) {
|
if !(is_param || is_binding) {
|
||||||
// This pattern is not an actual variable declaration, e.g. `Some(val) => {..}` match arm.
|
// This pattern is not an actual variable declaration, e.g. `Some(val) => {..}` match arm.
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let ident_type =
|
let ident_type = if is_param { IdentType::Parameter } else { IdentType::Variable };
|
||||||
if is_param { IdentType::Parameter } else { IdentType::Variable };
|
|
||||||
|
|
||||||
let diagnostic = IncorrectCase {
|
self.create_incorrect_case_diagnostic_for_ast_node(
|
||||||
file: source_ptr.file_id,
|
replacement,
|
||||||
|
source_ptr.file_id,
|
||||||
|
&ident_pat,
|
||||||
ident_type,
|
ident_type,
|
||||||
ident: AstPtr::new(&name_ast),
|
);
|
||||||
expected_case: replacement.expected_case,
|
|
||||||
ident_text: replacement.current_name.display(self.db.upcast()).to_string(),
|
|
||||||
suggested_text: replacement.suggested_text,
|
|
||||||
};
|
|
||||||
|
|
||||||
self.sink.push(diagnostic);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn validate_struct(&mut self, struct_id: StructId) {
|
fn validate_struct(&mut self, struct_id: StructId) {
|
||||||
let data = self.db.struct_data(struct_id);
|
// Check the structure name.
|
||||||
|
|
||||||
let non_camel_case_allowed =
|
let non_camel_case_allowed =
|
||||||
self.allowed(struct_id.into(), allow::NON_CAMEL_CASE_TYPES, false);
|
self.allowed(struct_id.into(), allow::NON_CAMEL_CASE_TYPES, false);
|
||||||
let non_snake_case_allowed = self.allowed(struct_id.into(), allow::NON_SNAKE_CASE, false);
|
if !non_camel_case_allowed {
|
||||||
|
let data = self.db.struct_data(struct_id);
|
||||||
// Check the structure name.
|
self.create_incorrect_case_diagnostic_for_item_name(
|
||||||
let struct_name = data.name.display(self.db.upcast()).to_string();
|
|
||||||
let struct_name_replacement = if !non_camel_case_allowed {
|
|
||||||
to_camel_case(&struct_name).map(|new_name| Replacement {
|
|
||||||
current_name: data.name.clone(),
|
|
||||||
suggested_text: new_name,
|
|
||||||
expected_case: CaseType::UpperCamelCase,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
// Check the field names.
|
|
||||||
let mut struct_fields_replacements = Vec::new();
|
|
||||||
|
|
||||||
if !non_snake_case_allowed {
|
|
||||||
if let VariantData::Record(fields) = data.variant_data.as_ref() {
|
|
||||||
for (_, field) in fields.iter() {
|
|
||||||
let field_name = field.name.display(self.db.upcast()).to_string();
|
|
||||||
if let Some(new_name) = to_lower_snake_case(&field_name) {
|
|
||||||
let replacement = Replacement {
|
|
||||||
current_name: field.name.clone(),
|
|
||||||
suggested_text: new_name,
|
|
||||||
expected_case: CaseType::LowerSnakeCase,
|
|
||||||
};
|
|
||||||
struct_fields_replacements.push(replacement);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If there is at least one element to spawn a warning on, go to the source map and generate a warning.
|
|
||||||
self.create_incorrect_case_diagnostic_for_struct(
|
|
||||||
struct_id,
|
struct_id,
|
||||||
struct_name_replacement,
|
&data.name,
|
||||||
struct_fields_replacements,
|
CaseType::UpperCamelCase,
|
||||||
|
IdentType::Structure,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Given the information about incorrect names in the struct declaration, looks up into the source code
|
// Check the field names.
|
||||||
/// for exact locations and adds diagnostics into the sink.
|
self.validate_struct_fields(struct_id);
|
||||||
fn create_incorrect_case_diagnostic_for_struct(
|
}
|
||||||
&mut self,
|
|
||||||
struct_id: StructId,
|
/// Check incorrect names for struct fields.
|
||||||
struct_name_replacement: Option<Replacement>,
|
fn validate_struct_fields(&mut self, struct_id: StructId) {
|
||||||
struct_fields_replacements: Vec<Replacement>,
|
if self.allowed(struct_id.into(), allow::NON_SNAKE_CASE, false) {
|
||||||
) {
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let data = self.db.struct_data(struct_id);
|
||||||
|
let VariantData::Record(fields) = data.variant_data.as_ref() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let mut struct_fields_replacements = fields
|
||||||
|
.iter()
|
||||||
|
.filter_map(|(_, field)| {
|
||||||
|
to_lower_snake_case(&field.name.to_smol_str()).map(|new_name| Replacement {
|
||||||
|
current_name: field.name.clone(),
|
||||||
|
suggested_text: new_name,
|
||||||
|
expected_case: CaseType::LowerSnakeCase,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.peekable();
|
||||||
|
|
||||||
// XXX: Only look at sources if we do have incorrect names.
|
// XXX: Only look at sources if we do have incorrect names.
|
||||||
if struct_name_replacement.is_none() && struct_fields_replacements.is_empty() {
|
if struct_fields_replacements.peek().is_none() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let struct_loc = struct_id.lookup(self.db.upcast());
|
let struct_loc = struct_id.lookup(self.db.upcast());
|
||||||
let struct_src = struct_loc.source(self.db.upcast());
|
let struct_src = struct_loc.source(self.db.upcast());
|
||||||
|
|
||||||
if let Some(replacement) = struct_name_replacement {
|
let Some(ast::FieldList::RecordFieldList(struct_fields_list)) =
|
||||||
let ast_ptr = match struct_src.value.name() {
|
struct_src.value.field_list()
|
||||||
Some(name) => name,
|
else {
|
||||||
None => {
|
|
||||||
never!(
|
|
||||||
"Replacement ({:?}) was generated for a structure without a name: {:?}",
|
|
||||||
replacement,
|
|
||||||
struct_src
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let diagnostic = IncorrectCase {
|
|
||||||
file: struct_src.file_id,
|
|
||||||
ident_type: IdentType::Structure,
|
|
||||||
ident: AstPtr::new(&ast_ptr),
|
|
||||||
expected_case: replacement.expected_case,
|
|
||||||
ident_text: replacement.current_name.display(self.db.upcast()).to_string(),
|
|
||||||
suggested_text: replacement.suggested_text,
|
|
||||||
};
|
|
||||||
|
|
||||||
self.sink.push(diagnostic);
|
|
||||||
}
|
|
||||||
|
|
||||||
let struct_fields_list = match struct_src.value.field_list() {
|
|
||||||
Some(ast::FieldList::RecordFieldList(fields)) => fields,
|
|
||||||
_ => {
|
|
||||||
always!(
|
always!(
|
||||||
struct_fields_replacements.is_empty(),
|
struct_fields_replacements.peek().is_none(),
|
||||||
"Replacements ({:?}) were generated for a structure fields which had no fields list: {:?}",
|
"Replacements ({:?}) were generated for a structure fields \
|
||||||
struct_fields_replacements,
|
which had no fields list: {:?}",
|
||||||
|
struct_fields_replacements.collect::<Vec<_>>(),
|
||||||
struct_src
|
struct_src
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
};
|
};
|
||||||
let mut struct_fields_iter = struct_fields_list.fields();
|
let mut struct_fields_iter = struct_fields_list.fields();
|
||||||
for field_to_rename in struct_fields_replacements {
|
for field_replacement in struct_fields_replacements {
|
||||||
// We assume that parameters in replacement are in the same order as in the
|
// We assume that parameters in replacement are in the same order as in the
|
||||||
// actual params list, but just some of them (ones that named correctly) are skipped.
|
// actual params list, but just some of them (ones that named correctly) are skipped.
|
||||||
let ast_ptr = loop {
|
let field = loop {
|
||||||
match struct_fields_iter.next().and_then(|field| field.name()) {
|
if let Some(field) = struct_fields_iter.next() {
|
||||||
Some(field_name) => {
|
let Some(field_name) = field.name() else {
|
||||||
if field_name.as_name() == field_to_rename.current_name {
|
continue;
|
||||||
break field_name;
|
};
|
||||||
|
if field_name.as_name() == field_replacement.current_name {
|
||||||
|
break field;
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
None => {
|
|
||||||
never!(
|
never!(
|
||||||
"Replacement ({:?}) was generated for a structure field which was not found: {:?}",
|
"Replacement ({:?}) was generated for a structure field \
|
||||||
field_to_rename, struct_src
|
which was not found: {:?}",
|
||||||
|
field_replacement,
|
||||||
|
struct_src
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let diagnostic = IncorrectCase {
|
self.create_incorrect_case_diagnostic_for_ast_node(
|
||||||
file: struct_src.file_id,
|
field_replacement,
|
||||||
ident_type: IdentType::Field,
|
struct_src.file_id,
|
||||||
ident: AstPtr::new(&ast_ptr),
|
&field,
|
||||||
expected_case: field_to_rename.expected_case,
|
IdentType::Field,
|
||||||
ident_text: field_to_rename.current_name.display(self.db.upcast()).to_string(),
|
);
|
||||||
suggested_text: field_to_rename.suggested_text,
|
|
||||||
};
|
|
||||||
|
|
||||||
self.sink.push(diagnostic);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -572,163 +475,103 @@ impl<'a> DeclValidator<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check the enum name.
|
// Check the enum name.
|
||||||
let enum_name = data.name.display(self.db.upcast()).to_string();
|
self.create_incorrect_case_diagnostic_for_item_name(
|
||||||
let enum_name_replacement = to_camel_case(&enum_name).map(|new_name| Replacement {
|
enum_id,
|
||||||
current_name: data.name.clone(),
|
&data.name,
|
||||||
suggested_text: new_name,
|
CaseType::UpperCamelCase,
|
||||||
expected_case: CaseType::UpperCamelCase,
|
IdentType::Enum,
|
||||||
});
|
);
|
||||||
|
|
||||||
// Check the field names.
|
// Check the variant names.
|
||||||
let enum_fields_replacements = data
|
self.validate_enum_variants(enum_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check incorrect names for enum variants.
|
||||||
|
fn validate_enum_variants(&mut self, enum_id: EnumId) {
|
||||||
|
let data = self.db.enum_data(enum_id);
|
||||||
|
let mut enum_variants_replacements = data
|
||||||
.variants
|
.variants
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|(_, name)| {
|
.filter_map(|(_, name)| {
|
||||||
Some(Replacement {
|
to_camel_case(&name.to_smol_str()).map(|new_name| Replacement {
|
||||||
current_name: name.clone(),
|
current_name: name.clone(),
|
||||||
suggested_text: to_camel_case(&name.to_smol_str())?,
|
suggested_text: new_name,
|
||||||
expected_case: CaseType::UpperCamelCase,
|
expected_case: CaseType::UpperCamelCase,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.collect();
|
.peekable();
|
||||||
|
|
||||||
// If there is at least one element to spawn a warning on, go to the source map and generate a warning.
|
|
||||||
self.create_incorrect_case_diagnostic_for_enum(
|
|
||||||
enum_id,
|
|
||||||
enum_name_replacement,
|
|
||||||
enum_fields_replacements,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Given the information about incorrect names in the struct declaration, looks up into the source code
|
|
||||||
/// for exact locations and adds diagnostics into the sink.
|
|
||||||
fn create_incorrect_case_diagnostic_for_enum(
|
|
||||||
&mut self,
|
|
||||||
enum_id: EnumId,
|
|
||||||
enum_name_replacement: Option<Replacement>,
|
|
||||||
enum_variants_replacements: Vec<Replacement>,
|
|
||||||
) {
|
|
||||||
// XXX: only look at sources if we do have incorrect names
|
// XXX: only look at sources if we do have incorrect names
|
||||||
if enum_name_replacement.is_none() && enum_variants_replacements.is_empty() {
|
if enum_variants_replacements.peek().is_none() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let enum_loc = enum_id.lookup(self.db.upcast());
|
let enum_loc = enum_id.lookup(self.db.upcast());
|
||||||
let enum_src = enum_loc.source(self.db.upcast());
|
let enum_src = enum_loc.source(self.db.upcast());
|
||||||
|
|
||||||
if let Some(replacement) = enum_name_replacement {
|
let Some(enum_variants_list) = enum_src.value.variant_list() else {
|
||||||
let ast_ptr = match enum_src.value.name() {
|
|
||||||
Some(name) => name,
|
|
||||||
None => {
|
|
||||||
never!(
|
|
||||||
"Replacement ({:?}) was generated for a enum without a name: {:?}",
|
|
||||||
replacement,
|
|
||||||
enum_src
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let diagnostic = IncorrectCase {
|
|
||||||
file: enum_src.file_id,
|
|
||||||
ident_type: IdentType::Enum,
|
|
||||||
ident: AstPtr::new(&ast_ptr),
|
|
||||||
expected_case: replacement.expected_case,
|
|
||||||
ident_text: replacement.current_name.display(self.db.upcast()).to_string(),
|
|
||||||
suggested_text: replacement.suggested_text,
|
|
||||||
};
|
|
||||||
|
|
||||||
self.sink.push(diagnostic);
|
|
||||||
}
|
|
||||||
|
|
||||||
let enum_variants_list = match enum_src.value.variant_list() {
|
|
||||||
Some(variants) => variants,
|
|
||||||
_ => {
|
|
||||||
always!(
|
always!(
|
||||||
enum_variants_replacements.is_empty(),
|
enum_variants_replacements.peek().is_none(),
|
||||||
"Replacements ({:?}) were generated for a enum variants which had no fields list: {:?}",
|
"Replacements ({:?}) were generated for enum variants \
|
||||||
|
which had no fields list: {:?}",
|
||||||
enum_variants_replacements,
|
enum_variants_replacements,
|
||||||
enum_src
|
enum_src
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
};
|
};
|
||||||
let mut enum_variants_iter = enum_variants_list.variants();
|
let mut enum_variants_iter = enum_variants_list.variants();
|
||||||
for variant_to_rename in enum_variants_replacements {
|
for variant_replacement in enum_variants_replacements {
|
||||||
// We assume that parameters in replacement are in the same order as in the
|
// We assume that parameters in replacement are in the same order as in the
|
||||||
// actual params list, but just some of them (ones that named correctly) are skipped.
|
// actual params list, but just some of them (ones that named correctly) are skipped.
|
||||||
let ast_ptr = loop {
|
let variant = loop {
|
||||||
match enum_variants_iter.next().and_then(|v| v.name()) {
|
if let Some(variant) = enum_variants_iter.next() {
|
||||||
Some(variant_name) => {
|
let Some(variant_name) = variant.name() else {
|
||||||
if variant_name.as_name() == variant_to_rename.current_name {
|
continue;
|
||||||
break variant_name;
|
};
|
||||||
|
if variant_name.as_name() == variant_replacement.current_name {
|
||||||
|
break variant;
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
None => {
|
|
||||||
never!(
|
never!(
|
||||||
"Replacement ({:?}) was generated for a enum variant which was not found: {:?}",
|
"Replacement ({:?}) was generated for an enum variant \
|
||||||
variant_to_rename, enum_src
|
which was not found: {:?}",
|
||||||
|
variant_replacement,
|
||||||
|
enum_src
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let diagnostic = IncorrectCase {
|
self.create_incorrect_case_diagnostic_for_ast_node(
|
||||||
file: enum_src.file_id,
|
variant_replacement,
|
||||||
ident_type: IdentType::Variant,
|
enum_src.file_id,
|
||||||
ident: AstPtr::new(&ast_ptr),
|
&variant,
|
||||||
expected_case: variant_to_rename.expected_case,
|
IdentType::Variant,
|
||||||
ident_text: variant_to_rename.current_name.display(self.db.upcast()).to_string(),
|
);
|
||||||
suggested_text: variant_to_rename.suggested_text,
|
|
||||||
};
|
|
||||||
|
|
||||||
self.sink.push(diagnostic);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn validate_const(&mut self, const_id: ConstId) {
|
fn validate_const(&mut self, const_id: ConstId) {
|
||||||
let data = self.db.const_data(const_id);
|
let container = const_id.lookup(self.db.upcast()).container;
|
||||||
|
if self.is_trait_impl_container(container) {
|
||||||
|
cov_mark::hit!(trait_impl_assoc_const_incorrect_case_ignored);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if self.allowed(const_id.into(), allow::NON_UPPER_CASE_GLOBAL, false) {
|
if self.allowed(const_id.into(), allow::NON_UPPER_CASE_GLOBAL, false) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let name = match &data.name {
|
let data = self.db.const_data(const_id);
|
||||||
Some(name) => name,
|
let Some(name) = &data.name else {
|
||||||
None => return,
|
|
||||||
};
|
|
||||||
|
|
||||||
let const_name = name.to_smol_str();
|
|
||||||
let replacement = if let Some(new_name) = to_upper_snake_case(&const_name) {
|
|
||||||
Replacement {
|
|
||||||
current_name: name.clone(),
|
|
||||||
suggested_text: new_name,
|
|
||||||
expected_case: CaseType::UpperSnakeCase,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Nothing to do here.
|
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
self.create_incorrect_case_diagnostic_for_item_name(
|
||||||
let const_loc = const_id.lookup(self.db.upcast());
|
const_id,
|
||||||
let const_src = const_loc.source(self.db.upcast());
|
name,
|
||||||
|
CaseType::UpperSnakeCase,
|
||||||
let ast_ptr = match const_src.value.name() {
|
IdentType::Constant,
|
||||||
Some(name) => name,
|
);
|
||||||
None => return,
|
|
||||||
};
|
|
||||||
|
|
||||||
let diagnostic = IncorrectCase {
|
|
||||||
file: const_src.file_id,
|
|
||||||
ident_type: IdentType::Constant,
|
|
||||||
ident: AstPtr::new(&ast_ptr),
|
|
||||||
expected_case: replacement.expected_case,
|
|
||||||
ident_text: replacement.current_name.display(self.db.upcast()).to_string(),
|
|
||||||
suggested_text: replacement.suggested_text,
|
|
||||||
};
|
|
||||||
|
|
||||||
self.sink.push(diagnostic);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn validate_static(&mut self, static_id: StaticId) {
|
fn validate_static(&mut self, static_id: StaticId) {
|
||||||
|
@ -742,32 +585,91 @@ impl<'a> DeclValidator<'a> {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let name = &data.name;
|
self.create_incorrect_case_diagnostic_for_item_name(
|
||||||
|
static_id,
|
||||||
let static_name = name.to_smol_str();
|
&data.name,
|
||||||
let replacement = if let Some(new_name) = to_upper_snake_case(&static_name) {
|
CaseType::UpperSnakeCase,
|
||||||
Replacement {
|
IdentType::StaticVariable,
|
||||||
current_name: name.clone(),
|
);
|
||||||
suggested_text: new_name,
|
|
||||||
expected_case: CaseType::UpperSnakeCase,
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// Nothing to do here.
|
fn validate_type_alias(&mut self, type_alias_id: TypeAliasId) {
|
||||||
|
let container = type_alias_id.lookup(self.db.upcast()).container;
|
||||||
|
if self.is_trait_impl_container(container) {
|
||||||
|
cov_mark::hit!(trait_impl_assoc_type_incorrect_case_ignored);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check whether non-snake case identifiers are allowed for this type alias.
|
||||||
|
if self.allowed(type_alias_id.into(), allow::NON_CAMEL_CASE_TYPES, false) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the type alias name.
|
||||||
|
let data = self.db.type_alias_data(type_alias_id);
|
||||||
|
self.create_incorrect_case_diagnostic_for_item_name(
|
||||||
|
type_alias_id,
|
||||||
|
&data.name,
|
||||||
|
CaseType::UpperCamelCase,
|
||||||
|
IdentType::TypeAlias,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_incorrect_case_diagnostic_for_item_name<N, S, L>(
|
||||||
|
&mut self,
|
||||||
|
item_id: L,
|
||||||
|
name: &Name,
|
||||||
|
expected_case: CaseType,
|
||||||
|
ident_type: IdentType,
|
||||||
|
) where
|
||||||
|
N: AstNode + HasName + fmt::Debug,
|
||||||
|
S: HasSource<Value = N>,
|
||||||
|
L: Lookup<Data = S, Database<'a> = dyn DefDatabase + 'a>,
|
||||||
|
{
|
||||||
|
let to_expected_case_type = match expected_case {
|
||||||
|
CaseType::LowerSnakeCase => to_lower_snake_case,
|
||||||
|
CaseType::UpperSnakeCase => to_upper_snake_case,
|
||||||
|
CaseType::UpperCamelCase => to_camel_case,
|
||||||
|
};
|
||||||
|
let Some(replacement) = to_expected_case_type(&name.to_smol_str()).map(|new_name| {
|
||||||
|
Replacement { current_name: name.clone(), suggested_text: new_name, expected_case }
|
||||||
|
}) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
let static_loc = static_id.lookup(self.db.upcast());
|
let item_loc = item_id.lookup(self.db.upcast());
|
||||||
let static_src = static_loc.source(self.db.upcast());
|
let item_src = item_loc.source(self.db.upcast());
|
||||||
|
self.create_incorrect_case_diagnostic_for_ast_node(
|
||||||
|
replacement,
|
||||||
|
item_src.file_id,
|
||||||
|
&item_src.value,
|
||||||
|
ident_type,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
let ast_ptr = match static_src.value.name() {
|
fn create_incorrect_case_diagnostic_for_ast_node<T>(
|
||||||
Some(name) => name,
|
&mut self,
|
||||||
None => return,
|
replacement: Replacement,
|
||||||
|
file_id: HirFileId,
|
||||||
|
node: &T,
|
||||||
|
ident_type: IdentType,
|
||||||
|
) where
|
||||||
|
T: AstNode + HasName + fmt::Debug,
|
||||||
|
{
|
||||||
|
let Some(name_ast) = node.name() else {
|
||||||
|
never!(
|
||||||
|
"Replacement ({:?}) was generated for a {:?} without a name: {:?}",
|
||||||
|
replacement,
|
||||||
|
ident_type,
|
||||||
|
node
|
||||||
|
);
|
||||||
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
let diagnostic = IncorrectCase {
|
let diagnostic = IncorrectCase {
|
||||||
file: static_src.file_id,
|
file: file_id,
|
||||||
ident_type: IdentType::StaticVariable,
|
ident_type,
|
||||||
ident: AstPtr::new(&ast_ptr),
|
ident: AstPtr::new(&name_ast),
|
||||||
expected_case: replacement.expected_case,
|
expected_case: replacement.expected_case,
|
||||||
ident_text: replacement.current_name.display(self.db.upcast()).to_string(),
|
ident_text: replacement.current_name.display(self.db.upcast()).to_string(),
|
||||||
suggested_text: replacement.suggested_text,
|
suggested_text: replacement.suggested_text,
|
||||||
|
@ -775,4 +677,13 @@ impl<'a> DeclValidator<'a> {
|
||||||
|
|
||||||
self.sink.push(diagnostic);
|
self.sink.push(diagnostic);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_trait_impl_container(&self, container_id: ItemContainerId) -> bool {
|
||||||
|
if let ItemContainerId::ImplId(impl_id) = container_id {
|
||||||
|
if self.db.impl_trait(impl_id).is_some() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,7 @@ use crate::{
|
||||||
|
|
||||||
pub(crate) use hir_def::{
|
pub(crate) use hir_def::{
|
||||||
body::Body,
|
body::Body,
|
||||||
hir::{Expr, ExprId, MatchArm, Pat, PatId},
|
hir::{Expr, ExprId, MatchArm, Pat, PatId, Statement},
|
||||||
LocalFieldId, VariantId,
|
LocalFieldId, VariantId,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -44,6 +44,12 @@ pub enum BodyValidationDiagnostic {
|
||||||
match_expr: ExprId,
|
match_expr: ExprId,
|
||||||
uncovered_patterns: String,
|
uncovered_patterns: String,
|
||||||
},
|
},
|
||||||
|
RemoveTrailingReturn {
|
||||||
|
return_expr: ExprId,
|
||||||
|
},
|
||||||
|
RemoveUnnecessaryElse {
|
||||||
|
if_expr: ExprId,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BodyValidationDiagnostic {
|
impl BodyValidationDiagnostic {
|
||||||
|
@ -72,6 +78,10 @@ impl ExprValidator {
|
||||||
let body = db.body(self.owner);
|
let body = db.body(self.owner);
|
||||||
let mut filter_map_next_checker = None;
|
let mut filter_map_next_checker = None;
|
||||||
|
|
||||||
|
if matches!(self.owner, DefWithBodyId::FunctionId(_)) {
|
||||||
|
self.check_for_trailing_return(body.body_expr, &body);
|
||||||
|
}
|
||||||
|
|
||||||
for (id, expr) in body.exprs.iter() {
|
for (id, expr) in body.exprs.iter() {
|
||||||
if let Some((variant, missed_fields, true)) =
|
if let Some((variant, missed_fields, true)) =
|
||||||
record_literal_missing_fields(db, &self.infer, id, expr)
|
record_literal_missing_fields(db, &self.infer, id, expr)
|
||||||
|
@ -90,9 +100,16 @@ impl ExprValidator {
|
||||||
Expr::Call { .. } | Expr::MethodCall { .. } => {
|
Expr::Call { .. } | Expr::MethodCall { .. } => {
|
||||||
self.validate_call(db, id, expr, &mut filter_map_next_checker);
|
self.validate_call(db, id, expr, &mut filter_map_next_checker);
|
||||||
}
|
}
|
||||||
|
Expr::Closure { body: body_expr, .. } => {
|
||||||
|
self.check_for_trailing_return(*body_expr, &body);
|
||||||
|
}
|
||||||
|
Expr::If { .. } => {
|
||||||
|
self.check_for_unnecessary_else(id, expr, &body);
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (id, pat) in body.pats.iter() {
|
for (id, pat) in body.pats.iter() {
|
||||||
if let Some((variant, missed_fields, true)) =
|
if let Some((variant, missed_fields, true)) =
|
||||||
record_pattern_missing_fields(db, &self.infer, id, pat)
|
record_pattern_missing_fields(db, &self.infer, id, pat)
|
||||||
|
@ -153,14 +170,7 @@ impl ExprValidator {
|
||||||
}
|
}
|
||||||
|
|
||||||
let pattern_arena = Arena::new();
|
let pattern_arena = Arena::new();
|
||||||
let ty_arena = Arena::new();
|
let cx = MatchCheckCtx::new(self.owner.module(db.upcast()), self.owner, db, &pattern_arena);
|
||||||
let cx = MatchCheckCtx::new(
|
|
||||||
self.owner.module(db.upcast()),
|
|
||||||
self.owner,
|
|
||||||
db,
|
|
||||||
&pattern_arena,
|
|
||||||
&ty_arena,
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut m_arms = Vec::with_capacity(arms.len());
|
let mut m_arms = Vec::with_capacity(arms.len());
|
||||||
let mut has_lowering_errors = false;
|
let mut has_lowering_errors = false;
|
||||||
|
@ -207,7 +217,7 @@ impl ExprValidator {
|
||||||
}
|
}
|
||||||
|
|
||||||
let report = match compute_match_usefulness(
|
let report = match compute_match_usefulness(
|
||||||
rustc_pattern_analysis::MatchCtxt { tycx: &cx },
|
&cx,
|
||||||
m_arms.as_slice(),
|
m_arms.as_slice(),
|
||||||
scrut_ty.clone(),
|
scrut_ty.clone(),
|
||||||
ValidityConstraint::ValidOnly,
|
ValidityConstraint::ValidOnly,
|
||||||
|
@ -244,6 +254,59 @@ impl ExprValidator {
|
||||||
}
|
}
|
||||||
pattern
|
pattern
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn check_for_trailing_return(&mut self, body_expr: ExprId, body: &Body) {
|
||||||
|
match &body.exprs[body_expr] {
|
||||||
|
Expr::Block { statements, tail, .. } => {
|
||||||
|
let last_stmt = tail.or_else(|| match statements.last()? {
|
||||||
|
Statement::Expr { expr, .. } => Some(*expr),
|
||||||
|
_ => None,
|
||||||
|
});
|
||||||
|
if let Some(last_stmt) = last_stmt {
|
||||||
|
self.check_for_trailing_return(last_stmt, body);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Expr::If { then_branch, else_branch, .. } => {
|
||||||
|
self.check_for_trailing_return(*then_branch, body);
|
||||||
|
if let Some(else_branch) = else_branch {
|
||||||
|
self.check_for_trailing_return(*else_branch, body);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Expr::Match { arms, .. } => {
|
||||||
|
for arm in arms.iter() {
|
||||||
|
let MatchArm { expr, .. } = arm;
|
||||||
|
self.check_for_trailing_return(*expr, body);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Expr::Return { .. } => {
|
||||||
|
self.diagnostics.push(BodyValidationDiagnostic::RemoveTrailingReturn {
|
||||||
|
return_expr: body_expr,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_for_unnecessary_else(&mut self, id: ExprId, expr: &Expr, body: &Body) {
|
||||||
|
if let Expr::If { condition: _, then_branch, else_branch } = expr {
|
||||||
|
if else_branch.is_none() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if let Expr::Block { statements, tail, .. } = &body.exprs[*then_branch] {
|
||||||
|
let last_then_expr = tail.or_else(|| match statements.last()? {
|
||||||
|
Statement::Expr { expr, .. } => Some(*expr),
|
||||||
|
_ => None,
|
||||||
|
});
|
||||||
|
if let Some(last_then_expr) = last_then_expr {
|
||||||
|
let last_then_expr_ty = &self.infer[last_then_expr];
|
||||||
|
if last_then_expr_ty.is_never() {
|
||||||
|
self.diagnostics
|
||||||
|
.push(BodyValidationDiagnostic::RemoveUnnecessaryElse { if_expr: id })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct FilterMapNextChecker {
|
struct FilterMapNextChecker {
|
||||||
|
|
|
@ -9,7 +9,7 @@ use rustc_pattern_analysis::{
|
||||||
index::IdxContainer,
|
index::IdxContainer,
|
||||||
Captures, TypeCx,
|
Captures, TypeCx,
|
||||||
};
|
};
|
||||||
use smallvec::SmallVec;
|
use smallvec::{smallvec, SmallVec};
|
||||||
use stdx::never;
|
use stdx::never;
|
||||||
use typed_arena::Arena;
|
use typed_arena::Arena;
|
||||||
|
|
||||||
|
@ -41,8 +41,14 @@ pub(crate) struct MatchCheckCtx<'p> {
|
||||||
body: DefWithBodyId,
|
body: DefWithBodyId,
|
||||||
pub(crate) db: &'p dyn HirDatabase,
|
pub(crate) db: &'p dyn HirDatabase,
|
||||||
pub(crate) pattern_arena: &'p Arena<DeconstructedPat<'p>>,
|
pub(crate) pattern_arena: &'p Arena<DeconstructedPat<'p>>,
|
||||||
ty_arena: &'p Arena<Ty>,
|
|
||||||
exhaustive_patterns: bool,
|
exhaustive_patterns: bool,
|
||||||
|
min_exhaustive_patterns: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub(crate) struct PatData<'p> {
|
||||||
|
/// Keep db around so that we can print variant names in `Debug`.
|
||||||
|
pub(crate) db: &'p dyn HirDatabase,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'p> MatchCheckCtx<'p> {
|
impl<'p> MatchCheckCtx<'p> {
|
||||||
|
@ -51,11 +57,12 @@ impl<'p> MatchCheckCtx<'p> {
|
||||||
body: DefWithBodyId,
|
body: DefWithBodyId,
|
||||||
db: &'p dyn HirDatabase,
|
db: &'p dyn HirDatabase,
|
||||||
pattern_arena: &'p Arena<DeconstructedPat<'p>>,
|
pattern_arena: &'p Arena<DeconstructedPat<'p>>,
|
||||||
ty_arena: &'p Arena<Ty>,
|
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let def_map = db.crate_def_map(module.krate());
|
let def_map = db.crate_def_map(module.krate());
|
||||||
let exhaustive_patterns = def_map.is_unstable_feature_enabled("exhaustive_patterns");
|
let exhaustive_patterns = def_map.is_unstable_feature_enabled("exhaustive_patterns");
|
||||||
Self { module, body, db, pattern_arena, exhaustive_patterns, ty_arena }
|
let min_exhaustive_patterns =
|
||||||
|
def_map.is_unstable_feature_enabled("min_exhaustive_patterns");
|
||||||
|
Self { module, body, db, pattern_arena, exhaustive_patterns, min_exhaustive_patterns }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_uninhabited(&self, ty: &Ty) -> bool {
|
fn is_uninhabited(&self, ty: &Ty) -> bool {
|
||||||
|
@ -75,18 +82,15 @@ impl<'p> MatchCheckCtx<'p> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn variant_id_for_adt(&self, ctor: &Constructor<Self>, adt: hir_def::AdtId) -> VariantId {
|
fn variant_id_for_adt(ctor: &Constructor<Self>, adt: hir_def::AdtId) -> Option<VariantId> {
|
||||||
match ctor {
|
match ctor {
|
||||||
&Variant(id) => id.into(),
|
&Variant(id) => Some(id.into()),
|
||||||
Struct | UnionField => {
|
Struct | UnionField => match adt {
|
||||||
assert!(!matches!(adt, hir_def::AdtId::EnumId(_)));
|
hir_def::AdtId::EnumId(_) => None,
|
||||||
match adt {
|
hir_def::AdtId::StructId(id) => Some(id.into()),
|
||||||
hir_def::AdtId::EnumId(_) => unreachable!(),
|
hir_def::AdtId::UnionId(id) => Some(id.into()),
|
||||||
hir_def::AdtId::StructId(id) => id.into(),
|
},
|
||||||
hir_def::AdtId::UnionId(id) => id.into(),
|
_ => panic!("bad constructor {ctor:?} for adt {adt:?}"),
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => panic!("bad constructor {self:?} for adt {adt:?}"),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -200,7 +204,7 @@ impl<'p> MatchCheckCtx<'p> {
|
||||||
Wildcard
|
Wildcard
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let variant = self.variant_id_for_adt(&ctor, adt.0);
|
let variant = Self::variant_id_for_adt(&ctor, adt.0).unwrap();
|
||||||
let fields_len = variant.variant_data(self.db.upcast()).fields().len();
|
let fields_len = variant.variant_data(self.db.upcast()).fields().len();
|
||||||
// For each field in the variant, we store the relevant index into `self.fields` if any.
|
// For each field in the variant, we store the relevant index into `self.fields` if any.
|
||||||
let mut field_id_to_id: Vec<Option<usize>> = vec![None; fields_len];
|
let mut field_id_to_id: Vec<Option<usize>> = vec![None; fields_len];
|
||||||
|
@ -241,7 +245,8 @@ impl<'p> MatchCheckCtx<'p> {
|
||||||
fields = self.pattern_arena.alloc_extend(subpats);
|
fields = self.pattern_arena.alloc_extend(subpats);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DeconstructedPat::new(ctor, fields, pat.ty.clone(), ())
|
let data = PatData { db: self.db };
|
||||||
|
DeconstructedPat::new(ctor, fields, pat.ty.clone(), data)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn hoist_witness_pat(&self, pat: &WitnessPat<'p>) -> Pat {
|
pub(crate) fn hoist_witness_pat(&self, pat: &WitnessPat<'p>) -> Pat {
|
||||||
|
@ -266,7 +271,7 @@ impl<'p> MatchCheckCtx<'p> {
|
||||||
PatKind::Deref { subpattern: subpatterns.next().unwrap() }
|
PatKind::Deref { subpattern: subpatterns.next().unwrap() }
|
||||||
}
|
}
|
||||||
TyKind::Adt(adt, substs) => {
|
TyKind::Adt(adt, substs) => {
|
||||||
let variant = self.variant_id_for_adt(pat.ctor(), adt.0);
|
let variant = Self::variant_id_for_adt(pat.ctor(), adt.0).unwrap();
|
||||||
let subpatterns = self
|
let subpatterns = self
|
||||||
.list_variant_nonhidden_fields(pat.ty(), variant)
|
.list_variant_nonhidden_fields(pat.ty(), variant)
|
||||||
.zip(subpatterns)
|
.zip(subpatterns)
|
||||||
|
@ -307,11 +312,14 @@ impl<'p> TypeCx for MatchCheckCtx<'p> {
|
||||||
type VariantIdx = EnumVariantId;
|
type VariantIdx = EnumVariantId;
|
||||||
type StrLit = Void;
|
type StrLit = Void;
|
||||||
type ArmData = ();
|
type ArmData = ();
|
||||||
type PatData = ();
|
type PatData = PatData<'p>;
|
||||||
|
|
||||||
fn is_exhaustive_patterns_feature_on(&self) -> bool {
|
fn is_exhaustive_patterns_feature_on(&self) -> bool {
|
||||||
self.exhaustive_patterns
|
self.exhaustive_patterns
|
||||||
}
|
}
|
||||||
|
fn is_min_exhaustive_patterns_feature_on(&self) -> bool {
|
||||||
|
self.min_exhaustive_patterns
|
||||||
|
}
|
||||||
|
|
||||||
fn ctor_arity(
|
fn ctor_arity(
|
||||||
&self,
|
&self,
|
||||||
|
@ -327,7 +335,7 @@ impl<'p> TypeCx for MatchCheckCtx<'p> {
|
||||||
// patterns. If we're here we can assume this is a box pattern.
|
// patterns. If we're here we can assume this is a box pattern.
|
||||||
1
|
1
|
||||||
} else {
|
} else {
|
||||||
let variant = self.variant_id_for_adt(ctor, adt);
|
let variant = Self::variant_id_for_adt(ctor, adt).unwrap();
|
||||||
self.list_variant_nonhidden_fields(ty, variant).count()
|
self.list_variant_nonhidden_fields(ty, variant).count()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -347,54 +355,51 @@ impl<'p> TypeCx for MatchCheckCtx<'p> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ctor_sub_tys(
|
fn ctor_sub_tys<'a>(
|
||||||
&self,
|
&'a self,
|
||||||
ctor: &rustc_pattern_analysis::constructor::Constructor<Self>,
|
ctor: &'a rustc_pattern_analysis::constructor::Constructor<Self>,
|
||||||
ty: &Self::Ty,
|
ty: &'a Self::Ty,
|
||||||
) -> &[Self::Ty] {
|
) -> impl ExactSizeIterator<Item = Self::Ty> + Captures<'a> {
|
||||||
use std::iter::once;
|
let single = |ty| smallvec![ty];
|
||||||
fn alloc<'a>(cx: &'a MatchCheckCtx<'_>, iter: impl Iterator<Item = Ty>) -> &'a [Ty] {
|
let tys: SmallVec<[_; 2]> = match ctor {
|
||||||
cx.ty_arena.alloc_extend(iter)
|
|
||||||
}
|
|
||||||
match ctor {
|
|
||||||
Struct | Variant(_) | UnionField => match ty.kind(Interner) {
|
Struct | Variant(_) | UnionField => match ty.kind(Interner) {
|
||||||
TyKind::Tuple(_, substs) => {
|
TyKind::Tuple(_, substs) => {
|
||||||
let tys = substs.iter(Interner).map(|ty| ty.assert_ty_ref(Interner));
|
let tys = substs.iter(Interner).map(|ty| ty.assert_ty_ref(Interner));
|
||||||
alloc(self, tys.cloned())
|
tys.cloned().collect()
|
||||||
}
|
}
|
||||||
TyKind::Ref(.., rty) => alloc(self, once(rty.clone())),
|
TyKind::Ref(.., rty) => single(rty.clone()),
|
||||||
&TyKind::Adt(AdtId(adt), ref substs) => {
|
&TyKind::Adt(AdtId(adt), ref substs) => {
|
||||||
if is_box(self.db, adt) {
|
if is_box(self.db, adt) {
|
||||||
// The only legal patterns of type `Box` (outside `std`) are `_` and box
|
// The only legal patterns of type `Box` (outside `std`) are `_` and box
|
||||||
// patterns. If we're here we can assume this is a box pattern.
|
// patterns. If we're here we can assume this is a box pattern.
|
||||||
let subst_ty = substs.at(Interner, 0).assert_ty_ref(Interner).clone();
|
let subst_ty = substs.at(Interner, 0).assert_ty_ref(Interner).clone();
|
||||||
alloc(self, once(subst_ty))
|
single(subst_ty)
|
||||||
} else {
|
} else {
|
||||||
let variant = self.variant_id_for_adt(ctor, adt);
|
let variant = Self::variant_id_for_adt(ctor, adt).unwrap();
|
||||||
let tys = self.list_variant_nonhidden_fields(ty, variant).map(|(_, ty)| ty);
|
self.list_variant_nonhidden_fields(ty, variant).map(|(_, ty)| ty).collect()
|
||||||
alloc(self, tys)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ty_kind => {
|
ty_kind => {
|
||||||
never!("Unexpected type for `{:?}` constructor: {:?}", ctor, ty_kind);
|
never!("Unexpected type for `{:?}` constructor: {:?}", ctor, ty_kind);
|
||||||
alloc(self, once(ty.clone()))
|
single(ty.clone())
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Ref => match ty.kind(Interner) {
|
Ref => match ty.kind(Interner) {
|
||||||
TyKind::Ref(.., rty) => alloc(self, once(rty.clone())),
|
TyKind::Ref(.., rty) => single(rty.clone()),
|
||||||
ty_kind => {
|
ty_kind => {
|
||||||
never!("Unexpected type for `{:?}` constructor: {:?}", ctor, ty_kind);
|
never!("Unexpected type for `{:?}` constructor: {:?}", ctor, ty_kind);
|
||||||
alloc(self, once(ty.clone()))
|
single(ty.clone())
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Slice(_) => unreachable!("Found a `Slice` constructor in match checking"),
|
Slice(_) => unreachable!("Found a `Slice` constructor in match checking"),
|
||||||
Bool(..) | IntRange(..) | F32Range(..) | F64Range(..) | Str(..) | Opaque(..)
|
Bool(..) | IntRange(..) | F32Range(..) | F64Range(..) | Str(..) | Opaque(..)
|
||||||
| NonExhaustive | Hidden | Missing | Wildcard => &[],
|
| NonExhaustive | Hidden | Missing | Wildcard => smallvec![],
|
||||||
Or => {
|
Or => {
|
||||||
never!("called `Fields::wildcards` on an `Or` ctor");
|
never!("called `Fields::wildcards` on an `Or` ctor");
|
||||||
&[]
|
smallvec![]
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
tys.into_iter()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ctors_for_ty(
|
fn ctors_for_ty(
|
||||||
|
@ -456,11 +461,27 @@ impl<'p> TypeCx for MatchCheckCtx<'p> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn debug_pat(
|
fn write_variant_name(
|
||||||
_f: &mut fmt::Formatter<'_>,
|
f: &mut fmt::Formatter<'_>,
|
||||||
_pat: &rustc_pattern_analysis::pat::DeconstructedPat<'_, Self>,
|
pat: &rustc_pattern_analysis::pat::DeconstructedPat<'_, Self>,
|
||||||
) -> fmt::Result {
|
) -> fmt::Result {
|
||||||
// FIXME: implement this, as using `unimplemented!()` causes panics in `tracing`.
|
let variant =
|
||||||
|
pat.ty().as_adt().and_then(|(adt, _)| Self::variant_id_for_adt(pat.ctor(), adt));
|
||||||
|
|
||||||
|
let db = pat.data().unwrap().db;
|
||||||
|
if let Some(variant) = variant {
|
||||||
|
match variant {
|
||||||
|
VariantId::EnumVariantId(v) => {
|
||||||
|
write!(f, "{}", db.enum_variant_data(v).name.display(db.upcast()))?;
|
||||||
|
}
|
||||||
|
VariantId::StructId(s) => {
|
||||||
|
write!(f, "{}", db.struct_data(s).name.display(db.upcast()))?
|
||||||
|
}
|
||||||
|
VariantId::UnionId(u) => {
|
||||||
|
write!(f, "{}", db.union_data(u).name.display(db.upcast()))?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,7 @@ use triomphe::Arc;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
consteval::try_const_usize,
|
consteval::try_const_usize,
|
||||||
db::HirDatabase,
|
db::{HirDatabase, InternedClosure},
|
||||||
from_assoc_type_id, from_foreign_def_id, from_placeholder_idx,
|
from_assoc_type_id, from_foreign_def_id, from_placeholder_idx,
|
||||||
layout::Layout,
|
layout::Layout,
|
||||||
lt_from_placeholder_idx,
|
lt_from_placeholder_idx,
|
||||||
|
@ -814,9 +814,8 @@ impl HirDisplay for Ty {
|
||||||
|
|
||||||
// Don't count Sized but count when it absent
|
// Don't count Sized but count when it absent
|
||||||
// (i.e. when explicit ?Sized bound is set).
|
// (i.e. when explicit ?Sized bound is set).
|
||||||
let default_sized = SizedByDefault::Sized {
|
let default_sized =
|
||||||
anchor: func.lookup(db.upcast()).module(db.upcast()).krate(),
|
SizedByDefault::Sized { anchor: func.krate(db.upcast()) };
|
||||||
};
|
|
||||||
let sized_bounds = bounds
|
let sized_bounds = bounds
|
||||||
.skip_binders()
|
.skip_binders()
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -1025,7 +1024,7 @@ impl HirDisplay for Ty {
|
||||||
let data =
|
let data =
|
||||||
(*datas).as_ref().map(|rpit| rpit.impl_traits[idx].bounds.clone());
|
(*datas).as_ref().map(|rpit| rpit.impl_traits[idx].bounds.clone());
|
||||||
let bounds = data.substitute(Interner, ¶meters);
|
let bounds = data.substitute(Interner, ¶meters);
|
||||||
let krate = func.lookup(db.upcast()).module(db.upcast()).krate();
|
let krate = func.krate(db.upcast());
|
||||||
write_bounds_like_dyn_trait_with_prefix(
|
write_bounds_like_dyn_trait_with_prefix(
|
||||||
f,
|
f,
|
||||||
"impl",
|
"impl",
|
||||||
|
@ -1086,7 +1085,7 @@ impl HirDisplay for Ty {
|
||||||
}
|
}
|
||||||
let sig = ClosureSubst(substs).sig_ty().callable_sig(db);
|
let sig = ClosureSubst(substs).sig_ty().callable_sig(db);
|
||||||
if let Some(sig) = sig {
|
if let Some(sig) = sig {
|
||||||
let (def, _) = db.lookup_intern_closure((*id).into());
|
let InternedClosure(def, _) = db.lookup_intern_closure((*id).into());
|
||||||
let infer = db.infer(def);
|
let infer = db.infer(def);
|
||||||
let (_, kind) = infer.closure_info(id);
|
let (_, kind) = infer.closure_info(id);
|
||||||
match f.closure_style {
|
match f.closure_style {
|
||||||
|
@ -1191,7 +1190,7 @@ impl HirDisplay for Ty {
|
||||||
let data =
|
let data =
|
||||||
(*datas).as_ref().map(|rpit| rpit.impl_traits[idx].bounds.clone());
|
(*datas).as_ref().map(|rpit| rpit.impl_traits[idx].bounds.clone());
|
||||||
let bounds = data.substitute(Interner, &opaque_ty.substitution);
|
let bounds = data.substitute(Interner, &opaque_ty.substitution);
|
||||||
let krate = func.lookup(db.upcast()).module(db.upcast()).krate();
|
let krate = func.krate(db.upcast());
|
||||||
write_bounds_like_dyn_trait_with_prefix(
|
write_bounds_like_dyn_trait_with_prefix(
|
||||||
f,
|
f,
|
||||||
"impl",
|
"impl",
|
||||||
|
|
|
@ -21,7 +21,7 @@ use smallvec::SmallVec;
|
||||||
use stdx::never;
|
use stdx::never;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
db::HirDatabase,
|
db::{HirDatabase, InternedClosure},
|
||||||
from_placeholder_idx, make_binders,
|
from_placeholder_idx, make_binders,
|
||||||
mir::{BorrowKind, MirSpan, ProjectionElem},
|
mir::{BorrowKind, MirSpan, ProjectionElem},
|
||||||
static_lifetime, to_chalk_trait_id,
|
static_lifetime, to_chalk_trait_id,
|
||||||
|
@ -194,17 +194,15 @@ impl CapturedItem {
|
||||||
}
|
}
|
||||||
let variant_data = f.parent.variant_data(db.upcast());
|
let variant_data = f.parent.variant_data(db.upcast());
|
||||||
let field = match &*variant_data {
|
let field = match &*variant_data {
|
||||||
VariantData::Record(fields) => fields[f.local_id]
|
VariantData::Record(fields) => {
|
||||||
.name
|
fields[f.local_id].name.as_str().unwrap_or("[missing field]").to_owned()
|
||||||
.as_str()
|
}
|
||||||
.unwrap_or("[missing field]")
|
|
||||||
.to_string(),
|
|
||||||
VariantData::Tuple(fields) => fields
|
VariantData::Tuple(fields) => fields
|
||||||
.iter()
|
.iter()
|
||||||
.position(|it| it.0 == f.local_id)
|
.position(|it| it.0 == f.local_id)
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
.to_string(),
|
.to_string(),
|
||||||
VariantData::Unit => "[missing field]".to_string(),
|
VariantData::Unit => "[missing field]".to_owned(),
|
||||||
};
|
};
|
||||||
result = format!("{result}.{field}");
|
result = format!("{result}.{field}");
|
||||||
field_need_paren = false;
|
field_need_paren = false;
|
||||||
|
@ -718,7 +716,7 @@ impl InferenceContext<'_> {
|
||||||
|
|
||||||
fn is_upvar(&self, place: &HirPlace) -> bool {
|
fn is_upvar(&self, place: &HirPlace) -> bool {
|
||||||
if let Some(c) = self.current_closure {
|
if let Some(c) = self.current_closure {
|
||||||
let (_, root) = self.db.lookup_intern_closure(c.into());
|
let InternedClosure(_, root) = self.db.lookup_intern_closure(c.into());
|
||||||
return self.body.is_binding_upvar(place.local, root);
|
return self.body.is_binding_upvar(place.local, root);
|
||||||
}
|
}
|
||||||
false
|
false
|
||||||
|
@ -940,7 +938,7 @@ impl InferenceContext<'_> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn analyze_closure(&mut self, closure: ClosureId) -> FnTrait {
|
fn analyze_closure(&mut self, closure: ClosureId) -> FnTrait {
|
||||||
let (_, root) = self.db.lookup_intern_closure(closure.into());
|
let InternedClosure(_, root) = self.db.lookup_intern_closure(closure.into());
|
||||||
self.current_closure = Some(closure);
|
self.current_closure = Some(closure);
|
||||||
let Expr::Closure { body, capture_by, .. } = &self.body[root] else {
|
let Expr::Closure { body, capture_by, .. } = &self.body[root] else {
|
||||||
unreachable!("Closure expression id is always closure");
|
unreachable!("Closure expression id is always closure");
|
||||||
|
|
|
@ -23,6 +23,7 @@ use syntax::ast::RangeOp;
|
||||||
use crate::{
|
use crate::{
|
||||||
autoderef::{builtin_deref, deref_by_trait, Autoderef},
|
autoderef::{builtin_deref, deref_by_trait, Autoderef},
|
||||||
consteval,
|
consteval,
|
||||||
|
db::{InternedClosure, InternedCoroutine},
|
||||||
infer::{
|
infer::{
|
||||||
coerce::{CoerceMany, CoercionCause},
|
coerce::{CoerceMany, CoercionCause},
|
||||||
find_continuable,
|
find_continuable,
|
||||||
|
@ -253,13 +254,17 @@ impl InferenceContext<'_> {
|
||||||
.push(ret_ty.clone())
|
.push(ret_ty.clone())
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
let coroutine_id = self.db.intern_coroutine((self.owner, tgt_expr)).into();
|
let coroutine_id = self
|
||||||
|
.db
|
||||||
|
.intern_coroutine(InternedCoroutine(self.owner, tgt_expr))
|
||||||
|
.into();
|
||||||
let coroutine_ty = TyKind::Coroutine(coroutine_id, subst).intern(Interner);
|
let coroutine_ty = TyKind::Coroutine(coroutine_id, subst).intern(Interner);
|
||||||
|
|
||||||
(None, coroutine_ty, Some((resume_ty, yield_ty)))
|
(None, coroutine_ty, Some((resume_ty, yield_ty)))
|
||||||
}
|
}
|
||||||
ClosureKind::Closure | ClosureKind::Async => {
|
ClosureKind::Closure | ClosureKind::Async => {
|
||||||
let closure_id = self.db.intern_closure((self.owner, tgt_expr)).into();
|
let closure_id =
|
||||||
|
self.db.intern_closure(InternedClosure(self.owner, tgt_expr)).into();
|
||||||
let closure_ty = TyKind::Closure(
|
let closure_ty = TyKind::Closure(
|
||||||
closure_id,
|
closure_id,
|
||||||
TyBuilder::subst_for_closure(self.db, self.owner, sig_ty.clone()),
|
TyBuilder::subst_for_closure(self.db, self.owner, sig_ty.clone()),
|
||||||
|
|
|
@ -469,12 +469,14 @@ impl<'a> InferenceTable<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(skip_all)]
|
||||||
pub(crate) fn rollback_to(&mut self, snapshot: InferenceTableSnapshot) {
|
pub(crate) fn rollback_to(&mut self, snapshot: InferenceTableSnapshot) {
|
||||||
self.var_unification_table.rollback_to(snapshot.var_table_snapshot);
|
self.var_unification_table.rollback_to(snapshot.var_table_snapshot);
|
||||||
self.type_variable_table = snapshot.type_variable_table_snapshot;
|
self.type_variable_table = snapshot.type_variable_table_snapshot;
|
||||||
self.pending_obligations = snapshot.pending_obligations;
|
self.pending_obligations = snapshot.pending_obligations;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(skip_all)]
|
||||||
pub(crate) fn run_in_snapshot<T>(&mut self, f: impl FnOnce(&mut InferenceTable<'_>) -> T) -> T {
|
pub(crate) fn run_in_snapshot<T>(&mut self, f: impl FnOnce(&mut InferenceTable<'_>) -> T) -> T {
|
||||||
let snapshot = self.snapshot();
|
let snapshot = self.snapshot();
|
||||||
let result = f(self);
|
let result = f(self);
|
||||||
|
|
|
@ -19,8 +19,12 @@ use stdx::never;
|
||||||
use triomphe::Arc;
|
use triomphe::Arc;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
consteval::try_const_usize, db::HirDatabase, infer::normalize, layout::adt::struct_variant_idx,
|
consteval::try_const_usize,
|
||||||
utils::ClosureSubst, Interner, ProjectionTy, Substitution, TraitEnvironment, Ty,
|
db::{HirDatabase, InternedClosure},
|
||||||
|
infer::normalize,
|
||||||
|
layout::adt::struct_variant_idx,
|
||||||
|
utils::ClosureSubst,
|
||||||
|
Interner, ProjectionTy, Substitution, TraitEnvironment, Ty,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub use self::{
|
pub use self::{
|
||||||
|
@ -391,7 +395,7 @@ pub fn layout_of_ty_query(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TyKind::Closure(c, subst) => {
|
TyKind::Closure(c, subst) => {
|
||||||
let (def, _) = db.lookup_intern_closure((*c).into());
|
let InternedClosure(def, _) = db.lookup_intern_closure((*c).into());
|
||||||
let infer = db.infer(def);
|
let infer = db.infer(def);
|
||||||
let (captures, _) = infer.closure_info(c);
|
let (captures, _) = infer.closure_info(c);
|
||||||
let fields = captures
|
let fields = captures
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
use chalk_ir::{AdtId, TyKind};
|
use chalk_ir::{AdtId, TyKind};
|
||||||
use either::Either;
|
use either::Either;
|
||||||
use hir_def::db::DefDatabase;
|
use hir_def::db::DefDatabase;
|
||||||
|
use rustc_hash::FxHashMap;
|
||||||
use test_fixture::WithFixture;
|
use test_fixture::WithFixture;
|
||||||
use triomphe::Arc;
|
use triomphe::Arc;
|
||||||
|
|
||||||
|
@ -16,7 +15,7 @@ use crate::{
|
||||||
mod closure;
|
mod closure;
|
||||||
|
|
||||||
fn current_machine_data_layout() -> String {
|
fn current_machine_data_layout() -> String {
|
||||||
project_model::target_data_layout::get(None, None, &HashMap::default()).unwrap()
|
project_model::target_data_layout::get(None, None, &FxHashMap::default()).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eval_goal(ra_fixture: &str, minicore: &str) -> Result<Arc<Layout>, LayoutError> {
|
fn eval_goal(ra_fixture: &str, minicore: &str) -> Result<Arc<Layout>, LayoutError> {
|
||||||
|
|
|
@ -51,6 +51,7 @@ use std::{
|
||||||
hash::{BuildHasherDefault, Hash},
|
hash::{BuildHasherDefault, Hash},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use base_db::salsa::impl_intern_value_trivial;
|
||||||
use chalk_ir::{
|
use chalk_ir::{
|
||||||
fold::{Shift, TypeFoldable},
|
fold::{Shift, TypeFoldable},
|
||||||
interner::HasInterner,
|
interner::HasInterner,
|
||||||
|
@ -228,7 +229,7 @@ impl MemoryMap {
|
||||||
&self,
|
&self,
|
||||||
mut f: impl FnMut(&[u8], usize) -> Result<usize, MirEvalError>,
|
mut f: impl FnMut(&[u8], usize) -> Result<usize, MirEvalError>,
|
||||||
) -> Result<FxHashMap<usize, usize>, MirEvalError> {
|
) -> Result<FxHashMap<usize, usize>, MirEvalError> {
|
||||||
let mut transform = |(addr, val): (&usize, &Box<[u8]>)| {
|
let mut transform = |(addr, val): (&usize, &[u8])| {
|
||||||
let addr = *addr;
|
let addr = *addr;
|
||||||
let align = if addr == 0 { 64 } else { (addr - (addr & (addr - 1))).min(64) };
|
let align = if addr == 0 { 64 } else { (addr - (addr & (addr - 1))).min(64) };
|
||||||
f(val, align).map(|it| (addr, it))
|
f(val, align).map(|it| (addr, it))
|
||||||
|
@ -240,7 +241,9 @@ impl MemoryMap {
|
||||||
map.insert(addr, val);
|
map.insert(addr, val);
|
||||||
map
|
map
|
||||||
}),
|
}),
|
||||||
MemoryMap::Complex(cm) => cm.memory.iter().map(transform).collect(),
|
MemoryMap::Complex(cm) => {
|
||||||
|
cm.memory.iter().map(|(addr, val)| transform((addr, val))).collect()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -584,6 +587,7 @@ pub enum ImplTraitId {
|
||||||
ReturnTypeImplTrait(hir_def::FunctionId, RpitId),
|
ReturnTypeImplTrait(hir_def::FunctionId, RpitId),
|
||||||
AsyncBlockTypeImplTrait(hir_def::DefWithBodyId, ExprId),
|
AsyncBlockTypeImplTrait(hir_def::DefWithBodyId, ExprId),
|
||||||
}
|
}
|
||||||
|
impl_intern_value_trivial!(ImplTraitId);
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
|
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
|
||||||
pub struct ReturnTypeImplTraits {
|
pub struct ReturnTypeImplTraits {
|
||||||
|
|
|
@ -10,7 +10,10 @@ use std::{
|
||||||
iter,
|
iter,
|
||||||
};
|
};
|
||||||
|
|
||||||
use base_db::{salsa::Cycle, CrateId};
|
use base_db::{
|
||||||
|
salsa::{impl_intern_value_trivial, Cycle},
|
||||||
|
CrateId,
|
||||||
|
};
|
||||||
use chalk_ir::{
|
use chalk_ir::{
|
||||||
cast::Cast, fold::Shift, fold::TypeFoldable, interner::HasInterner, Mutability, Safety,
|
cast::Cast, fold::Shift, fold::TypeFoldable, interner::HasInterner, Mutability, Safety,
|
||||||
};
|
};
|
||||||
|
@ -1225,7 +1228,7 @@ impl<'a> TyLoweringContext<'a> {
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
if !ctx.unsized_types.borrow().contains(&self_ty) {
|
if !ctx.unsized_types.borrow().contains(&self_ty) {
|
||||||
let krate = func.lookup(ctx.db.upcast()).module(ctx.db.upcast()).krate();
|
let krate = func.krate(ctx.db.upcast());
|
||||||
let sized_trait = ctx
|
let sized_trait = ctx
|
||||||
.db
|
.db
|
||||||
.lang_item(krate, LangItem::Sized)
|
.lang_item(krate, LangItem::Sized)
|
||||||
|
@ -1809,6 +1812,7 @@ pub enum CallableDefId {
|
||||||
StructId(StructId),
|
StructId(StructId),
|
||||||
EnumVariantId(EnumVariantId),
|
EnumVariantId(EnumVariantId),
|
||||||
}
|
}
|
||||||
|
impl_intern_value_trivial!(CallableDefId);
|
||||||
impl_from!(FunctionId, StructId, EnumVariantId for CallableDefId);
|
impl_from!(FunctionId, StructId, EnumVariantId for CallableDefId);
|
||||||
impl From<CallableDefId> for ModuleDefId {
|
impl From<CallableDefId> for ModuleDefId {
|
||||||
fn from(def: CallableDefId) -> ModuleDefId {
|
fn from(def: CallableDefId) -> ModuleDefId {
|
||||||
|
@ -1824,11 +1828,10 @@ impl CallableDefId {
|
||||||
pub fn krate(self, db: &dyn HirDatabase) -> CrateId {
|
pub fn krate(self, db: &dyn HirDatabase) -> CrateId {
|
||||||
let db = db.upcast();
|
let db = db.upcast();
|
||||||
match self {
|
match self {
|
||||||
CallableDefId::FunctionId(f) => f.lookup(db).module(db),
|
CallableDefId::FunctionId(f) => f.krate(db),
|
||||||
CallableDefId::StructId(s) => s.lookup(db).container,
|
CallableDefId::StructId(s) => s.krate(db),
|
||||||
CallableDefId::EnumVariantId(e) => e.module(db),
|
CallableDefId::EnumVariantId(e) => e.krate(db),
|
||||||
}
|
}
|
||||||
.krate()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -931,6 +931,15 @@ pub fn iterate_method_candidates_dyn(
|
||||||
mode: LookupMode,
|
mode: LookupMode,
|
||||||
callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId, bool) -> ControlFlow<()>,
|
callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId, bool) -> ControlFlow<()>,
|
||||||
) -> ControlFlow<()> {
|
) -> ControlFlow<()> {
|
||||||
|
let _p = tracing::span!(
|
||||||
|
tracing::Level::INFO,
|
||||||
|
"iterate_method_candidates_dyn",
|
||||||
|
?mode,
|
||||||
|
?name,
|
||||||
|
traits_in_scope_len = traits_in_scope.len()
|
||||||
|
)
|
||||||
|
.entered();
|
||||||
|
|
||||||
match mode {
|
match mode {
|
||||||
LookupMode::MethodCall => {
|
LookupMode::MethodCall => {
|
||||||
// For method calls, rust first does any number of autoderef, and
|
// For method calls, rust first does any number of autoderef, and
|
||||||
|
@ -984,6 +993,7 @@ pub fn iterate_method_candidates_dyn(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(skip_all, fields(name = ?name))]
|
||||||
fn iterate_method_candidates_with_autoref(
|
fn iterate_method_candidates_with_autoref(
|
||||||
receiver_ty: &Canonical<Ty>,
|
receiver_ty: &Canonical<Ty>,
|
||||||
first_adjustment: ReceiverAdjustments,
|
first_adjustment: ReceiverAdjustments,
|
||||||
|
@ -1041,6 +1051,7 @@ fn iterate_method_candidates_with_autoref(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(skip_all, fields(name = ?name))]
|
||||||
fn iterate_method_candidates_by_receiver(
|
fn iterate_method_candidates_by_receiver(
|
||||||
receiver_ty: &Canonical<Ty>,
|
receiver_ty: &Canonical<Ty>,
|
||||||
receiver_adjustments: ReceiverAdjustments,
|
receiver_adjustments: ReceiverAdjustments,
|
||||||
|
@ -1088,6 +1099,7 @@ fn iterate_method_candidates_by_receiver(
|
||||||
ControlFlow::Continue(())
|
ControlFlow::Continue(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(skip_all, fields(name = ?name))]
|
||||||
fn iterate_method_candidates_for_self_ty(
|
fn iterate_method_candidates_for_self_ty(
|
||||||
self_ty: &Canonical<Ty>,
|
self_ty: &Canonical<Ty>,
|
||||||
db: &dyn HirDatabase,
|
db: &dyn HirDatabase,
|
||||||
|
@ -1119,6 +1131,7 @@ fn iterate_method_candidates_for_self_ty(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(skip_all, fields(name = ?name, visible_from_module, receiver_ty))]
|
||||||
fn iterate_trait_method_candidates(
|
fn iterate_trait_method_candidates(
|
||||||
self_ty: &Ty,
|
self_ty: &Ty,
|
||||||
table: &mut InferenceTable<'_>,
|
table: &mut InferenceTable<'_>,
|
||||||
|
@ -1175,6 +1188,7 @@ fn iterate_trait_method_candidates(
|
||||||
ControlFlow::Continue(())
|
ControlFlow::Continue(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(skip_all, fields(name = ?name, visible_from_module, receiver_ty))]
|
||||||
fn iterate_inherent_methods(
|
fn iterate_inherent_methods(
|
||||||
self_ty: &Ty,
|
self_ty: &Ty,
|
||||||
table: &mut InferenceTable<'_>,
|
table: &mut InferenceTable<'_>,
|
||||||
|
@ -1267,6 +1281,7 @@ fn iterate_inherent_methods(
|
||||||
}
|
}
|
||||||
return ControlFlow::Continue(());
|
return ControlFlow::Continue(());
|
||||||
|
|
||||||
|
#[tracing::instrument(skip_all, fields(name = ?name, visible_from_module, receiver_ty))]
|
||||||
fn iterate_inherent_trait_methods(
|
fn iterate_inherent_trait_methods(
|
||||||
self_ty: &Ty,
|
self_ty: &Ty,
|
||||||
table: &mut InferenceTable<'_>,
|
table: &mut InferenceTable<'_>,
|
||||||
|
@ -1293,6 +1308,7 @@ fn iterate_inherent_methods(
|
||||||
ControlFlow::Continue(())
|
ControlFlow::Continue(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(skip_all, fields(name = ?name, visible_from_module, receiver_ty))]
|
||||||
fn impls_for_self_ty(
|
fn impls_for_self_ty(
|
||||||
impls: &InherentImpls,
|
impls: &InherentImpls,
|
||||||
self_ty: &Ty,
|
self_ty: &Ty,
|
||||||
|
@ -1356,6 +1372,7 @@ macro_rules! check_that {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(skip_all, fields(name))]
|
||||||
fn is_valid_candidate(
|
fn is_valid_candidate(
|
||||||
table: &mut InferenceTable<'_>,
|
table: &mut InferenceTable<'_>,
|
||||||
name: Option<&Name>,
|
name: Option<&Name>,
|
||||||
|
@ -1403,6 +1420,7 @@ enum IsValidCandidate {
|
||||||
NotVisible,
|
NotVisible,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(skip_all, fields(name))]
|
||||||
fn is_valid_fn_candidate(
|
fn is_valid_fn_candidate(
|
||||||
table: &mut InferenceTable<'_>,
|
table: &mut InferenceTable<'_>,
|
||||||
fn_id: FunctionId,
|
fn_id: FunctionId,
|
||||||
|
@ -1439,15 +1457,15 @@ fn is_valid_fn_candidate(
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let fn_subst = TyBuilder::subst_for_def(db, fn_id, Some(impl_subst.clone()))
|
|
||||||
.fill_with_inference_vars(table)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
check_that!(table.unify(&expect_self_ty, self_ty));
|
check_that!(table.unify(&expect_self_ty, self_ty));
|
||||||
|
|
||||||
if let Some(receiver_ty) = receiver_ty {
|
if let Some(receiver_ty) = receiver_ty {
|
||||||
check_that!(data.has_self_param());
|
check_that!(data.has_self_param());
|
||||||
|
|
||||||
|
let fn_subst = TyBuilder::subst_for_def(db, fn_id, Some(impl_subst.clone()))
|
||||||
|
.fill_with_inference_vars(table)
|
||||||
|
.build();
|
||||||
|
|
||||||
let sig = db.callable_item_signature(fn_id.into());
|
let sig = db.callable_item_signature(fn_id.into());
|
||||||
let expected_receiver =
|
let expected_receiver =
|
||||||
sig.map(|s| s.params()[0].clone()).substitute(Interner, &fn_subst);
|
sig.map(|s| s.params()[0].clone()).substitute(Interner, &fn_subst);
|
||||||
|
@ -1540,6 +1558,7 @@ pub fn implements_trait_unique(
|
||||||
|
|
||||||
/// This creates Substs for a trait with the given Self type and type variables
|
/// This creates Substs for a trait with the given Self type and type variables
|
||||||
/// for all other parameters, to query Chalk with it.
|
/// for all other parameters, to query Chalk with it.
|
||||||
|
#[tracing::instrument(skip_all)]
|
||||||
fn generic_implements_goal(
|
fn generic_implements_goal(
|
||||||
db: &dyn HirDatabase,
|
db: &dyn HirDatabase,
|
||||||
env: Arc<TraitEnvironment>,
|
env: Arc<TraitEnvironment>,
|
||||||
|
|
|
@ -11,7 +11,10 @@ use stdx::never;
|
||||||
use triomphe::Arc;
|
use triomphe::Arc;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
db::HirDatabase, mir::Operand, utils::ClosureSubst, ClosureId, Interner, Ty, TyExt, TypeFlags,
|
db::{HirDatabase, InternedClosure},
|
||||||
|
mir::Operand,
|
||||||
|
utils::ClosureSubst,
|
||||||
|
ClosureId, Interner, Ty, TyExt, TypeFlags,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
|
@ -97,7 +100,7 @@ fn moved_out_of_ref(db: &dyn HirDatabase, body: &MirBody) -> Vec<MovedOutOfRef>
|
||||||
ty,
|
ty,
|
||||||
db,
|
db,
|
||||||
|c, subst, f| {
|
|c, subst, f| {
|
||||||
let (def, _) = db.lookup_intern_closure(c.into());
|
let InternedClosure(def, _) = db.lookup_intern_closure(c.into());
|
||||||
let infer = db.infer(def);
|
let infer = db.infer(def);
|
||||||
let (captures, _) = infer.closure_info(&c);
|
let (captures, _) = infer.closure_info(&c);
|
||||||
let parent_subst = ClosureSubst(subst).parent_subst();
|
let parent_subst = ClosureSubst(subst).parent_subst();
|
||||||
|
@ -215,7 +218,7 @@ fn place_case(db: &dyn HirDatabase, body: &MirBody, lvalue: &Place) -> Projectio
|
||||||
ty,
|
ty,
|
||||||
db,
|
db,
|
||||||
|c, subst, f| {
|
|c, subst, f| {
|
||||||
let (def, _) = db.lookup_intern_closure(c.into());
|
let InternedClosure(def, _) = db.lookup_intern_closure(c.into());
|
||||||
let infer = db.infer(def);
|
let infer = db.infer(def);
|
||||||
let (captures, _) = infer.closure_info(&c);
|
let (captures, _) = infer.closure_info(&c);
|
||||||
let parent_subst = ClosureSubst(subst).parent_subst();
|
let parent_subst = ClosureSubst(subst).parent_subst();
|
||||||
|
|
|
@ -25,7 +25,7 @@ use triomphe::Arc;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
consteval::{intern_const_scalar, try_const_usize, ConstEvalError},
|
consteval::{intern_const_scalar, try_const_usize, ConstEvalError},
|
||||||
db::HirDatabase,
|
db::{HirDatabase, InternedClosure},
|
||||||
display::{ClosureStyle, HirDisplay},
|
display::{ClosureStyle, HirDisplay},
|
||||||
infer::PointerCast,
|
infer::PointerCast,
|
||||||
layout::{Layout, LayoutError, RustcEnumVariantIdx},
|
layout::{Layout, LayoutError, RustcEnumVariantIdx},
|
||||||
|
@ -647,7 +647,7 @@ impl Evaluator<'_> {
|
||||||
ty.clone(),
|
ty.clone(),
|
||||||
self.db,
|
self.db,
|
||||||
|c, subst, f| {
|
|c, subst, f| {
|
||||||
let (def, _) = self.db.lookup_intern_closure(c.into());
|
let InternedClosure(def, _) = self.db.lookup_intern_closure(c.into());
|
||||||
let infer = self.db.infer(def);
|
let infer = self.db.infer(def);
|
||||||
let (captures, _) = infer.closure_info(&c);
|
let (captures, _) = infer.closure_info(&c);
|
||||||
let parent_subst = ClosureSubst(subst).parent_subst();
|
let parent_subst = ClosureSubst(subst).parent_subst();
|
||||||
|
@ -1763,7 +1763,7 @@ impl Evaluator<'_> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
mem.get(pos..pos + size)
|
mem.get(pos..pos + size)
|
||||||
.ok_or_else(|| MirEvalError::UndefinedBehavior("out of bound memory read".to_string()))
|
.ok_or_else(|| MirEvalError::UndefinedBehavior("out of bound memory read".to_owned()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_memory_using_ref(&mut self, addr: Address, size: usize) -> Result<&mut [u8]> {
|
fn write_memory_using_ref(&mut self, addr: Address, size: usize) -> Result<&mut [u8]> {
|
||||||
|
@ -1777,7 +1777,7 @@ impl Evaluator<'_> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
mem.get_mut(pos..pos + size)
|
mem.get_mut(pos..pos + size)
|
||||||
.ok_or_else(|| MirEvalError::UndefinedBehavior("out of bound memory write".to_string()))
|
.ok_or_else(|| MirEvalError::UndefinedBehavior("out of bound memory write".to_owned()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_memory(&mut self, addr: Address, r: &[u8]) -> Result<()> {
|
fn write_memory(&mut self, addr: Address, r: &[u8]) -> Result<()> {
|
||||||
|
@ -1800,7 +1800,7 @@ impl Evaluator<'_> {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let oob = || MirEvalError::UndefinedBehavior("out of bounds memory write".to_string());
|
let oob = || MirEvalError::UndefinedBehavior("out of bounds memory write".to_owned());
|
||||||
|
|
||||||
match (addr, r.addr) {
|
match (addr, r.addr) {
|
||||||
(Stack(dst), Stack(src)) => {
|
(Stack(dst), Stack(src)) => {
|
||||||
|
@ -2653,7 +2653,7 @@ pub fn render_const_using_debug_impl(
|
||||||
ptr: ArenaMap::new(),
|
ptr: ArenaMap::new(),
|
||||||
body: db
|
body: db
|
||||||
.mir_body(owner.into())
|
.mir_body(owner.into())
|
||||||
.map_err(|_| MirEvalError::NotSupported("unreachable".to_string()))?,
|
.map_err(|_| MirEvalError::NotSupported("unreachable".to_owned()))?,
|
||||||
drop_flags: DropFlags::default(),
|
drop_flags: DropFlags::default(),
|
||||||
};
|
};
|
||||||
let data = evaluator.allocate_const_in_heap(locals, c)?;
|
let data = evaluator.allocate_const_in_heap(locals, c)?;
|
||||||
|
|
|
@ -178,7 +178,7 @@ impl Evaluator<'_> {
|
||||||
not_supported!("wrong arg count for clone");
|
not_supported!("wrong arg count for clone");
|
||||||
};
|
};
|
||||||
let addr = Address::from_bytes(arg.get(self)?)?;
|
let addr = Address::from_bytes(arg.get(self)?)?;
|
||||||
let (closure_owner, _) = self.db.lookup_intern_closure((*id).into());
|
let InternedClosure(closure_owner, _) = self.db.lookup_intern_closure((*id).into());
|
||||||
let infer = self.db.infer(closure_owner);
|
let infer = self.db.infer(closure_owner);
|
||||||
let (captures, _) = infer.closure_info(id);
|
let (captures, _) = infer.closure_info(id);
|
||||||
let layout = self.layout(&self_ty)?;
|
let layout = self.layout(&self_ty)?;
|
||||||
|
@ -304,7 +304,7 @@ impl Evaluator<'_> {
|
||||||
use LangItem::*;
|
use LangItem::*;
|
||||||
let mut args = args.iter();
|
let mut args = args.iter();
|
||||||
match it {
|
match it {
|
||||||
BeginPanic => Err(MirEvalError::Panic("<unknown-panic-payload>".to_string())),
|
BeginPanic => Err(MirEvalError::Panic("<unknown-panic-payload>".to_owned())),
|
||||||
PanicFmt => {
|
PanicFmt => {
|
||||||
let message = (|| {
|
let message = (|| {
|
||||||
let resolver = self
|
let resolver = self
|
||||||
|
|
|
@ -25,7 +25,7 @@ use triomphe::Arc;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
consteval::ConstEvalError,
|
consteval::ConstEvalError,
|
||||||
db::HirDatabase,
|
db::{HirDatabase, InternedClosure},
|
||||||
display::HirDisplay,
|
display::HirDisplay,
|
||||||
infer::{CaptureKind, CapturedItem, TypeMismatch},
|
infer::{CaptureKind, CapturedItem, TypeMismatch},
|
||||||
inhabitedness::is_ty_uninhabited_from,
|
inhabitedness::is_ty_uninhabited_from,
|
||||||
|
@ -126,6 +126,10 @@ impl DropScopeToken {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Drop for DropScopeToken {
|
||||||
|
fn drop(&mut self) {}
|
||||||
|
}
|
||||||
|
|
||||||
// Uncomment this to make `DropScopeToken` a drop bomb. Unfortunately we can't do this in release, since
|
// Uncomment this to make `DropScopeToken` a drop bomb. Unfortunately we can't do this in release, since
|
||||||
// in cases that mir lowering fails, we don't handle (and don't need to handle) drop scopes so it will be
|
// in cases that mir lowering fails, we don't handle (and don't need to handle) drop scopes so it will be
|
||||||
// actually reached. `pop_drop_scope_assert_finished` will also detect this case, but doesn't show useful
|
// actually reached. `pop_drop_scope_assert_finished` will also detect this case, but doesn't show useful
|
||||||
|
@ -1630,7 +1634,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
||||||
self.set_goto(prev_block, begin, span);
|
self.set_goto(prev_block, begin, span);
|
||||||
f(self, begin)?;
|
f(self, begin)?;
|
||||||
let my = mem::replace(&mut self.current_loop_blocks, prev).ok_or(
|
let my = mem::replace(&mut self.current_loop_blocks, prev).ok_or(
|
||||||
MirLowerError::ImplementationError("current_loop_blocks is corrupt".to_string()),
|
MirLowerError::ImplementationError("current_loop_blocks is corrupt".to_owned()),
|
||||||
)?;
|
)?;
|
||||||
if let Some(prev) = prev_label {
|
if let Some(prev) = prev_label {
|
||||||
self.labeled_loop_blocks.insert(label.unwrap(), prev);
|
self.labeled_loop_blocks.insert(label.unwrap(), prev);
|
||||||
|
@ -1665,7 +1669,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
||||||
.current_loop_blocks
|
.current_loop_blocks
|
||||||
.as_mut()
|
.as_mut()
|
||||||
.ok_or(MirLowerError::ImplementationError(
|
.ok_or(MirLowerError::ImplementationError(
|
||||||
"Current loop access out of loop".to_string(),
|
"Current loop access out of loop".to_owned(),
|
||||||
))?
|
))?
|
||||||
.end
|
.end
|
||||||
{
|
{
|
||||||
|
@ -1675,7 +1679,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
||||||
self.current_loop_blocks
|
self.current_loop_blocks
|
||||||
.as_mut()
|
.as_mut()
|
||||||
.ok_or(MirLowerError::ImplementationError(
|
.ok_or(MirLowerError::ImplementationError(
|
||||||
"Current loop access out of loop".to_string(),
|
"Current loop access out of loop".to_owned(),
|
||||||
))?
|
))?
|
||||||
.end = Some(s);
|
.end = Some(s);
|
||||||
s
|
s
|
||||||
|
@ -1973,7 +1977,7 @@ pub fn mir_body_for_closure_query(
|
||||||
db: &dyn HirDatabase,
|
db: &dyn HirDatabase,
|
||||||
closure: ClosureId,
|
closure: ClosureId,
|
||||||
) -> Result<Arc<MirBody>> {
|
) -> Result<Arc<MirBody>> {
|
||||||
let (owner, expr) = db.lookup_intern_closure(closure.into());
|
let InternedClosure(owner, expr) = db.lookup_intern_closure(closure.into());
|
||||||
let body = db.body(owner);
|
let body = db.body(owner);
|
||||||
let infer = db.infer(owner);
|
let infer = db.infer(owner);
|
||||||
let Expr::Closure { args, body: root, .. } = &body[expr] else {
|
let Expr::Closure { args, body: root, .. } = &body[expr] else {
|
||||||
|
|
|
@ -225,7 +225,7 @@ impl MirLowerCtx<'_> {
|
||||||
{
|
{
|
||||||
let Some(index_fn) = self.infer.method_resolution(expr_id) else {
|
let Some(index_fn) = self.infer.method_resolution(expr_id) else {
|
||||||
return Err(MirLowerError::UnresolvedMethod(
|
return Err(MirLowerError::UnresolvedMethod(
|
||||||
"[overloaded index]".to_string(),
|
"[overloaded index]".to_owned(),
|
||||||
));
|
));
|
||||||
};
|
};
|
||||||
let Some((base_place, current)) =
|
let Some((base_place, current)) =
|
||||||
|
|
|
@ -19,7 +19,7 @@ use triomphe::Arc;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
consteval::{intern_const_scalar, unknown_const},
|
consteval::{intern_const_scalar, unknown_const},
|
||||||
db::HirDatabase,
|
db::{HirDatabase, InternedClosure},
|
||||||
from_placeholder_idx,
|
from_placeholder_idx,
|
||||||
infer::normalize,
|
infer::normalize,
|
||||||
utils::{generics, Generics},
|
utils::{generics, Generics},
|
||||||
|
@ -315,7 +315,7 @@ pub fn monomorphized_mir_body_for_closure_query(
|
||||||
subst: Substitution,
|
subst: Substitution,
|
||||||
trait_env: Arc<crate::TraitEnvironment>,
|
trait_env: Arc<crate::TraitEnvironment>,
|
||||||
) -> Result<Arc<MirBody>, MirLowerError> {
|
) -> Result<Arc<MirBody>, MirLowerError> {
|
||||||
let (owner, _) = db.lookup_intern_closure(closure.into());
|
let InternedClosure(owner, _) = db.lookup_intern_closure(closure.into());
|
||||||
let generics = owner.as_generic_def_id().map(|g_def| generics(db.upcast(), g_def));
|
let generics = owner.as_generic_def_id().map(|g_def| generics(db.upcast(), g_def));
|
||||||
let filler = &mut Filler { db, subst: &subst, trait_env, generics, owner };
|
let filler = &mut Filler { db, subst: &subst, trait_env, generics, owner };
|
||||||
let body = db.mir_body_for_closure(closure)?;
|
let body = db.mir_body_for_closure(closure)?;
|
||||||
|
|
|
@ -10,7 +10,7 @@ mod regression;
|
||||||
mod simple;
|
mod simple;
|
||||||
mod traits;
|
mod traits;
|
||||||
|
|
||||||
use std::{collections::HashMap, env};
|
use std::env;
|
||||||
|
|
||||||
use base_db::{FileRange, SourceDatabaseExt};
|
use base_db::{FileRange, SourceDatabaseExt};
|
||||||
use expect_test::Expect;
|
use expect_test::Expect;
|
||||||
|
@ -25,6 +25,7 @@ use hir_def::{
|
||||||
};
|
};
|
||||||
use hir_expand::{db::ExpandDatabase, InFile};
|
use hir_expand::{db::ExpandDatabase, InFile};
|
||||||
use once_cell::race::OnceBool;
|
use once_cell::race::OnceBool;
|
||||||
|
use rustc_hash::FxHashMap;
|
||||||
use stdx::format_to;
|
use stdx::format_to;
|
||||||
use syntax::{
|
use syntax::{
|
||||||
ast::{self, AstNode, HasName},
|
ast::{self, AstNode, HasName},
|
||||||
|
@ -90,16 +91,16 @@ fn check_impl(ra_fixture: &str, allow_none: bool, only_types: bool, display_sour
|
||||||
let (db, files) = TestDB::with_many_files(ra_fixture);
|
let (db, files) = TestDB::with_many_files(ra_fixture);
|
||||||
|
|
||||||
let mut had_annotations = false;
|
let mut had_annotations = false;
|
||||||
let mut mismatches = HashMap::new();
|
let mut mismatches = FxHashMap::default();
|
||||||
let mut types = HashMap::new();
|
let mut types = FxHashMap::default();
|
||||||
let mut adjustments = HashMap::<_, Vec<_>>::new();
|
let mut adjustments = FxHashMap::<_, Vec<_>>::default();
|
||||||
for (file_id, annotations) in db.extract_annotations() {
|
for (file_id, annotations) in db.extract_annotations() {
|
||||||
for (range, expected) in annotations {
|
for (range, expected) in annotations {
|
||||||
let file_range = FileRange { file_id, range };
|
let file_range = FileRange { file_id, range };
|
||||||
if only_types {
|
if only_types {
|
||||||
types.insert(file_range, expected);
|
types.insert(file_range, expected);
|
||||||
} else if expected.starts_with("type: ") {
|
} else if expected.starts_with("type: ") {
|
||||||
types.insert(file_range, expected.trim_start_matches("type: ").to_string());
|
types.insert(file_range, expected.trim_start_matches("type: ").to_owned());
|
||||||
} else if expected.starts_with("expected") {
|
} else if expected.starts_with("expected") {
|
||||||
mismatches.insert(file_range, expected);
|
mismatches.insert(file_range, expected);
|
||||||
} else if expected.starts_with("adjustments:") {
|
} else if expected.starts_with("adjustments:") {
|
||||||
|
@ -109,7 +110,7 @@ fn check_impl(ra_fixture: &str, allow_none: bool, only_types: bool, display_sour
|
||||||
.trim_start_matches("adjustments:")
|
.trim_start_matches("adjustments:")
|
||||||
.trim()
|
.trim()
|
||||||
.split(',')
|
.split(',')
|
||||||
.map(|it| it.trim().to_string())
|
.map(|it| it.trim().to_owned())
|
||||||
.filter(|it| !it.is_empty())
|
.filter(|it| !it.is_empty())
|
||||||
.collect(),
|
.collect(),
|
||||||
);
|
);
|
||||||
|
@ -330,7 +331,7 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String {
|
||||||
});
|
});
|
||||||
for (node, ty) in &types {
|
for (node, ty) in &types {
|
||||||
let (range, text) = if let Some(self_param) = ast::SelfParam::cast(node.value.clone()) {
|
let (range, text) = if let Some(self_param) = ast::SelfParam::cast(node.value.clone()) {
|
||||||
(self_param.name().unwrap().syntax().text_range(), "self".to_string())
|
(self_param.name().unwrap().syntax().text_range(), "self".to_owned())
|
||||||
} else {
|
} else {
|
||||||
(node.value.text_range(), node.value.text().to_string().replace('\n', " "))
|
(node.value.text_range(), node.value.text().to_string().replace('\n', " "))
|
||||||
};
|
};
|
||||||
|
|
|
@ -1373,3 +1373,34 @@ pub fn attr_macro() {}
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn clone_with_type_bound() {
|
||||||
|
check_types(
|
||||||
|
r#"
|
||||||
|
//- minicore: derive, clone, builtin_impls
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct Float;
|
||||||
|
|
||||||
|
trait TensorKind: Clone {
|
||||||
|
/// The primitive type of the tensor.
|
||||||
|
type Primitive: Clone;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TensorKind for Float {
|
||||||
|
type Primitive = f64;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct Tensor<K = Float> where K: TensorKind
|
||||||
|
{
|
||||||
|
primitive: K::Primitive,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn foo(t: Tensor) {
|
||||||
|
let x = t.clone();
|
||||||
|
//^ Tensor<Float>
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -104,8 +104,8 @@ pub(crate) fn trait_solve_query(
|
||||||
GoalData::DomainGoal(DomainGoal::Holds(WhereClause::Implemented(it))) => {
|
GoalData::DomainGoal(DomainGoal::Holds(WhereClause::Implemented(it))) => {
|
||||||
db.trait_data(it.hir_trait_id()).name.display(db.upcast()).to_string()
|
db.trait_data(it.hir_trait_id()).name.display(db.upcast()).to_string()
|
||||||
}
|
}
|
||||||
GoalData::DomainGoal(DomainGoal::Holds(WhereClause::AliasEq(_))) => "alias_eq".to_string(),
|
GoalData::DomainGoal(DomainGoal::Holds(WhereClause::AliasEq(_))) => "alias_eq".to_owned(),
|
||||||
_ => "??".to_string(),
|
_ => "??".to_owned(),
|
||||||
};
|
};
|
||||||
let _p = tracing::span!(tracing::Level::INFO, "trait_solve_query", ?detail).entered();
|
let _p = tracing::span!(tracing::Level::INFO, "trait_solve_query", ?detail).entered();
|
||||||
tracing::info!("trait_solve_query({:?})", goal.value.goal);
|
tracing::info!("trait_solve_query({:?})", goal.value.goal);
|
||||||
|
@ -187,7 +187,7 @@ struct LoggingRustIrDatabaseLoggingOnDrop<'a>(LoggingRustIrDatabase<Interner, Ch
|
||||||
|
|
||||||
impl Drop for LoggingRustIrDatabaseLoggingOnDrop<'_> {
|
impl Drop for LoggingRustIrDatabaseLoggingOnDrop<'_> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
eprintln!("chalk program:\n{}", self.0);
|
tracing::info!("chalk program:\n{}", self.0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,7 @@ macro_rules! impl_has_attrs {
|
||||||
impl HasAttrs for $def {
|
impl HasAttrs for $def {
|
||||||
fn attrs(self, db: &dyn HirDatabase) -> AttrsWithOwner {
|
fn attrs(self, db: &dyn HirDatabase) -> AttrsWithOwner {
|
||||||
let def = AttrDefId::$def_id(self.into());
|
let def = AttrDefId::$def_id(self.into());
|
||||||
AttrsWithOwner::attrs_with_owner(db.upcast(), def)
|
AttrsWithOwner::new(db.upcast(), def)
|
||||||
}
|
}
|
||||||
fn attr_id(self) -> AttrDefId {
|
fn attr_id(self) -> AttrDefId {
|
||||||
AttrDefId::$def_id(self.into())
|
AttrDefId::$def_id(self.into())
|
||||||
|
|
|
@ -67,6 +67,8 @@ diagnostics![
|
||||||
NoSuchField,
|
NoSuchField,
|
||||||
PrivateAssocItem,
|
PrivateAssocItem,
|
||||||
PrivateField,
|
PrivateField,
|
||||||
|
RemoveTrailingReturn,
|
||||||
|
RemoveUnnecessaryElse,
|
||||||
ReplaceFilterMapNextWithFindMap,
|
ReplaceFilterMapNextWithFindMap,
|
||||||
TraitImplIncorrectSafety,
|
TraitImplIncorrectSafety,
|
||||||
TraitImplMissingAssocItems,
|
TraitImplMissingAssocItems,
|
||||||
|
@ -342,6 +344,16 @@ pub struct TraitImplRedundantAssocItems {
|
||||||
pub assoc_item: (Name, AssocItem),
|
pub assoc_item: (Name, AssocItem),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct RemoveTrailingReturn {
|
||||||
|
pub return_expr: InFile<AstPtr<ast::ReturnExpr>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct RemoveUnnecessaryElse {
|
||||||
|
pub if_expr: InFile<AstPtr<ast::IfExpr>>,
|
||||||
|
}
|
||||||
|
|
||||||
impl AnyDiagnostic {
|
impl AnyDiagnostic {
|
||||||
pub(crate) fn body_validation_diagnostic(
|
pub(crate) fn body_validation_diagnostic(
|
||||||
db: &dyn HirDatabase,
|
db: &dyn HirDatabase,
|
||||||
|
@ -444,6 +456,29 @@ impl AnyDiagnostic {
|
||||||
Err(SyntheticSyntax) => (),
|
Err(SyntheticSyntax) => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
BodyValidationDiagnostic::RemoveTrailingReturn { return_expr } => {
|
||||||
|
if let Ok(source_ptr) = source_map.expr_syntax(return_expr) {
|
||||||
|
// Filters out desugared return expressions (e.g. desugared try operators).
|
||||||
|
if let Some(ptr) = source_ptr.value.cast::<ast::ReturnExpr>() {
|
||||||
|
return Some(
|
||||||
|
RemoveTrailingReturn {
|
||||||
|
return_expr: InFile::new(source_ptr.file_id, ptr),
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BodyValidationDiagnostic::RemoveUnnecessaryElse { if_expr } => {
|
||||||
|
if let Ok(source_ptr) = source_map.expr_syntax(if_expr) {
|
||||||
|
if let Some(ptr) = source_ptr.value.cast::<ast::IfExpr>() {
|
||||||
|
return Some(
|
||||||
|
RemoveUnnecessaryElse { if_expr: InFile::new(source_ptr.file_id, ptr) }
|
||||||
|
.into(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
@ -546,9 +581,7 @@ impl AnyDiagnostic {
|
||||||
source_map.pat_syntax(pat).expect("unexpected synthetic");
|
source_map.pat_syntax(pat).expect("unexpected synthetic");
|
||||||
|
|
||||||
// cast from Either<Pat, SelfParam> -> Either<_, Pat>
|
// cast from Either<Pat, SelfParam> -> Either<_, Pat>
|
||||||
let Some(ptr) = AstPtr::try_from_raw(value.syntax_node_ptr()) else {
|
let ptr = AstPtr::try_from_raw(value.syntax_node_ptr())?;
|
||||||
return None;
|
|
||||||
};
|
|
||||||
InFile { file_id, value: ptr }
|
InFile { file_id, value: ptr }
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -158,7 +158,8 @@ impl HirDisplay for Adt {
|
||||||
|
|
||||||
impl HirDisplay for Struct {
|
impl HirDisplay for Struct {
|
||||||
fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
|
fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
|
||||||
write_visibility(self.module(f.db).id, self.visibility(f.db), f)?;
|
let module_id = self.module(f.db).id;
|
||||||
|
write_visibility(module_id, self.visibility(f.db), f)?;
|
||||||
f.write_str("struct ")?;
|
f.write_str("struct ")?;
|
||||||
write!(f, "{}", self.name(f.db).display(f.db.upcast()))?;
|
write!(f, "{}", self.name(f.db).display(f.db.upcast()))?;
|
||||||
let def_id = GenericDefId::AdtId(AdtId::StructId(self.id));
|
let def_id = GenericDefId::AdtId(AdtId::StructId(self.id));
|
||||||
|
@ -171,6 +172,7 @@ impl HirDisplay for Struct {
|
||||||
|
|
||||||
while let Some((id, _)) = it.next() {
|
while let Some((id, _)) = it.next() {
|
||||||
let field = Field { parent: (*self).into(), id };
|
let field = Field { parent: (*self).into(), id };
|
||||||
|
write_visibility(module_id, field.visibility(f.db), f)?;
|
||||||
field.ty(f.db).hir_fmt(f)?;
|
field.ty(f.db).hir_fmt(f)?;
|
||||||
if it.peek().is_some() {
|
if it.peek().is_some() {
|
||||||
f.write_str(", ")?;
|
f.write_str(", ")?;
|
||||||
|
|
|
@ -44,7 +44,7 @@ use hir_def::{
|
||||||
data::adt::VariantData,
|
data::adt::VariantData,
|
||||||
generics::{LifetimeParamData, TypeOrConstParamData, TypeParamProvenance},
|
generics::{LifetimeParamData, TypeOrConstParamData, TypeParamProvenance},
|
||||||
hir::{BindingAnnotation, BindingId, ExprOrPatId, LabelId, Pat},
|
hir::{BindingAnnotation, BindingId, ExprOrPatId, LabelId, Pat},
|
||||||
item_tree::ItemTreeModItemNode,
|
item_tree::ItemTreeNode,
|
||||||
lang_item::LangItemTarget,
|
lang_item::LangItemTarget,
|
||||||
layout::{self, ReprOptions, TargetDataLayout},
|
layout::{self, ReprOptions, TargetDataLayout},
|
||||||
nameres::{self, diagnostics::DefDiagnostic},
|
nameres::{self, diagnostics::DefDiagnostic},
|
||||||
|
@ -62,6 +62,7 @@ use hir_expand::{attrs::collect_attrs, name::name, proc_macro::ProcMacroKind, Ma
|
||||||
use hir_ty::{
|
use hir_ty::{
|
||||||
all_super_traits, autoderef, check_orphan_rules,
|
all_super_traits, autoderef, check_orphan_rules,
|
||||||
consteval::{try_const_usize, unknown_const_as_generic, ConstExt},
|
consteval::{try_const_usize, unknown_const_as_generic, ConstExt},
|
||||||
|
db::InternedClosure,
|
||||||
diagnostics::BodyValidationDiagnostic,
|
diagnostics::BodyValidationDiagnostic,
|
||||||
known_const_to_ast,
|
known_const_to_ast,
|
||||||
layout::{Layout as TyLayout, RustcEnumVariantIdx, RustcFieldIdx, TagEncoding},
|
layout::{Layout as TyLayout, RustcEnumVariantIdx, RustcFieldIdx, TagEncoding},
|
||||||
|
@ -563,6 +564,11 @@ impl Module {
|
||||||
for diag in db.trait_data_with_diagnostics(t.id).1.iter() {
|
for diag in db.trait_data_with_diagnostics(t.id).1.iter() {
|
||||||
emit_def_diagnostic(db, acc, diag);
|
emit_def_diagnostic(db, acc, diag);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for item in t.items(db) {
|
||||||
|
item.diagnostics(db, acc);
|
||||||
|
}
|
||||||
|
|
||||||
acc.extend(def.diagnostics(db))
|
acc.extend(def.diagnostics(db))
|
||||||
}
|
}
|
||||||
ModuleDef::Adt(adt) => {
|
ModuleDef::Adt(adt) => {
|
||||||
|
@ -730,13 +736,7 @@ impl Module {
|
||||||
}
|
}
|
||||||
|
|
||||||
for &item in &db.impl_data(impl_def.id).items {
|
for &item in &db.impl_data(impl_def.id).items {
|
||||||
let def: DefWithBody = match AssocItem::from(item) {
|
AssocItem::from(item).diagnostics(db, acc);
|
||||||
AssocItem::Function(it) => it.into(),
|
|
||||||
AssocItem::Const(it) => it.into(),
|
|
||||||
AssocItem::TypeAlias(_) => continue,
|
|
||||||
};
|
|
||||||
|
|
||||||
def.diagnostics(db, acc);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1769,7 +1769,7 @@ pub struct Function {
|
||||||
|
|
||||||
impl Function {
|
impl Function {
|
||||||
pub fn module(self, db: &dyn HirDatabase) -> Module {
|
pub fn module(self, db: &dyn HirDatabase) -> Module {
|
||||||
self.id.lookup(db.upcast()).module(db.upcast()).into()
|
self.id.module(db.upcast()).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn name(self, db: &dyn HirDatabase) -> Name {
|
pub fn name(self, db: &dyn HirDatabase) -> Name {
|
||||||
|
@ -1910,8 +1910,7 @@ impl Function {
|
||||||
{
|
{
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let loc = self.id.lookup(db.upcast());
|
let def_map = db.crate_def_map(HasModule::krate(&self.id, db.upcast()));
|
||||||
let def_map = db.crate_def_map(loc.krate(db).into());
|
|
||||||
def_map.fn_as_proc_macro(self.id).map(|id| Macro { id: id.into() })
|
def_map.fn_as_proc_macro(self.id).map(|id| Macro { id: id.into() })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1934,7 +1933,7 @@ impl Function {
|
||||||
};
|
};
|
||||||
let (result, output) = interpret_mir(db, body, false, None);
|
let (result, output) = interpret_mir(db, body, false, None);
|
||||||
let mut text = match result {
|
let mut text = match result {
|
||||||
Ok(_) => "pass".to_string(),
|
Ok(_) => "pass".to_owned(),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let mut r = String::new();
|
let mut r = String::new();
|
||||||
_ = e.pretty_print(&mut r, db, &span_formatter);
|
_ = e.pretty_print(&mut r, db, &span_formatter);
|
||||||
|
@ -2120,7 +2119,7 @@ pub struct Const {
|
||||||
|
|
||||||
impl Const {
|
impl Const {
|
||||||
pub fn module(self, db: &dyn HirDatabase) -> Module {
|
pub fn module(self, db: &dyn HirDatabase) -> Module {
|
||||||
Module { id: self.id.lookup(db.upcast()).module(db.upcast()) }
|
Module { id: self.id.module(db.upcast()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn name(self, db: &dyn HirDatabase) -> Option<Name> {
|
pub fn name(self, db: &dyn HirDatabase) -> Option<Name> {
|
||||||
|
@ -2175,7 +2174,7 @@ pub struct Static {
|
||||||
|
|
||||||
impl Static {
|
impl Static {
|
||||||
pub fn module(self, db: &dyn HirDatabase) -> Module {
|
pub fn module(self, db: &dyn HirDatabase) -> Module {
|
||||||
Module { id: self.id.lookup(db.upcast()).module(db.upcast()) }
|
Module { id: self.id.module(db.upcast()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn name(self, db: &dyn HirDatabase) -> Name {
|
pub fn name(self, db: &dyn HirDatabase) -> Name {
|
||||||
|
@ -2294,7 +2293,7 @@ impl TypeAlias {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn module(self, db: &dyn HirDatabase) -> Module {
|
pub fn module(self, db: &dyn HirDatabase) -> Module {
|
||||||
Module { id: self.id.lookup(db.upcast()).module(db.upcast()) }
|
Module { id: self.id.module(db.upcast()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn type_ref(self, db: &dyn HirDatabase) -> Option<TypeRef> {
|
pub fn type_ref(self, db: &dyn HirDatabase) -> Option<TypeRef> {
|
||||||
|
@ -2517,11 +2516,13 @@ pub enum AssocItem {
|
||||||
Const(Const),
|
Const(Const),
|
||||||
TypeAlias(TypeAlias),
|
TypeAlias(TypeAlias),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum AssocItemContainer {
|
pub enum AssocItemContainer {
|
||||||
Trait(Trait),
|
Trait(Trait),
|
||||||
Impl(Impl),
|
Impl(Impl),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait AsAssocItem {
|
pub trait AsAssocItem {
|
||||||
fn as_assoc_item(self, db: &dyn HirDatabase) -> Option<AssocItem>;
|
fn as_assoc_item(self, db: &dyn HirDatabase) -> Option<AssocItem>;
|
||||||
}
|
}
|
||||||
|
@ -2531,16 +2532,19 @@ impl AsAssocItem for Function {
|
||||||
as_assoc_item(db, AssocItem::Function, self.id)
|
as_assoc_item(db, AssocItem::Function, self.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsAssocItem for Const {
|
impl AsAssocItem for Const {
|
||||||
fn as_assoc_item(self, db: &dyn HirDatabase) -> Option<AssocItem> {
|
fn as_assoc_item(self, db: &dyn HirDatabase) -> Option<AssocItem> {
|
||||||
as_assoc_item(db, AssocItem::Const, self.id)
|
as_assoc_item(db, AssocItem::Const, self.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsAssocItem for TypeAlias {
|
impl AsAssocItem for TypeAlias {
|
||||||
fn as_assoc_item(self, db: &dyn HirDatabase) -> Option<AssocItem> {
|
fn as_assoc_item(self, db: &dyn HirDatabase) -> Option<AssocItem> {
|
||||||
as_assoc_item(db, AssocItem::TypeAlias, self.id)
|
as_assoc_item(db, AssocItem::TypeAlias, self.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsAssocItem for ModuleDef {
|
impl AsAssocItem for ModuleDef {
|
||||||
fn as_assoc_item(self, db: &dyn HirDatabase) -> Option<AssocItem> {
|
fn as_assoc_item(self, db: &dyn HirDatabase) -> Option<AssocItem> {
|
||||||
match self {
|
match self {
|
||||||
|
@ -2551,6 +2555,7 @@ impl AsAssocItem for ModuleDef {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsAssocItem for DefWithBody {
|
impl AsAssocItem for DefWithBody {
|
||||||
fn as_assoc_item(self, db: &dyn HirDatabase) -> Option<AssocItem> {
|
fn as_assoc_item(self, db: &dyn HirDatabase) -> Option<AssocItem> {
|
||||||
match self {
|
match self {
|
||||||
|
@ -2561,16 +2566,15 @@ impl AsAssocItem for DefWithBody {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_assoc_item<'db, ID, DEF, CTOR, AST>(
|
fn as_assoc_item<'db, ID, DEF, LOC>(
|
||||||
db: &(dyn HirDatabase + 'db),
|
db: &(dyn HirDatabase + 'db),
|
||||||
ctor: CTOR,
|
ctor: impl FnOnce(DEF) -> AssocItem,
|
||||||
id: ID,
|
id: ID,
|
||||||
) -> Option<AssocItem>
|
) -> Option<AssocItem>
|
||||||
where
|
where
|
||||||
ID: Lookup<Database<'db> = dyn DefDatabase + 'db, Data = AssocItemLoc<AST>>,
|
ID: Lookup<Database<'db> = dyn DefDatabase + 'db, Data = AssocItemLoc<LOC>>,
|
||||||
DEF: From<ID>,
|
DEF: From<ID>,
|
||||||
CTOR: FnOnce(DEF) -> AssocItem,
|
LOC: ItemTreeNode,
|
||||||
AST: ItemTreeModItemNode,
|
|
||||||
{
|
{
|
||||||
match id.lookup(db.upcast()).container {
|
match id.lookup(db.upcast()).container {
|
||||||
ItemContainerId::TraitId(_) | ItemContainerId::ImplId(_) => Some(ctor(DEF::from(id))),
|
ItemContainerId::TraitId(_) | ItemContainerId::ImplId(_) => Some(ctor(DEF::from(id))),
|
||||||
|
@ -2610,27 +2614,34 @@ impl AssocItem {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn containing_trait(self, db: &dyn HirDatabase) -> Option<Trait> {
|
pub fn container_trait(self, db: &dyn HirDatabase) -> Option<Trait> {
|
||||||
match self.container(db) {
|
match self.container(db) {
|
||||||
AssocItemContainer::Trait(t) => Some(t),
|
AssocItemContainer::Trait(t) => Some(t),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn containing_trait_impl(self, db: &dyn HirDatabase) -> Option<Trait> {
|
pub fn implemented_trait(self, db: &dyn HirDatabase) -> Option<Trait> {
|
||||||
match self.container(db) {
|
match self.container(db) {
|
||||||
AssocItemContainer::Impl(i) => i.trait_(db),
|
AssocItemContainer::Impl(i) => i.trait_(db),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn containing_trait_or_trait_impl(self, db: &dyn HirDatabase) -> Option<Trait> {
|
pub fn container_or_implemented_trait(self, db: &dyn HirDatabase) -> Option<Trait> {
|
||||||
match self.container(db) {
|
match self.container(db) {
|
||||||
AssocItemContainer::Trait(t) => Some(t),
|
AssocItemContainer::Trait(t) => Some(t),
|
||||||
AssocItemContainer::Impl(i) => i.trait_(db),
|
AssocItemContainer::Impl(i) => i.trait_(db),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn implementing_ty(self, db: &dyn HirDatabase) -> Option<Type> {
|
||||||
|
match self.container(db) {
|
||||||
|
AssocItemContainer::Impl(i) => Some(i.self_ty(db)),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn as_function(self) -> Option<Function> {
|
pub fn as_function(self) -> Option<Function> {
|
||||||
match self {
|
match self {
|
||||||
Self::Function(v) => Some(v),
|
Self::Function(v) => Some(v),
|
||||||
|
@ -2651,6 +2662,22 @@ impl AssocItem {
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn diagnostics(self, db: &dyn HirDatabase, acc: &mut Vec<AnyDiagnostic>) {
|
||||||
|
match self {
|
||||||
|
AssocItem::Function(func) => {
|
||||||
|
DefWithBody::from(func).diagnostics(db, acc);
|
||||||
|
}
|
||||||
|
AssocItem::Const(const_) => {
|
||||||
|
DefWithBody::from(const_).diagnostics(db, acc);
|
||||||
|
}
|
||||||
|
AssocItem::TypeAlias(type_alias) => {
|
||||||
|
for diag in hir_ty::diagnostics::incorrect_case(db, type_alias.id.into()) {
|
||||||
|
acc.push(diag.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HasVisibility for AssocItem {
|
impl HasVisibility for AssocItem {
|
||||||
|
@ -3306,7 +3333,7 @@ impl Impl {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn items(self, db: &dyn HirDatabase) -> Vec<AssocItem> {
|
pub fn items(self, db: &dyn HirDatabase) -> Vec<AssocItem> {
|
||||||
db.impl_data(self.id).items.iter().map(|it| (*it).into()).collect()
|
db.impl_data(self.id).items.iter().map(|&it| it.into()).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_negative(self, db: &dyn HirDatabase) -> bool {
|
pub fn is_negative(self, db: &dyn HirDatabase) -> bool {
|
||||||
|
@ -3662,7 +3689,7 @@ impl Type {
|
||||||
.and_then(|it| {
|
.and_then(|it| {
|
||||||
let into_future_fn = it.as_function()?;
|
let into_future_fn = it.as_function()?;
|
||||||
let assoc_item = as_assoc_item(db, AssocItem::Function, into_future_fn)?;
|
let assoc_item = as_assoc_item(db, AssocItem::Function, into_future_fn)?;
|
||||||
let into_future_trait = assoc_item.containing_trait_or_trait_impl(db)?;
|
let into_future_trait = assoc_item.container_or_implemented_trait(db)?;
|
||||||
Some(into_future_trait.id)
|
Some(into_future_trait.id)
|
||||||
})
|
})
|
||||||
.or_else(|| {
|
.or_else(|| {
|
||||||
|
@ -4101,6 +4128,14 @@ impl Type {
|
||||||
name: Option<&Name>,
|
name: Option<&Name>,
|
||||||
callback: &mut dyn FnMut(AssocItemId) -> ControlFlow<()>,
|
callback: &mut dyn FnMut(AssocItemId) -> ControlFlow<()>,
|
||||||
) {
|
) {
|
||||||
|
let _p = tracing::span!(
|
||||||
|
tracing::Level::INFO,
|
||||||
|
"iterate_method_candidates_dyn",
|
||||||
|
with_local_impls = traits_in_scope.len(),
|
||||||
|
traits_in_scope = traits_in_scope.len(),
|
||||||
|
?name,
|
||||||
|
)
|
||||||
|
.entered();
|
||||||
// There should be no inference vars in types passed here
|
// There should be no inference vars in types passed here
|
||||||
let canonical = hir_ty::replace_errors_with_variables(&self.ty);
|
let canonical = hir_ty::replace_errors_with_variables(&self.ty);
|
||||||
|
|
||||||
|
@ -4122,6 +4157,7 @@ impl Type {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(skip_all, fields(name = ?name))]
|
||||||
pub fn iterate_path_candidates<T>(
|
pub fn iterate_path_candidates<T>(
|
||||||
&self,
|
&self,
|
||||||
db: &dyn HirDatabase,
|
db: &dyn HirDatabase,
|
||||||
|
@ -4150,6 +4186,7 @@ impl Type {
|
||||||
slot
|
slot
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(skip_all, fields(name = ?name))]
|
||||||
fn iterate_path_candidates_dyn(
|
fn iterate_path_candidates_dyn(
|
||||||
&self,
|
&self,
|
||||||
db: &dyn HirDatabase,
|
db: &dyn HirDatabase,
|
||||||
|
@ -4463,7 +4500,7 @@ impl Callable {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn closure_source(db: &dyn HirDatabase, closure: ClosureId) -> Option<ast::ClosureExpr> {
|
fn closure_source(db: &dyn HirDatabase, closure: ClosureId) -> Option<ast::ClosureExpr> {
|
||||||
let (owner, expr_id) = db.lookup_intern_closure(closure.into());
|
let InternedClosure(owner, expr_id) = db.lookup_intern_closure(closure.into());
|
||||||
let (_, source_map) = db.body_with_source_map(owner);
|
let (_, source_map) = db.body_with_source_map(owner);
|
||||||
let ast = source_map.expr_syntax(expr_id).ok()?;
|
let ast = source_map.expr_syntax(expr_id).ok()?;
|
||||||
let root = ast.file_syntax(db.upcast());
|
let root = ast.file_syntax(db.upcast());
|
||||||
|
|
|
@ -96,7 +96,7 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
|
||||||
let dm_lhs = demorganed.lhs()?;
|
let dm_lhs = demorganed.lhs()?;
|
||||||
|
|
||||||
acc.add_group(
|
acc.add_group(
|
||||||
&GroupLabel("Apply De Morgan's law".to_string()),
|
&GroupLabel("Apply De Morgan's law".to_owned()),
|
||||||
AssistId("apply_demorgan", AssistKind::RefactorRewrite),
|
AssistId("apply_demorgan", AssistKind::RefactorRewrite),
|
||||||
"Apply De Morgan's law",
|
"Apply De Morgan's law",
|
||||||
op_range,
|
op_range,
|
||||||
|
@ -187,7 +187,7 @@ pub(crate) fn apply_demorgan_iterator(acc: &mut Assists, ctx: &AssistContext<'_>
|
||||||
let op_range = method_call.syntax().text_range();
|
let op_range = method_call.syntax().text_range();
|
||||||
let label = format!("Apply De Morgan's law to `Iterator::{}`", name.text().as_str());
|
let label = format!("Apply De Morgan's law to `Iterator::{}`", name.text().as_str());
|
||||||
acc.add_group(
|
acc.add_group(
|
||||||
&GroupLabel("Apply De Morgan's law".to_string()),
|
&GroupLabel("Apply De Morgan's law".to_owned()),
|
||||||
AssistId("apply_demorgan_iterator", AssistKind::RefactorRewrite),
|
AssistId("apply_demorgan_iterator", AssistKind::RefactorRewrite),
|
||||||
label,
|
label,
|
||||||
op_range,
|
op_range,
|
||||||
|
|
|
@ -163,9 +163,8 @@ pub(crate) fn convert_bool_then_to_if(acc: &mut Assists, ctx: &AssistContext<'_>
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let assoc = func.as_assoc_item(ctx.sema.db)?;
|
let assoc = func.as_assoc_item(ctx.sema.db)?;
|
||||||
match assoc.container(ctx.sema.db) {
|
if !assoc.implementing_ty(ctx.sema.db)?.is_bool() {
|
||||||
hir::AssocItemContainer::Impl(impl_) if impl_.self_ty(ctx.sema.db).is_bool() => {}
|
return None;
|
||||||
_ => return None,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let target = mcall.syntax().text_range();
|
let target = mcall.syntax().text_range();
|
||||||
|
|
|
@ -57,7 +57,7 @@ fn block_to_line(acc: &mut Assists, comment: ast::Comment) -> Option<()> {
|
||||||
|
|
||||||
// Don't introduce trailing whitespace
|
// Don't introduce trailing whitespace
|
||||||
if line.is_empty() {
|
if line.is_empty() {
|
||||||
line_prefix.to_string()
|
line_prefix.to_owned()
|
||||||
} else {
|
} else {
|
||||||
format!("{line_prefix} {line}")
|
format!("{line_prefix} {line}")
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
use std::iter::once;
|
use std::iter::once;
|
||||||
|
|
||||||
use ide_db::syntax_helpers::node_ext::{is_pattern_cond, single_let};
|
use ide_db::{
|
||||||
|
syntax_helpers::node_ext::{is_pattern_cond, single_let},
|
||||||
|
ty_filter::TryEnum,
|
||||||
|
};
|
||||||
use syntax::{
|
use syntax::{
|
||||||
ast::{
|
ast::{
|
||||||
self,
|
self,
|
||||||
|
@ -41,13 +44,35 @@ use crate::{
|
||||||
// }
|
// }
|
||||||
// ```
|
// ```
|
||||||
pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
|
pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
|
||||||
let if_expr: ast::IfExpr = ctx.find_node_at_offset()?;
|
if let Some(let_stmt) = ctx.find_node_at_offset() {
|
||||||
|
let_stmt_to_guarded_return(let_stmt, acc, ctx)
|
||||||
|
} else if let Some(if_expr) = ctx.find_node_at_offset() {
|
||||||
|
if_expr_to_guarded_return(if_expr, acc, ctx)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn if_expr_to_guarded_return(
|
||||||
|
if_expr: ast::IfExpr,
|
||||||
|
acc: &mut Assists,
|
||||||
|
ctx: &AssistContext<'_>,
|
||||||
|
) -> Option<()> {
|
||||||
if if_expr.else_branch().is_some() {
|
if if_expr.else_branch().is_some() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let cond = if_expr.condition()?;
|
let cond = if_expr.condition()?;
|
||||||
|
|
||||||
|
let if_token_range = if_expr.if_token()?.text_range();
|
||||||
|
let if_cond_range = cond.syntax().text_range();
|
||||||
|
|
||||||
|
let cursor_in_range =
|
||||||
|
if_token_range.cover(if_cond_range).contains_range(ctx.selection_trimmed());
|
||||||
|
if !cursor_in_range {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
// Check if there is an IfLet that we can handle.
|
// Check if there is an IfLet that we can handle.
|
||||||
let (if_let_pat, cond_expr) = if is_pattern_cond(cond.clone()) {
|
let (if_let_pat, cond_expr) = if is_pattern_cond(cond.clone()) {
|
||||||
let let_ = single_let(cond)?;
|
let let_ = single_let(cond)?;
|
||||||
|
@ -148,6 +173,65 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext<'
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn let_stmt_to_guarded_return(
|
||||||
|
let_stmt: ast::LetStmt,
|
||||||
|
acc: &mut Assists,
|
||||||
|
ctx: &AssistContext<'_>,
|
||||||
|
) -> Option<()> {
|
||||||
|
let pat = let_stmt.pat()?;
|
||||||
|
let expr = let_stmt.initializer()?;
|
||||||
|
|
||||||
|
let let_token_range = let_stmt.let_token()?.text_range();
|
||||||
|
let let_pattern_range = pat.syntax().text_range();
|
||||||
|
let cursor_in_range =
|
||||||
|
let_token_range.cover(let_pattern_range).contains_range(ctx.selection_trimmed());
|
||||||
|
|
||||||
|
if !cursor_in_range {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let try_enum =
|
||||||
|
ctx.sema.type_of_expr(&expr).and_then(|ty| TryEnum::from_ty(&ctx.sema, &ty.adjusted()))?;
|
||||||
|
|
||||||
|
let happy_pattern = try_enum.happy_pattern(pat);
|
||||||
|
let target = let_stmt.syntax().text_range();
|
||||||
|
|
||||||
|
let early_expression: ast::Expr = {
|
||||||
|
let parent_block =
|
||||||
|
let_stmt.syntax().parent()?.ancestors().find_map(ast::BlockExpr::cast)?;
|
||||||
|
let parent_container = parent_block.syntax().parent()?;
|
||||||
|
|
||||||
|
match parent_container.kind() {
|
||||||
|
WHILE_EXPR | LOOP_EXPR | FOR_EXPR => make::expr_continue(None),
|
||||||
|
FN => make::expr_return(None),
|
||||||
|
_ => return None,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
acc.add(
|
||||||
|
AssistId("convert_to_guarded_return", AssistKind::RefactorRewrite),
|
||||||
|
"Convert to guarded return",
|
||||||
|
target,
|
||||||
|
|edit| {
|
||||||
|
let let_stmt = edit.make_mut(let_stmt);
|
||||||
|
let let_indent_level = IndentLevel::from_node(let_stmt.syntax());
|
||||||
|
|
||||||
|
let replacement = {
|
||||||
|
let let_else_stmt = make::let_else_stmt(
|
||||||
|
happy_pattern,
|
||||||
|
let_stmt.ty(),
|
||||||
|
expr,
|
||||||
|
ast::make::tail_only_block_expr(early_expression),
|
||||||
|
);
|
||||||
|
let let_else_stmt = let_else_stmt.indent(let_indent_level);
|
||||||
|
let_else_stmt.syntax().clone_for_update()
|
||||||
|
};
|
||||||
|
|
||||||
|
ted::replace(let_stmt.syntax(), replacement)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::tests::{check_assist, check_assist_not_applicable};
|
use crate::tests::{check_assist, check_assist_not_applicable};
|
||||||
|
@ -450,6 +534,62 @@ fn main() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn convert_let_stmt_inside_fn() {
|
||||||
|
check_assist(
|
||||||
|
convert_to_guarded_return,
|
||||||
|
r#"
|
||||||
|
//- minicore: option
|
||||||
|
fn foo() -> Option<i32> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let x$0 = foo();
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
fn foo() -> Option<i32> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let Some(x) = foo() else { return };
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn convert_let_stmt_inside_loop() {
|
||||||
|
check_assist(
|
||||||
|
convert_to_guarded_return,
|
||||||
|
r#"
|
||||||
|
//- minicore: option
|
||||||
|
fn foo() -> Option<i32> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
loop {
|
||||||
|
let x$0 = foo();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
fn foo() -> Option<i32> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
loop {
|
||||||
|
let Some(x) = foo() else { continue };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn convert_arbitrary_if_let_patterns() {
|
fn convert_arbitrary_if_let_patterns() {
|
||||||
check_assist(
|
check_assist(
|
||||||
|
@ -591,6 +731,37 @@ fn main() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn ignore_inside_if_stmt() {
|
||||||
|
check_assist_not_applicable(
|
||||||
|
convert_to_guarded_return,
|
||||||
|
r#"
|
||||||
|
fn main() {
|
||||||
|
if false {
|
||||||
|
foo()$0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn ignore_inside_let_initializer() {
|
||||||
|
check_assist_not_applicable(
|
||||||
|
convert_to_guarded_return,
|
||||||
|
r#"
|
||||||
|
//- minicore: option
|
||||||
|
fn foo() -> Option<i32> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let x = foo()$0;
|
||||||
|
}
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,9 +27,7 @@ use crate::{
|
||||||
pub(crate) fn desugar_doc_comment(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
|
pub(crate) fn desugar_doc_comment(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
|
||||||
let comment = ctx.find_token_at_offset::<ast::Comment>()?;
|
let comment = ctx.find_token_at_offset::<ast::Comment>()?;
|
||||||
// Only allow doc comments
|
// Only allow doc comments
|
||||||
let Some(placement) = comment.kind().doc else {
|
let placement = comment.kind().doc?;
|
||||||
return None;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Only allow comments which are alone on their line
|
// Only allow comments which are alone on their line
|
||||||
if let Some(prev) = comment.syntax().prev_token() {
|
if let Some(prev) = comment.syntax().prev_token() {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use std::iter;
|
use std::{iter, ops::RangeInclusive};
|
||||||
|
|
||||||
use ast::make;
|
use ast::make;
|
||||||
use either::Either;
|
use either::Either;
|
||||||
|
@ -12,27 +12,25 @@ use ide_db::{
|
||||||
helpers::mod_path_to_ast,
|
helpers::mod_path_to_ast,
|
||||||
imports::insert_use::{insert_use, ImportScope},
|
imports::insert_use::{insert_use, ImportScope},
|
||||||
search::{FileReference, ReferenceCategory, SearchScope},
|
search::{FileReference, ReferenceCategory, SearchScope},
|
||||||
|
source_change::SourceChangeBuilder,
|
||||||
syntax_helpers::node_ext::{
|
syntax_helpers::node_ext::{
|
||||||
for_each_tail_expr, preorder_expr, walk_expr, walk_pat, walk_patterns_in_expr,
|
for_each_tail_expr, preorder_expr, walk_expr, walk_pat, walk_patterns_in_expr,
|
||||||
},
|
},
|
||||||
FxIndexSet, RootDatabase,
|
FxIndexSet, RootDatabase,
|
||||||
};
|
};
|
||||||
use itertools::Itertools;
|
|
||||||
use stdx::format_to;
|
|
||||||
use syntax::{
|
use syntax::{
|
||||||
ast::{
|
ast::{
|
||||||
self,
|
self, edit::IndentLevel, edit_in_place::Indent, AstNode, AstToken, HasGenericParams,
|
||||||
edit::{AstNodeEdit, IndentLevel},
|
HasName,
|
||||||
AstNode, HasGenericParams,
|
|
||||||
},
|
},
|
||||||
match_ast, ted, AstToken, SyntaxElement,
|
match_ast, ted, SyntaxElement,
|
||||||
SyntaxKind::{self, COMMENT},
|
SyntaxKind::{self, COMMENT},
|
||||||
SyntaxNode, SyntaxToken, TextRange, TextSize, TokenAtOffset, WalkEvent, T,
|
SyntaxNode, SyntaxToken, TextRange, TextSize, TokenAtOffset, WalkEvent, T,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
assist_context::{AssistContext, Assists, TreeMutator},
|
assist_context::{AssistContext, Assists, TreeMutator},
|
||||||
utils::generate_impl_text,
|
utils::generate_impl,
|
||||||
AssistId,
|
AssistId,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -134,17 +132,65 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
|
||||||
let new_indent = IndentLevel::from_node(&insert_after);
|
let new_indent = IndentLevel::from_node(&insert_after);
|
||||||
let old_indent = fun.body.indent_level();
|
let old_indent = fun.body.indent_level();
|
||||||
|
|
||||||
builder.replace(target_range, make_call(ctx, &fun, old_indent));
|
let insert_after = builder.make_syntax_mut(insert_after);
|
||||||
|
|
||||||
|
let call_expr = make_call(ctx, &fun, old_indent);
|
||||||
|
|
||||||
|
// Map the element range to replace into the mutable version
|
||||||
|
let elements = match &fun.body {
|
||||||
|
FunctionBody::Expr(expr) => {
|
||||||
|
// expr itself becomes the replacement target
|
||||||
|
let expr = &builder.make_mut(expr.clone());
|
||||||
|
let node = SyntaxElement::Node(expr.syntax().clone());
|
||||||
|
|
||||||
|
node.clone()..=node
|
||||||
|
}
|
||||||
|
FunctionBody::Span { parent, elements, .. } => {
|
||||||
|
// Map the element range into the mutable versions
|
||||||
|
let parent = builder.make_mut(parent.clone());
|
||||||
|
|
||||||
|
let start = parent
|
||||||
|
.syntax()
|
||||||
|
.children_with_tokens()
|
||||||
|
.nth(elements.start().index())
|
||||||
|
.expect("should be able to find mutable start element");
|
||||||
|
|
||||||
|
let end = parent
|
||||||
|
.syntax()
|
||||||
|
.children_with_tokens()
|
||||||
|
.nth(elements.end().index())
|
||||||
|
.expect("should be able to find mutable end element");
|
||||||
|
|
||||||
|
start..=end
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let has_impl_wrapper =
|
let has_impl_wrapper =
|
||||||
insert_after.ancestors().any(|a| a.kind() == SyntaxKind::IMPL && a != insert_after);
|
insert_after.ancestors().any(|a| a.kind() == SyntaxKind::IMPL && a != insert_after);
|
||||||
|
|
||||||
|
let fn_def = format_function(ctx, module, &fun, old_indent).clone_for_update();
|
||||||
|
|
||||||
|
if let Some(cap) = ctx.config.snippet_cap {
|
||||||
|
if let Some(name) = fn_def.name() {
|
||||||
|
builder.add_tabstop_before(cap, name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let fn_def = match fun.self_param_adt(ctx) {
|
let fn_def = match fun.self_param_adt(ctx) {
|
||||||
Some(adt) if anchor == Anchor::Method && !has_impl_wrapper => {
|
Some(adt) if anchor == Anchor::Method && !has_impl_wrapper => {
|
||||||
let fn_def = format_function(ctx, module, &fun, old_indent, new_indent + 1);
|
fn_def.indent(1.into());
|
||||||
generate_impl_text(&adt, &fn_def).replace("{\n\n", "{")
|
|
||||||
|
let impl_ = generate_impl(&adt);
|
||||||
|
impl_.indent(new_indent);
|
||||||
|
impl_.get_or_create_assoc_item_list().add_item(fn_def.into());
|
||||||
|
|
||||||
|
impl_.syntax().clone()
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
fn_def.indent(new_indent);
|
||||||
|
|
||||||
|
fn_def.syntax().clone()
|
||||||
}
|
}
|
||||||
_ => format_function(ctx, module, &fun, old_indent, new_indent),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// There are external control flows
|
// There are external control flows
|
||||||
|
@ -177,12 +223,15 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let insert_offset = insert_after.text_range().end();
|
// Replace the call site with the call to the new function
|
||||||
|
fixup_call_site(builder, &fun.body);
|
||||||
|
ted::replace_all(elements, vec![call_expr.into()]);
|
||||||
|
|
||||||
match ctx.config.snippet_cap {
|
// Insert the newly extracted function (or impl)
|
||||||
Some(cap) => builder.insert_snippet(cap, insert_offset, fn_def),
|
ted::insert_all_raw(
|
||||||
None => builder.insert(insert_offset, fn_def),
|
ted::Position::after(insert_after),
|
||||||
};
|
vec![make::tokens::whitespace(&format!("\n\n{new_indent}")).into(), fn_def.into()],
|
||||||
|
);
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -195,7 +244,7 @@ fn make_function_name(semantics_scope: &hir::SemanticsScope<'_>) -> ast::NameRef
|
||||||
|
|
||||||
let default_name = "fun_name";
|
let default_name = "fun_name";
|
||||||
|
|
||||||
let mut name = default_name.to_string();
|
let mut name = default_name.to_owned();
|
||||||
let mut counter = 0;
|
let mut counter = 0;
|
||||||
while names_in_scope.contains(&name) {
|
while names_in_scope.contains(&name) {
|
||||||
counter += 1;
|
counter += 1;
|
||||||
|
@ -225,10 +274,10 @@ fn extraction_target(node: &SyntaxNode, selection_range: TextRange) -> Option<Fu
|
||||||
if let Some(stmt) = ast::Stmt::cast(node.clone()) {
|
if let Some(stmt) = ast::Stmt::cast(node.clone()) {
|
||||||
return match stmt {
|
return match stmt {
|
||||||
ast::Stmt::Item(_) => None,
|
ast::Stmt::Item(_) => None,
|
||||||
ast::Stmt::ExprStmt(_) | ast::Stmt::LetStmt(_) => Some(FunctionBody::from_range(
|
ast::Stmt::ExprStmt(_) | ast::Stmt::LetStmt(_) => FunctionBody::from_range(
|
||||||
node.parent().and_then(ast::StmtList::cast)?,
|
node.parent().and_then(ast::StmtList::cast)?,
|
||||||
node.text_range(),
|
node.text_range(),
|
||||||
)),
|
),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -241,7 +290,7 @@ fn extraction_target(node: &SyntaxNode, selection_range: TextRange) -> Option<Fu
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract the full statements.
|
// Extract the full statements.
|
||||||
return Some(FunctionBody::from_range(stmt_list, selection_range));
|
return FunctionBody::from_range(stmt_list, selection_range);
|
||||||
}
|
}
|
||||||
|
|
||||||
let expr = ast::Expr::cast(node.clone())?;
|
let expr = ast::Expr::cast(node.clone())?;
|
||||||
|
@ -371,7 +420,7 @@ impl RetType {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum FunctionBody {
|
enum FunctionBody {
|
||||||
Expr(ast::Expr),
|
Expr(ast::Expr),
|
||||||
Span { parent: ast::StmtList, text_range: TextRange },
|
Span { parent: ast::StmtList, elements: RangeInclusive<SyntaxElement>, text_range: TextRange },
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -569,26 +618,38 @@ impl FunctionBody {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_range(parent: ast::StmtList, selected: TextRange) -> FunctionBody {
|
fn from_range(parent: ast::StmtList, selected: TextRange) -> Option<FunctionBody> {
|
||||||
let full_body = parent.syntax().children_with_tokens();
|
let full_body = parent.syntax().children_with_tokens();
|
||||||
|
|
||||||
let mut text_range = full_body
|
// Get all of the elements intersecting with the selection
|
||||||
|
let mut stmts_in_selection = full_body
|
||||||
.filter(|it| ast::Stmt::can_cast(it.kind()) || it.kind() == COMMENT)
|
.filter(|it| ast::Stmt::can_cast(it.kind()) || it.kind() == COMMENT)
|
||||||
.map(|element| element.text_range())
|
.filter(|it| selected.intersect(it.text_range()).filter(|it| !it.is_empty()).is_some());
|
||||||
.filter(|&range| selected.intersect(range).filter(|it| !it.is_empty()).is_some())
|
|
||||||
.reduce(|acc, stmt| acc.cover(stmt));
|
|
||||||
|
|
||||||
if let Some(tail_range) = parent
|
let first_element = stmts_in_selection.next();
|
||||||
.tail_expr()
|
|
||||||
.map(|it| it.syntax().text_range())
|
// If the tail expr is part of the selection too, make that the last element
|
||||||
.filter(|&it| selected.intersect(it).is_some())
|
// Otherwise use the last stmt
|
||||||
|
let last_element = if let Some(tail_expr) =
|
||||||
|
parent.tail_expr().filter(|it| selected.intersect(it.syntax().text_range()).is_some())
|
||||||
{
|
{
|
||||||
text_range = Some(match text_range {
|
Some(tail_expr.syntax().clone().into())
|
||||||
Some(text_range) => text_range.cover(tail_range),
|
} else {
|
||||||
None => tail_range,
|
stmts_in_selection.last()
|
||||||
});
|
};
|
||||||
|
|
||||||
|
let elements = match (first_element, last_element) {
|
||||||
|
(None, _) => {
|
||||||
|
cov_mark::hit!(extract_function_empty_selection_is_not_applicable);
|
||||||
|
return None;
|
||||||
}
|
}
|
||||||
Self::Span { parent, text_range: text_range.unwrap_or(selected) }
|
(Some(first), None) => first.clone()..=first,
|
||||||
|
(Some(first), Some(last)) => first..=last,
|
||||||
|
};
|
||||||
|
|
||||||
|
let text_range = elements.start().text_range().cover(elements.end().text_range());
|
||||||
|
|
||||||
|
Some(Self::Span { parent, elements, text_range })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn indent_level(&self) -> IndentLevel {
|
fn indent_level(&self) -> IndentLevel {
|
||||||
|
@ -601,7 +662,7 @@ impl FunctionBody {
|
||||||
fn tail_expr(&self) -> Option<ast::Expr> {
|
fn tail_expr(&self) -> Option<ast::Expr> {
|
||||||
match &self {
|
match &self {
|
||||||
FunctionBody::Expr(expr) => Some(expr.clone()),
|
FunctionBody::Expr(expr) => Some(expr.clone()),
|
||||||
FunctionBody::Span { parent, text_range } => {
|
FunctionBody::Span { parent, text_range, .. } => {
|
||||||
let tail_expr = parent.tail_expr()?;
|
let tail_expr = parent.tail_expr()?;
|
||||||
text_range.contains_range(tail_expr.syntax().text_range()).then_some(tail_expr)
|
text_range.contains_range(tail_expr.syntax().text_range()).then_some(tail_expr)
|
||||||
}
|
}
|
||||||
|
@ -611,7 +672,7 @@ impl FunctionBody {
|
||||||
fn walk_expr(&self, cb: &mut dyn FnMut(ast::Expr)) {
|
fn walk_expr(&self, cb: &mut dyn FnMut(ast::Expr)) {
|
||||||
match self {
|
match self {
|
||||||
FunctionBody::Expr(expr) => walk_expr(expr, cb),
|
FunctionBody::Expr(expr) => walk_expr(expr, cb),
|
||||||
FunctionBody::Span { parent, text_range } => {
|
FunctionBody::Span { parent, text_range, .. } => {
|
||||||
parent
|
parent
|
||||||
.statements()
|
.statements()
|
||||||
.filter(|stmt| text_range.contains_range(stmt.syntax().text_range()))
|
.filter(|stmt| text_range.contains_range(stmt.syntax().text_range()))
|
||||||
|
@ -634,7 +695,7 @@ impl FunctionBody {
|
||||||
fn preorder_expr(&self, cb: &mut dyn FnMut(WalkEvent<ast::Expr>) -> bool) {
|
fn preorder_expr(&self, cb: &mut dyn FnMut(WalkEvent<ast::Expr>) -> bool) {
|
||||||
match self {
|
match self {
|
||||||
FunctionBody::Expr(expr) => preorder_expr(expr, cb),
|
FunctionBody::Expr(expr) => preorder_expr(expr, cb),
|
||||||
FunctionBody::Span { parent, text_range } => {
|
FunctionBody::Span { parent, text_range, .. } => {
|
||||||
parent
|
parent
|
||||||
.statements()
|
.statements()
|
||||||
.filter(|stmt| text_range.contains_range(stmt.syntax().text_range()))
|
.filter(|stmt| text_range.contains_range(stmt.syntax().text_range()))
|
||||||
|
@ -657,7 +718,7 @@ impl FunctionBody {
|
||||||
fn walk_pat(&self, cb: &mut dyn FnMut(ast::Pat)) {
|
fn walk_pat(&self, cb: &mut dyn FnMut(ast::Pat)) {
|
||||||
match self {
|
match self {
|
||||||
FunctionBody::Expr(expr) => walk_patterns_in_expr(expr, cb),
|
FunctionBody::Expr(expr) => walk_patterns_in_expr(expr, cb),
|
||||||
FunctionBody::Span { parent, text_range } => {
|
FunctionBody::Span { parent, text_range, .. } => {
|
||||||
parent
|
parent
|
||||||
.statements()
|
.statements()
|
||||||
.filter(|stmt| text_range.contains_range(stmt.syntax().text_range()))
|
.filter(|stmt| text_range.contains_range(stmt.syntax().text_range()))
|
||||||
|
@ -1151,7 +1212,7 @@ impl HasTokenAtOffset for FunctionBody {
|
||||||
fn token_at_offset(&self, offset: TextSize) -> TokenAtOffset<SyntaxToken> {
|
fn token_at_offset(&self, offset: TextSize) -> TokenAtOffset<SyntaxToken> {
|
||||||
match self {
|
match self {
|
||||||
FunctionBody::Expr(expr) => expr.syntax().token_at_offset(offset),
|
FunctionBody::Expr(expr) => expr.syntax().token_at_offset(offset),
|
||||||
FunctionBody::Span { parent, text_range } => {
|
FunctionBody::Span { parent, text_range, .. } => {
|
||||||
match parent.syntax().token_at_offset(offset) {
|
match parent.syntax().token_at_offset(offset) {
|
||||||
TokenAtOffset::None => TokenAtOffset::None,
|
TokenAtOffset::None => TokenAtOffset::None,
|
||||||
TokenAtOffset::Single(t) => {
|
TokenAtOffset::Single(t) => {
|
||||||
|
@ -1316,7 +1377,19 @@ fn impl_type_name(impl_node: &ast::Impl) -> Option<String> {
|
||||||
Some(impl_node.self_ty()?.to_string())
|
Some(impl_node.self_ty()?.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_call(ctx: &AssistContext<'_>, fun: &Function, indent: IndentLevel) -> String {
|
/// Fixes up the call site before the target expressions are replaced with the call expression
|
||||||
|
fn fixup_call_site(builder: &mut SourceChangeBuilder, body: &FunctionBody) {
|
||||||
|
let parent_match_arm = body.parent().and_then(ast::MatchArm::cast);
|
||||||
|
|
||||||
|
if let Some(parent_match_arm) = parent_match_arm {
|
||||||
|
if parent_match_arm.comma_token().is_none() {
|
||||||
|
let parent_match_arm = builder.make_mut(parent_match_arm);
|
||||||
|
ted::append_child_raw(parent_match_arm.syntax(), make::token(T![,]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_call(ctx: &AssistContext<'_>, fun: &Function, indent: IndentLevel) -> SyntaxNode {
|
||||||
let ret_ty = fun.return_type(ctx);
|
let ret_ty = fun.return_type(ctx);
|
||||||
|
|
||||||
let args = make::arg_list(fun.params.iter().map(|param| param.to_arg(ctx)));
|
let args = make::arg_list(fun.params.iter().map(|param| param.to_arg(ctx)));
|
||||||
|
@ -1334,44 +1407,45 @@ fn make_call(ctx: &AssistContext<'_>, fun: &Function, indent: IndentLevel) -> St
|
||||||
if fun.control_flow.is_async {
|
if fun.control_flow.is_async {
|
||||||
call_expr = make::expr_await(call_expr);
|
call_expr = make::expr_await(call_expr);
|
||||||
}
|
}
|
||||||
let expr = handler.make_call_expr(call_expr).indent(indent);
|
|
||||||
|
|
||||||
let mut_modifier = |var: &OutlivedLocal| if var.mut_usage_outside_body { "mut " } else { "" };
|
let expr = handler.make_call_expr(call_expr).clone_for_update();
|
||||||
|
expr.indent(indent);
|
||||||
|
|
||||||
let mut buf = String::new();
|
let outliving_bindings = match fun.outliving_locals.as_slice() {
|
||||||
match fun.outliving_locals.as_slice() {
|
[] => None,
|
||||||
[] => {}
|
|
||||||
[var] => {
|
[var] => {
|
||||||
let modifier = mut_modifier(var);
|
|
||||||
let name = var.local.name(ctx.db());
|
let name = var.local.name(ctx.db());
|
||||||
format_to!(buf, "let {modifier}{} = ", name.display(ctx.db()))
|
let name = make::name(&name.display(ctx.db()).to_string());
|
||||||
|
Some(ast::Pat::IdentPat(make::ident_pat(false, var.mut_usage_outside_body, name)))
|
||||||
}
|
}
|
||||||
vars => {
|
vars => {
|
||||||
buf.push_str("let (");
|
let binding_pats = vars.iter().map(|var| {
|
||||||
let bindings = vars.iter().format_with(", ", |local, f| {
|
let name = var.local.name(ctx.db());
|
||||||
let modifier = mut_modifier(local);
|
let name = make::name(&name.display(ctx.db()).to_string());
|
||||||
let name = local.local.name(ctx.db());
|
make::ident_pat(false, var.mut_usage_outside_body, name).into()
|
||||||
f(&format_args!("{modifier}{}", name.display(ctx.db())))?;
|
|
||||||
Ok(())
|
|
||||||
});
|
});
|
||||||
format_to!(buf, "{bindings}");
|
Some(ast::Pat::TuplePat(make::tuple_pat(binding_pats)))
|
||||||
buf.push_str(") = ");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
format_to!(buf, "{expr}");
|
|
||||||
let parent_match_arm = fun.body.parent().and_then(ast::MatchArm::cast);
|
let parent_match_arm = fun.body.parent().and_then(ast::MatchArm::cast);
|
||||||
let insert_comma = parent_match_arm.as_ref().is_some_and(|it| it.comma_token().is_none());
|
|
||||||
|
|
||||||
if insert_comma {
|
if let Some(bindings) = outliving_bindings {
|
||||||
buf.push(',');
|
// with bindings that outlive it
|
||||||
} else if parent_match_arm.is_none()
|
make::let_stmt(bindings, None, Some(expr)).syntax().clone_for_update()
|
||||||
|
} else if parent_match_arm.as_ref().is_some() {
|
||||||
|
// as a tail expr for a match arm
|
||||||
|
expr.syntax().clone()
|
||||||
|
} else if parent_match_arm.as_ref().is_none()
|
||||||
&& fun.ret_ty.is_unit()
|
&& fun.ret_ty.is_unit()
|
||||||
&& (!fun.outliving_locals.is_empty() || !expr.is_block_like())
|
&& (!fun.outliving_locals.is_empty() || !expr.is_block_like())
|
||||||
{
|
{
|
||||||
buf.push(';');
|
// as an expr stmt
|
||||||
|
make::expr_stmt(expr).syntax().clone_for_update()
|
||||||
|
} else {
|
||||||
|
// as a tail expr, or a block
|
||||||
|
expr.syntax().clone()
|
||||||
}
|
}
|
||||||
buf
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enum FlowHandler {
|
enum FlowHandler {
|
||||||
|
@ -1500,42 +1574,25 @@ fn format_function(
|
||||||
module: hir::Module,
|
module: hir::Module,
|
||||||
fun: &Function,
|
fun: &Function,
|
||||||
old_indent: IndentLevel,
|
old_indent: IndentLevel,
|
||||||
new_indent: IndentLevel,
|
) -> ast::Fn {
|
||||||
) -> String {
|
let fun_name = make::name(&fun.name.text());
|
||||||
let mut fn_def = String::new();
|
|
||||||
|
|
||||||
let fun_name = &fun.name;
|
|
||||||
let params = fun.make_param_list(ctx, module);
|
let params = fun.make_param_list(ctx, module);
|
||||||
let ret_ty = fun.make_ret_ty(ctx, module);
|
let ret_ty = fun.make_ret_ty(ctx, module);
|
||||||
let body = make_body(ctx, old_indent, new_indent, fun);
|
let body = make_body(ctx, old_indent, fun);
|
||||||
let const_kw = if fun.mods.is_const { "const " } else { "" };
|
|
||||||
let async_kw = if fun.control_flow.is_async { "async " } else { "" };
|
|
||||||
let unsafe_kw = if fun.control_flow.is_unsafe { "unsafe " } else { "" };
|
|
||||||
let (generic_params, where_clause) = make_generic_params_and_where_clause(ctx, fun);
|
let (generic_params, where_clause) = make_generic_params_and_where_clause(ctx, fun);
|
||||||
|
|
||||||
format_to!(fn_def, "\n\n{new_indent}{const_kw}{async_kw}{unsafe_kw}");
|
make::fn_(
|
||||||
match ctx.config.snippet_cap {
|
None,
|
||||||
Some(_) => format_to!(fn_def, "fn $0{fun_name}"),
|
fun_name,
|
||||||
None => format_to!(fn_def, "fn {fun_name}"),
|
generic_params,
|
||||||
}
|
where_clause,
|
||||||
|
params,
|
||||||
if let Some(generic_params) = generic_params {
|
body,
|
||||||
format_to!(fn_def, "{generic_params}");
|
ret_ty,
|
||||||
}
|
fun.control_flow.is_async,
|
||||||
|
fun.mods.is_const,
|
||||||
format_to!(fn_def, "{params}");
|
fun.control_flow.is_unsafe,
|
||||||
|
)
|
||||||
if let Some(ret_ty) = ret_ty {
|
|
||||||
format_to!(fn_def, " {ret_ty}");
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(where_clause) = where_clause {
|
|
||||||
format_to!(fn_def, " {where_clause}");
|
|
||||||
}
|
|
||||||
|
|
||||||
format_to!(fn_def, " {body}");
|
|
||||||
|
|
||||||
fn_def
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_generic_params_and_where_clause(
|
fn make_generic_params_and_where_clause(
|
||||||
|
@ -1716,12 +1773,7 @@ impl FunType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_body(
|
fn make_body(ctx: &AssistContext<'_>, old_indent: IndentLevel, fun: &Function) -> ast::BlockExpr {
|
||||||
ctx: &AssistContext<'_>,
|
|
||||||
old_indent: IndentLevel,
|
|
||||||
new_indent: IndentLevel,
|
|
||||||
fun: &Function,
|
|
||||||
) -> ast::BlockExpr {
|
|
||||||
let ret_ty = fun.return_type(ctx);
|
let ret_ty = fun.return_type(ctx);
|
||||||
let handler = FlowHandler::from_ret_ty(fun, &ret_ty);
|
let handler = FlowHandler::from_ret_ty(fun, &ret_ty);
|
||||||
|
|
||||||
|
@ -1732,7 +1784,7 @@ fn make_body(
|
||||||
match expr {
|
match expr {
|
||||||
ast::Expr::BlockExpr(block) => {
|
ast::Expr::BlockExpr(block) => {
|
||||||
// If the extracted expression is itself a block, there is no need to wrap it inside another block.
|
// If the extracted expression is itself a block, there is no need to wrap it inside another block.
|
||||||
let block = block.dedent(old_indent);
|
block.dedent(old_indent);
|
||||||
let elements = block.stmt_list().map_or_else(
|
let elements = block.stmt_list().map_or_else(
|
||||||
|| Either::Left(iter::empty()),
|
|| Either::Left(iter::empty()),
|
||||||
|stmt_list| {
|
|stmt_list| {
|
||||||
|
@ -1752,13 +1804,13 @@ fn make_body(
|
||||||
make::hacky_block_expr(elements, block.tail_expr())
|
make::hacky_block_expr(elements, block.tail_expr())
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
let expr = expr.dedent(old_indent).indent(IndentLevel(1));
|
expr.reindent_to(1.into());
|
||||||
|
|
||||||
make::block_expr(Vec::new(), Some(expr))
|
make::block_expr(Vec::new(), Some(expr))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
FunctionBody::Span { parent, text_range } => {
|
FunctionBody::Span { parent, text_range, .. } => {
|
||||||
let mut elements: Vec<_> = parent
|
let mut elements: Vec<_> = parent
|
||||||
.syntax()
|
.syntax()
|
||||||
.children_with_tokens()
|
.children_with_tokens()
|
||||||
|
@ -1801,8 +1853,8 @@ fn make_body(
|
||||||
.map(|node_or_token| match &node_or_token {
|
.map(|node_or_token| match &node_or_token {
|
||||||
syntax::NodeOrToken::Node(node) => match ast::Stmt::cast(node.clone()) {
|
syntax::NodeOrToken::Node(node) => match ast::Stmt::cast(node.clone()) {
|
||||||
Some(stmt) => {
|
Some(stmt) => {
|
||||||
let indented = stmt.dedent(old_indent).indent(body_indent);
|
stmt.reindent_to(body_indent);
|
||||||
let ast_node = indented.syntax().clone_subtree();
|
let ast_node = stmt.syntax().clone_subtree();
|
||||||
syntax::NodeOrToken::Node(ast_node)
|
syntax::NodeOrToken::Node(ast_node)
|
||||||
}
|
}
|
||||||
_ => node_or_token,
|
_ => node_or_token,
|
||||||
|
@ -1810,13 +1862,15 @@ fn make_body(
|
||||||
_ => node_or_token,
|
_ => node_or_token,
|
||||||
})
|
})
|
||||||
.collect::<Vec<SyntaxElement>>();
|
.collect::<Vec<SyntaxElement>>();
|
||||||
let tail_expr = tail_expr.map(|expr| expr.dedent(old_indent).indent(body_indent));
|
if let Some(tail_expr) = &mut tail_expr {
|
||||||
|
tail_expr.reindent_to(body_indent);
|
||||||
|
}
|
||||||
|
|
||||||
make::hacky_block_expr(elements, tail_expr)
|
make::hacky_block_expr(elements, tail_expr)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let block = match &handler {
|
match &handler {
|
||||||
FlowHandler::None => block,
|
FlowHandler::None => block,
|
||||||
FlowHandler::Try { kind } => {
|
FlowHandler::Try { kind } => {
|
||||||
let block = with_default_tail_expr(block, make::expr_unit());
|
let block = with_default_tail_expr(block, make::expr_unit());
|
||||||
|
@ -1851,9 +1905,7 @@ fn make_body(
|
||||||
let args = make::arg_list(iter::once(tail_expr));
|
let args = make::arg_list(iter::once(tail_expr));
|
||||||
make::expr_call(ok, args)
|
make::expr_call(ok, args)
|
||||||
}),
|
}),
|
||||||
};
|
}
|
||||||
|
|
||||||
block.indent(new_indent)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn map_tail_expr(block: ast::BlockExpr, f: impl FnOnce(ast::Expr) -> ast::Expr) -> ast::BlockExpr {
|
fn map_tail_expr(block: ast::BlockExpr, f: impl FnOnce(ast::Expr) -> ast::Expr) -> ast::BlockExpr {
|
||||||
|
@ -1897,7 +1949,7 @@ fn with_tail_expr(block: ast::BlockExpr, tail_expr: ast::Expr) -> ast::BlockExpr
|
||||||
}
|
}
|
||||||
|
|
||||||
fn format_type(ty: &hir::Type, ctx: &AssistContext<'_>, module: hir::Module) -> String {
|
fn format_type(ty: &hir::Type, ctx: &AssistContext<'_>, module: hir::Module) -> String {
|
||||||
ty.display_source_code(ctx.db(), module.into(), true).ok().unwrap_or_else(|| "_".to_string())
|
ty.display_source_code(ctx.db(), module.into(), true).ok().unwrap_or_else(|| "_".to_owned())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_ty(ty: &hir::Type, ctx: &AssistContext<'_>, module: hir::Module) -> ast::Type {
|
fn make_ty(ty: &hir::Type, ctx: &AssistContext<'_>, module: hir::Module) -> ast::Type {
|
||||||
|
@ -2551,6 +2603,20 @@ fn $0fun_name(n: u32) -> u32 {
|
||||||
check_assist_not_applicable(extract_function, r"fn main() { 1 + /* $0comment$0 */ 1; }");
|
check_assist_not_applicable(extract_function, r"fn main() { 1 + /* $0comment$0 */ 1; }");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn empty_selection_is_not_applicable() {
|
||||||
|
cov_mark::check!(extract_function_empty_selection_is_not_applicable);
|
||||||
|
check_assist_not_applicable(
|
||||||
|
extract_function,
|
||||||
|
r#"
|
||||||
|
fn main() {
|
||||||
|
$0
|
||||||
|
|
||||||
|
$0
|
||||||
|
}"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn part_of_expr_stmt() {
|
fn part_of_expr_stmt() {
|
||||||
check_assist(
|
check_assist(
|
||||||
|
|
|
@ -1,7 +1,4 @@
|
||||||
use std::{
|
use std::iter;
|
||||||
collections::{HashMap, HashSet},
|
|
||||||
iter,
|
|
||||||
};
|
|
||||||
|
|
||||||
use hir::{HasSource, HirFileIdExt, ModuleSource};
|
use hir::{HasSource, HirFileIdExt, ModuleSource};
|
||||||
use ide_db::{
|
use ide_db::{
|
||||||
|
@ -9,6 +6,7 @@ use ide_db::{
|
||||||
base_db::FileId,
|
base_db::FileId,
|
||||||
defs::{Definition, NameClass, NameRefClass},
|
defs::{Definition, NameClass, NameRefClass},
|
||||||
search::{FileReference, SearchScope},
|
search::{FileReference, SearchScope},
|
||||||
|
FxHashMap, FxHashSet,
|
||||||
};
|
};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
@ -235,9 +233,9 @@ impl Module {
|
||||||
fn get_usages_and_record_fields(
|
fn get_usages_and_record_fields(
|
||||||
&self,
|
&self,
|
||||||
ctx: &AssistContext<'_>,
|
ctx: &AssistContext<'_>,
|
||||||
) -> (HashMap<FileId, Vec<(TextRange, String)>>, Vec<SyntaxNode>) {
|
) -> (FxHashMap<FileId, Vec<(TextRange, String)>>, Vec<SyntaxNode>) {
|
||||||
let mut adt_fields = Vec::new();
|
let mut adt_fields = Vec::new();
|
||||||
let mut refs: HashMap<FileId, Vec<(TextRange, String)>> = HashMap::new();
|
let mut refs: FxHashMap<FileId, Vec<(TextRange, String)>> = FxHashMap::default();
|
||||||
|
|
||||||
//Here impl is not included as each item inside impl will be tied to the parent of
|
//Here impl is not included as each item inside impl will be tied to the parent of
|
||||||
//implementing block(a struct, enum, etc), if the parent is in selected module, it will
|
//implementing block(a struct, enum, etc), if the parent is in selected module, it will
|
||||||
|
@ -320,7 +318,7 @@ impl Module {
|
||||||
&self,
|
&self,
|
||||||
ctx: &AssistContext<'_>,
|
ctx: &AssistContext<'_>,
|
||||||
node_def: Definition,
|
node_def: Definition,
|
||||||
refs_in_files: &mut HashMap<FileId, Vec<(TextRange, String)>>,
|
refs_in_files: &mut FxHashMap<FileId, Vec<(TextRange, String)>>,
|
||||||
) {
|
) {
|
||||||
for (file_id, references) in node_def.usages(&ctx.sema).all() {
|
for (file_id, references) in node_def.usages(&ctx.sema).all() {
|
||||||
let source_file = ctx.sema.parse(file_id);
|
let source_file = ctx.sema.parse(file_id);
|
||||||
|
@ -400,7 +398,7 @@ impl Module {
|
||||||
ctx: &AssistContext<'_>,
|
ctx: &AssistContext<'_>,
|
||||||
) -> Vec<TextRange> {
|
) -> Vec<TextRange> {
|
||||||
let mut import_paths_to_be_removed: Vec<TextRange> = vec![];
|
let mut import_paths_to_be_removed: Vec<TextRange> = vec![];
|
||||||
let mut node_set: HashSet<String> = HashSet::new();
|
let mut node_set: FxHashSet<String> = FxHashSet::default();
|
||||||
|
|
||||||
for item in self.body_items.clone() {
|
for item in self.body_items.clone() {
|
||||||
for x in item.syntax().descendants() {
|
for x in item.syntax().descendants() {
|
||||||
|
|
|
@ -115,7 +115,7 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
|
||||||
let trailing_ws = if prev_ws.is_some_and(|it| it.text().starts_with('\n')) {
|
let trailing_ws = if prev_ws.is_some_and(|it| it.text().starts_with('\n')) {
|
||||||
format!("\n{indent_to}")
|
format!("\n{indent_to}")
|
||||||
} else {
|
} else {
|
||||||
" ".to_string()
|
" ".to_owned()
|
||||||
};
|
};
|
||||||
|
|
||||||
ted::insert_all_raw(
|
ted::insert_all_raw(
|
||||||
|
|
|
@ -58,6 +58,11 @@ mod tests {
|
||||||
check_assist_not_applicable(flip_trait_bound, "struct S<T> where T: $0A { }")
|
check_assist_not_applicable(flip_trait_bound, "struct S<T> where T: $0A { }")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn flip_trait_bound_works_for_dyn() {
|
||||||
|
check_assist(flip_trait_bound, "fn f<'a>(x: dyn Copy $0+ 'a)", "fn f<'a>(x: dyn 'a + Copy)")
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn flip_trait_bound_works_for_struct() {
|
fn flip_trait_bound_works_for_struct() {
|
||||||
check_assist(
|
check_assist(
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
use std::collections::HashSet;
|
|
||||||
|
|
||||||
use hir::{self, HasCrate, HasVisibility};
|
use hir::{self, HasCrate, HasVisibility};
|
||||||
use ide_db::path_transform::PathTransform;
|
use ide_db::{path_transform::PathTransform, FxHashSet};
|
||||||
use syntax::{
|
use syntax::{
|
||||||
ast::{
|
ast::{
|
||||||
self, edit_in_place::Indent, make, AstNode, HasGenericParams, HasName, HasVisibility as _,
|
self, edit_in_place::Indent, make, AstNode, HasGenericParams, HasName, HasVisibility as _,
|
||||||
|
@ -71,7 +69,7 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<'
|
||||||
|
|
||||||
let sema_field_ty = ctx.sema.resolve_type(&field_ty)?;
|
let sema_field_ty = ctx.sema.resolve_type(&field_ty)?;
|
||||||
let mut methods = vec![];
|
let mut methods = vec![];
|
||||||
let mut seen_names = HashSet::new();
|
let mut seen_names = FxHashSet::default();
|
||||||
|
|
||||||
for ty in sema_field_ty.autoderef(ctx.db()) {
|
for ty in sema_field_ty.autoderef(ctx.db()) {
|
||||||
let krate = ty.krate(ctx.db());
|
let krate = ty.krate(ctx.db());
|
||||||
|
@ -163,13 +161,13 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<'
|
||||||
Some(impl_def) => edit.make_mut(impl_def),
|
Some(impl_def) => edit.make_mut(impl_def),
|
||||||
None => {
|
None => {
|
||||||
let name = &strukt_name.to_string();
|
let name = &strukt_name.to_string();
|
||||||
let params = strukt.generic_param_list();
|
let ty_params = strukt.generic_param_list();
|
||||||
let ty_params = params;
|
let ty_args = ty_params.as_ref().map(|it| it.to_generic_args());
|
||||||
let where_clause = strukt.where_clause();
|
let where_clause = strukt.where_clause();
|
||||||
|
|
||||||
let impl_def = make::impl_(
|
let impl_def = make::impl_(
|
||||||
ty_params,
|
ty_params,
|
||||||
None,
|
ty_args,
|
||||||
make::ty_path(make::ext::ident_path(name)),
|
make::ty_path(make::ext::ident_path(name)),
|
||||||
where_clause,
|
where_clause,
|
||||||
None,
|
None,
|
||||||
|
|
|
@ -502,9 +502,7 @@ fn generate_args_for_impl(
|
||||||
trait_params: &Option<GenericParamList>,
|
trait_params: &Option<GenericParamList>,
|
||||||
old_trait_args: &FxHashSet<String>,
|
old_trait_args: &FxHashSet<String>,
|
||||||
) -> Option<ast::GenericArgList> {
|
) -> Option<ast::GenericArgList> {
|
||||||
let Some(old_impl_args) = old_impl_gpl.map(|gpl| gpl.to_generic_args().generic_args()) else {
|
let old_impl_args = old_impl_gpl.map(|gpl| gpl.to_generic_args().generic_args())?;
|
||||||
return None;
|
|
||||||
};
|
|
||||||
// Create pairs of the args of `self_ty` and corresponding `field_ty` to
|
// Create pairs of the args of `self_ty` and corresponding `field_ty` to
|
||||||
// form the substitution list
|
// form the substitution list
|
||||||
let mut arg_substs = FxHashMap::default();
|
let mut arg_substs = FxHashMap::default();
|
||||||
|
@ -958,7 +956,8 @@ where
|
||||||
impl<T> AnotherTrait for S<T>
|
impl<T> AnotherTrait for S<T>
|
||||||
where
|
where
|
||||||
T: AnotherTrait,
|
T: AnotherTrait,
|
||||||
{}"#,
|
{
|
||||||
|
}"#,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1448,7 +1447,8 @@ where
|
||||||
impl<T> AnotherTrait for S<T>
|
impl<T> AnotherTrait for S<T>
|
||||||
where
|
where
|
||||||
T: AnotherTrait,
|
T: AnotherTrait,
|
||||||
{}"#,
|
{
|
||||||
|
}"#,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -364,7 +364,7 @@ fn is_in_trait_impl(ast_func: &ast::Fn, ctx: &AssistContext<'_>) -> bool {
|
||||||
ctx.sema
|
ctx.sema
|
||||||
.to_def(ast_func)
|
.to_def(ast_func)
|
||||||
.and_then(|hir_func| hir_func.as_assoc_item(ctx.db()))
|
.and_then(|hir_func| hir_func.as_assoc_item(ctx.db()))
|
||||||
.and_then(|assoc_item| assoc_item.containing_trait_impl(ctx.db()))
|
.and_then(|assoc_item| assoc_item.implemented_trait(ctx.db()))
|
||||||
.is_some()
|
.is_some()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -373,7 +373,7 @@ fn is_in_trait_def(ast_func: &ast::Fn, ctx: &AssistContext<'_>) -> bool {
|
||||||
ctx.sema
|
ctx.sema
|
||||||
.to_def(ast_func)
|
.to_def(ast_func)
|
||||||
.and_then(|hir_func| hir_func.as_assoc_item(ctx.db()))
|
.and_then(|hir_func| hir_func.as_assoc_item(ctx.db()))
|
||||||
.and_then(|assoc_item| assoc_item.containing_trait(ctx.db()))
|
.and_then(|assoc_item| assoc_item.container_trait(ctx.db()))
|
||||||
.is_some()
|
.is_some()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -416,9 +416,9 @@ fn arguments_from_params(param_list: &ast::ParamList) -> String {
|
||||||
true => format!("&mut {name}"),
|
true => format!("&mut {name}"),
|
||||||
false => name.to_string(),
|
false => name.to_string(),
|
||||||
},
|
},
|
||||||
None => "_".to_string(),
|
None => "_".to_owned(),
|
||||||
},
|
},
|
||||||
_ => "_".to_string(),
|
_ => "_".to_owned(),
|
||||||
});
|
});
|
||||||
args_iter.format(", ").to_string()
|
args_iter.format(", ").to_string()
|
||||||
}
|
}
|
||||||
|
|
|
@ -162,7 +162,7 @@ fn make_record_field_list(
|
||||||
fn name_from_field(field: &ast::RecordExprField) -> ast::Name {
|
fn name_from_field(field: &ast::RecordExprField) -> ast::Name {
|
||||||
let text = match field.name_ref() {
|
let text = match field.name_ref() {
|
||||||
Some(it) => it.to_string(),
|
Some(it) => it.to_string(),
|
||||||
None => name_from_field_shorthand(field).unwrap_or("unknown".to_string()),
|
None => name_from_field_shorthand(field).unwrap_or("unknown".to_owned()),
|
||||||
};
|
};
|
||||||
make::name(&text)
|
make::name(&text)
|
||||||
}
|
}
|
||||||
|
|
|
@ -202,7 +202,7 @@ fn get_adt_source(
|
||||||
let file = ctx.sema.parse(range.file_id);
|
let file = ctx.sema.parse(range.file_id);
|
||||||
let adt_source =
|
let adt_source =
|
||||||
ctx.sema.find_node_at_offset_with_macros(file.syntax(), range.range.start())?;
|
ctx.sema.find_node_at_offset_with_macros(file.syntax(), range.range.start())?;
|
||||||
find_struct_impl(ctx, &adt_source, &[fn_name.to_string()]).map(|impl_| (impl_, range.file_id))
|
find_struct_impl(ctx, &adt_source, &[fn_name.to_owned()]).map(|impl_| (impl_, range.file_id))
|
||||||
}
|
}
|
||||||
|
|
||||||
struct FunctionTemplate {
|
struct FunctionTemplate {
|
||||||
|
@ -908,7 +908,7 @@ fn filter_unnecessary_bounds(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let starting_nodes = necessary_params.iter().map(|param| param_map[param]);
|
let starting_nodes = necessary_params.iter().flat_map(|param| param_map.get(param).copied());
|
||||||
let reachable = graph.compute_reachable_nodes(starting_nodes);
|
let reachable = graph.compute_reachable_nodes(starting_nodes);
|
||||||
|
|
||||||
// Not pretty, but effective. If only there were `Vec::retain_index()`...
|
// Not pretty, but effective. If only there were `Vec::retain_index()`...
|
||||||
|
@ -1007,7 +1007,7 @@ fn fn_arg_name(sema: &Semantics<'_, RootDatabase>, arg_expr: &ast::Expr) -> Stri
|
||||||
name
|
name
|
||||||
}
|
}
|
||||||
Some(name) => name,
|
Some(name) => name,
|
||||||
None => "arg".to_string(),
|
None => "arg".to_owned(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1033,7 +1033,7 @@ fn fn_arg_type(
|
||||||
if ty.is_reference() || ty.is_mutable_reference() {
|
if ty.is_reference() || ty.is_mutable_reference() {
|
||||||
let famous_defs = &FamousDefs(&ctx.sema, ctx.sema.scope(fn_arg.syntax())?.krate());
|
let famous_defs = &FamousDefs(&ctx.sema, ctx.sema.scope(fn_arg.syntax())?.krate());
|
||||||
convert_reference_type(ty.strip_references(), ctx.db(), famous_defs)
|
convert_reference_type(ty.strip_references(), ctx.db(), famous_defs)
|
||||||
.map(|conversion| conversion.convert_type(ctx.db()))
|
.map(|conversion| conversion.convert_type(ctx.db()).to_string())
|
||||||
.or_else(|| ty.display_source_code(ctx.db(), target_module.into(), true).ok())
|
.or_else(|| ty.display_source_code(ctx.db(), target_module.into(), true).ok())
|
||||||
} else {
|
} else {
|
||||||
ty.display_source_code(ctx.db(), target_module.into(), true).ok()
|
ty.display_source_code(ctx.db(), target_module.into(), true).ok()
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
use ide_db::{famous_defs::FamousDefs, source_change::SourceChangeBuilder};
|
use ide_db::{famous_defs::FamousDefs, source_change::SourceChangeBuilder};
|
||||||
use stdx::{format_to, to_lower_snake_case};
|
use stdx::{format_to, to_lower_snake_case};
|
||||||
use syntax::{
|
use syntax::{
|
||||||
ast::{self, AstNode, HasName, HasVisibility},
|
ast::{self, edit_in_place::Indent, make, AstNode, HasName, HasVisibility},
|
||||||
TextRange,
|
ted, TextRange,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
utils::{convert_reference_type, find_impl_block_end, find_struct_impl, generate_impl_text},
|
utils::{convert_reference_type, find_struct_impl, generate_impl},
|
||||||
AssistContext, AssistId, AssistKind, Assists, GroupLabel,
|
AssistContext, AssistId, AssistKind, Assists, GroupLabel,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -75,7 +75,7 @@ pub(crate) fn generate_setter(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt
|
||||||
// Generate a getter method.
|
// Generate a getter method.
|
||||||
//
|
//
|
||||||
// ```
|
// ```
|
||||||
// # //- minicore: as_ref
|
// # //- minicore: as_ref, deref
|
||||||
// # pub struct String;
|
// # pub struct String;
|
||||||
// # impl AsRef<str> for String {
|
// # impl AsRef<str> for String {
|
||||||
// # fn as_ref(&self) -> &str {
|
// # fn as_ref(&self) -> &str {
|
||||||
|
@ -83,6 +83,13 @@ pub(crate) fn generate_setter(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt
|
||||||
// # }
|
// # }
|
||||||
// # }
|
// # }
|
||||||
// #
|
// #
|
||||||
|
// # impl core::ops::Deref for String {
|
||||||
|
// # type Target = str;
|
||||||
|
// # fn deref(&self) -> &Self::Target {
|
||||||
|
// # ""
|
||||||
|
// # }
|
||||||
|
// # }
|
||||||
|
// #
|
||||||
// struct Person {
|
// struct Person {
|
||||||
// nam$0e: String,
|
// nam$0e: String,
|
||||||
// }
|
// }
|
||||||
|
@ -96,13 +103,20 @@ pub(crate) fn generate_setter(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt
|
||||||
// # }
|
// # }
|
||||||
// # }
|
// # }
|
||||||
// #
|
// #
|
||||||
|
// # impl core::ops::Deref for String {
|
||||||
|
// # type Target = str;
|
||||||
|
// # fn deref(&self) -> &Self::Target {
|
||||||
|
// # ""
|
||||||
|
// # }
|
||||||
|
// # }
|
||||||
|
// #
|
||||||
// struct Person {
|
// struct Person {
|
||||||
// name: String,
|
// name: String,
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// impl Person {
|
// impl Person {
|
||||||
// fn $0name(&self) -> &str {
|
// fn $0name(&self) -> &str {
|
||||||
// self.name.as_ref()
|
// &self.name
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
// ```
|
// ```
|
||||||
|
@ -200,14 +214,14 @@ fn generate_getter_from_info(
|
||||||
ctx: &AssistContext<'_>,
|
ctx: &AssistContext<'_>,
|
||||||
info: &AssistInfo,
|
info: &AssistInfo,
|
||||||
record_field_info: &RecordFieldInfo,
|
record_field_info: &RecordFieldInfo,
|
||||||
) -> String {
|
) -> ast::Fn {
|
||||||
let mut buf = String::with_capacity(512);
|
|
||||||
|
|
||||||
let vis = info.strukt.visibility().map_or(String::new(), |v| format!("{v} "));
|
|
||||||
let (ty, body) = if matches!(info.assist_type, AssistType::MutGet) {
|
let (ty, body) = if matches!(info.assist_type, AssistType::MutGet) {
|
||||||
(
|
(
|
||||||
format!("&mut {}", record_field_info.field_ty),
|
make::ty_ref(record_field_info.field_ty.clone(), true),
|
||||||
format!("&mut self.{}", record_field_info.field_name),
|
make::expr_ref(
|
||||||
|
make::expr_field(make::ext::expr_self(), &record_field_info.field_name.text()),
|
||||||
|
true,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
(|| {
|
(|| {
|
||||||
|
@ -226,41 +240,52 @@ fn generate_getter_from_info(
|
||||||
})()
|
})()
|
||||||
.unwrap_or_else(|| {
|
.unwrap_or_else(|| {
|
||||||
(
|
(
|
||||||
format!("&{}", record_field_info.field_ty),
|
make::ty_ref(record_field_info.field_ty.clone(), false),
|
||||||
format!("&self.{}", record_field_info.field_name),
|
make::expr_ref(
|
||||||
|
make::expr_field(make::ext::expr_self(), &record_field_info.field_name.text()),
|
||||||
|
false,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
format_to!(
|
let self_param = if matches!(info.assist_type, AssistType::MutGet) {
|
||||||
buf,
|
make::mut_self_param()
|
||||||
" {}fn {}(&{}self) -> {} {{
|
} else {
|
||||||
{}
|
make::self_param()
|
||||||
}}",
|
};
|
||||||
vis,
|
|
||||||
record_field_info.fn_name,
|
|
||||||
matches!(info.assist_type, AssistType::MutGet).then_some("mut ").unwrap_or_default(),
|
|
||||||
ty,
|
|
||||||
body,
|
|
||||||
);
|
|
||||||
|
|
||||||
buf
|
let strukt = &info.strukt;
|
||||||
|
let fn_name = make::name(&record_field_info.fn_name);
|
||||||
|
let params = make::param_list(Some(self_param), []);
|
||||||
|
let ret_type = Some(make::ret_type(ty));
|
||||||
|
let body = make::block_expr([], Some(body));
|
||||||
|
|
||||||
|
make::fn_(strukt.visibility(), fn_name, None, None, params, body, ret_type, false, false, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_setter_from_info(info: &AssistInfo, record_field_info: &RecordFieldInfo) -> String {
|
fn generate_setter_from_info(info: &AssistInfo, record_field_info: &RecordFieldInfo) -> ast::Fn {
|
||||||
let mut buf = String::with_capacity(512);
|
|
||||||
let strukt = &info.strukt;
|
let strukt = &info.strukt;
|
||||||
let fn_name = &record_field_info.fn_name;
|
let field_name = &record_field_info.fn_name;
|
||||||
|
let fn_name = make::name(&format!("set_{field_name}"));
|
||||||
let field_ty = &record_field_info.field_ty;
|
let field_ty = &record_field_info.field_ty;
|
||||||
let vis = strukt.visibility().map_or(String::new(), |v| format!("{v} "));
|
|
||||||
format_to!(
|
|
||||||
buf,
|
|
||||||
" {vis}fn set_{fn_name}(&mut self, {fn_name}: {field_ty}) {{
|
|
||||||
self.{fn_name} = {fn_name};
|
|
||||||
}}"
|
|
||||||
);
|
|
||||||
|
|
||||||
buf
|
// Make the param list
|
||||||
|
// `(&mut self, $field_name: $field_ty)`
|
||||||
|
let field_param =
|
||||||
|
make::param(make::ident_pat(false, false, make::name(field_name)).into(), field_ty.clone());
|
||||||
|
let params = make::param_list(Some(make::mut_self_param()), [field_param]);
|
||||||
|
|
||||||
|
// Make the assignment body
|
||||||
|
// `self.$field_name = $field_name`
|
||||||
|
let self_expr = make::ext::expr_self();
|
||||||
|
let lhs = make::expr_field(self_expr, field_name);
|
||||||
|
let rhs = make::expr_path(make::ext::ident_path(field_name));
|
||||||
|
let assign_stmt = make::expr_stmt(make::expr_assignment(lhs, rhs));
|
||||||
|
let body = make::block_expr([assign_stmt.into()], None);
|
||||||
|
|
||||||
|
// Make the setter fn
|
||||||
|
make::fn_(strukt.visibility(), fn_name, None, None, params, body, None, false, false, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extract_and_parse(
|
fn extract_and_parse(
|
||||||
|
@ -353,74 +378,45 @@ fn build_source_change(
|
||||||
) {
|
) {
|
||||||
let record_fields_count = info_of_record_fields.len();
|
let record_fields_count = info_of_record_fields.len();
|
||||||
|
|
||||||
let mut buf = String::with_capacity(512);
|
let impl_def = if let Some(impl_def) = &assist_info.impl_def {
|
||||||
|
// We have an existing impl to add to
|
||||||
|
builder.make_mut(impl_def.clone())
|
||||||
|
} else {
|
||||||
|
// Generate a new impl to add the methods to
|
||||||
|
let impl_def = generate_impl(&ast::Adt::Struct(assist_info.strukt.clone()));
|
||||||
|
|
||||||
// Check if an impl exists
|
// Insert it after the adt
|
||||||
if let Some(impl_def) = &assist_info.impl_def {
|
let strukt = builder.make_mut(assist_info.strukt.clone());
|
||||||
// Check if impl is empty
|
|
||||||
if let Some(assoc_item_list) = impl_def.assoc_item_list() {
|
|
||||||
if assoc_item_list.assoc_items().next().is_some() {
|
|
||||||
// If not empty then only insert a new line
|
|
||||||
buf.push('\n');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i, record_field_info) in info_of_record_fields.iter().enumerate() {
|
ted::insert_all_raw(
|
||||||
// this buf inserts a newline at the end of a getter
|
ted::Position::after(strukt.syntax()),
|
||||||
// automatically, if one wants to add one more newline
|
vec![make::tokens::blank_line().into(), impl_def.syntax().clone().into()],
|
||||||
// for separating it from other assoc items, that needs
|
);
|
||||||
// to be handled separately
|
|
||||||
let mut getter_buf = match assist_info.assist_type {
|
impl_def
|
||||||
AssistType::Set => generate_setter_from_info(&assist_info, record_field_info),
|
|
||||||
_ => generate_getter_from_info(ctx, &assist_info, record_field_info),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Insert `$0` only for last getter we generate
|
let assoc_item_list = impl_def.get_or_create_assoc_item_list();
|
||||||
if i == record_fields_count - 1 && ctx.config.snippet_cap.is_some() {
|
|
||||||
getter_buf = getter_buf.replacen("fn ", "fn $0", 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// For first element we do not merge with '\n', as
|
for (i, record_field_info) in info_of_record_fields.iter().enumerate() {
|
||||||
// that can be inserted by impl_def check defined
|
// Make the new getter or setter fn
|
||||||
// above, for other cases which are:
|
let new_fn = match assist_info.assist_type {
|
||||||
//
|
AssistType::Set => generate_setter_from_info(&assist_info, record_field_info),
|
||||||
// - impl exists but it empty, here we would ideally
|
_ => generate_getter_from_info(ctx, &assist_info, record_field_info),
|
||||||
// not want to keep newline between impl <struct> {
|
|
||||||
// and fn <fn-name>() { line
|
|
||||||
//
|
|
||||||
// - next if impl itself does not exist, in this
|
|
||||||
// case we ourselves generate a new impl and that
|
|
||||||
// again ends up with the same reasoning as above
|
|
||||||
// for not keeping newline
|
|
||||||
if i == 0 {
|
|
||||||
buf = buf + &getter_buf;
|
|
||||||
} else {
|
|
||||||
buf = buf + "\n" + &getter_buf;
|
|
||||||
}
|
}
|
||||||
|
.clone_for_update();
|
||||||
|
new_fn.indent(1.into());
|
||||||
|
|
||||||
// We don't insert a new line at the end of
|
// Insert a tabstop only for last method we generate
|
||||||
// last getter as it will end up in the end
|
if i == record_fields_count - 1 {
|
||||||
// of an impl where we would not like to keep
|
if let Some(cap) = ctx.config.snippet_cap {
|
||||||
// getter and end of impl ( i.e. `}` ) with an
|
if let Some(name) = new_fn.name() {
|
||||||
// extra line for no reason
|
builder.add_tabstop_before(cap, name);
|
||||||
if i < record_fields_count - 1 {
|
}
|
||||||
buf += "\n";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let start_offset = assist_info
|
assoc_item_list.add_item(new_fn.clone().into());
|
||||||
.impl_def
|
|
||||||
.as_ref()
|
|
||||||
.and_then(|impl_def| find_impl_block_end(impl_def.to_owned(), &mut buf))
|
|
||||||
.unwrap_or_else(|| {
|
|
||||||
buf = generate_impl_text(&ast::Adt::Struct(assist_info.strukt.clone()), &buf);
|
|
||||||
assist_info.strukt.syntax().text_range().end()
|
|
||||||
});
|
|
||||||
|
|
||||||
match ctx.config.snippet_cap {
|
|
||||||
Some(cap) => builder.insert_snippet(cap, start_offset, buf),
|
|
||||||
None => builder.insert(start_offset, buf),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
use syntax::ast::{self, AstNode, HasName};
|
use syntax::{
|
||||||
|
ast::{self, make, AstNode, HasName},
|
||||||
use crate::{
|
ted,
|
||||||
utils::{generate_impl_text, generate_trait_impl_text_intransitive},
|
|
||||||
AssistContext, AssistId, AssistKind, Assists,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use crate::{utils, AssistContext, AssistId, AssistKind, Assists};
|
||||||
|
|
||||||
// Assist: generate_impl
|
// Assist: generate_impl
|
||||||
//
|
//
|
||||||
// Adds a new inherent impl for a type.
|
// Adds a new inherent impl for a type.
|
||||||
|
@ -20,9 +20,7 @@ use crate::{
|
||||||
// data: T,
|
// data: T,
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// impl<T: Clone> Ctx<T> {
|
// impl<T: Clone> Ctx<T> {$0}
|
||||||
// $0
|
|
||||||
// }
|
|
||||||
// ```
|
// ```
|
||||||
pub(crate) fn generate_impl(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
|
pub(crate) fn generate_impl(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
|
||||||
let nominal = ctx.find_node_at_offset::<ast::Adt>()?;
|
let nominal = ctx.find_node_at_offset::<ast::Adt>()?;
|
||||||
|
@ -38,17 +36,22 @@ pub(crate) fn generate_impl(acc: &mut Assists, ctx: &AssistContext<'_>) -> Optio
|
||||||
format!("Generate impl for `{name}`"),
|
format!("Generate impl for `{name}`"),
|
||||||
target,
|
target,
|
||||||
|edit| {
|
|edit| {
|
||||||
let start_offset = nominal.syntax().text_range().end();
|
// Generate the impl
|
||||||
match ctx.config.snippet_cap {
|
let impl_ = utils::generate_impl(&nominal);
|
||||||
Some(cap) => {
|
|
||||||
let snippet = generate_impl_text(&nominal, " $0");
|
// Add a tabstop after the left curly brace
|
||||||
edit.insert_snippet(cap, start_offset, snippet);
|
if let Some(cap) = ctx.config.snippet_cap {
|
||||||
}
|
if let Some(l_curly) = impl_.assoc_item_list().and_then(|it| it.l_curly_token()) {
|
||||||
None => {
|
edit.add_tabstop_after_token(cap, l_curly);
|
||||||
let snippet = generate_impl_text(&nominal, "");
|
|
||||||
edit.insert(start_offset, snippet);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add the impl after the adt
|
||||||
|
let nominal = edit.make_mut(nominal);
|
||||||
|
ted::insert_all_raw(
|
||||||
|
ted::Position::after(nominal.syntax()),
|
||||||
|
vec![make::tokens::blank_line().into(), impl_.syntax().clone().into()],
|
||||||
|
);
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -68,9 +71,7 @@ pub(crate) fn generate_impl(acc: &mut Assists, ctx: &AssistContext<'_>) -> Optio
|
||||||
// data: T,
|
// data: T,
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// impl<T: Clone> $0 for Ctx<T> {
|
// impl<T: Clone> ${0:_} for Ctx<T> {}
|
||||||
//
|
|
||||||
// }
|
|
||||||
// ```
|
// ```
|
||||||
pub(crate) fn generate_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
|
pub(crate) fn generate_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
|
||||||
let nominal = ctx.find_node_at_offset::<ast::Adt>()?;
|
let nominal = ctx.find_node_at_offset::<ast::Adt>()?;
|
||||||
|
@ -86,17 +87,22 @@ pub(crate) fn generate_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_>) ->
|
||||||
format!("Generate trait impl for `{name}`"),
|
format!("Generate trait impl for `{name}`"),
|
||||||
target,
|
target,
|
||||||
|edit| {
|
|edit| {
|
||||||
let start_offset = nominal.syntax().text_range().end();
|
// Generate the impl
|
||||||
match ctx.config.snippet_cap {
|
let impl_ = utils::generate_trait_impl_intransitive(&nominal, make::ty_placeholder());
|
||||||
Some(cap) => {
|
|
||||||
let snippet = generate_trait_impl_text_intransitive(&nominal, "$0", "");
|
// Make the trait type a placeholder snippet
|
||||||
edit.insert_snippet(cap, start_offset, snippet);
|
if let Some(cap) = ctx.config.snippet_cap {
|
||||||
}
|
if let Some(trait_) = impl_.trait_() {
|
||||||
None => {
|
edit.add_placeholder_snippet(cap, trait_);
|
||||||
let text = generate_trait_impl_text_intransitive(&nominal, "", "");
|
|
||||||
edit.insert(start_offset, text);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add the impl after the adt
|
||||||
|
let nominal = edit.make_mut(nominal);
|
||||||
|
ted::insert_all_raw(
|
||||||
|
ted::Position::after(nominal.syntax()),
|
||||||
|
vec![make::tokens::blank_line().into(), impl_.syntax().clone().into()],
|
||||||
|
);
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -117,9 +123,7 @@ mod tests {
|
||||||
r#"
|
r#"
|
||||||
struct Foo {}
|
struct Foo {}
|
||||||
|
|
||||||
impl Foo {
|
impl Foo {$0}
|
||||||
$0
|
|
||||||
}
|
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -134,9 +138,7 @@ mod tests {
|
||||||
r#"
|
r#"
|
||||||
struct Foo<T: Clone> {}
|
struct Foo<T: Clone> {}
|
||||||
|
|
||||||
impl<T: Clone> Foo<T> {
|
impl<T: Clone> Foo<T> {$0}
|
||||||
$0
|
|
||||||
}
|
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -151,9 +153,7 @@ mod tests {
|
||||||
r#"
|
r#"
|
||||||
struct Foo<'a, T: Foo<'a>> {}
|
struct Foo<'a, T: Foo<'a>> {}
|
||||||
|
|
||||||
impl<'a, T: Foo<'a>> Foo<'a, T> {
|
impl<'a, T: Foo<'a>> Foo<'a, T> {$0}
|
||||||
$0
|
|
||||||
}
|
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -171,9 +171,7 @@ mod tests {
|
||||||
struct Foo<'a, T: Foo<'a>> {}
|
struct Foo<'a, T: Foo<'a>> {}
|
||||||
|
|
||||||
#[cfg(feature = "foo")]
|
#[cfg(feature = "foo")]
|
||||||
impl<'a, T: Foo<'a>> Foo<'a, T> {
|
impl<'a, T: Foo<'a>> Foo<'a, T> {$0}
|
||||||
$0
|
|
||||||
}
|
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -188,9 +186,7 @@ mod tests {
|
||||||
r#"
|
r#"
|
||||||
struct Defaulted<T = i32> {}
|
struct Defaulted<T = i32> {}
|
||||||
|
|
||||||
impl<T> Defaulted<T> {
|
impl<T> Defaulted<T> {$0}
|
||||||
$0
|
|
||||||
}
|
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -205,9 +201,7 @@ mod tests {
|
||||||
r#"
|
r#"
|
||||||
struct Defaulted<'a, 'b: 'a, T: Debug + Clone + 'a + 'b = String, const S: usize> {}
|
struct Defaulted<'a, 'b: 'a, T: Debug + Clone + 'a + 'b = String, const S: usize> {}
|
||||||
|
|
||||||
impl<'a, 'b: 'a, T: Debug + Clone + 'a + 'b, const S: usize> Defaulted<'a, 'b, T, S> {
|
impl<'a, 'b: 'a, T: Debug + Clone + 'a + 'b, const S: usize> Defaulted<'a, 'b, T, S> {$0}
|
||||||
$0
|
|
||||||
}
|
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -222,9 +216,7 @@ mod tests {
|
||||||
r#"
|
r#"
|
||||||
struct Defaulted<const N: i32 = 0> {}
|
struct Defaulted<const N: i32 = 0> {}
|
||||||
|
|
||||||
impl<const N: i32> Defaulted<N> {
|
impl<const N: i32> Defaulted<N> {$0}
|
||||||
$0
|
|
||||||
}
|
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -254,8 +246,7 @@ mod tests {
|
||||||
impl<T> Struct<T>
|
impl<T> Struct<T>
|
||||||
where
|
where
|
||||||
T: Trait,
|
T: Trait,
|
||||||
{
|
{$0
|
||||||
$0
|
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
|
@ -285,9 +276,7 @@ mod tests {
|
||||||
r#"
|
r#"
|
||||||
struct Foo {}
|
struct Foo {}
|
||||||
|
|
||||||
impl $0 for Foo {
|
impl ${0:_} for Foo {}
|
||||||
|
|
||||||
}
|
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -302,9 +291,7 @@ mod tests {
|
||||||
r#"
|
r#"
|
||||||
struct Foo<T: Clone> {}
|
struct Foo<T: Clone> {}
|
||||||
|
|
||||||
impl<T: Clone> $0 for Foo<T> {
|
impl<T: Clone> ${0:_} for Foo<T> {}
|
||||||
|
|
||||||
}
|
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -319,9 +306,7 @@ mod tests {
|
||||||
r#"
|
r#"
|
||||||
struct Foo<'a, T: Foo<'a>> {}
|
struct Foo<'a, T: Foo<'a>> {}
|
||||||
|
|
||||||
impl<'a, T: Foo<'a>> $0 for Foo<'a, T> {
|
impl<'a, T: Foo<'a>> ${0:_} for Foo<'a, T> {}
|
||||||
|
|
||||||
}
|
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -339,9 +324,7 @@ mod tests {
|
||||||
struct Foo<'a, T: Foo<'a>> {}
|
struct Foo<'a, T: Foo<'a>> {}
|
||||||
|
|
||||||
#[cfg(feature = "foo")]
|
#[cfg(feature = "foo")]
|
||||||
impl<'a, T: Foo<'a>> $0 for Foo<'a, T> {
|
impl<'a, T: Foo<'a>> ${0:_} for Foo<'a, T> {}
|
||||||
|
|
||||||
}
|
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -356,9 +339,7 @@ mod tests {
|
||||||
r#"
|
r#"
|
||||||
struct Defaulted<T = i32> {}
|
struct Defaulted<T = i32> {}
|
||||||
|
|
||||||
impl<T> $0 for Defaulted<T> {
|
impl<T> ${0:_} for Defaulted<T> {}
|
||||||
|
|
||||||
}
|
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -373,9 +354,7 @@ mod tests {
|
||||||
r#"
|
r#"
|
||||||
struct Defaulted<'a, 'b: 'a, T: Debug + Clone + 'a + 'b = String, const S: usize> {}
|
struct Defaulted<'a, 'b: 'a, T: Debug + Clone + 'a + 'b = String, const S: usize> {}
|
||||||
|
|
||||||
impl<'a, 'b: 'a, T: Debug + Clone + 'a + 'b, const S: usize> $0 for Defaulted<'a, 'b, T, S> {
|
impl<'a, 'b: 'a, T: Debug + Clone + 'a + 'b, const S: usize> ${0:_} for Defaulted<'a, 'b, T, S> {}
|
||||||
|
|
||||||
}
|
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -390,9 +369,7 @@ mod tests {
|
||||||
r#"
|
r#"
|
||||||
struct Defaulted<const N: i32 = 0> {}
|
struct Defaulted<const N: i32 = 0> {}
|
||||||
|
|
||||||
impl<const N: i32> $0 for Defaulted<N> {
|
impl<const N: i32> ${0:_} for Defaulted<N> {}
|
||||||
|
|
||||||
}
|
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -419,11 +396,10 @@ mod tests {
|
||||||
inner: T,
|
inner: T,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> $0 for Struct<T>
|
impl<T> ${0:_} for Struct<T>
|
||||||
where
|
where
|
||||||
T: Trait,
|
T: Trait,
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
|
|
|
@ -79,7 +79,7 @@ pub(crate) fn generate_is_empty_from_len(acc: &mut Assists, ctx: &AssistContext<
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.len() == 0
|
self.len() == 0
|
||||||
}"#
|
}"#
|
||||||
.to_string();
|
.to_owned();
|
||||||
builder.insert(range.end(), code)
|
builder.insert(range.end(), code)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
use ide_db::{
|
use ide_db::{
|
||||||
imports::import_assets::item_for_path_search, use_trivial_constructor::use_trivial_constructor,
|
imports::import_assets::item_for_path_search, use_trivial_constructor::use_trivial_constructor,
|
||||||
};
|
};
|
||||||
use itertools::Itertools;
|
use syntax::{
|
||||||
use stdx::format_to;
|
ast::{self, edit_in_place::Indent, make, AstNode, HasName, HasVisibility, StructKind},
|
||||||
use syntax::ast::{self, AstNode, HasName, HasVisibility, StructKind};
|
ted,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
utils::{find_impl_block_start, find_struct_impl, generate_impl_text},
|
utils::{find_struct_impl, generate_impl},
|
||||||
AssistContext, AssistId, AssistKind, Assists,
|
AssistContext, AssistId, AssistKind, Assists,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -26,7 +27,9 @@ use crate::{
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// impl<T: Clone> Ctx<T> {
|
// impl<T: Clone> Ctx<T> {
|
||||||
// fn $0new(data: T) -> Self { Self { data } }
|
// fn $0new(data: T) -> Self {
|
||||||
|
// Self { data }
|
||||||
|
// }
|
||||||
// }
|
// }
|
||||||
// ```
|
// ```
|
||||||
pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
|
pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
|
||||||
|
@ -46,14 +49,6 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option
|
||||||
|
|
||||||
let target = strukt.syntax().text_range();
|
let target = strukt.syntax().text_range();
|
||||||
acc.add(AssistId("generate_new", AssistKind::Generate), "Generate `new`", target, |builder| {
|
acc.add(AssistId("generate_new", AssistKind::Generate), "Generate `new`", target, |builder| {
|
||||||
let mut buf = String::with_capacity(512);
|
|
||||||
|
|
||||||
if impl_def.is_some() {
|
|
||||||
buf.push('\n');
|
|
||||||
}
|
|
||||||
|
|
||||||
let vis = strukt.visibility().map_or(String::new(), |v| format!("{v} "));
|
|
||||||
|
|
||||||
let trivial_constructors = field_list
|
let trivial_constructors = field_list
|
||||||
.fields()
|
.fields()
|
||||||
.map(|f| {
|
.map(|f| {
|
||||||
|
@ -76,54 +71,79 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option
|
||||||
&ty,
|
&ty,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
Some(format!("{name}: {expr}"))
|
Some(make::record_expr_field(make::name_ref(&name.text()), Some(expr)))
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let params = field_list
|
let params = field_list.fields().enumerate().filter_map(|(i, f)| {
|
||||||
.fields()
|
|
||||||
.enumerate()
|
|
||||||
.filter_map(|(i, f)| {
|
|
||||||
if trivial_constructors[i].is_none() {
|
if trivial_constructors[i].is_none() {
|
||||||
let name = f.name()?;
|
let name = f.name()?;
|
||||||
let ty = f.ty()?;
|
let ty = f.ty()?;
|
||||||
|
|
||||||
Some(format!("{name}: {ty}"))
|
Some(make::param(make::ident_pat(false, false, name).into(), ty))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
.format(", ");
|
let params = make::param_list(None, params);
|
||||||
|
|
||||||
let fields = field_list
|
let fields = field_list.fields().enumerate().filter_map(|(i, f)| {
|
||||||
.fields()
|
|
||||||
.enumerate()
|
|
||||||
.filter_map(|(i, f)| {
|
|
||||||
let constructor = trivial_constructors[i].clone();
|
let constructor = trivial_constructors[i].clone();
|
||||||
if constructor.is_some() {
|
if constructor.is_some() {
|
||||||
constructor
|
constructor
|
||||||
} else {
|
} else {
|
||||||
Some(f.name()?.to_string())
|
Some(make::record_expr_field(make::name_ref(&f.name()?.text()), None))
|
||||||
}
|
}
|
||||||
})
|
|
||||||
.format(", ");
|
|
||||||
|
|
||||||
format_to!(buf, " {vis}fn new({params}) -> Self {{ Self {{ {fields} }} }}");
|
|
||||||
|
|
||||||
let start_offset = impl_def
|
|
||||||
.and_then(|impl_def| find_impl_block_start(impl_def, &mut buf))
|
|
||||||
.unwrap_or_else(|| {
|
|
||||||
buf = generate_impl_text(&ast::Adt::Struct(strukt.clone()), &buf);
|
|
||||||
strukt.syntax().text_range().end()
|
|
||||||
});
|
});
|
||||||
|
let fields = make::record_expr_field_list(fields);
|
||||||
|
|
||||||
match ctx.config.snippet_cap {
|
let record_expr = make::record_expr(make::ext::ident_path("Self"), fields);
|
||||||
None => builder.insert(start_offset, buf),
|
let body = make::block_expr(None, Some(record_expr.into()));
|
||||||
Some(cap) => {
|
|
||||||
buf = buf.replace("fn new", "fn $0new");
|
let ret_type = make::ret_type(make::ty_path(make::ext::ident_path("Self")));
|
||||||
builder.insert_snippet(cap, start_offset, buf);
|
|
||||||
|
let fn_ = make::fn_(
|
||||||
|
strukt.visibility(),
|
||||||
|
make::name("new"),
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
params,
|
||||||
|
body,
|
||||||
|
Some(ret_type),
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
.clone_for_update();
|
||||||
|
fn_.indent(1.into());
|
||||||
|
|
||||||
|
// Add a tabstop before the name
|
||||||
|
if let Some(cap) = ctx.config.snippet_cap {
|
||||||
|
if let Some(name) = fn_.name() {
|
||||||
|
builder.add_tabstop_before(cap, name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get the mutable version of the impl to modify
|
||||||
|
let impl_def = if let Some(impl_def) = impl_def {
|
||||||
|
builder.make_mut(impl_def)
|
||||||
|
} else {
|
||||||
|
// Generate a new impl to add the method to
|
||||||
|
let impl_def = generate_impl(&ast::Adt::Struct(strukt.clone()));
|
||||||
|
|
||||||
|
// Insert it after the adt
|
||||||
|
let strukt = builder.make_mut(strukt.clone());
|
||||||
|
|
||||||
|
ted::insert_all_raw(
|
||||||
|
ted::Position::after(strukt.syntax()),
|
||||||
|
vec![make::tokens::blank_line().into(), impl_def.syntax().clone().into()],
|
||||||
|
);
|
||||||
|
|
||||||
|
impl_def
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add the `new` method at the start of the impl
|
||||||
|
impl_def.get_or_create_assoc_item_list().add_item_at_start(fn_.into());
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,7 +168,9 @@ struct Empty;
|
||||||
struct Foo { empty: Empty }
|
struct Foo { empty: Empty }
|
||||||
|
|
||||||
impl Foo {
|
impl Foo {
|
||||||
fn $0new() -> Self { Self { empty: Empty } }
|
fn $0new() -> Self {
|
||||||
|
Self { empty: Empty }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
|
@ -165,7 +187,9 @@ struct Empty;
|
||||||
struct Foo { baz: String, empty: Empty }
|
struct Foo { baz: String, empty: Empty }
|
||||||
|
|
||||||
impl Foo {
|
impl Foo {
|
||||||
fn $0new(baz: String) -> Self { Self { baz, empty: Empty } }
|
fn $0new(baz: String) -> Self {
|
||||||
|
Self { baz, empty: Empty }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
|
@ -182,7 +206,9 @@ enum Empty { Bar }
|
||||||
struct Foo { empty: Empty }
|
struct Foo { empty: Empty }
|
||||||
|
|
||||||
impl Foo {
|
impl Foo {
|
||||||
fn $0new() -> Self { Self { empty: Empty::Bar } }
|
fn $0new() -> Self {
|
||||||
|
Self { empty: Empty::Bar }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
|
@ -201,7 +227,9 @@ struct Empty {}
|
||||||
struct Foo { empty: Empty }
|
struct Foo { empty: Empty }
|
||||||
|
|
||||||
impl Foo {
|
impl Foo {
|
||||||
fn $0new(empty: Empty) -> Self { Self { empty } }
|
fn $0new(empty: Empty) -> Self {
|
||||||
|
Self { empty }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
|
@ -218,7 +246,9 @@ enum Empty { Bar {} }
|
||||||
struct Foo { empty: Empty }
|
struct Foo { empty: Empty }
|
||||||
|
|
||||||
impl Foo {
|
impl Foo {
|
||||||
fn $0new(empty: Empty) -> Self { Self { empty } }
|
fn $0new(empty: Empty) -> Self {
|
||||||
|
Self { empty }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
|
@ -235,7 +265,9 @@ struct Foo {$0}
|
||||||
struct Foo {}
|
struct Foo {}
|
||||||
|
|
||||||
impl Foo {
|
impl Foo {
|
||||||
fn $0new() -> Self { Self { } }
|
fn $0new() -> Self {
|
||||||
|
Self { }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
|
@ -248,7 +280,9 @@ struct Foo<T: Clone> {$0}
|
||||||
struct Foo<T: Clone> {}
|
struct Foo<T: Clone> {}
|
||||||
|
|
||||||
impl<T: Clone> Foo<T> {
|
impl<T: Clone> Foo<T> {
|
||||||
fn $0new() -> Self { Self { } }
|
fn $0new() -> Self {
|
||||||
|
Self { }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
|
@ -261,7 +295,9 @@ struct Foo<'a, T: Foo<'a>> {$0}
|
||||||
struct Foo<'a, T: Foo<'a>> {}
|
struct Foo<'a, T: Foo<'a>> {}
|
||||||
|
|
||||||
impl<'a, T: Foo<'a>> Foo<'a, T> {
|
impl<'a, T: Foo<'a>> Foo<'a, T> {
|
||||||
fn $0new() -> Self { Self { } }
|
fn $0new() -> Self {
|
||||||
|
Self { }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
|
@ -274,7 +310,9 @@ struct Foo { baz: String $0}
|
||||||
struct Foo { baz: String }
|
struct Foo { baz: String }
|
||||||
|
|
||||||
impl Foo {
|
impl Foo {
|
||||||
fn $0new(baz: String) -> Self { Self { baz } }
|
fn $0new(baz: String) -> Self {
|
||||||
|
Self { baz }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
|
@ -287,7 +325,9 @@ struct Foo { baz: String, qux: Vec<i32> $0}
|
||||||
struct Foo { baz: String, qux: Vec<i32> }
|
struct Foo { baz: String, qux: Vec<i32> }
|
||||||
|
|
||||||
impl Foo {
|
impl Foo {
|
||||||
fn $0new(baz: String, qux: Vec<i32>) -> Self { Self { baz, qux } }
|
fn $0new(baz: String, qux: Vec<i32>) -> Self {
|
||||||
|
Self { baz, qux }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
|
@ -304,7 +344,9 @@ struct Foo { pub baz: String, pub qux: Vec<i32> $0}
|
||||||
struct Foo { pub baz: String, pub qux: Vec<i32> }
|
struct Foo { pub baz: String, pub qux: Vec<i32> }
|
||||||
|
|
||||||
impl Foo {
|
impl Foo {
|
||||||
fn $0new(baz: String, qux: Vec<i32>) -> Self { Self { baz, qux } }
|
fn $0new(baz: String, qux: Vec<i32>) -> Self {
|
||||||
|
Self { baz, qux }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
|
@ -323,7 +365,9 @@ impl Foo {}
|
||||||
struct Foo {}
|
struct Foo {}
|
||||||
|
|
||||||
impl Foo {
|
impl Foo {
|
||||||
fn $0new() -> Self { Self { } }
|
fn $0new() -> Self {
|
||||||
|
Self { }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
|
@ -340,7 +384,9 @@ impl Foo {
|
||||||
struct Foo {}
|
struct Foo {}
|
||||||
|
|
||||||
impl Foo {
|
impl Foo {
|
||||||
fn $0new() -> Self { Self { } }
|
fn $0new() -> Self {
|
||||||
|
Self { }
|
||||||
|
}
|
||||||
|
|
||||||
fn qux(&self) {}
|
fn qux(&self) {}
|
||||||
}
|
}
|
||||||
|
@ -363,7 +409,9 @@ impl Foo {
|
||||||
struct Foo {}
|
struct Foo {}
|
||||||
|
|
||||||
impl Foo {
|
impl Foo {
|
||||||
fn $0new() -> Self { Self { } }
|
fn $0new() -> Self {
|
||||||
|
Self { }
|
||||||
|
}
|
||||||
|
|
||||||
fn qux(&self) {}
|
fn qux(&self) {}
|
||||||
fn baz() -> i32 {
|
fn baz() -> i32 {
|
||||||
|
@ -385,7 +433,9 @@ pub struct Foo {$0}
|
||||||
pub struct Foo {}
|
pub struct Foo {}
|
||||||
|
|
||||||
impl Foo {
|
impl Foo {
|
||||||
pub fn $0new() -> Self { Self { } }
|
pub fn $0new() -> Self {
|
||||||
|
Self { }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
|
@ -398,7 +448,9 @@ pub(crate) struct Foo {$0}
|
||||||
pub(crate) struct Foo {}
|
pub(crate) struct Foo {}
|
||||||
|
|
||||||
impl Foo {
|
impl Foo {
|
||||||
pub(crate) fn $0new() -> Self { Self { } }
|
pub(crate) fn $0new() -> Self {
|
||||||
|
Self { }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
|
@ -493,7 +545,9 @@ pub struct Source<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Source<T> {
|
impl<T> Source<T> {
|
||||||
pub fn $0new(file_id: HirFileId, ast: T) -> Self { Self { file_id, ast } }
|
pub fn $0new(file_id: HirFileId, ast: T) -> Self {
|
||||||
|
Self { file_id, ast }
|
||||||
|
}
|
||||||
|
|
||||||
pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> Source<U> {
|
pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> Source<U> {
|
||||||
Source { file_id: self.file_id, ast: f(self.ast) }
|
Source { file_id: self.file_id, ast: f(self.ast) }
|
||||||
|
|
|
@ -118,7 +118,7 @@ pub(crate) fn generate_trait_from_impl(acc: &mut Assists, ctx: &AssistContext<'_
|
||||||
let arg_list = if let Some(genpars) = impl_ast.generic_param_list() {
|
let arg_list = if let Some(genpars) = impl_ast.generic_param_list() {
|
||||||
genpars.to_generic_args().to_string()
|
genpars.to_generic_args().to_string()
|
||||||
} else {
|
} else {
|
||||||
"".to_string()
|
"".to_owned()
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(snippet_cap) = ctx.config.snippet_cap {
|
if let Some(snippet_cap) = ctx.config.snippet_cap {
|
||||||
|
|
|
@ -415,7 +415,24 @@ fn inline(
|
||||||
let expr: &ast::Expr = expr;
|
let expr: &ast::Expr = expr;
|
||||||
|
|
||||||
let mut insert_let_stmt = || {
|
let mut insert_let_stmt = || {
|
||||||
let ty = sema.type_of_expr(expr).filter(TypeInfo::has_adjustment).and(param_ty.clone());
|
let param_ty = match param_ty {
|
||||||
|
None => None,
|
||||||
|
Some(param_ty) => {
|
||||||
|
if sema.hir_file_for(param_ty.syntax()).is_macro() {
|
||||||
|
if let Some(param_ty) =
|
||||||
|
ast::Type::cast(insert_ws_into(param_ty.syntax().clone()))
|
||||||
|
{
|
||||||
|
Some(param_ty)
|
||||||
|
} else {
|
||||||
|
Some(param_ty.clone_for_update())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Some(param_ty.clone_for_update())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let ty: Option<syntax::ast::Type> =
|
||||||
|
sema.type_of_expr(expr).filter(TypeInfo::has_adjustment).and(param_ty);
|
||||||
|
|
||||||
let is_self = param
|
let is_self = param
|
||||||
.name(sema.db)
|
.name(sema.db)
|
||||||
|
@ -1732,6 +1749,49 @@ pub fn main() {
|
||||||
this.0 += 1;
|
this.0 += 1;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn inline_call_with_reference_in_macros() {
|
||||||
|
check_assist(
|
||||||
|
inline_call,
|
||||||
|
r#"
|
||||||
|
fn _write_u64(s: &mut u64, x: u64) {
|
||||||
|
*s += x;
|
||||||
|
}
|
||||||
|
macro_rules! impl_write {
|
||||||
|
($(($ty:ident, $meth:ident),)*) => {$(
|
||||||
|
fn _hash(inner_self_: &u64, state: &mut u64) {
|
||||||
|
$meth(state, *inner_self_)
|
||||||
|
}
|
||||||
|
)*}
|
||||||
|
}
|
||||||
|
impl_write! { (u64, _write_u64), }
|
||||||
|
fn _hash2(self_: &u64, state: &mut u64) {
|
||||||
|
$0_hash(&self_, state);
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
fn _write_u64(s: &mut u64, x: u64) {
|
||||||
|
*s += x;
|
||||||
|
}
|
||||||
|
macro_rules! impl_write {
|
||||||
|
($(($ty:ident, $meth:ident),)*) => {$(
|
||||||
|
fn _hash(inner_self_: &u64, state: &mut u64) {
|
||||||
|
$meth(state, *inner_self_)
|
||||||
|
}
|
||||||
|
)*}
|
||||||
|
}
|
||||||
|
impl_write! { (u64, _write_u64), }
|
||||||
|
fn _hash2(self_: &u64, state: &mut u64) {
|
||||||
|
{
|
||||||
|
let inner_self_: &u64 = &self_;
|
||||||
|
let state: &mut u64 = state;
|
||||||
|
_write_u64(state, *inner_self_)
|
||||||
|
};
|
||||||
|
}
|
||||||
"#,
|
"#,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,7 +60,7 @@ pub(crate) fn inline_const_as_literal(acc: &mut Assists, ctx: &AssistContext<'_>
|
||||||
|
|
||||||
let id = AssistId("inline_const_as_literal", AssistKind::RefactorInline);
|
let id = AssistId("inline_const_as_literal", AssistKind::RefactorInline);
|
||||||
|
|
||||||
let label = "Inline const as literal".to_string();
|
let label = "Inline const as literal".to_owned();
|
||||||
let target = variable.syntax().text_range();
|
let target = variable.syntax().text_range();
|
||||||
|
|
||||||
return acc.add(id, label, target, |edit| {
|
return acc.add(id, label, target, |edit| {
|
||||||
|
|
|
@ -41,7 +41,7 @@ pub(crate) fn inline_macro(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option
|
||||||
|
|
||||||
acc.add(
|
acc.add(
|
||||||
AssistId("inline_macro", AssistKind::RefactorInline),
|
AssistId("inline_macro", AssistKind::RefactorInline),
|
||||||
"Inline macro".to_string(),
|
"Inline macro".to_owned(),
|
||||||
text_range,
|
text_range,
|
||||||
|builder| builder.replace(text_range, expanded.to_string()),
|
|builder| builder.replace(text_range, expanded.to_string()),
|
||||||
)
|
)
|
||||||
|
|
|
@ -3,12 +3,12 @@
|
||||||
// - Remove unused aliases if there are no longer any users, see inline_call.rs.
|
// - Remove unused aliases if there are no longer any users, see inline_call.rs.
|
||||||
|
|
||||||
use hir::{HasSource, PathResolution};
|
use hir::{HasSource, PathResolution};
|
||||||
|
use ide_db::FxHashMap;
|
||||||
use ide_db::{
|
use ide_db::{
|
||||||
defs::Definition, imports::insert_use::ast_to_remove_for_path_in_use_stmt,
|
defs::Definition, imports::insert_use::ast_to_remove_for_path_in_use_stmt,
|
||||||
search::FileReference,
|
search::FileReference,
|
||||||
};
|
};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use std::collections::HashMap;
|
|
||||||
use syntax::{
|
use syntax::{
|
||||||
ast::{self, make, HasGenericParams, HasName},
|
ast::{self, make, HasGenericParams, HasName},
|
||||||
ted, AstNode, NodeOrToken, SyntaxNode,
|
ted, AstNode, NodeOrToken, SyntaxNode,
|
||||||
|
@ -189,14 +189,14 @@ fn inline(alias_def: &ast::TypeAlias, alias_instance: &ast::PathType) -> Option<
|
||||||
Some(repl)
|
Some(repl)
|
||||||
}
|
}
|
||||||
|
|
||||||
struct LifetimeMap(HashMap<String, ast::Lifetime>);
|
struct LifetimeMap(FxHashMap<String, ast::Lifetime>);
|
||||||
|
|
||||||
impl LifetimeMap {
|
impl LifetimeMap {
|
||||||
fn new(
|
fn new(
|
||||||
instance_args: &Option<ast::GenericArgList>,
|
instance_args: &Option<ast::GenericArgList>,
|
||||||
alias_generics: &ast::GenericParamList,
|
alias_generics: &ast::GenericParamList,
|
||||||
) -> Option<Self> {
|
) -> Option<Self> {
|
||||||
let mut inner = HashMap::new();
|
let mut inner = FxHashMap::default();
|
||||||
|
|
||||||
let wildcard_lifetime = make::lifetime("'_");
|
let wildcard_lifetime = make::lifetime("'_");
|
||||||
let lifetimes = alias_generics
|
let lifetimes = alias_generics
|
||||||
|
@ -231,14 +231,14 @@ impl LifetimeMap {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ConstAndTypeMap(HashMap<String, SyntaxNode>);
|
struct ConstAndTypeMap(FxHashMap<String, SyntaxNode>);
|
||||||
|
|
||||||
impl ConstAndTypeMap {
|
impl ConstAndTypeMap {
|
||||||
fn new(
|
fn new(
|
||||||
instance_args: &Option<ast::GenericArgList>,
|
instance_args: &Option<ast::GenericArgList>,
|
||||||
alias_generics: &ast::GenericParamList,
|
alias_generics: &ast::GenericParamList,
|
||||||
) -> Option<Self> {
|
) -> Option<Self> {
|
||||||
let mut inner = HashMap::new();
|
let mut inner = FxHashMap::default();
|
||||||
let instance_generics = generic_args_to_const_and_type_generics(instance_args);
|
let instance_generics = generic_args_to_const_and_type_generics(instance_args);
|
||||||
let alias_generics = generic_param_list_to_const_and_type_generics(alias_generics);
|
let alias_generics = generic_param_list_to_const_and_type_generics(alias_generics);
|
||||||
|
|
||||||
|
|
|
@ -48,7 +48,7 @@ pub(crate) fn into_to_qualified_from(acc: &mut Assists, ctx: &AssistContext<'_>)
|
||||||
let fnc = sema.resolve_method_call(&method_call)?;
|
let fnc = sema.resolve_method_call(&method_call)?;
|
||||||
let scope = sema.scope(method_call.syntax())?;
|
let scope = sema.scope(method_call.syntax())?;
|
||||||
// Check if the method call refers to Into trait.
|
// Check if the method call refers to Into trait.
|
||||||
if fnc.as_assoc_item(db)?.containing_trait_impl(db)?
|
if fnc.as_assoc_item(db)?.implemented_trait(db)?
|
||||||
== FamousDefs(sema, scope.krate()).core_convert_Into()?
|
== FamousDefs(sema, scope.krate()).core_convert_Into()?
|
||||||
{
|
{
|
||||||
let type_call = sema.type_of_expr(&method_call.clone().into())?;
|
let type_call = sema.type_of_expr(&method_call.clone().into())?;
|
||||||
|
|
|
@ -129,7 +129,7 @@ fn generate_unique_lifetime_param_name(
|
||||||
type_params.lifetime_params().map(|p| p.syntax().text().to_string()).collect();
|
type_params.lifetime_params().map(|p| p.syntax().text().to_string()).collect();
|
||||||
('a'..='z').map(|it| format!("'{it}")).find(|it| !used_lifetime_params.contains(it))
|
('a'..='z').map(|it| format!("'{it}")).find(|it| !used_lifetime_params.contains(it))
|
||||||
}
|
}
|
||||||
None => Some("'a".to_string()),
|
None => Some("'a".to_owned()),
|
||||||
}
|
}
|
||||||
.map(|it| make::lifetime(&it))
|
.map(|it| make::lifetime(&it))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use hir::Type;
|
use hir::Type;
|
||||||
use std::{collections::HashMap, iter::successors};
|
use ide_db::FxHashMap;
|
||||||
|
use std::iter::successors;
|
||||||
use syntax::{
|
use syntax::{
|
||||||
algo::neighbor,
|
algo::neighbor,
|
||||||
ast::{self, AstNode, HasName},
|
ast::{self, AstNode, HasName},
|
||||||
|
@ -95,7 +96,7 @@ fn contains_placeholder(a: &ast::MatchArm) -> bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn are_same_types(
|
fn are_same_types(
|
||||||
current_arm_types: &HashMap<String, Option<Type>>,
|
current_arm_types: &FxHashMap<String, Option<Type>>,
|
||||||
arm: &ast::MatchArm,
|
arm: &ast::MatchArm,
|
||||||
ctx: &AssistContext<'_>,
|
ctx: &AssistContext<'_>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
|
@ -114,11 +115,11 @@ fn are_same_types(
|
||||||
fn get_arm_types(
|
fn get_arm_types(
|
||||||
context: &AssistContext<'_>,
|
context: &AssistContext<'_>,
|
||||||
arm: &ast::MatchArm,
|
arm: &ast::MatchArm,
|
||||||
) -> HashMap<String, Option<Type>> {
|
) -> FxHashMap<String, Option<Type>> {
|
||||||
let mut mapping: HashMap<String, Option<Type>> = HashMap::new();
|
let mut mapping: FxHashMap<String, Option<Type>> = FxHashMap::default();
|
||||||
|
|
||||||
fn recurse(
|
fn recurse(
|
||||||
map: &mut HashMap<String, Option<Type>>,
|
map: &mut FxHashMap<String, Option<Type>>,
|
||||||
ctx: &AssistContext<'_>,
|
ctx: &AssistContext<'_>,
|
||||||
pat: &Option<ast::Pat>,
|
pat: &Option<ast::Pat>,
|
||||||
) {
|
) {
|
||||||
|
|
|
@ -75,7 +75,7 @@ pub(crate) fn move_module_to_file(acc: &mut Assists, ctx: &AssistContext<'_>) ->
|
||||||
let contents = {
|
let contents = {
|
||||||
let items = module_items.dedent(IndentLevel(1)).to_string();
|
let items = module_items.dedent(IndentLevel(1)).to_string();
|
||||||
let mut items =
|
let mut items =
|
||||||
items.trim_start_matches('{').trim_end_matches('}').trim().to_string();
|
items.trim_start_matches('{').trim_end_matches('}').trim().to_owned();
|
||||||
if !items.is_empty() {
|
if !items.is_empty() {
|
||||||
items.push('\n');
|
items.push('\n');
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,7 @@ pub(crate) fn reformat_number_literal(acc: &mut Assists, ctx: &AssistContext<'_>
|
||||||
}
|
}
|
||||||
|
|
||||||
let radix = literal.radix();
|
let radix = literal.radix();
|
||||||
let mut converted = prefix.to_string();
|
let mut converted = prefix.to_owned();
|
||||||
converted.push_str(&add_group_separators(value, group_size(radix)));
|
converted.push_str(&add_group_separators(value, group_size(radix)));
|
||||||
converted.push_str(suffix);
|
converted.push_str(suffix);
|
||||||
|
|
||||||
|
|
|
@ -203,7 +203,7 @@ fn item_as_trait(db: &RootDatabase, item: hir::ItemInNs) -> Option<hir::Trait> {
|
||||||
|
|
||||||
match item_module_def {
|
match item_module_def {
|
||||||
hir::ModuleDef::Trait(trait_) => Some(trait_),
|
hir::ModuleDef::Trait(trait_) => Some(trait_),
|
||||||
_ => item_module_def.as_assoc_item(db)?.containing_trait(db),
|
_ => item_module_def.as_assoc_item(db)?.container_trait(db),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
use std::collections::{hash_map::Entry, HashMap};
|
use std::collections::hash_map::Entry;
|
||||||
|
|
||||||
use hir::{HirFileIdExt, InFile, InRealFile, Module, ModuleSource};
|
use hir::{HirFileIdExt, InFile, InRealFile, Module, ModuleSource};
|
||||||
use ide_db::{
|
use ide_db::{
|
||||||
base_db::FileRange,
|
base_db::FileRange,
|
||||||
defs::Definition,
|
defs::Definition,
|
||||||
search::{FileReference, ReferenceCategory, SearchScope},
|
search::{FileReference, ReferenceCategory, SearchScope},
|
||||||
RootDatabase,
|
FxHashMap, RootDatabase,
|
||||||
};
|
};
|
||||||
use syntax::{ast, AstNode};
|
use syntax::{ast, AstNode};
|
||||||
use text_edit::TextRange;
|
use text_edit::TextRange;
|
||||||
|
@ -44,7 +44,7 @@ pub(crate) fn remove_unused_imports(acc: &mut Assists, ctx: &AssistContext<'_>)
|
||||||
let uses = uses_up.chain(uses_down).collect::<Vec<_>>();
|
let uses = uses_up.chain(uses_down).collect::<Vec<_>>();
|
||||||
|
|
||||||
// Maps use nodes to the scope that we should search through to find
|
// Maps use nodes to the scope that we should search through to find
|
||||||
let mut search_scopes = HashMap::<Module, Vec<SearchScope>>::new();
|
let mut search_scopes = FxHashMap::<Module, Vec<SearchScope>>::default();
|
||||||
|
|
||||||
// iterator over all unused use trees
|
// iterator over all unused use trees
|
||||||
let mut unused = uses
|
let mut unused = uses
|
||||||
|
|
|
@ -2,15 +2,17 @@ use hir::{InFile, MacroFileIdExt, ModuleDef};
|
||||||
use ide_db::{helpers::mod_path_to_ast, imports::import_assets::NameToImport, items_locator};
|
use ide_db::{helpers::mod_path_to_ast, imports::import_assets::NameToImport, items_locator};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use syntax::{
|
use syntax::{
|
||||||
ast::{self, AstNode, HasName},
|
ast::{self, make, AstNode, HasName},
|
||||||
|
ted,
|
||||||
SyntaxKind::WHITESPACE,
|
SyntaxKind::WHITESPACE,
|
||||||
|
T,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
assist_context::{AssistContext, Assists, SourceChangeBuilder},
|
assist_context::{AssistContext, Assists, SourceChangeBuilder},
|
||||||
utils::{
|
utils::{
|
||||||
add_trait_assoc_items_to_impl, filter_assoc_items, gen_trait_fn_body,
|
add_trait_assoc_items_to_impl, filter_assoc_items, gen_trait_fn_body, generate_trait_impl,
|
||||||
generate_trait_impl_text, render_snippet, Cursor, DefaultMethods, IgnoreAssocItems,
|
DefaultMethods, IgnoreAssocItems,
|
||||||
},
|
},
|
||||||
AssistId, AssistKind,
|
AssistId, AssistKind,
|
||||||
};
|
};
|
||||||
|
@ -132,35 +134,59 @@ fn add_assist(
|
||||||
label,
|
label,
|
||||||
target,
|
target,
|
||||||
|builder| {
|
|builder| {
|
||||||
let insert_pos = adt.syntax().text_range().end();
|
let insert_after = ted::Position::after(builder.make_mut(adt.clone()).syntax());
|
||||||
|
|
||||||
let impl_def_with_items =
|
let impl_def_with_items =
|
||||||
impl_def_from_trait(&ctx.sema, adt, &annotated_name, trait_, replace_trait_path);
|
impl_def_from_trait(&ctx.sema, adt, &annotated_name, trait_, replace_trait_path);
|
||||||
update_attribute(builder, old_derives, old_tree, old_trait_path, attr);
|
update_attribute(builder, old_derives, old_tree, old_trait_path, attr);
|
||||||
let trait_path = replace_trait_path.to_string();
|
|
||||||
|
let trait_path = make::ty_path(replace_trait_path.clone());
|
||||||
|
|
||||||
match (ctx.config.snippet_cap, impl_def_with_items) {
|
match (ctx.config.snippet_cap, impl_def_with_items) {
|
||||||
(None, _) => {
|
(None, _) => {
|
||||||
builder.insert(insert_pos, generate_trait_impl_text(adt, &trait_path, ""))
|
let impl_def = generate_trait_impl(adt, trait_path);
|
||||||
|
|
||||||
|
ted::insert_all(
|
||||||
|
insert_after,
|
||||||
|
vec![make::tokens::blank_line().into(), impl_def.syntax().clone().into()],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
(Some(cap), None) => {
|
||||||
|
let impl_def = generate_trait_impl(adt, trait_path);
|
||||||
|
|
||||||
|
if let Some(l_curly) =
|
||||||
|
impl_def.assoc_item_list().and_then(|it| it.l_curly_token())
|
||||||
|
{
|
||||||
|
builder.add_tabstop_after_token(cap, l_curly);
|
||||||
|
}
|
||||||
|
|
||||||
|
ted::insert_all(
|
||||||
|
insert_after,
|
||||||
|
vec![make::tokens::blank_line().into(), impl_def.syntax().clone().into()],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
(Some(cap), None) => builder.insert_snippet(
|
|
||||||
cap,
|
|
||||||
insert_pos,
|
|
||||||
generate_trait_impl_text(adt, &trait_path, " $0"),
|
|
||||||
),
|
|
||||||
(Some(cap), Some((impl_def, first_assoc_item))) => {
|
(Some(cap), Some((impl_def, first_assoc_item))) => {
|
||||||
let mut cursor = Cursor::Before(first_assoc_item.syntax());
|
let mut added_snippet = false;
|
||||||
let placeholder;
|
|
||||||
if let ast::AssocItem::Fn(ref func) = first_assoc_item {
|
if let ast::AssocItem::Fn(ref func) = first_assoc_item {
|
||||||
if let Some(m) = func.syntax().descendants().find_map(ast::MacroCall::cast)
|
if let Some(m) = func.syntax().descendants().find_map(ast::MacroCall::cast)
|
||||||
{
|
{
|
||||||
if m.syntax().text() == "todo!()" {
|
if m.syntax().text() == "todo!()" {
|
||||||
placeholder = m;
|
// Make the `todo!()` a placeholder
|
||||||
cursor = Cursor::Replace(placeholder.syntax());
|
builder.add_placeholder_snippet(cap, m);
|
||||||
|
added_snippet = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let rendered = render_snippet(cap, impl_def.syntax(), cursor);
|
if !added_snippet {
|
||||||
builder.insert_snippet(cap, insert_pos, format!("\n\n{rendered}"))
|
// If we haven't already added a snippet, add a tabstop before the generated function
|
||||||
|
builder.add_tabstop_before(cap, first_assoc_item);
|
||||||
|
}
|
||||||
|
|
||||||
|
ted::insert_all(
|
||||||
|
insert_after,
|
||||||
|
vec![make::tokens::blank_line().into(), impl_def.syntax().clone().into()],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
@ -190,28 +216,7 @@ fn impl_def_from_trait(
|
||||||
if trait_items.is_empty() {
|
if trait_items.is_empty() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let impl_def = {
|
let impl_def = generate_trait_impl(adt, make::ty_path(trait_path.clone()));
|
||||||
use syntax::ast::Impl;
|
|
||||||
let text = generate_trait_impl_text(adt, trait_path.to_string().as_str(), "");
|
|
||||||
// FIXME: `generate_trait_impl_text` currently generates two newlines
|
|
||||||
// at the front, but these leading newlines should really instead be
|
|
||||||
// inserted at the same time the impl is inserted
|
|
||||||
assert_eq!(&text[..2], "\n\n", "`generate_trait_impl_text` output changed");
|
|
||||||
let parse = syntax::SourceFile::parse(&text[2..]);
|
|
||||||
let node = match parse.tree().syntax().descendants().find_map(Impl::cast) {
|
|
||||||
Some(it) => it,
|
|
||||||
None => {
|
|
||||||
panic!(
|
|
||||||
"Failed to make ast node `{}` from text {}",
|
|
||||||
std::any::type_name::<Impl>(),
|
|
||||||
text
|
|
||||||
)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let node = node.clone_for_update();
|
|
||||||
assert_eq!(node.syntax().text_range().start(), 0.into());
|
|
||||||
node
|
|
||||||
};
|
|
||||||
|
|
||||||
let first_assoc_item =
|
let first_assoc_item =
|
||||||
add_trait_assoc_items_to_impl(sema, &trait_items, trait_, &impl_def, target_scope);
|
add_trait_assoc_items_to_impl(sema, &trait_items, trait_, &impl_def, target_scope);
|
||||||
|
@ -238,20 +243,34 @@ fn update_attribute(
|
||||||
let has_more_derives = !new_derives.is_empty();
|
let has_more_derives = !new_derives.is_empty();
|
||||||
|
|
||||||
if has_more_derives {
|
if has_more_derives {
|
||||||
let new_derives = format!("({})", new_derives.iter().format(", "));
|
let old_tree = builder.make_mut(old_tree.clone());
|
||||||
builder.replace(old_tree.syntax().text_range(), new_derives);
|
|
||||||
} else {
|
|
||||||
let attr_range = attr.syntax().text_range();
|
|
||||||
builder.delete(attr_range);
|
|
||||||
|
|
||||||
if let Some(line_break_range) = attr
|
// Make the paths into flat lists of tokens in a vec
|
||||||
.syntax()
|
let tt = new_derives.iter().map(|path| path.syntax().clone()).map(|node| {
|
||||||
.next_sibling_or_token()
|
node.descendants_with_tokens()
|
||||||
.filter(|t| t.kind() == WHITESPACE)
|
.filter_map(|element| element.into_token())
|
||||||
.map(|t| t.text_range())
|
.collect::<Vec<_>>()
|
||||||
|
});
|
||||||
|
// ...which are interspersed with ", "
|
||||||
|
let tt = Itertools::intersperse(tt, vec![make::token(T![,]), make::tokens::single_space()]);
|
||||||
|
// ...wrap them into the appropriate `NodeOrToken` variant
|
||||||
|
let tt = tt.flatten().map(syntax::NodeOrToken::Token);
|
||||||
|
// ...and make them into a flat list of tokens
|
||||||
|
let tt = tt.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let new_tree = make::token_tree(T!['('], tt).clone_for_update();
|
||||||
|
ted::replace(old_tree.syntax(), new_tree.syntax());
|
||||||
|
} else {
|
||||||
|
// Remove the attr and any trailing whitespace
|
||||||
|
let attr = builder.make_mut(attr.clone());
|
||||||
|
|
||||||
|
if let Some(line_break) =
|
||||||
|
attr.syntax().next_sibling_or_token().filter(|t| t.kind() == WHITESPACE)
|
||||||
{
|
{
|
||||||
builder.delete(line_break_range);
|
ted::remove(line_break)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ted::remove(attr.syntax())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1168,9 +1187,7 @@ struct Foo {
|
||||||
bar: String,
|
bar: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for Foo {
|
impl Debug for Foo {$0}
|
||||||
$0
|
|
||||||
}
|
|
||||||
"#,
|
"#,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1191,9 +1208,7 @@ pub struct Foo {
|
||||||
bar: String,
|
bar: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for Foo {
|
impl Debug for Foo {$0}
|
||||||
$0
|
|
||||||
}
|
|
||||||
"#,
|
"#,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1211,9 +1226,7 @@ struct Foo {}
|
||||||
#[derive(Display, Serialize)]
|
#[derive(Display, Serialize)]
|
||||||
struct Foo {}
|
struct Foo {}
|
||||||
|
|
||||||
impl Debug for Foo {
|
impl Debug for Foo {$0}
|
||||||
$0
|
|
||||||
}
|
|
||||||
"#,
|
"#,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue