mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-27 13:33:31 +00:00
⬆️ rust-analyzer
This commit is contained in:
parent
8536eb016c
commit
a99a48e786
86 changed files with 3149 additions and 1653 deletions
27
.github/workflows/ci.yaml
vendored
27
.github/workflows/ci.yaml
vendored
|
@ -43,14 +43,31 @@ jobs:
|
||||||
rustup component add rustfmt rust-src
|
rustup component add rustfmt rust-src
|
||||||
|
|
||||||
- name: Cache Dependencies
|
- name: Cache Dependencies
|
||||||
uses: Swatinem/rust-cache@ce325b60658c1b38465c06cc965b79baf32c1e72
|
uses: Swatinem/rust-cache@76686c56f2b581d1bb5bda44b51f7e24bd9b8b8e
|
||||||
|
|
||||||
- name: Compile
|
- name: Bump opt-level
|
||||||
|
if: matrix.os == 'ubuntu-latest'
|
||||||
|
run: sed -i '/\[profile.dev]/a opt-level=1' Cargo.toml
|
||||||
|
|
||||||
|
- name: Compile (tests)
|
||||||
run: cargo test --no-run --locked
|
run: cargo test --no-run --locked
|
||||||
|
|
||||||
|
# It's faster to `test` before `build` ¯\_(ツ)_/¯
|
||||||
|
- name: Compile (rust-analyzer)
|
||||||
|
if: matrix.os == 'ubuntu-latest'
|
||||||
|
run: cargo build --quiet
|
||||||
|
|
||||||
- name: Test
|
- name: Test
|
||||||
run: cargo test -- --nocapture --quiet
|
run: cargo test -- --nocapture --quiet
|
||||||
|
|
||||||
|
- name: Run analysis-stats on rust-analyzer
|
||||||
|
if: matrix.os == 'ubuntu-latest'
|
||||||
|
run: target/${{ matrix.target }}/debug/rust-analyzer analysis-stats .
|
||||||
|
|
||||||
|
- name: Run analysis-stats on rust std library
|
||||||
|
if: matrix.os == 'ubuntu-latest'
|
||||||
|
run: target/${{ matrix.target }}/debug/rust-analyzer analysis-stats --with-deps $(rustc --print sysroot)/lib/rustlib/src/rust/library/std
|
||||||
|
|
||||||
# Weird targets to catch non-portable code
|
# Weird targets to catch non-portable code
|
||||||
rust-cross:
|
rust-cross:
|
||||||
if: github.repository == 'rust-lang/rust-analyzer'
|
if: github.repository == 'rust-lang/rust-analyzer'
|
||||||
|
@ -73,7 +90,7 @@ jobs:
|
||||||
rustup target add ${{ env.targets }} ${{ env.targets_ide }}
|
rustup target add ${{ env.targets }} ${{ env.targets_ide }}
|
||||||
|
|
||||||
- name: Cache Dependencies
|
- name: Cache Dependencies
|
||||||
uses: Swatinem/rust-cache@ce325b60658c1b38465c06cc965b79baf32c1e72
|
uses: Swatinem/rust-cache@76686c56f2b581d1bb5bda44b51f7e24bd9b8b8e
|
||||||
|
|
||||||
- name: Check
|
- name: Check
|
||||||
run: |
|
run: |
|
||||||
|
@ -99,9 +116,9 @@ jobs:
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Install Nodejs
|
- name: Install Nodejs
|
||||||
uses: actions/setup-node@v1
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: 16.x
|
node-version: 16
|
||||||
|
|
||||||
- name: Install xvfb
|
- name: Install xvfb
|
||||||
if: matrix.os == 'ubuntu-latest'
|
if: matrix.os == 'ubuntu-latest'
|
||||||
|
|
10
.github/workflows/release.yaml
vendored
10
.github/workflows/release.yaml
vendored
|
@ -76,9 +76,9 @@ jobs:
|
||||||
rustup component add rust-src
|
rustup component add rust-src
|
||||||
|
|
||||||
- name: Install Node.js
|
- name: Install Node.js
|
||||||
uses: actions/setup-node@v1
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: 16.x
|
node-version: 16
|
||||||
|
|
||||||
- name: Update apt repositories
|
- name: Update apt repositories
|
||||||
if: matrix.target == 'aarch64-unknown-linux-gnu' || matrix.target == 'arm-unknown-linux-gnueabihf'
|
if: matrix.target == 'aarch64-unknown-linux-gnu' || matrix.target == 'arm-unknown-linux-gnueabihf'
|
||||||
|
@ -184,9 +184,9 @@ jobs:
|
||||||
needs: ["dist", "dist-x86_64-unknown-linux-musl"]
|
needs: ["dist", "dist-x86_64-unknown-linux-musl"]
|
||||||
steps:
|
steps:
|
||||||
- name: Install Nodejs
|
- name: Install Nodejs
|
||||||
uses: actions/setup-node@v1
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: 16.x
|
node-version: 16
|
||||||
|
|
||||||
- run: echo "TAG=$(date --iso -u)" >> $GITHUB_ENV
|
- run: echo "TAG=$(date --iso -u)" >> $GITHUB_ENV
|
||||||
if: github.ref == 'refs/heads/release'
|
if: github.ref == 'refs/heads/release'
|
||||||
|
@ -259,6 +259,7 @@ jobs:
|
||||||
working-directory: ./editors/code
|
working-directory: ./editors/code
|
||||||
# token from https://dev.azure.com/rust-analyzer/
|
# token from https://dev.azure.com/rust-analyzer/
|
||||||
run: npx ovsx publish --pat ${{ secrets.OPENVSX_TOKEN }} --packagePath ../../dist/rust-analyzer-*.vsix || true
|
run: npx ovsx publish --pat ${{ secrets.OPENVSX_TOKEN }} --packagePath ../../dist/rust-analyzer-*.vsix || true
|
||||||
|
timeout-minutes: 2
|
||||||
|
|
||||||
- name: Publish Extension (Code Marketplace, nightly)
|
- name: Publish Extension (Code Marketplace, nightly)
|
||||||
if: github.ref != 'refs/heads/release' && (github.repository == 'rust-analyzer/rust-analyzer' || github.repository == 'rust-lang/rust-analyzer')
|
if: github.ref != 'refs/heads/release' && (github.repository == 'rust-analyzer/rust-analyzer' || github.repository == 'rust-lang/rust-analyzer')
|
||||||
|
@ -269,3 +270,4 @@ jobs:
|
||||||
if: github.ref != 'refs/heads/release' && (github.repository == 'rust-analyzer/rust-analyzer' || github.repository == 'rust-lang/rust-analyzer')
|
if: github.ref != 'refs/heads/release' && (github.repository == 'rust-analyzer/rust-analyzer' || github.repository == 'rust-lang/rust-analyzer')
|
||||||
working-directory: ./editors/code
|
working-directory: ./editors/code
|
||||||
run: npx ovsx publish --pat ${{ secrets.OPENVSX_TOKEN }} --packagePath ../../dist/rust-analyzer-*.vsix || true
|
run: npx ovsx publish --pat ${{ secrets.OPENVSX_TOKEN }} --packagePath ../../dist/rust-analyzer-*.vsix || true
|
||||||
|
timeout-minutes: 2
|
||||||
|
|
189
Cargo.lock
generated
189
Cargo.lock
generated
|
@ -37,9 +37,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anyhow"
|
name = "anyhow"
|
||||||
version = "1.0.62"
|
version = "1.0.65"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1485d4d2cc45e7b201ee3767015c96faa5904387c9d87c6efdd0fb511f12d305"
|
checksum = "98161a4e3e2184da77bb14f02184cdd111e83bbbcc9979dfee3c44b9a85f5602"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anymap"
|
name = "anymap"
|
||||||
|
@ -49,9 +49,9 @@ checksum = "8f1f8f5a6f3d50d89e3797d7593a50f96bb2aaa20ca0cc7be1fb673232c91d72"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "arbitrary"
|
name = "arbitrary"
|
||||||
version = "1.1.3"
|
version = "1.1.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5a7924531f38b1970ff630f03eb20a2fde69db5c590c93b0f3482e95dcc5fd60"
|
checksum = "d86fd10d912cab78764cc44307d9cd5f164e09abbeb87fb19fb6d95937e8da5f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "arrayvec"
|
name = "arrayvec"
|
||||||
|
@ -171,9 +171,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "chalk-derive"
|
name = "chalk-derive"
|
||||||
version = "0.84.0"
|
version = "0.86.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cf29c109d57f8d57b0e7675391be37a9285d86dd93278bd5f14a0ad3c447a6c2"
|
checksum = "5499d415d855b5094366a824815341893ad3de0ecb6048c430118bdae6d27402"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -183,9 +183,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "chalk-ir"
|
name = "chalk-ir"
|
||||||
version = "0.84.0"
|
version = "0.86.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d391763027b5e50a5e15caf6d2857ec585fd68160367bbeac9e1804209620918"
|
checksum = "3800118c76a48507b0eece3a01f3a429b5c478d203c493096e6040c67ab960e1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"chalk-derive",
|
"chalk-derive",
|
||||||
|
@ -194,9 +194,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "chalk-recursive"
|
name = "chalk-recursive"
|
||||||
version = "0.84.0"
|
version = "0.86.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "afafd92dcdc7fe0ea940ee94bdd8cc5bd18f4a4a84c593d6d7025fe16c150478"
|
checksum = "1baf60628fd73104d1f8562586a52d48f37f1e84435aab2e62674b1fd935b8c8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chalk-derive",
|
"chalk-derive",
|
||||||
"chalk-ir",
|
"chalk-ir",
|
||||||
|
@ -207,9 +207,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "chalk-solve"
|
name = "chalk-solve"
|
||||||
version = "0.84.0"
|
version = "0.86.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3af1d111f11c91c48ace02e93e470c5bae6d2631bd112e4545317da53660d7fc"
|
checksum = "0e9c3c068f9358786348e58a1b94ef0a5cf90a9810fc1f10fda896f0b5d80185"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chalk-derive",
|
"chalk-derive",
|
||||||
"chalk-ir",
|
"chalk-ir",
|
||||||
|
@ -270,45 +270,44 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossbeam-epoch"
|
name = "crossbeam-epoch"
|
||||||
version = "0.9.10"
|
version = "0.9.11"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "045ebe27666471bb549370b4b0b3e51b07f56325befa4284db65fc89c02511b1"
|
checksum = "f916dfc5d356b0ed9dae65f1db9fc9770aa2851d2662b988ccf4fe3516e86348"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"crossbeam-utils",
|
"crossbeam-utils",
|
||||||
"memoffset",
|
"memoffset",
|
||||||
"once_cell",
|
|
||||||
"scopeguard",
|
"scopeguard",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossbeam-utils"
|
name = "crossbeam-utils"
|
||||||
version = "0.8.11"
|
version = "0.8.12"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "51887d4adc7b564537b15adcfb307936f8075dfcd5f00dde9a9f1d29383682bc"
|
checksum = "edbafec5fa1f196ca66527c1b12c2ec4745ca14b50f1ad8f9f6f720b55d11fac"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"once_cell",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dashmap"
|
name = "dashmap"
|
||||||
version = "5.3.4"
|
version = "5.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3495912c9c1ccf2e18976439f4443f3fee0fd61f424ff99fde6a66b15ecb448f"
|
checksum = "907076dfda823b0b36d2a1bb5f90c96660a5bbcd7729e10727f07858f22c4edc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"hashbrown",
|
"hashbrown",
|
||||||
"lock_api",
|
"lock_api",
|
||||||
|
"once_cell",
|
||||||
"parking_lot_core 0.9.3",
|
"parking_lot_core 0.9.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "derive_arbitrary"
|
name = "derive_arbitrary"
|
||||||
version = "1.1.3"
|
version = "1.1.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c9a577516173adb681466d517d39bd468293bc2c2a16439375ef0f35bba45f3d"
|
checksum = "226ad66541d865d7a7173ad6a9e691c33fdb910ac723f4bc734b3e5294a1f931"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -404,11 +403,10 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "form_urlencoded"
|
name = "form_urlencoded"
|
||||||
version = "1.0.1"
|
version = "1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191"
|
checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"matches",
|
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -546,6 +544,7 @@ version = "0.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arrayvec",
|
"arrayvec",
|
||||||
"base-db",
|
"base-db",
|
||||||
|
"chalk-derive",
|
||||||
"chalk-ir",
|
"chalk-ir",
|
||||||
"chalk-recursive",
|
"chalk-recursive",
|
||||||
"chalk-solve",
|
"chalk-solve",
|
||||||
|
@ -573,9 +572,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "home"
|
name = "home"
|
||||||
version = "0.5.3"
|
version = "0.5.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2456aef2e6b6a9784192ae780c0f15bc57df0e918585282325e8c8ac27737654"
|
checksum = "747309b4b440c06d57b0b25f2aee03ee9b5e5397d288c60e21fc709bb98a7408"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
@ -714,11 +713,10 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "idna"
|
name = "idna"
|
||||||
version = "0.2.3"
|
version = "0.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8"
|
checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"matches",
|
|
||||||
"unicode-bidi",
|
"unicode-bidi",
|
||||||
"unicode-normalization",
|
"unicode-normalization",
|
||||||
]
|
]
|
||||||
|
@ -764,18 +762,18 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itertools"
|
name = "itertools"
|
||||||
version = "0.10.3"
|
version = "0.10.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3"
|
checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"either",
|
"either",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "itoa"
|
||||||
version = "1.0.3"
|
version = "1.0.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754"
|
checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jod-thread"
|
name = "jod-thread"
|
||||||
|
@ -815,9 +813,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.132"
|
version = "0.2.135"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8371e4e5341c3a96db127eb2465ac681ced4c433e01dd0e938adbef26ba93ba5"
|
checksum = "68783febc7782c6c5cb401fbda4de5a9898be1762314da0bb2c10ced61f18b0c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libloading"
|
name = "libloading"
|
||||||
|
@ -831,9 +829,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libmimalloc-sys"
|
name = "libmimalloc-sys"
|
||||||
version = "0.1.25"
|
version = "0.1.26"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "11ca136052550448f55df7898c6dbe651c6b574fe38a0d9ea687a9f8088a2e2c"
|
checksum = "8fc093ab289b0bfda3aa1bdfab9c9542be29c7ef385cfcbe77f8c9813588eb48"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cc",
|
"cc",
|
||||||
]
|
]
|
||||||
|
@ -844,9 +842,9 @@ version = "0.0.0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lock_api"
|
name = "lock_api"
|
||||||
version = "0.4.7"
|
version = "0.4.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53"
|
checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
"scopeguard",
|
"scopeguard",
|
||||||
|
@ -894,12 +892,6 @@ dependencies = [
|
||||||
"regex-automata",
|
"regex-automata",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "matches"
|
|
||||||
version = "0.1.9"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mbe"
|
name = "mbe"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
|
@ -941,18 +933,18 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mimalloc"
|
name = "mimalloc"
|
||||||
version = "0.1.29"
|
version = "0.1.30"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2f64ad83c969af2e732e907564deb0d0ed393cec4af80776f77dd77a1a427698"
|
checksum = "76ce6a4b40d3bff9eb3ce9881ca0737a85072f9f975886082640cd46a75cdb35"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libmimalloc-sys",
|
"libmimalloc-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "miniz_oxide"
|
name = "miniz_oxide"
|
||||||
version = "0.5.3"
|
version = "0.5.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6f5c75688da582b8ffc1f1799e9db273f32133c49e048f614d22ec3256773ccc"
|
checksum = "96590ba8f175222643a85693f33d26e9c8a015f599c216509b1a6894af675d34"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"adler",
|
"adler",
|
||||||
]
|
]
|
||||||
|
@ -980,9 +972,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "notify"
|
name = "notify"
|
||||||
version = "5.0.0-pre.16"
|
version = "5.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "530f6314d6904508082f4ea424a0275cf62d341e118b313663f266429cb19693"
|
checksum = "ed2c66da08abae1c024c01d635253e402341b4060a12e99b31c7594063bf490a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"crossbeam-channel",
|
"crossbeam-channel",
|
||||||
|
@ -1017,9 +1009,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "once_cell"
|
name = "once_cell"
|
||||||
version = "1.13.1"
|
version = "1.15.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "074864da206b4973b84eb91683020dbefd6a8c3f0f38e054d93954e891935e4e"
|
checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oorandom"
|
name = "oorandom"
|
||||||
|
@ -1088,9 +1080,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "paste"
|
name = "paste"
|
||||||
version = "1.0.8"
|
version = "1.0.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9423e2b32f7a043629287a536f21951e8c6a82482d0acb1eeebfc90bc2225b22"
|
checksum = "b1de2e551fb905ac83f73f7aedf2f0cb4a0da7e35efa24a202a936269f1f18e1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "paths"
|
name = "paths"
|
||||||
|
@ -1098,9 +1090,9 @@ version = "0.0.0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "percent-encoding"
|
name = "percent-encoding"
|
||||||
version = "2.1.0"
|
version = "2.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
|
checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "perf-event"
|
name = "perf-event"
|
||||||
|
@ -1190,9 +1182,9 @@ version = "0.0.0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.46"
|
version = "1.0.47"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "94e2ef8dbfc347b10c094890f778ee2e36ca9bb4262e86dc99cd217e35f3470b"
|
checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
@ -1265,9 +1257,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pulldown-cmark-to-cmark"
|
name = "pulldown-cmark-to-cmark"
|
||||||
version = "10.0.2"
|
version = "10.0.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c1353ac408192fa925228d3e60ff746167d03f4f7e54835d78ef79e08225d913"
|
checksum = "0194e6e1966c23cc5fd988714f85b18d548d773e81965413555d96569931833d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"pulldown-cmark",
|
"pulldown-cmark",
|
||||||
]
|
]
|
||||||
|
@ -1340,9 +1332,9 @@ checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rowan"
|
name = "rowan"
|
||||||
version = "0.15.8"
|
version = "0.15.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e88acf7b001007e9e8c989fe7449f6601d909e5dd2c56399fc158977ad6c56e8"
|
checksum = "5811547e7ba31e903fe48c8ceab10d40d70a101f3d15523c847cce91aa71f332"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"countme",
|
"countme",
|
||||||
"hashbrown",
|
"hashbrown",
|
||||||
|
@ -1493,27 +1485,27 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "semver"
|
name = "semver"
|
||||||
version = "1.0.13"
|
version = "1.0.14"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "93f6841e709003d68bb2deee8c343572bf446003ec20a583e76f7b15cebf3711"
|
checksum = "e25dfac463d778e353db5be2449d1cce89bd6fd23c9f1ea21310ce6e5a1b29c4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.144"
|
version = "1.0.145"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0f747710de3dcd43b88c9168773254e809d8ddbdf9653b84e2554ab219f17860"
|
checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.144"
|
version = "1.0.145"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "94ed3a816fb1d101812f83e789f888322c34e291f894f19590dc310963e87a00"
|
checksum = "81fa1584d3d1bcacd84c277a0dfe21f5b0f6accf4a23d04d4c6d61f1af522b4c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -1522,9 +1514,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.85"
|
version = "1.0.86"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44"
|
checksum = "41feea4228a6f1cd09ec7a3593a682276702cd67b5273544757dae23c096f074"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"itoa",
|
"itoa",
|
||||||
|
@ -1554,9 +1546,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "smallvec"
|
name = "smallvec"
|
||||||
version = "1.9.0"
|
version = "1.10.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1"
|
checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "smol_str"
|
name = "smol_str"
|
||||||
|
@ -1666,18 +1658,18 @@ checksum = "288cb548dbe72b652243ea797201f3d481a0609a967980fcc5b2315ea811560a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.31"
|
version = "1.0.37"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a"
|
checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"thiserror-impl",
|
"thiserror-impl",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror-impl"
|
name = "thiserror-impl"
|
||||||
version = "1.0.31"
|
version = "1.0.37"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a"
|
checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -1715,9 +1707,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tikv-jemalloc-sys"
|
name = "tikv-jemalloc-sys"
|
||||||
version = "0.5.1+5.3.0-patched"
|
version = "0.5.2+5.3.0-patched"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "931e876f91fed0827f863a2d153897790da0b24d882c721a79cb3beb0b903261"
|
checksum = "ec45c14da997d0925c7835883e4d5c181f196fa142f8c19d7643d1e9af2592c3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cc",
|
"cc",
|
||||||
"fs_extra",
|
"fs_extra",
|
||||||
|
@ -1758,9 +1750,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tracing"
|
name = "tracing"
|
||||||
version = "0.1.36"
|
version = "0.1.37"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2fce9567bd60a67d08a16488756721ba392f24f29006402881e43b19aac64307"
|
checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
|
@ -1770,9 +1762,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tracing-attributes"
|
name = "tracing-attributes"
|
||||||
version = "0.1.22"
|
version = "0.1.23"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "11c75893af559bc8e10716548bdef5cb2b983f8e637db9d0e15126b61b484ee2"
|
checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -1781,9 +1773,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tracing-core"
|
name = "tracing-core"
|
||||||
version = "0.1.29"
|
version = "0.1.30"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5aeea4303076558a00714b823f9ad67d58a3bbda1df83d8827d21193156e22f7"
|
checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"valuable",
|
"valuable",
|
||||||
|
@ -1802,9 +1794,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tracing-subscriber"
|
name = "tracing-subscriber"
|
||||||
version = "0.3.15"
|
version = "0.3.16"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "60db860322da191b40952ad9affe65ea23e7dd6a5c442c2c42865810c6ab8e6b"
|
checksum = "a6176eae26dd70d0c919749377897b54a9276bd7061339665dd68777926b5a70"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"matchers",
|
"matchers",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
|
@ -1866,40 +1858,39 @@ checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
version = "1.0.1"
|
version = "1.0.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c"
|
checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-normalization"
|
name = "unicode-normalization"
|
||||||
version = "0.1.21"
|
version = "0.1.22"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "854cbdc4f7bc6ae19c820d44abdc3277ac3e1b2b93db20a636825d9322fb60e6"
|
checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"tinyvec",
|
"tinyvec",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-segmentation"
|
name = "unicode-segmentation"
|
||||||
version = "1.9.0"
|
version = "1.10.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99"
|
checksum = "0fdbf052a0783de01e944a6ce7a8cb939e295b1e7be835a1112c3b9a7f047a5a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-xid"
|
name = "unicode-xid"
|
||||||
version = "0.2.3"
|
version = "0.2.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04"
|
checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "url"
|
name = "url"
|
||||||
version = "2.2.2"
|
version = "2.3.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c"
|
checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"form_urlencoded",
|
"form_urlencoded",
|
||||||
"idna",
|
"idna",
|
||||||
"matches",
|
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
|
@ -27,6 +27,7 @@ debug = 0
|
||||||
# chalk-solve = { path = "../chalk/chalk-solve" }
|
# chalk-solve = { path = "../chalk/chalk-solve" }
|
||||||
# chalk-ir = { path = "../chalk/chalk-ir" }
|
# chalk-ir = { path = "../chalk/chalk-ir" }
|
||||||
# chalk-recursive = { path = "../chalk/chalk-recursive" }
|
# chalk-recursive = { path = "../chalk/chalk-recursive" }
|
||||||
|
# chalk-derive = { path = "../chalk/chalk-derive" }
|
||||||
|
|
||||||
# ungrammar = { path = "../ungrammar" }
|
# ungrammar = { path = "../ungrammar" }
|
||||||
|
|
||||||
|
|
|
@ -22,5 +22,5 @@ oorandom = "11.1.3"
|
||||||
# We depend on both individually instead of using `features = ["derive"]` to microoptimize the
|
# We depend on both individually instead of using `features = ["derive"]` to microoptimize the
|
||||||
# build graph: if the feature was enabled, syn would be built early on in the graph if `smolstr`
|
# build graph: if the feature was enabled, syn would be built early on in the graph if `smolstr`
|
||||||
# supports `arbitrary`. This way, we avoid feature unification.
|
# supports `arbitrary`. This way, we avoid feature unification.
|
||||||
arbitrary = "1.1.0"
|
arbitrary = "1.1.7"
|
||||||
derive_arbitrary = "1.1.0"
|
derive_arbitrary = "1.1.6"
|
||||||
|
|
|
@ -11,11 +11,11 @@ doctest = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
crossbeam-channel = "0.5.5"
|
crossbeam-channel = "0.5.5"
|
||||||
tracing = "0.1.35"
|
tracing = "0.1.37"
|
||||||
cargo_metadata = "0.15.0"
|
cargo_metadata = "0.15.0"
|
||||||
rustc-hash = "1.1.0"
|
rustc-hash = "1.1.0"
|
||||||
serde = { version = "1.0.137", features = ["derive"] }
|
serde = { version = "1.0.137", features = ["derive"] }
|
||||||
serde_json = "1.0.81"
|
serde_json = "1.0.86"
|
||||||
jod-thread = "0.1.2"
|
jod-thread = "0.1.2"
|
||||||
|
|
||||||
toolchain = { path = "../toolchain", version = "0.0.0" }
|
toolchain = { path = "../toolchain", version = "0.0.0" }
|
||||||
|
|
|
@ -15,17 +15,17 @@ arrayvec = "0.7.2"
|
||||||
bitflags = "1.3.2"
|
bitflags = "1.3.2"
|
||||||
cov-mark = "2.0.0-pre.1"
|
cov-mark = "2.0.0-pre.1"
|
||||||
# We need to freeze the version of the crate, as the raw-api feature is considered unstable
|
# We need to freeze the version of the crate, as the raw-api feature is considered unstable
|
||||||
dashmap = { version = "=5.3.4", features = ["raw-api"] }
|
dashmap = { version = "=5.4.0", features = ["raw-api"] }
|
||||||
drop_bomb = "0.1.5"
|
drop_bomb = "0.1.5"
|
||||||
either = "1.7.0"
|
either = "1.7.0"
|
||||||
fst = { version = "0.4.7", default-features = false }
|
fst = { version = "0.4.7", default-features = false }
|
||||||
hashbrown = { version = "0.12.1", default-features = false }
|
hashbrown = { version = "0.12.1", default-features = false }
|
||||||
indexmap = "1.9.1"
|
indexmap = "1.9.1"
|
||||||
itertools = "0.10.3"
|
itertools = "0.10.5"
|
||||||
la-arena = { version = "0.3.0", path = "../../lib/la-arena" }
|
la-arena = { version = "0.3.0", path = "../../lib/la-arena" }
|
||||||
once_cell = "1.12.0"
|
once_cell = "1.15.0"
|
||||||
rustc-hash = "1.1.0"
|
rustc-hash = "1.1.0"
|
||||||
smallvec = "1.9.0"
|
smallvec = "1.10.0"
|
||||||
tracing = "0.1.35"
|
tracing = "0.1.35"
|
||||||
|
|
||||||
stdx = { path = "../stdx", version = "0.0.0" }
|
stdx = { path = "../stdx", version = "0.0.0" }
|
||||||
|
|
|
@ -311,7 +311,20 @@ impl Body {
|
||||||
DefWithBodyId::FunctionId(f) => {
|
DefWithBodyId::FunctionId(f) => {
|
||||||
let f = f.lookup(db);
|
let f = f.lookup(db);
|
||||||
let src = f.source(db);
|
let src = f.source(db);
|
||||||
params = src.value.param_list();
|
params = src.value.param_list().map(|param_list| {
|
||||||
|
let item_tree = f.id.item_tree(db);
|
||||||
|
let func = &item_tree[f.id.value];
|
||||||
|
let krate = f.container.module(db).krate;
|
||||||
|
let crate_graph = db.crate_graph();
|
||||||
|
(
|
||||||
|
param_list,
|
||||||
|
func.params.clone().map(move |param| {
|
||||||
|
item_tree
|
||||||
|
.attrs(db, krate, param.into())
|
||||||
|
.is_cfg_enabled(&crate_graph[krate].cfg_options)
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
});
|
||||||
(src.file_id, f.module(db), src.value.body().map(ast::Expr::from))
|
(src.file_id, f.module(db), src.value.body().map(ast::Expr::from))
|
||||||
}
|
}
|
||||||
DefWithBodyId::ConstId(c) => {
|
DefWithBodyId::ConstId(c) => {
|
||||||
|
@ -334,6 +347,7 @@ impl Body {
|
||||||
let expander = Expander::new(db, file_id, module);
|
let expander = Expander::new(db, file_id, module);
|
||||||
let (mut body, source_map) = Body::new(db, expander, params, body);
|
let (mut body, source_map) = Body::new(db, expander, params, body);
|
||||||
body.shrink_to_fit();
|
body.shrink_to_fit();
|
||||||
|
|
||||||
(Arc::new(body), Arc::new(source_map))
|
(Arc::new(body), Arc::new(source_map))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -370,7 +384,7 @@ impl Body {
|
||||||
fn new(
|
fn new(
|
||||||
db: &dyn DefDatabase,
|
db: &dyn DefDatabase,
|
||||||
expander: Expander,
|
expander: Expander,
|
||||||
params: Option<ast::ParamList>,
|
params: Option<(ast::ParamList, impl Iterator<Item = bool>)>,
|
||||||
body: Option<ast::Expr>,
|
body: Option<ast::Expr>,
|
||||||
) -> (Body, BodySourceMap) {
|
) -> (Body, BodySourceMap) {
|
||||||
lower::lower(db, expander, params, body)
|
lower::lower(db, expander, params, body)
|
||||||
|
|
|
@ -77,7 +77,7 @@ impl<'a> LowerCtx<'a> {
|
||||||
pub(super) fn lower(
|
pub(super) fn lower(
|
||||||
db: &dyn DefDatabase,
|
db: &dyn DefDatabase,
|
||||||
expander: Expander,
|
expander: Expander,
|
||||||
params: Option<ast::ParamList>,
|
params: Option<(ast::ParamList, impl Iterator<Item = bool>)>,
|
||||||
body: Option<ast::Expr>,
|
body: Option<ast::Expr>,
|
||||||
) -> (Body, BodySourceMap) {
|
) -> (Body, BodySourceMap) {
|
||||||
ExprCollector {
|
ExprCollector {
|
||||||
|
@ -119,11 +119,13 @@ struct ExprCollector<'a> {
|
||||||
impl ExprCollector<'_> {
|
impl ExprCollector<'_> {
|
||||||
fn collect(
|
fn collect(
|
||||||
mut self,
|
mut self,
|
||||||
param_list: Option<ast::ParamList>,
|
param_list: Option<(ast::ParamList, impl Iterator<Item = bool>)>,
|
||||||
body: Option<ast::Expr>,
|
body: Option<ast::Expr>,
|
||||||
) -> (Body, BodySourceMap) {
|
) -> (Body, BodySourceMap) {
|
||||||
if let Some(param_list) = param_list {
|
if let Some((param_list, mut attr_enabled)) = param_list {
|
||||||
if let Some(self_param) = param_list.self_param() {
|
if let Some(self_param) =
|
||||||
|
param_list.self_param().filter(|_| attr_enabled.next().unwrap_or(false))
|
||||||
|
{
|
||||||
let ptr = AstPtr::new(&self_param);
|
let ptr = AstPtr::new(&self_param);
|
||||||
let param_pat = self.alloc_pat(
|
let param_pat = self.alloc_pat(
|
||||||
Pat::Bind {
|
Pat::Bind {
|
||||||
|
@ -139,7 +141,11 @@ impl ExprCollector<'_> {
|
||||||
self.body.params.push(param_pat);
|
self.body.params.push(param_pat);
|
||||||
}
|
}
|
||||||
|
|
||||||
for pat in param_list.params().filter_map(|param| param.pat()) {
|
for pat in param_list
|
||||||
|
.params()
|
||||||
|
.zip(attr_enabled)
|
||||||
|
.filter_map(|(param, enabled)| param.pat().filter(|_| enabled))
|
||||||
|
{
|
||||||
let param_pat = self.collect_pat(pat);
|
let param_pat = self.collect_pat(pat);
|
||||||
self.body.params.push(param_pat);
|
self.body.params.push(param_pat);
|
||||||
}
|
}
|
||||||
|
|
|
@ -93,12 +93,12 @@ macro_rules! option_env {() => {}}
|
||||||
|
|
||||||
fn main() { option_env!("TEST_ENV_VAR"); }
|
fn main() { option_env!("TEST_ENV_VAR"); }
|
||||||
"#,
|
"#,
|
||||||
expect![[r##"
|
expect![[r#"
|
||||||
#[rustc_builtin_macro]
|
#[rustc_builtin_macro]
|
||||||
macro_rules! option_env {() => {}}
|
macro_rules! option_env {() => {}}
|
||||||
|
|
||||||
fn main() { std::option::Option::None:: < &str>; }
|
fn main() { $crate::option::Option::None:: < &str>; }
|
||||||
"##]],
|
"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -191,7 +191,7 @@ fn main() {
|
||||||
format_args!("{} {:?}", arg1(a, b, c), arg2);
|
format_args!("{} {:?}", arg1(a, b, c), arg2);
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
expect![[r##"
|
expect![[r#"
|
||||||
#[rustc_builtin_macro]
|
#[rustc_builtin_macro]
|
||||||
macro_rules! format_args {
|
macro_rules! format_args {
|
||||||
($fmt:expr) => ({ /* compiler built-in */ });
|
($fmt:expr) => ({ /* compiler built-in */ });
|
||||||
|
@ -199,9 +199,9 @@ macro_rules! format_args {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
std::fmt::Arguments::new_v1(&[], &[std::fmt::ArgumentV1::new(&(arg1(a, b, c)), std::fmt::Display::fmt), std::fmt::ArgumentV1::new(&(arg2), std::fmt::Display::fmt), ]);
|
$crate::fmt::Arguments::new_v1(&[], &[$crate::fmt::ArgumentV1::new(&(arg1(a, b, c)), $crate::fmt::Display::fmt), $crate::fmt::ArgumentV1::new(&(arg2), $crate::fmt::Display::fmt), ]);
|
||||||
}
|
}
|
||||||
"##]],
|
"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -219,7 +219,7 @@ fn main() {
|
||||||
format_args!("{} {:?}", a::<A,B>(), b);
|
format_args!("{} {:?}", a::<A,B>(), b);
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
expect![[r##"
|
expect![[r#"
|
||||||
#[rustc_builtin_macro]
|
#[rustc_builtin_macro]
|
||||||
macro_rules! format_args {
|
macro_rules! format_args {
|
||||||
($fmt:expr) => ({ /* compiler built-in */ });
|
($fmt:expr) => ({ /* compiler built-in */ });
|
||||||
|
@ -227,9 +227,9 @@ macro_rules! format_args {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
std::fmt::Arguments::new_v1(&[], &[std::fmt::ArgumentV1::new(&(a::<A, B>()), std::fmt::Display::fmt), std::fmt::ArgumentV1::new(&(b), std::fmt::Display::fmt), ]);
|
$crate::fmt::Arguments::new_v1(&[], &[$crate::fmt::ArgumentV1::new(&(a::<A, B>()), $crate::fmt::Display::fmt), $crate::fmt::ArgumentV1::new(&(b), $crate::fmt::Display::fmt), ]);
|
||||||
}
|
}
|
||||||
"##]],
|
"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -248,7 +248,7 @@ fn main() {
|
||||||
format_args!/*+errors*/("{} {:?}", a.);
|
format_args!/*+errors*/("{} {:?}", a.);
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
expect![[r##"
|
expect![[r#"
|
||||||
#[rustc_builtin_macro]
|
#[rustc_builtin_macro]
|
||||||
macro_rules! format_args {
|
macro_rules! format_args {
|
||||||
($fmt:expr) => ({ /* compiler built-in */ });
|
($fmt:expr) => ({ /* compiler built-in */ });
|
||||||
|
@ -258,9 +258,9 @@ macro_rules! format_args {
|
||||||
fn main() {
|
fn main() {
|
||||||
let _ =
|
let _ =
|
||||||
/* parse error: expected field name or number */
|
/* parse error: expected field name or number */
|
||||||
std::fmt::Arguments::new_v1(&[], &[std::fmt::ArgumentV1::new(&(a.), std::fmt::Display::fmt), ]);
|
$crate::fmt::Arguments::new_v1(&[], &[$crate::fmt::ArgumentV1::new(&(a.), $crate::fmt::Display::fmt), ]);
|
||||||
}
|
}
|
||||||
"##]],
|
"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,11 +15,11 @@ tracing = "0.1.35"
|
||||||
either = "1.7.0"
|
either = "1.7.0"
|
||||||
rustc-hash = "1.1.0"
|
rustc-hash = "1.1.0"
|
||||||
la-arena = { version = "0.3.0", path = "../../lib/la-arena" }
|
la-arena = { version = "0.3.0", path = "../../lib/la-arena" }
|
||||||
itertools = "0.10.3"
|
itertools = "0.10.5"
|
||||||
hashbrown = { version = "0.12.1", features = [
|
hashbrown = { version = "0.12.1", features = [
|
||||||
"inline-more",
|
"inline-more",
|
||||||
], default-features = false }
|
], default-features = false }
|
||||||
smallvec = { version = "1.9.0", features = ["const_new"] }
|
smallvec = { version = "1.10.0", features = ["const_new"] }
|
||||||
|
|
||||||
stdx = { path = "../stdx", version = "0.0.0" }
|
stdx = { path = "../stdx", version = "0.0.0" }
|
||||||
base-db = { path = "../base-db", version = "0.0.0" }
|
base-db = { path = "../base-db", version = "0.0.0" }
|
||||||
|
|
|
@ -238,9 +238,9 @@ fn format_args_expand(
|
||||||
) -> ExpandResult<tt::Subtree> {
|
) -> ExpandResult<tt::Subtree> {
|
||||||
// We expand `format_args!("", a1, a2)` to
|
// We expand `format_args!("", a1, a2)` to
|
||||||
// ```
|
// ```
|
||||||
// std::fmt::Arguments::new_v1(&[], &[
|
// $crate::fmt::Arguments::new_v1(&[], &[
|
||||||
// std::fmt::ArgumentV1::new(&arg1,std::fmt::Display::fmt),
|
// $crate::fmt::ArgumentV1::new(&arg1,$crate::fmt::Display::fmt),
|
||||||
// std::fmt::ArgumentV1::new(&arg2,std::fmt::Display::fmt),
|
// $crate::fmt::ArgumentV1::new(&arg2,$crate::fmt::Display::fmt),
|
||||||
// ])
|
// ])
|
||||||
// ```,
|
// ```,
|
||||||
// which is still not really correct, but close enough for now
|
// which is still not really correct, but close enough for now
|
||||||
|
@ -262,10 +262,10 @@ fn format_args_expand(
|
||||||
}
|
}
|
||||||
let _format_string = args.remove(0);
|
let _format_string = args.remove(0);
|
||||||
let arg_tts = args.into_iter().flat_map(|arg| {
|
let arg_tts = args.into_iter().flat_map(|arg| {
|
||||||
quote! { std::fmt::ArgumentV1::new(&(#arg), std::fmt::Display::fmt), }
|
quote! { #DOLLAR_CRATE::fmt::ArgumentV1::new(&(#arg), #DOLLAR_CRATE::fmt::Display::fmt), }
|
||||||
}.token_trees);
|
}.token_trees);
|
||||||
let expanded = quote! {
|
let expanded = quote! {
|
||||||
std::fmt::Arguments::new_v1(&[], &[##arg_tts])
|
#DOLLAR_CRATE::fmt::Arguments::new_v1(&[], &[##arg_tts])
|
||||||
};
|
};
|
||||||
ExpandResult::ok(expanded)
|
ExpandResult::ok(expanded)
|
||||||
}
|
}
|
||||||
|
@ -675,8 +675,8 @@ fn option_env_expand(
|
||||||
};
|
};
|
||||||
|
|
||||||
let expanded = match get_env_inner(db, arg_id, &key) {
|
let expanded = match get_env_inner(db, arg_id, &key) {
|
||||||
None => quote! { std::option::Option::None::<&str> },
|
None => quote! { #DOLLAR_CRATE::option::Option::None::<&str> },
|
||||||
Some(s) => quote! { std::option::Some(#s) },
|
Some(s) => quote! { #DOLLAR_CRATE::option::Some(#s) },
|
||||||
};
|
};
|
||||||
|
|
||||||
ExpandResult::ok(ExpandedEager::new(expanded))
|
ExpandResult::ok(ExpandedEager::new(expanded))
|
||||||
|
|
|
@ -221,8 +221,16 @@ pub fn expand_speculative(
|
||||||
fixup::reverse_fixups(&mut speculative_expansion.value, &spec_args_tmap, &fixups.undo_info);
|
fixup::reverse_fixups(&mut speculative_expansion.value, &spec_args_tmap, &fixups.undo_info);
|
||||||
let (node, rev_tmap) = token_tree_to_syntax_node(&speculative_expansion.value, expand_to);
|
let (node, rev_tmap) = token_tree_to_syntax_node(&speculative_expansion.value, expand_to);
|
||||||
|
|
||||||
let range = rev_tmap.first_range_by_token(token_id, token_to_map.kind())?;
|
let syntax_node = node.syntax_node();
|
||||||
let token = node.syntax_node().covering_element(range).into_token()?;
|
let token = rev_tmap
|
||||||
|
.ranges_by_token(token_id, token_to_map.kind())
|
||||||
|
.filter_map(|range| syntax_node.covering_element(range).into_token())
|
||||||
|
.min_by_key(|t| {
|
||||||
|
// prefer tokens of the same kind and text
|
||||||
|
// Note the inversion of the score here, as we want to prefer the first token in case
|
||||||
|
// of all tokens having the same score
|
||||||
|
(t.kind() != token_to_map.kind()) as u8 + (t.text() != token_to_map.text()) as u8
|
||||||
|
})?;
|
||||||
Some((node.syntax_node(), token))
|
Some((node.syntax_node(), token))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -259,6 +259,7 @@ macro_rules! __known_path {
|
||||||
(core::future::Future) => {};
|
(core::future::Future) => {};
|
||||||
(core::future::IntoFuture) => {};
|
(core::future::IntoFuture) => {};
|
||||||
(core::ops::Try) => {};
|
(core::ops::Try) => {};
|
||||||
|
(core::ops::FromResidual) => {};
|
||||||
($path:path) => {
|
($path:path) => {
|
||||||
compile_error!("Please register your known path in the path module")
|
compile_error!("Please register your known path in the path module")
|
||||||
};
|
};
|
||||||
|
|
|
@ -279,6 +279,8 @@ pub mod known {
|
||||||
RangeToInclusive,
|
RangeToInclusive,
|
||||||
RangeTo,
|
RangeTo,
|
||||||
Range,
|
Range,
|
||||||
|
Residual,
|
||||||
|
FromResidual,
|
||||||
Neg,
|
Neg,
|
||||||
Not,
|
Not,
|
||||||
None,
|
None,
|
||||||
|
|
|
@ -11,18 +11,19 @@ doctest = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
cov-mark = "2.0.0-pre.1"
|
cov-mark = "2.0.0-pre.1"
|
||||||
itertools = "0.10.3"
|
itertools = "0.10.5"
|
||||||
arrayvec = "0.7.2"
|
arrayvec = "0.7.2"
|
||||||
smallvec = "1.9.0"
|
smallvec = "1.10.0"
|
||||||
ena = "0.14.0"
|
ena = "0.14.0"
|
||||||
tracing = "0.1.35"
|
tracing = "0.1.35"
|
||||||
rustc-hash = "1.1.0"
|
rustc-hash = "1.1.0"
|
||||||
scoped-tls = "1.0.0"
|
scoped-tls = "1.0.0"
|
||||||
chalk-solve = { version = "0.84.0", default-features = false }
|
chalk-solve = { version = "0.86.0", default-features = false }
|
||||||
chalk-ir = "0.84.0"
|
chalk-ir = "0.86.0"
|
||||||
chalk-recursive = { version = "0.84.0", default-features = false }
|
chalk-recursive = { version = "0.86.0", default-features = false }
|
||||||
|
chalk-derive = "0.86.0"
|
||||||
la-arena = { version = "0.3.0", path = "../../lib/la-arena" }
|
la-arena = { version = "0.3.0", path = "../../lib/la-arena" }
|
||||||
once_cell = "1.12.0"
|
once_cell = "1.15.0"
|
||||||
typed-arena = "2.0.1"
|
typed-arena = "2.0.1"
|
||||||
|
|
||||||
stdx = { path = "../stdx", version = "0.0.0" }
|
stdx = { path = "../stdx", version = "0.0.0" }
|
||||||
|
@ -37,7 +38,7 @@ limit = { path = "../limit", version = "0.0.0" }
|
||||||
test-utils = { path = "../test-utils" }
|
test-utils = { path = "../test-utils" }
|
||||||
expect-test = "1.4.0"
|
expect-test = "1.4.0"
|
||||||
tracing = "0.1.35"
|
tracing = "0.1.35"
|
||||||
tracing-subscriber = { version = "0.3.14", default-features = false, features = [
|
tracing-subscriber = { version = "0.3.16", default-features = false, features = [
|
||||||
"env-filter",
|
"env-filter",
|
||||||
"registry",
|
"registry",
|
||||||
] }
|
] }
|
||||||
|
|
|
@ -823,10 +823,10 @@ pub(super) fn generic_predicate_to_inline_bound(
|
||||||
Some(chalk_ir::Binders::new(binders, rust_ir::InlineBound::TraitBound(trait_bound)))
|
Some(chalk_ir::Binders::new(binders, rust_ir::InlineBound::TraitBound(trait_bound)))
|
||||||
}
|
}
|
||||||
WhereClause::AliasEq(AliasEq { alias: AliasTy::Projection(projection_ty), ty }) => {
|
WhereClause::AliasEq(AliasEq { alias: AliasTy::Projection(projection_ty), ty }) => {
|
||||||
if projection_ty.self_type_parameter(Interner) != self_ty_shifted_in {
|
let trait_ = projection_ty.trait_(db);
|
||||||
|
if projection_ty.self_type_parameter(db) != self_ty_shifted_in {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let trait_ = projection_ty.trait_(db);
|
|
||||||
let args_no_self = projection_ty.substitution.as_slice(Interner)[1..]
|
let args_no_self = projection_ty.substitution.as_slice(Interner)[1..]
|
||||||
.iter()
|
.iter()
|
||||||
.map(|ty| ty.clone().cast(Interner))
|
.map(|ty| ty.clone().cast(Interner))
|
||||||
|
|
|
@ -262,7 +262,7 @@ impl TyExt for Ty {
|
||||||
WhereClause::AliasEq(AliasEq {
|
WhereClause::AliasEq(AliasEq {
|
||||||
alias: AliasTy::Projection(proj),
|
alias: AliasTy::Projection(proj),
|
||||||
ty: _,
|
ty: _,
|
||||||
}) => &proj.self_type_parameter(Interner) == self,
|
}) => &proj.self_type_parameter(db) == self,
|
||||||
_ => false,
|
_ => false,
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
@ -333,6 +333,7 @@ impl TyExt for Ty {
|
||||||
pub trait ProjectionTyExt {
|
pub trait ProjectionTyExt {
|
||||||
fn trait_ref(&self, db: &dyn HirDatabase) -> TraitRef;
|
fn trait_ref(&self, db: &dyn HirDatabase) -> TraitRef;
|
||||||
fn trait_(&self, db: &dyn HirDatabase) -> TraitId;
|
fn trait_(&self, db: &dyn HirDatabase) -> TraitId;
|
||||||
|
fn self_type_parameter(&self, db: &dyn HirDatabase) -> Ty;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ProjectionTyExt for ProjectionTy {
|
impl ProjectionTyExt for ProjectionTy {
|
||||||
|
@ -349,6 +350,10 @@ impl ProjectionTyExt for ProjectionTy {
|
||||||
_ => panic!("projection ty without parent trait"),
|
_ => panic!("projection ty without parent trait"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn self_type_parameter(&self, db: &dyn HirDatabase) -> Ty {
|
||||||
|
self.trait_ref(db).self_type_parameter(Interner)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait TraitRefExt {
|
pub trait TraitRefExt {
|
||||||
|
|
|
@ -291,7 +291,7 @@ impl HirDisplay for ProjectionTy {
|
||||||
|
|
||||||
let trait_ = f.db.trait_data(self.trait_(f.db));
|
let trait_ = f.db.trait_data(self.trait_(f.db));
|
||||||
write!(f, "<")?;
|
write!(f, "<")?;
|
||||||
self.self_type_parameter(Interner).hir_fmt(f)?;
|
self.self_type_parameter(f.db).hir_fmt(f)?;
|
||||||
write!(f, " as {}", trait_.name)?;
|
write!(f, " as {}", trait_.name)?;
|
||||||
if self.substitution.len(Interner) > 1 {
|
if self.substitution.len(Interner) > 1 {
|
||||||
write!(f, "<")?;
|
write!(f, "<")?;
|
||||||
|
@ -731,7 +731,7 @@ impl HirDisplay for Ty {
|
||||||
WhereClause::AliasEq(AliasEq {
|
WhereClause::AliasEq(AliasEq {
|
||||||
alias: AliasTy::Projection(proj),
|
alias: AliasTy::Projection(proj),
|
||||||
ty: _,
|
ty: _,
|
||||||
}) => &proj.self_type_parameter(Interner) == self,
|
}) => &proj.self_type_parameter(f.db) == self,
|
||||||
_ => false,
|
_ => false,
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
@ -751,9 +751,19 @@ impl HirDisplay for Ty {
|
||||||
}
|
}
|
||||||
TyKind::BoundVar(idx) => idx.hir_fmt(f)?,
|
TyKind::BoundVar(idx) => idx.hir_fmt(f)?,
|
||||||
TyKind::Dyn(dyn_ty) => {
|
TyKind::Dyn(dyn_ty) => {
|
||||||
|
// Reorder bounds to satisfy `write_bounds_like_dyn_trait()`'s expectation.
|
||||||
|
// FIXME: `Iterator::partition_in_place()` or `Vec::drain_filter()` may make it
|
||||||
|
// more efficient when either of them hits stable.
|
||||||
|
let mut bounds: SmallVec<[_; 4]> =
|
||||||
|
dyn_ty.bounds.skip_binders().iter(Interner).cloned().collect();
|
||||||
|
let (auto_traits, others): (SmallVec<[_; 4]>, _) =
|
||||||
|
bounds.drain(1..).partition(|b| b.skip_binders().trait_id().is_some());
|
||||||
|
bounds.extend(others);
|
||||||
|
bounds.extend(auto_traits);
|
||||||
|
|
||||||
write_bounds_like_dyn_trait_with_prefix(
|
write_bounds_like_dyn_trait_with_prefix(
|
||||||
"dyn",
|
"dyn",
|
||||||
dyn_ty.bounds.skip_binders().interned(),
|
&bounds,
|
||||||
SizedByDefault::NotSized,
|
SizedByDefault::NotSized,
|
||||||
f,
|
f,
|
||||||
)?;
|
)?;
|
||||||
|
|
|
@ -190,7 +190,9 @@ pub(crate) type InferResult<T> = Result<InferOk<T>, TypeError>;
|
||||||
pub enum InferenceDiagnostic {
|
pub enum InferenceDiagnostic {
|
||||||
NoSuchField { expr: ExprId },
|
NoSuchField { expr: ExprId },
|
||||||
BreakOutsideOfLoop { expr: ExprId, is_break: bool },
|
BreakOutsideOfLoop { expr: ExprId, is_break: bool },
|
||||||
|
IncorrectTryTarget { expr: ExprId },
|
||||||
MismatchedArgCount { call_expr: ExprId, expected: usize, found: usize },
|
MismatchedArgCount { call_expr: ExprId, expected: usize, found: usize },
|
||||||
|
DoesNotImplement { expr: ExprId, trait_: TraitId, ty: Ty },
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A mismatch between an expected and an inferred type.
|
/// A mismatch between an expected and an inferred type.
|
||||||
|
@ -905,17 +907,6 @@ impl<'a> InferenceContext<'a> {
|
||||||
self.db.trait_data(trait_).associated_type_by_name(&name![Item])
|
self.db.trait_data(trait_).associated_type_by_name(&name![Item])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_ops_try_ok(&self) -> Option<TypeAliasId> {
|
|
||||||
// FIXME resolve via lang_item once try v2 is stable
|
|
||||||
let path = path![core::ops::Try];
|
|
||||||
let trait_ = self.resolver.resolve_known_trait(self.db.upcast(), &path)?;
|
|
||||||
let trait_data = self.db.trait_data(trait_);
|
|
||||||
trait_data
|
|
||||||
// FIXME remove once try v2 is stable
|
|
||||||
.associated_type_by_name(&name![Ok])
|
|
||||||
.or_else(|| trait_data.associated_type_by_name(&name![Output]))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn resolve_ops_neg_output(&self) -> Option<TypeAliasId> {
|
fn resolve_ops_neg_output(&self) -> Option<TypeAliasId> {
|
||||||
let trait_ = self.resolve_lang_item(name![neg])?.as_trait()?;
|
let trait_ = self.resolve_lang_item(name![neg])?.as_trait()?;
|
||||||
self.db.trait_data(trait_).associated_type_by_name(&name![Output])
|
self.db.trait_data(trait_).associated_type_by_name(&name![Output])
|
||||||
|
|
|
@ -19,24 +19,24 @@ use hir_def::{
|
||||||
resolver::resolver_for_expr,
|
resolver::resolver_for_expr,
|
||||||
ConstParamId, FieldId, ItemContainerId, Lookup,
|
ConstParamId, FieldId, ItemContainerId, Lookup,
|
||||||
};
|
};
|
||||||
use hir_expand::name::Name;
|
use hir_expand::{name, name::Name};
|
||||||
use stdx::always;
|
use stdx::always;
|
||||||
use syntax::ast::RangeOp;
|
use syntax::ast::RangeOp;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
autoderef::{self, Autoderef},
|
autoderef::{self, Autoderef},
|
||||||
consteval,
|
consteval,
|
||||||
infer::{coerce::CoerceMany, find_continuable, BreakableKind},
|
infer::{coerce::CoerceMany, find_continuable, path, BreakableKind},
|
||||||
lower::{
|
lower::{
|
||||||
const_or_path_to_chalk, generic_arg_to_chalk, lower_to_chalk_mutability, ParamLoweringMode,
|
const_or_path_to_chalk, generic_arg_to_chalk, lower_to_chalk_mutability, ParamLoweringMode,
|
||||||
},
|
},
|
||||||
mapping::{from_chalk, ToChalk},
|
mapping::{from_chalk, ToChalk},
|
||||||
method_resolution::{self, lang_names_for_bin_op, VisibleFromModule},
|
method_resolution::{self, lang_names_for_bin_op, VisibleFromModule},
|
||||||
primitive::{self, UintTy},
|
primitive::{self, UintTy},
|
||||||
static_lifetime, to_chalk_trait_id,
|
static_lifetime, to_assoc_type_id, to_chalk_trait_id,
|
||||||
utils::{generics, Generics},
|
utils::{generics, Generics},
|
||||||
AdtId, Binders, CallableDefId, FnPointer, FnSig, FnSubst, Interner, Rawness, Scalar,
|
AdtId, AliasEq, AliasTy, Binders, CallableDefId, FnPointer, FnSig, FnSubst, Interner,
|
||||||
Substitution, TraitRef, Ty, TyBuilder, TyExt, TyKind,
|
ProjectionTy, Rawness, Scalar, Substitution, TraitRef, Ty, TyBuilder, TyExt, TyKind,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
|
@ -564,9 +564,29 @@ impl<'a> InferenceContext<'a> {
|
||||||
let inner_ty = self.infer_expr_inner(*expr, &Expectation::none());
|
let inner_ty = self.infer_expr_inner(*expr, &Expectation::none());
|
||||||
self.resolve_associated_type(inner_ty, self.resolve_future_future_output())
|
self.resolve_associated_type(inner_ty, self.resolve_future_future_output())
|
||||||
}
|
}
|
||||||
Expr::Try { expr } => {
|
&Expr::Try { expr } => {
|
||||||
let inner_ty = self.infer_expr_inner(*expr, &Expectation::none());
|
let inner_ty = self.infer_expr_inner(expr, &Expectation::none());
|
||||||
self.resolve_associated_type(inner_ty, self.resolve_ops_try_ok())
|
match self.resolve_try_impl_for(inner_ty.clone()) {
|
||||||
|
Some((_, Some((output, residual)))) => {
|
||||||
|
if let Some((_trait, false)) =
|
||||||
|
self.implements_from_residual(self.return_ty.clone(), residual)
|
||||||
|
{
|
||||||
|
self.push_diagnostic(InferenceDiagnostic::IncorrectTryTarget {
|
||||||
|
expr: tgt_expr,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
output
|
||||||
|
}
|
||||||
|
Some((trait_, None)) => {
|
||||||
|
self.push_diagnostic(InferenceDiagnostic::DoesNotImplement {
|
||||||
|
expr,
|
||||||
|
trait_,
|
||||||
|
ty: inner_ty,
|
||||||
|
});
|
||||||
|
self.err_ty()
|
||||||
|
}
|
||||||
|
None => self.err_ty(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Expr::Cast { expr, type_ref } => {
|
Expr::Cast { expr, type_ref } => {
|
||||||
// FIXME: propagate the "castable to" expectation (and find a test case that shows this is necessary)
|
// FIXME: propagate the "castable to" expectation (and find a test case that shows this is necessary)
|
||||||
|
@ -1530,4 +1550,67 @@ impl<'a> InferenceContext<'a> {
|
||||||
let ctx = self.breakables.pop().expect("breakable stack broken");
|
let ctx = self.breakables.pop().expect("breakable stack broken");
|
||||||
(ctx.may_break.then(|| ctx.coerce.complete()), res)
|
(ctx.may_break.then(|| ctx.coerce.complete()), res)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Check whether `ty` implements `FromResidual<r>`
|
||||||
|
fn implements_from_residual(&mut self, ty: Ty, r: Ty) -> Option<(hir_def::TraitId, bool)> {
|
||||||
|
let from_residual_trait = self
|
||||||
|
.resolver
|
||||||
|
.resolve_known_trait(self.db.upcast(), &(super::path![core::ops::FromResidual]))?;
|
||||||
|
let r = GenericArgData::Ty(r).intern(Interner);
|
||||||
|
let b = TyBuilder::trait_ref(self.db, from_residual_trait);
|
||||||
|
if b.remaining() != 2 {
|
||||||
|
return Some((from_residual_trait, false));
|
||||||
|
}
|
||||||
|
let trait_ref = b.push(ty).push(r).build();
|
||||||
|
Some((from_residual_trait, self.table.try_obligation(trait_ref.cast(Interner)).is_some()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve_try_impl_for(&mut self, ty: Ty) -> Option<(hir_def::TraitId, Option<(Ty, Ty)>)> {
|
||||||
|
let path = path![core::ops::Try];
|
||||||
|
let trait_ = self.resolver.resolve_known_trait(self.db.upcast(), &path)?;
|
||||||
|
|
||||||
|
let trait_ref = TyBuilder::trait_ref(self.db, trait_).push(ty).build();
|
||||||
|
let substitution = trait_ref.substitution.clone();
|
||||||
|
self.push_obligation(trait_ref.clone().cast(Interner));
|
||||||
|
|
||||||
|
let trait_data = self.db.trait_data(trait_);
|
||||||
|
let output = trait_data.associated_type_by_name(&name![Output]);
|
||||||
|
let residual = trait_data.associated_type_by_name(&name![Residual]);
|
||||||
|
|
||||||
|
let output_ty = match output {
|
||||||
|
Some(output) => {
|
||||||
|
let output_ty = self.table.new_type_var();
|
||||||
|
let alias_eq = AliasEq {
|
||||||
|
alias: AliasTy::Projection(ProjectionTy {
|
||||||
|
associated_ty_id: to_assoc_type_id(output),
|
||||||
|
substitution: substitution.clone(),
|
||||||
|
}),
|
||||||
|
ty: output_ty.clone(),
|
||||||
|
};
|
||||||
|
self.push_obligation(alias_eq.cast(Interner));
|
||||||
|
output_ty
|
||||||
|
}
|
||||||
|
None => self.err_ty(),
|
||||||
|
};
|
||||||
|
let residual_ty = match residual {
|
||||||
|
Some(residual) => {
|
||||||
|
let residual_ty = self.table.new_type_var();
|
||||||
|
let alias_eq = AliasEq {
|
||||||
|
alias: AliasTy::Projection(ProjectionTy {
|
||||||
|
associated_ty_id: to_assoc_type_id(residual),
|
||||||
|
substitution,
|
||||||
|
}),
|
||||||
|
ty: residual_ty.clone(),
|
||||||
|
};
|
||||||
|
self.push_obligation(alias_eq.cast(Interner));
|
||||||
|
residual_ty
|
||||||
|
}
|
||||||
|
None => self.err_ty(),
|
||||||
|
};
|
||||||
|
// FIXME: We are doing the work twice here I think?
|
||||||
|
Some((
|
||||||
|
trait_,
|
||||||
|
self.table.try_obligation(trait_ref.cast(Interner)).map(|_| (output_ty, residual_ty)),
|
||||||
|
))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ use std::{fmt, mem, sync::Arc};
|
||||||
|
|
||||||
use chalk_ir::{
|
use chalk_ir::{
|
||||||
cast::Cast, fold::TypeFoldable, interner::HasInterner, zip::Zip, CanonicalVarKind, FloatTy,
|
cast::Cast, fold::TypeFoldable, interner::HasInterner, zip::Zip, CanonicalVarKind, FloatTy,
|
||||||
IntTy, NoSolution, TyVariableKind, UniverseIndex,
|
IntTy, TyVariableKind, UniverseIndex,
|
||||||
};
|
};
|
||||||
use chalk_solve::infer::ParameterEnaVariableExt;
|
use chalk_solve::infer::ParameterEnaVariableExt;
|
||||||
use ena::unify::UnifyKey;
|
use ena::unify::UnifyKey;
|
||||||
|
@ -331,7 +331,6 @@ impl<'a> InferenceTable<'a> {
|
||||||
&mut resolve::Resolver { table: self, var_stack, fallback },
|
&mut resolve::Resolver { table: self, var_stack, fallback },
|
||||||
DebruijnIndex::INNERMOST,
|
DebruijnIndex::INNERMOST,
|
||||||
)
|
)
|
||||||
.expect("fold failed unexpectedly")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn resolve_completely<T>(&mut self, t: T) -> T
|
pub(crate) fn resolve_completely<T>(&mut self, t: T) -> T
|
||||||
|
@ -452,13 +451,14 @@ impl<'a> InferenceTable<'a> {
|
||||||
f: impl FnOnce(&mut Self) -> T,
|
f: impl FnOnce(&mut Self) -> T,
|
||||||
) -> T {
|
) -> T {
|
||||||
use chalk_ir::fold::TypeFolder;
|
use chalk_ir::fold::TypeFolder;
|
||||||
|
|
||||||
|
#[derive(chalk_derive::FallibleTypeFolder)]
|
||||||
|
#[has_interner(Interner)]
|
||||||
struct VarFudger<'a, 'b> {
|
struct VarFudger<'a, 'b> {
|
||||||
table: &'a mut InferenceTable<'b>,
|
table: &'a mut InferenceTable<'b>,
|
||||||
highest_known_var: InferenceVar,
|
highest_known_var: InferenceVar,
|
||||||
}
|
}
|
||||||
impl<'a, 'b> TypeFolder<Interner> for VarFudger<'a, 'b> {
|
impl<'a, 'b> TypeFolder<Interner> for VarFudger<'a, 'b> {
|
||||||
type Error = NoSolution;
|
|
||||||
|
|
||||||
fn as_dyn(&mut self) -> &mut dyn TypeFolder<Interner, Error = Self::Error> {
|
fn as_dyn(&mut self) -> &mut dyn TypeFolder<Interner, Error = Self::Error> {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
@ -472,24 +472,24 @@ impl<'a> InferenceTable<'a> {
|
||||||
var: chalk_ir::InferenceVar,
|
var: chalk_ir::InferenceVar,
|
||||||
kind: TyVariableKind,
|
kind: TyVariableKind,
|
||||||
_outer_binder: chalk_ir::DebruijnIndex,
|
_outer_binder: chalk_ir::DebruijnIndex,
|
||||||
) -> chalk_ir::Fallible<chalk_ir::Ty<Interner>> {
|
) -> chalk_ir::Ty<Interner> {
|
||||||
Ok(if var < self.highest_known_var {
|
if var < self.highest_known_var {
|
||||||
var.to_ty(Interner, kind)
|
var.to_ty(Interner, kind)
|
||||||
} else {
|
} else {
|
||||||
self.table.new_type_var()
|
self.table.new_type_var()
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fold_inference_lifetime(
|
fn fold_inference_lifetime(
|
||||||
&mut self,
|
&mut self,
|
||||||
var: chalk_ir::InferenceVar,
|
var: chalk_ir::InferenceVar,
|
||||||
_outer_binder: chalk_ir::DebruijnIndex,
|
_outer_binder: chalk_ir::DebruijnIndex,
|
||||||
) -> chalk_ir::Fallible<chalk_ir::Lifetime<Interner>> {
|
) -> chalk_ir::Lifetime<Interner> {
|
||||||
Ok(if var < self.highest_known_var {
|
if var < self.highest_known_var {
|
||||||
var.to_lifetime(Interner)
|
var.to_lifetime(Interner)
|
||||||
} else {
|
} else {
|
||||||
self.table.new_lifetime_var()
|
self.table.new_lifetime_var()
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fold_inference_const(
|
fn fold_inference_const(
|
||||||
|
@ -497,12 +497,12 @@ impl<'a> InferenceTable<'a> {
|
||||||
ty: chalk_ir::Ty<Interner>,
|
ty: chalk_ir::Ty<Interner>,
|
||||||
var: chalk_ir::InferenceVar,
|
var: chalk_ir::InferenceVar,
|
||||||
_outer_binder: chalk_ir::DebruijnIndex,
|
_outer_binder: chalk_ir::DebruijnIndex,
|
||||||
) -> chalk_ir::Fallible<chalk_ir::Const<Interner>> {
|
) -> chalk_ir::Const<Interner> {
|
||||||
Ok(if var < self.highest_known_var {
|
if var < self.highest_known_var {
|
||||||
var.to_const(Interner, ty)
|
var.to_const(Interner, ty)
|
||||||
} else {
|
} else {
|
||||||
self.table.new_const_var(ty)
|
self.table.new_const_var(ty)
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -512,7 +512,6 @@ impl<'a> InferenceTable<'a> {
|
||||||
self.rollback_to(snapshot);
|
self.rollback_to(snapshot);
|
||||||
result
|
result
|
||||||
.fold_with(&mut VarFudger { table: self, highest_known_var }, DebruijnIndex::INNERMOST)
|
.fold_with(&mut VarFudger { table: self, highest_known_var }, DebruijnIndex::INNERMOST)
|
||||||
.expect("fold_with with VarFudger")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This checks whether any of the free variables in the `canonicalized`
|
/// This checks whether any of the free variables in the `canonicalized`
|
||||||
|
@ -639,21 +638,24 @@ mod resolve {
|
||||||
use chalk_ir::{
|
use chalk_ir::{
|
||||||
cast::Cast,
|
cast::Cast,
|
||||||
fold::{TypeFoldable, TypeFolder},
|
fold::{TypeFoldable, TypeFolder},
|
||||||
Fallible, NoSolution,
|
|
||||||
};
|
};
|
||||||
use hir_def::type_ref::ConstScalar;
|
use hir_def::type_ref::ConstScalar;
|
||||||
|
|
||||||
pub(super) struct Resolver<'a, 'b, F> {
|
#[derive(chalk_derive::FallibleTypeFolder)]
|
||||||
|
#[has_interner(Interner)]
|
||||||
|
pub(super) struct Resolver<
|
||||||
|
'a,
|
||||||
|
'b,
|
||||||
|
F: Fn(InferenceVar, VariableKind, GenericArg, DebruijnIndex) -> GenericArg,
|
||||||
|
> {
|
||||||
pub(super) table: &'a mut InferenceTable<'b>,
|
pub(super) table: &'a mut InferenceTable<'b>,
|
||||||
pub(super) var_stack: &'a mut Vec<InferenceVar>,
|
pub(super) var_stack: &'a mut Vec<InferenceVar>,
|
||||||
pub(super) fallback: F,
|
pub(super) fallback: F,
|
||||||
}
|
}
|
||||||
impl<'a, 'b, 'i, F> TypeFolder<Interner> for Resolver<'a, 'b, F>
|
impl<'a, 'b, F> TypeFolder<Interner> for Resolver<'a, 'b, F>
|
||||||
where
|
where
|
||||||
F: Fn(InferenceVar, VariableKind, GenericArg, DebruijnIndex) -> GenericArg + 'i,
|
F: Fn(InferenceVar, VariableKind, GenericArg, DebruijnIndex) -> GenericArg,
|
||||||
{
|
{
|
||||||
type Error = NoSolution;
|
|
||||||
|
|
||||||
fn as_dyn(&mut self) -> &mut dyn TypeFolder<Interner, Error = Self::Error> {
|
fn as_dyn(&mut self) -> &mut dyn TypeFolder<Interner, Error = Self::Error> {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
@ -667,20 +669,19 @@ mod resolve {
|
||||||
var: InferenceVar,
|
var: InferenceVar,
|
||||||
kind: TyVariableKind,
|
kind: TyVariableKind,
|
||||||
outer_binder: DebruijnIndex,
|
outer_binder: DebruijnIndex,
|
||||||
) -> Fallible<Ty> {
|
) -> Ty {
|
||||||
let var = self.table.var_unification_table.inference_var_root(var);
|
let var = self.table.var_unification_table.inference_var_root(var);
|
||||||
if self.var_stack.contains(&var) {
|
if self.var_stack.contains(&var) {
|
||||||
// recursive type
|
// recursive type
|
||||||
let default = self.table.fallback_value(var, kind).cast(Interner);
|
let default = self.table.fallback_value(var, kind).cast(Interner);
|
||||||
return Ok((self.fallback)(var, VariableKind::Ty(kind), default, outer_binder)
|
return (self.fallback)(var, VariableKind::Ty(kind), default, outer_binder)
|
||||||
.assert_ty_ref(Interner)
|
.assert_ty_ref(Interner)
|
||||||
.clone());
|
.clone();
|
||||||
}
|
}
|
||||||
let result = if let Some(known_ty) = self.table.var_unification_table.probe_var(var) {
|
let result = if let Some(known_ty) = self.table.var_unification_table.probe_var(var) {
|
||||||
// known_ty may contain other variables that are known by now
|
// known_ty may contain other variables that are known by now
|
||||||
self.var_stack.push(var);
|
self.var_stack.push(var);
|
||||||
let result =
|
let result = known_ty.fold_with(self, outer_binder);
|
||||||
known_ty.fold_with(self, outer_binder).expect("fold failed unexpectedly");
|
|
||||||
self.var_stack.pop();
|
self.var_stack.pop();
|
||||||
result.assert_ty_ref(Interner).clone()
|
result.assert_ty_ref(Interner).clone()
|
||||||
} else {
|
} else {
|
||||||
|
@ -689,7 +690,7 @@ mod resolve {
|
||||||
.assert_ty_ref(Interner)
|
.assert_ty_ref(Interner)
|
||||||
.clone()
|
.clone()
|
||||||
};
|
};
|
||||||
Ok(result)
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fold_inference_const(
|
fn fold_inference_const(
|
||||||
|
@ -697,7 +698,7 @@ mod resolve {
|
||||||
ty: Ty,
|
ty: Ty,
|
||||||
var: InferenceVar,
|
var: InferenceVar,
|
||||||
outer_binder: DebruijnIndex,
|
outer_binder: DebruijnIndex,
|
||||||
) -> Fallible<Const> {
|
) -> Const {
|
||||||
let var = self.table.var_unification_table.inference_var_root(var);
|
let var = self.table.var_unification_table.inference_var_root(var);
|
||||||
let default = ConstData {
|
let default = ConstData {
|
||||||
ty: ty.clone(),
|
ty: ty.clone(),
|
||||||
|
@ -707,35 +708,33 @@ mod resolve {
|
||||||
.cast(Interner);
|
.cast(Interner);
|
||||||
if self.var_stack.contains(&var) {
|
if self.var_stack.contains(&var) {
|
||||||
// recursive
|
// recursive
|
||||||
return Ok((self.fallback)(var, VariableKind::Const(ty), default, outer_binder)
|
return (self.fallback)(var, VariableKind::Const(ty), default, outer_binder)
|
||||||
.assert_const_ref(Interner)
|
.assert_const_ref(Interner)
|
||||||
.clone());
|
.clone();
|
||||||
}
|
}
|
||||||
let result = if let Some(known_ty) = self.table.var_unification_table.probe_var(var) {
|
if let Some(known_ty) = self.table.var_unification_table.probe_var(var) {
|
||||||
// known_ty may contain other variables that are known by now
|
// known_ty may contain other variables that are known by now
|
||||||
self.var_stack.push(var);
|
self.var_stack.push(var);
|
||||||
let result =
|
let result = known_ty.fold_with(self, outer_binder);
|
||||||
known_ty.fold_with(self, outer_binder).expect("fold failed unexpectedly");
|
|
||||||
self.var_stack.pop();
|
self.var_stack.pop();
|
||||||
result.assert_const_ref(Interner).clone()
|
result.assert_const_ref(Interner).clone()
|
||||||
} else {
|
} else {
|
||||||
(self.fallback)(var, VariableKind::Const(ty), default, outer_binder)
|
(self.fallback)(var, VariableKind::Const(ty), default, outer_binder)
|
||||||
.assert_const_ref(Interner)
|
.assert_const_ref(Interner)
|
||||||
.clone()
|
.clone()
|
||||||
};
|
}
|
||||||
Ok(result)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fold_inference_lifetime(
|
fn fold_inference_lifetime(
|
||||||
&mut self,
|
&mut self,
|
||||||
_var: InferenceVar,
|
_var: InferenceVar,
|
||||||
_outer_binder: DebruijnIndex,
|
_outer_binder: DebruijnIndex,
|
||||||
) -> Fallible<Lifetime> {
|
) -> Lifetime {
|
||||||
// fall back all lifetimes to 'static -- currently we don't deal
|
// fall back all lifetimes to 'static -- currently we don't deal
|
||||||
// with any lifetimes, but we can sometimes get some lifetime
|
// with any lifetimes, but we can sometimes get some lifetime
|
||||||
// variables through Chalk's unification, and this at least makes
|
// variables through Chalk's unification, and this at least makes
|
||||||
// sure we don't leak them outside of inference
|
// sure we don't leak them outside of inference
|
||||||
Ok(crate::static_lifetime())
|
crate::static_lifetime()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -254,13 +254,13 @@ impl CallableSig {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TypeFoldable<Interner> for CallableSig {
|
impl TypeFoldable<Interner> for CallableSig {
|
||||||
fn fold_with<E>(
|
fn try_fold_with<E>(
|
||||||
self,
|
self,
|
||||||
folder: &mut dyn chalk_ir::fold::TypeFolder<Interner, Error = E>,
|
folder: &mut dyn chalk_ir::fold::FallibleTypeFolder<Interner, Error = E>,
|
||||||
outer_binder: DebruijnIndex,
|
outer_binder: DebruijnIndex,
|
||||||
) -> Result<Self, E> {
|
) -> Result<Self, E> {
|
||||||
let vec = self.params_and_return.to_vec();
|
let vec = self.params_and_return.to_vec();
|
||||||
let folded = vec.fold_with(folder, outer_binder)?;
|
let folded = vec.try_fold_with(folder, outer_binder)?;
|
||||||
Ok(CallableSig { params_and_return: folded.into(), is_varargs: self.is_varargs })
|
Ok(CallableSig { params_and_return: folded.into(), is_varargs: self.is_varargs })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -292,16 +292,19 @@ pub(crate) fn fold_free_vars<T: HasInterner<Interner = Interner> + TypeFoldable<
|
||||||
for_ty: impl FnMut(BoundVar, DebruijnIndex) -> Ty,
|
for_ty: impl FnMut(BoundVar, DebruijnIndex) -> Ty,
|
||||||
for_const: impl FnMut(Ty, BoundVar, DebruijnIndex) -> Const,
|
for_const: impl FnMut(Ty, BoundVar, DebruijnIndex) -> Const,
|
||||||
) -> T {
|
) -> T {
|
||||||
use chalk_ir::{fold::TypeFolder, Fallible};
|
use chalk_ir::fold::TypeFolder;
|
||||||
struct FreeVarFolder<F1, F2>(F1, F2);
|
|
||||||
|
#[derive(chalk_derive::FallibleTypeFolder)]
|
||||||
|
#[has_interner(Interner)]
|
||||||
|
struct FreeVarFolder<
|
||||||
|
F1: FnMut(BoundVar, DebruijnIndex) -> Ty,
|
||||||
|
F2: FnMut(Ty, BoundVar, DebruijnIndex) -> Const,
|
||||||
|
>(F1, F2);
|
||||||
impl<
|
impl<
|
||||||
'i,
|
F1: FnMut(BoundVar, DebruijnIndex) -> Ty,
|
||||||
F1: FnMut(BoundVar, DebruijnIndex) -> Ty + 'i,
|
F2: FnMut(Ty, BoundVar, DebruijnIndex) -> Const,
|
||||||
F2: FnMut(Ty, BoundVar, DebruijnIndex) -> Const + 'i,
|
|
||||||
> TypeFolder<Interner> for FreeVarFolder<F1, F2>
|
> TypeFolder<Interner> for FreeVarFolder<F1, F2>
|
||||||
{
|
{
|
||||||
type Error = NoSolution;
|
|
||||||
|
|
||||||
fn as_dyn(&mut self) -> &mut dyn TypeFolder<Interner, Error = Self::Error> {
|
fn as_dyn(&mut self) -> &mut dyn TypeFolder<Interner, Error = Self::Error> {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
@ -310,12 +313,8 @@ pub(crate) fn fold_free_vars<T: HasInterner<Interner = Interner> + TypeFoldable<
|
||||||
Interner
|
Interner
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fold_free_var_ty(
|
fn fold_free_var_ty(&mut self, bound_var: BoundVar, outer_binder: DebruijnIndex) -> Ty {
|
||||||
&mut self,
|
self.0(bound_var, outer_binder)
|
||||||
bound_var: BoundVar,
|
|
||||||
outer_binder: DebruijnIndex,
|
|
||||||
) -> Fallible<Ty> {
|
|
||||||
Ok(self.0(bound_var, outer_binder))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fold_free_var_const(
|
fn fold_free_var_const(
|
||||||
|
@ -323,12 +322,11 @@ pub(crate) fn fold_free_vars<T: HasInterner<Interner = Interner> + TypeFoldable<
|
||||||
ty: Ty,
|
ty: Ty,
|
||||||
bound_var: BoundVar,
|
bound_var: BoundVar,
|
||||||
outer_binder: DebruijnIndex,
|
outer_binder: DebruijnIndex,
|
||||||
) -> Fallible<Const> {
|
) -> Const {
|
||||||
Ok(self.1(ty, bound_var, outer_binder))
|
self.1(ty, bound_var, outer_binder)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
t.fold_with(&mut FreeVarFolder(for_ty, for_const), DebruijnIndex::INNERMOST)
|
t.fold_with(&mut FreeVarFolder(for_ty, for_const), DebruijnIndex::INNERMOST)
|
||||||
.expect("fold failed unexpectedly")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn fold_tys<T: HasInterner<Interner = Interner> + TypeFoldable<Interner>>(
|
pub(crate) fn fold_tys<T: HasInterner<Interner = Interner> + TypeFoldable<Interner>>(
|
||||||
|
@ -351,16 +349,13 @@ pub(crate) fn fold_tys_and_consts<T: HasInterner<Interner = Interner> + TypeFold
|
||||||
f: impl FnMut(Either<Ty, Const>, DebruijnIndex) -> Either<Ty, Const>,
|
f: impl FnMut(Either<Ty, Const>, DebruijnIndex) -> Either<Ty, Const>,
|
||||||
binders: DebruijnIndex,
|
binders: DebruijnIndex,
|
||||||
) -> T {
|
) -> T {
|
||||||
use chalk_ir::{
|
use chalk_ir::fold::{TypeFolder, TypeSuperFoldable};
|
||||||
fold::{TypeFolder, TypeSuperFoldable},
|
#[derive(chalk_derive::FallibleTypeFolder)]
|
||||||
Fallible,
|
#[has_interner(Interner)]
|
||||||
};
|
struct TyFolder<F: FnMut(Either<Ty, Const>, DebruijnIndex) -> Either<Ty, Const>>(F);
|
||||||
struct TyFolder<F>(F);
|
impl<F: FnMut(Either<Ty, Const>, DebruijnIndex) -> Either<Ty, Const>> TypeFolder<Interner>
|
||||||
impl<'i, F: FnMut(Either<Ty, Const>, DebruijnIndex) -> Either<Ty, Const> + 'i>
|
for TyFolder<F>
|
||||||
TypeFolder<Interner> for TyFolder<F>
|
|
||||||
{
|
{
|
||||||
type Error = NoSolution;
|
|
||||||
|
|
||||||
fn as_dyn(&mut self) -> &mut dyn TypeFolder<Interner, Error = Self::Error> {
|
fn as_dyn(&mut self) -> &mut dyn TypeFolder<Interner, Error = Self::Error> {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
@ -369,16 +364,16 @@ pub(crate) fn fold_tys_and_consts<T: HasInterner<Interner = Interner> + TypeFold
|
||||||
Interner
|
Interner
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fold_ty(&mut self, ty: Ty, outer_binder: DebruijnIndex) -> Fallible<Ty> {
|
fn fold_ty(&mut self, ty: Ty, outer_binder: DebruijnIndex) -> Ty {
|
||||||
let ty = ty.super_fold_with(self.as_dyn(), outer_binder)?;
|
let ty = ty.super_fold_with(self.as_dyn(), outer_binder);
|
||||||
Ok(self.0(Either::Left(ty), outer_binder).left().unwrap())
|
self.0(Either::Left(ty), outer_binder).left().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fold_const(&mut self, c: Const, outer_binder: DebruijnIndex) -> Fallible<Const> {
|
fn fold_const(&mut self, c: Const, outer_binder: DebruijnIndex) -> Const {
|
||||||
Ok(self.0(Either::Right(c), outer_binder).right().unwrap())
|
self.0(Either::Right(c), outer_binder).right().unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
t.fold_with(&mut TyFolder(f), binders).expect("fold failed unexpectedly")
|
t.fold_with(&mut TyFolder(f), binders)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 'Canonicalizes' the `t` by replacing any errors with new variables. Also
|
/// 'Canonicalizes' the `t` by replacing any errors with new variables. Also
|
||||||
|
@ -390,16 +385,16 @@ where
|
||||||
T: HasInterner<Interner = Interner>,
|
T: HasInterner<Interner = Interner>,
|
||||||
{
|
{
|
||||||
use chalk_ir::{
|
use chalk_ir::{
|
||||||
fold::{TypeFolder, TypeSuperFoldable},
|
fold::{FallibleTypeFolder, TypeSuperFoldable},
|
||||||
Fallible,
|
Fallible,
|
||||||
};
|
};
|
||||||
struct ErrorReplacer {
|
struct ErrorReplacer {
|
||||||
vars: usize,
|
vars: usize,
|
||||||
}
|
}
|
||||||
impl TypeFolder<Interner> for ErrorReplacer {
|
impl FallibleTypeFolder<Interner> for ErrorReplacer {
|
||||||
type Error = NoSolution;
|
type Error = NoSolution;
|
||||||
|
|
||||||
fn as_dyn(&mut self) -> &mut dyn TypeFolder<Interner, Error = Self::Error> {
|
fn as_dyn(&mut self) -> &mut dyn FallibleTypeFolder<Interner, Error = Self::Error> {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -407,18 +402,17 @@ where
|
||||||
Interner
|
Interner
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fold_ty(&mut self, ty: Ty, outer_binder: DebruijnIndex) -> Fallible<Ty> {
|
fn try_fold_ty(&mut self, ty: Ty, outer_binder: DebruijnIndex) -> Fallible<Ty> {
|
||||||
if let TyKind::Error = ty.kind(Interner) {
|
if let TyKind::Error = ty.kind(Interner) {
|
||||||
let index = self.vars;
|
let index = self.vars;
|
||||||
self.vars += 1;
|
self.vars += 1;
|
||||||
Ok(TyKind::BoundVar(BoundVar::new(outer_binder, index)).intern(Interner))
|
Ok(TyKind::BoundVar(BoundVar::new(outer_binder, index)).intern(Interner))
|
||||||
} else {
|
} else {
|
||||||
let ty = ty.super_fold_with(self.as_dyn(), outer_binder)?;
|
ty.try_super_fold_with(self.as_dyn(), outer_binder)
|
||||||
Ok(ty)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fold_inference_ty(
|
fn try_fold_inference_ty(
|
||||||
&mut self,
|
&mut self,
|
||||||
_var: InferenceVar,
|
_var: InferenceVar,
|
||||||
_kind: TyVariableKind,
|
_kind: TyVariableKind,
|
||||||
|
@ -433,7 +427,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fold_free_var_ty(
|
fn try_fold_free_var_ty(
|
||||||
&mut self,
|
&mut self,
|
||||||
_bound_var: BoundVar,
|
_bound_var: BoundVar,
|
||||||
_outer_binder: DebruijnIndex,
|
_outer_binder: DebruijnIndex,
|
||||||
|
@ -447,7 +441,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fold_inference_const(
|
fn try_fold_inference_const(
|
||||||
&mut self,
|
&mut self,
|
||||||
ty: Ty,
|
ty: Ty,
|
||||||
_var: InferenceVar,
|
_var: InferenceVar,
|
||||||
|
@ -460,7 +454,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fold_free_var_const(
|
fn try_fold_free_var_const(
|
||||||
&mut self,
|
&mut self,
|
||||||
ty: Ty,
|
ty: Ty,
|
||||||
_bound_var: BoundVar,
|
_bound_var: BoundVar,
|
||||||
|
@ -473,7 +467,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fold_inference_lifetime(
|
fn try_fold_inference_lifetime(
|
||||||
&mut self,
|
&mut self,
|
||||||
_var: InferenceVar,
|
_var: InferenceVar,
|
||||||
_outer_binder: DebruijnIndex,
|
_outer_binder: DebruijnIndex,
|
||||||
|
@ -485,7 +479,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fold_free_var_lifetime(
|
fn try_fold_free_var_lifetime(
|
||||||
&mut self,
|
&mut self,
|
||||||
_bound_var: BoundVar,
|
_bound_var: BoundVar,
|
||||||
_outer_binder: DebruijnIndex,
|
_outer_binder: DebruijnIndex,
|
||||||
|
@ -498,7 +492,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let mut error_replacer = ErrorReplacer { vars: 0 };
|
let mut error_replacer = ErrorReplacer { vars: 0 };
|
||||||
let value = match t.clone().fold_with(&mut error_replacer, DebruijnIndex::INNERMOST) {
|
let value = match t.clone().try_fold_with(&mut error_replacer, DebruijnIndex::INNERMOST) {
|
||||||
Ok(t) => t,
|
Ok(t) => t,
|
||||||
Err(_) => panic!("Encountered unbound or inference vars in {:?}", t),
|
Err(_) => panic!("Encountered unbound or inference vars in {:?}", t),
|
||||||
};
|
};
|
||||||
|
|
|
@ -1111,6 +1111,24 @@ pub fn resolve_indexing_op(
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
/// Returns the receiver type for the try branch trait call.
|
||||||
|
pub fn resolve_branch_op(
|
||||||
|
db: &dyn HirDatabase,
|
||||||
|
env: Arc<TraitEnvironment>,
|
||||||
|
ty: Canonical<Ty>,
|
||||||
|
try_trait: TraitId,
|
||||||
|
) -> Option<ReceiverAdjustments> {
|
||||||
|
let mut table = InferenceTable::new(db, env.clone());
|
||||||
|
let ty = table.instantiate_canonical(ty);
|
||||||
|
let (deref_chain, adj) = autoderef_method_receiver(&mut table, ty);
|
||||||
|
for (ty, adj) in deref_chain.into_iter().zip(adj) {
|
||||||
|
let goal = generic_implements_goal(db, env.clone(), try_trait, &ty);
|
||||||
|
if db.trait_solve(env.krate, goal.cast(Interner)).is_some() {
|
||||||
|
return Some(adj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
macro_rules! check_that {
|
macro_rules! check_that {
|
||||||
($cond:expr) => {
|
($cond:expr) => {
|
||||||
|
|
|
@ -55,6 +55,28 @@ fn main() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn render_dyn_ty_independent_of_order() {
|
||||||
|
check_types_source_code(
|
||||||
|
r#"
|
||||||
|
auto trait Send {}
|
||||||
|
trait A {
|
||||||
|
type Assoc;
|
||||||
|
}
|
||||||
|
trait B: A {}
|
||||||
|
|
||||||
|
fn test(
|
||||||
|
_: &(dyn A<Assoc = ()> + Send),
|
||||||
|
//^ &(dyn A<Assoc = ()> + Send)
|
||||||
|
_: &(dyn Send + A<Assoc = ()>),
|
||||||
|
//^ &(dyn A<Assoc = ()> + Send)
|
||||||
|
_: &dyn B<Assoc = ()>,
|
||||||
|
//^ &(dyn B<Assoc = ()>)
|
||||||
|
) {}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn render_dyn_for_ty() {
|
fn render_dyn_for_ty() {
|
||||||
// FIXME
|
// FIXME
|
||||||
|
|
|
@ -1070,3 +1070,13 @@ fn main() {
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn cfg_params() {
|
||||||
|
check_types(
|
||||||
|
r#"
|
||||||
|
fn my_fn(#[cfg(feature = "feature")] u8: u8, u32: u32) {}
|
||||||
|
//^^^ u32
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -162,98 +162,16 @@ fn test() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn infer_try() {
|
|
||||||
check_types(
|
|
||||||
r#"
|
|
||||||
//- /main.rs crate:main deps:core
|
|
||||||
fn test() {
|
|
||||||
let r: Result<i32, u64> = Result::Ok(1);
|
|
||||||
let v = r?;
|
|
||||||
v;
|
|
||||||
} //^ i32
|
|
||||||
|
|
||||||
//- /core.rs crate:core
|
|
||||||
pub mod ops {
|
|
||||||
pub trait Try {
|
|
||||||
type Ok;
|
|
||||||
type Error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod result {
|
|
||||||
pub enum Result<O, E> {
|
|
||||||
Ok(O),
|
|
||||||
Err(E)
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<O, E> crate::ops::Try for Result<O, E> {
|
|
||||||
type Ok = O;
|
|
||||||
type Error = E;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod prelude {
|
|
||||||
pub mod rust_2018 {
|
|
||||||
pub use crate::{result::*, ops::*};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"#,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn infer_try_trait_v2() {
|
fn infer_try_trait_v2() {
|
||||||
check_types(
|
check_types(
|
||||||
r#"
|
r#"
|
||||||
//- /main.rs crate:main deps:core
|
//- minicore: try
|
||||||
fn test() {
|
fn test() -> core::ops::ControlFlow<u32, f32> {
|
||||||
let r: Result<i32, u64> = Result::Ok(1);
|
let r: core::ops::ControlFlow<u32, f32> = core::ops::ControlFlow::Continue(1.0);
|
||||||
let v = r?;
|
let v = r?;
|
||||||
v;
|
//^ f32
|
||||||
} //^ i32
|
r
|
||||||
|
|
||||||
//- /core.rs crate:core
|
|
||||||
mod ops {
|
|
||||||
mod try_trait {
|
|
||||||
pub trait Try: FromResidual {
|
|
||||||
type Output;
|
|
||||||
type Residual;
|
|
||||||
}
|
|
||||||
pub trait FromResidual<R = <Self as Try>::Residual> {}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub use self::try_trait::FromResidual;
|
|
||||||
pub use self::try_trait::Try;
|
|
||||||
}
|
|
||||||
|
|
||||||
mod convert {
|
|
||||||
pub trait From<T> {}
|
|
||||||
impl<T> From<T> for T {}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod result {
|
|
||||||
use crate::convert::From;
|
|
||||||
use crate::ops::{Try, FromResidual};
|
|
||||||
|
|
||||||
pub enum Infallible {}
|
|
||||||
pub enum Result<O, E> {
|
|
||||||
Ok(O),
|
|
||||||
Err(E)
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<O, E> Try for Result<O, E> {
|
|
||||||
type Output = O;
|
|
||||||
type Error = Result<Infallible, E>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, E, F: From<E>> FromResidual<Result<Infallible, E>> for Result<T, F> {}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod prelude {
|
|
||||||
pub mod rust_2018 {
|
|
||||||
pub use crate::result::*;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
|
|
|
@ -13,8 +13,8 @@ use syntax::SmolStr;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
db::HirDatabase, infer::unify::InferenceTable, AliasEq, AliasTy, Canonical, DomainGoal, Goal,
|
db::HirDatabase, infer::unify::InferenceTable, AliasEq, AliasTy, Canonical, DomainGoal, Goal,
|
||||||
Guidance, InEnvironment, Interner, ProjectionTy, Solution, TraitRefExt, Ty, TyKind,
|
Guidance, InEnvironment, Interner, ProjectionTy, ProjectionTyExt, Solution, TraitRefExt, Ty,
|
||||||
WhereClause,
|
TyKind, WhereClause,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// This controls how much 'time' we give the Chalk solver before giving up.
|
/// This controls how much 'time' we give the Chalk solver before giving up.
|
||||||
|
@ -95,7 +95,7 @@ pub(crate) fn trait_solve_query(
|
||||||
..
|
..
|
||||||
}))) = &goal.value.goal.data(Interner)
|
}))) = &goal.value.goal.data(Interner)
|
||||||
{
|
{
|
||||||
if let TyKind::BoundVar(_) = projection_ty.self_type_parameter(Interner).kind(Interner) {
|
if let TyKind::BoundVar(_) = projection_ty.self_type_parameter(db).kind(Interner) {
|
||||||
// Hack: don't ask Chalk to normalize with an unknown self type, it'll say that's impossible
|
// Hack: don't ask Chalk to normalize with an unknown self type, it'll say that's impossible
|
||||||
return Some(Solution::Ambig(Guidance::Unknown));
|
return Some(Solution::Ambig(Guidance::Unknown));
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,9 +13,9 @@ doctest = false
|
||||||
rustc-hash = "1.1.0"
|
rustc-hash = "1.1.0"
|
||||||
either = "1.7.0"
|
either = "1.7.0"
|
||||||
arrayvec = "0.7.2"
|
arrayvec = "0.7.2"
|
||||||
itertools = "0.10.3"
|
itertools = "0.10.5"
|
||||||
smallvec = "1.9.0"
|
smallvec = "1.10.0"
|
||||||
once_cell = "1.12.0"
|
once_cell = "1.15.0"
|
||||||
|
|
||||||
stdx = { path = "../stdx", version = "0.0.0" }
|
stdx = { path = "../stdx", version = "0.0.0" }
|
||||||
syntax = { path = "../syntax", version = "0.0.0" }
|
syntax = { path = "../syntax", version = "0.0.0" }
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
use base_db::CrateId;
|
use base_db::CrateId;
|
||||||
use cfg::{CfgExpr, CfgOptions};
|
use cfg::{CfgExpr, CfgOptions};
|
||||||
use either::Either;
|
use either::Either;
|
||||||
use hir_def::path::ModPath;
|
use hir_def::{path::ModPath, TraitId};
|
||||||
use hir_expand::{name::Name, HirFileId, InFile};
|
use hir_expand::{name::Name, HirFileId, InFile};
|
||||||
use syntax::{ast, AstPtr, SyntaxNodePtr, TextRange};
|
use syntax::{ast, AstPtr, SyntaxNodePtr, TextRange};
|
||||||
|
|
||||||
|
@ -33,6 +33,7 @@ diagnostics![
|
||||||
BreakOutsideOfLoop,
|
BreakOutsideOfLoop,
|
||||||
InactiveCode,
|
InactiveCode,
|
||||||
IncorrectCase,
|
IncorrectCase,
|
||||||
|
IncorrectTryExpr,
|
||||||
InvalidDeriveTarget,
|
InvalidDeriveTarget,
|
||||||
MacroError,
|
MacroError,
|
||||||
MalformedDerive,
|
MalformedDerive,
|
||||||
|
@ -40,6 +41,7 @@ diagnostics![
|
||||||
MissingFields,
|
MissingFields,
|
||||||
MissingMatchArms,
|
MissingMatchArms,
|
||||||
MissingUnsafe,
|
MissingUnsafe,
|
||||||
|
NotImplemented,
|
||||||
NoSuchField,
|
NoSuchField,
|
||||||
ReplaceFilterMapNextWithFindMap,
|
ReplaceFilterMapNextWithFindMap,
|
||||||
TypeMismatch,
|
TypeMismatch,
|
||||||
|
@ -153,6 +155,16 @@ pub struct MismatchedArgCount {
|
||||||
pub expected: usize,
|
pub expected: usize,
|
||||||
pub found: usize,
|
pub found: usize,
|
||||||
}
|
}
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct IncorrectTryExpr {
|
||||||
|
pub expr: InFile<AstPtr<ast::Expr>>,
|
||||||
|
}
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct NotImplemented {
|
||||||
|
pub expr: InFile<AstPtr<ast::Expr>>,
|
||||||
|
pub trait_: TraitId,
|
||||||
|
pub ty: Type,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct MissingMatchArms {
|
pub struct MissingMatchArms {
|
||||||
|
|
|
@ -81,11 +81,12 @@ use crate::db::{DefDatabase, HirDatabase};
|
||||||
pub use crate::{
|
pub use crate::{
|
||||||
attrs::{HasAttrs, Namespace},
|
attrs::{HasAttrs, Namespace},
|
||||||
diagnostics::{
|
diagnostics::{
|
||||||
AnyDiagnostic, BreakOutsideOfLoop, InactiveCode, IncorrectCase, InvalidDeriveTarget,
|
AnyDiagnostic, BreakOutsideOfLoop, InactiveCode, IncorrectCase, IncorrectTryExpr,
|
||||||
MacroError, MalformedDerive, MismatchedArgCount, MissingFields, MissingMatchArms,
|
InvalidDeriveTarget, MacroError, MalformedDerive, MismatchedArgCount, MissingFields,
|
||||||
MissingUnsafe, NoSuchField, ReplaceFilterMapNextWithFindMap, TypeMismatch,
|
MissingMatchArms, MissingUnsafe, NoSuchField, NotImplemented,
|
||||||
UnimplementedBuiltinMacro, UnresolvedExternCrate, UnresolvedImport, UnresolvedMacroCall,
|
ReplaceFilterMapNextWithFindMap, TypeMismatch, UnimplementedBuiltinMacro,
|
||||||
UnresolvedModule, UnresolvedProcMacro,
|
UnresolvedExternCrate, UnresolvedImport, UnresolvedMacroCall, UnresolvedModule,
|
||||||
|
UnresolvedProcMacro,
|
||||||
},
|
},
|
||||||
has_source::HasSource,
|
has_source::HasSource,
|
||||||
semantics::{PathResolution, Semantics, SemanticsScope, TypeInfo, VisibleTraits},
|
semantics::{PathResolution, Semantics, SemanticsScope, TypeInfo, VisibleTraits},
|
||||||
|
@ -1282,30 +1283,45 @@ impl DefWithBody {
|
||||||
let infer = db.infer(self.into());
|
let infer = db.infer(self.into());
|
||||||
let source_map = Lazy::new(|| db.body_with_source_map(self.into()).1);
|
let source_map = Lazy::new(|| db.body_with_source_map(self.into()).1);
|
||||||
for d in &infer.diagnostics {
|
for d in &infer.diagnostics {
|
||||||
match d {
|
match *d {
|
||||||
hir_ty::InferenceDiagnostic::NoSuchField { expr } => {
|
hir_ty::InferenceDiagnostic::NoSuchField { expr } => {
|
||||||
let field = source_map.field_syntax(*expr);
|
let field = source_map.field_syntax(expr);
|
||||||
acc.push(NoSuchField { field }.into())
|
acc.push(NoSuchField { field }.into())
|
||||||
}
|
}
|
||||||
&hir_ty::InferenceDiagnostic::BreakOutsideOfLoop { expr, is_break } => {
|
hir_ty::InferenceDiagnostic::BreakOutsideOfLoop { expr, is_break } => {
|
||||||
let expr = source_map
|
let expr = source_map
|
||||||
.expr_syntax(expr)
|
.expr_syntax(expr)
|
||||||
.expect("break outside of loop in synthetic syntax");
|
.expect("break outside of loop in synthetic syntax");
|
||||||
acc.push(BreakOutsideOfLoop { expr, is_break }.into())
|
acc.push(BreakOutsideOfLoop { expr, is_break }.into())
|
||||||
}
|
}
|
||||||
hir_ty::InferenceDiagnostic::MismatchedArgCount { call_expr, expected, found } => {
|
hir_ty::InferenceDiagnostic::MismatchedArgCount { call_expr, expected, found } => {
|
||||||
match source_map.expr_syntax(*call_expr) {
|
match source_map.expr_syntax(call_expr) {
|
||||||
Ok(source_ptr) => acc.push(
|
Ok(source_ptr) => acc.push(
|
||||||
MismatchedArgCount {
|
MismatchedArgCount {
|
||||||
call_expr: source_ptr,
|
call_expr: source_ptr,
|
||||||
expected: *expected,
|
expected: expected,
|
||||||
found: *found,
|
found: found,
|
||||||
}
|
}
|
||||||
.into(),
|
.into(),
|
||||||
),
|
),
|
||||||
Err(SyntheticSyntax) => (),
|
Err(SyntheticSyntax) => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
hir_ty::InferenceDiagnostic::IncorrectTryTarget { expr } => {
|
||||||
|
let expr = source_map.expr_syntax(expr).expect("try in synthetic syntax");
|
||||||
|
acc.push(IncorrectTryExpr { expr }.into())
|
||||||
|
}
|
||||||
|
hir_ty::InferenceDiagnostic::DoesNotImplement { expr, trait_, ref ty } => {
|
||||||
|
let expr = source_map.expr_syntax(expr).expect("try in synthetic syntax");
|
||||||
|
acc.push(
|
||||||
|
NotImplemented {
|
||||||
|
expr,
|
||||||
|
trait_,
|
||||||
|
ty: Type::new(db, DefWithBodyId::from(self), ty.clone()),
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (expr, mismatch) in infer.expr_type_mismatches() {
|
for (expr, mismatch) in infer.expr_type_mismatches() {
|
||||||
|
|
|
@ -12,7 +12,7 @@ doctest = false
|
||||||
[dependencies]
|
[dependencies]
|
||||||
cov-mark = "2.0.0-pre.1"
|
cov-mark = "2.0.0-pre.1"
|
||||||
|
|
||||||
itertools = "0.10.3"
|
itertools = "0.10.5"
|
||||||
either = "1.7.0"
|
either = "1.7.0"
|
||||||
|
|
||||||
stdx = { path = "../stdx", version = "0.0.0" }
|
stdx = { path = "../stdx", version = "0.0.0" }
|
||||||
|
|
|
@ -156,6 +156,8 @@ pub(super) fn find_importable_node(
|
||||||
{
|
{
|
||||||
ImportAssets::for_method_call(&method_under_caret, &ctx.sema)
|
ImportAssets::for_method_call(&method_under_caret, &ctx.sema)
|
||||||
.zip(Some(method_under_caret.syntax().clone().into()))
|
.zip(Some(method_under_caret.syntax().clone().into()))
|
||||||
|
} else if let Some(_) = ctx.find_node_at_offset_with_descend::<ast::Param>() {
|
||||||
|
None
|
||||||
} else if let Some(pat) = ctx
|
} else if let Some(pat) = ctx
|
||||||
.find_node_at_offset_with_descend::<ast::IdentPat>()
|
.find_node_at_offset_with_descend::<ast::IdentPat>()
|
||||||
.filter(ast::IdentPat::is_simple_ident)
|
.filter(ast::IdentPat::is_simple_ident)
|
||||||
|
@ -268,6 +270,20 @@ mod tests {
|
||||||
assert_eq!(labels, order);
|
assert_eq!(labels, order);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn ignore_parameter_name() {
|
||||||
|
check_assist_not_applicable(
|
||||||
|
auto_import,
|
||||||
|
r"
|
||||||
|
mod foo {
|
||||||
|
pub mod bar {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn foo(bar$0: &str) {}
|
||||||
|
",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn prefer_shorter_paths() {
|
fn prefer_shorter_paths() {
|
||||||
let before = r"
|
let before = r"
|
||||||
|
|
|
@ -0,0 +1,822 @@
|
||||||
|
use either::Either;
|
||||||
|
use ide_db::defs::Definition;
|
||||||
|
use itertools::Itertools;
|
||||||
|
use syntax::{
|
||||||
|
ast::{self, AstNode, HasGenericParams, HasVisibility},
|
||||||
|
match_ast, SyntaxKind, SyntaxNode,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{assist_context::SourceChangeBuilder, AssistContext, AssistId, AssistKind, Assists};
|
||||||
|
|
||||||
|
// Assist: convert_named_struct_to_tuple_struct
|
||||||
|
//
|
||||||
|
// Converts struct with named fields to tuple struct, and analogously for enum variants with named
|
||||||
|
// fields.
|
||||||
|
//
|
||||||
|
// ```
|
||||||
|
// struct Point$0 { x: f32, y: f32 }
|
||||||
|
//
|
||||||
|
// impl Point {
|
||||||
|
// pub fn new(x: f32, y: f32) -> Self {
|
||||||
|
// Point { x, y }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// pub fn x(&self) -> f32 {
|
||||||
|
// self.x
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// pub fn y(&self) -> f32 {
|
||||||
|
// self.y
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// ```
|
||||||
|
// ->
|
||||||
|
// ```
|
||||||
|
// struct Point(f32, f32);
|
||||||
|
//
|
||||||
|
// impl Point {
|
||||||
|
// pub fn new(x: f32, y: f32) -> Self {
|
||||||
|
// Point(x, y)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// pub fn x(&self) -> f32 {
|
||||||
|
// self.0
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// pub fn y(&self) -> f32 {
|
||||||
|
// self.1
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// ```
|
||||||
|
pub(crate) fn convert_named_struct_to_tuple_struct(
|
||||||
|
acc: &mut Assists,
|
||||||
|
ctx: &AssistContext<'_>,
|
||||||
|
) -> Option<()> {
|
||||||
|
let strukt = ctx
|
||||||
|
.find_node_at_offset::<ast::Struct>()
|
||||||
|
.map(Either::Left)
|
||||||
|
.or_else(|| ctx.find_node_at_offset::<ast::Variant>().map(Either::Right))?;
|
||||||
|
let field_list = strukt.as_ref().either(|s| s.field_list(), |v| v.field_list())?;
|
||||||
|
let record_fields = match field_list {
|
||||||
|
ast::FieldList::RecordFieldList(it) => it,
|
||||||
|
ast::FieldList::TupleFieldList(_) => return None,
|
||||||
|
};
|
||||||
|
let strukt_def = match &strukt {
|
||||||
|
Either::Left(s) => Either::Left(ctx.sema.to_def(s)?),
|
||||||
|
Either::Right(v) => Either::Right(ctx.sema.to_def(v)?),
|
||||||
|
};
|
||||||
|
let target = strukt.as_ref().either(|s| s.syntax(), |v| v.syntax()).text_range();
|
||||||
|
|
||||||
|
acc.add(
|
||||||
|
AssistId("convert_named_struct_to_tuple_struct", AssistKind::RefactorRewrite),
|
||||||
|
"Convert to tuple struct",
|
||||||
|
target,
|
||||||
|
|edit| {
|
||||||
|
edit_field_references(ctx, edit, record_fields.fields());
|
||||||
|
edit_struct_references(ctx, edit, strukt_def);
|
||||||
|
edit_struct_def(ctx, edit, &strukt, record_fields);
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn edit_struct_def(
|
||||||
|
ctx: &AssistContext<'_>,
|
||||||
|
edit: &mut SourceChangeBuilder,
|
||||||
|
strukt: &Either<ast::Struct, ast::Variant>,
|
||||||
|
record_fields: ast::RecordFieldList,
|
||||||
|
) {
|
||||||
|
let tuple_fields = record_fields
|
||||||
|
.fields()
|
||||||
|
.filter_map(|f| Some(ast::make::tuple_field(f.visibility(), f.ty()?)));
|
||||||
|
let tuple_fields = ast::make::tuple_field_list(tuple_fields);
|
||||||
|
let record_fields_text_range = record_fields.syntax().text_range();
|
||||||
|
|
||||||
|
edit.edit_file(ctx.file_id());
|
||||||
|
edit.replace(record_fields_text_range, tuple_fields.syntax().text());
|
||||||
|
|
||||||
|
if let Either::Left(strukt) = strukt {
|
||||||
|
if let Some(w) = strukt.where_clause() {
|
||||||
|
let mut where_clause = w.to_string();
|
||||||
|
if where_clause.ends_with(',') {
|
||||||
|
where_clause.pop();
|
||||||
|
}
|
||||||
|
where_clause.push(';');
|
||||||
|
|
||||||
|
edit.delete(w.syntax().text_range());
|
||||||
|
edit.insert(record_fields_text_range.end(), ast::make::tokens::single_newline().text());
|
||||||
|
edit.insert(record_fields_text_range.end(), where_clause);
|
||||||
|
edit.insert(record_fields_text_range.end(), ast::make::tokens::single_newline().text());
|
||||||
|
|
||||||
|
if let Some(tok) = strukt
|
||||||
|
.generic_param_list()
|
||||||
|
.and_then(|l| l.r_angle_token())
|
||||||
|
.and_then(|tok| tok.next_token())
|
||||||
|
.filter(|tok| tok.kind() == SyntaxKind::WHITESPACE)
|
||||||
|
{
|
||||||
|
edit.delete(tok.text_range());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
edit.insert(record_fields_text_range.end(), ";");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(tok) = record_fields
|
||||||
|
.l_curly_token()
|
||||||
|
.and_then(|tok| tok.prev_token())
|
||||||
|
.filter(|tok| tok.kind() == SyntaxKind::WHITESPACE)
|
||||||
|
{
|
||||||
|
edit.delete(tok.text_range())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn edit_struct_references(
|
||||||
|
ctx: &AssistContext<'_>,
|
||||||
|
edit: &mut SourceChangeBuilder,
|
||||||
|
strukt: Either<hir::Struct, hir::Variant>,
|
||||||
|
) {
|
||||||
|
let strukt_def = match strukt {
|
||||||
|
Either::Left(s) => Definition::Adt(hir::Adt::Struct(s)),
|
||||||
|
Either::Right(v) => Definition::Variant(v),
|
||||||
|
};
|
||||||
|
let usages = strukt_def.usages(&ctx.sema).include_self_refs().all();
|
||||||
|
|
||||||
|
let edit_node = |edit: &mut SourceChangeBuilder, node: SyntaxNode| -> Option<()> {
|
||||||
|
match_ast! {
|
||||||
|
match node {
|
||||||
|
ast::RecordPat(record_struct_pat) => {
|
||||||
|
edit.replace(
|
||||||
|
record_struct_pat.syntax().text_range(),
|
||||||
|
ast::make::tuple_struct_pat(
|
||||||
|
record_struct_pat.path()?,
|
||||||
|
record_struct_pat
|
||||||
|
.record_pat_field_list()?
|
||||||
|
.fields()
|
||||||
|
.filter_map(|pat| pat.pat())
|
||||||
|
)
|
||||||
|
.to_string()
|
||||||
|
);
|
||||||
|
},
|
||||||
|
ast::RecordExpr(record_expr) => {
|
||||||
|
let path = record_expr.path()?;
|
||||||
|
let args = record_expr
|
||||||
|
.record_expr_field_list()?
|
||||||
|
.fields()
|
||||||
|
.filter_map(|f| f.expr())
|
||||||
|
.join(", ");
|
||||||
|
|
||||||
|
edit.replace(record_expr.syntax().text_range(), format!("{path}({args})"));
|
||||||
|
},
|
||||||
|
_ => return None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(())
|
||||||
|
};
|
||||||
|
|
||||||
|
for (file_id, refs) in usages {
|
||||||
|
edit.edit_file(file_id);
|
||||||
|
for r in refs {
|
||||||
|
for node in r.name.syntax().ancestors() {
|
||||||
|
if edit_node(edit, node).is_some() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn edit_field_references(
|
||||||
|
ctx: &AssistContext<'_>,
|
||||||
|
edit: &mut SourceChangeBuilder,
|
||||||
|
fields: impl Iterator<Item = ast::RecordField>,
|
||||||
|
) {
|
||||||
|
for (index, field) in fields.enumerate() {
|
||||||
|
let field = match ctx.sema.to_def(&field) {
|
||||||
|
Some(it) => it,
|
||||||
|
None => continue,
|
||||||
|
};
|
||||||
|
let def = Definition::Field(field);
|
||||||
|
let usages = def.usages(&ctx.sema).all();
|
||||||
|
for (file_id, refs) in usages {
|
||||||
|
edit.edit_file(file_id);
|
||||||
|
for r in refs {
|
||||||
|
if let Some(name_ref) = r.name.as_name_ref() {
|
||||||
|
// Only edit the field reference if it's part of a `.field` access
|
||||||
|
if name_ref.syntax().parent().and_then(ast::FieldExpr::cast).is_some() {
|
||||||
|
edit.replace(name_ref.syntax().text_range(), index.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::tests::{check_assist, check_assist_not_applicable};
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn not_applicable_other_than_record_struct() {
|
||||||
|
check_assist_not_applicable(convert_named_struct_to_tuple_struct, r#"struct Foo$0(u32)"#);
|
||||||
|
check_assist_not_applicable(convert_named_struct_to_tuple_struct, r#"struct Foo$0;"#);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn convert_simple_struct() {
|
||||||
|
check_assist(
|
||||||
|
convert_named_struct_to_tuple_struct,
|
||||||
|
r#"
|
||||||
|
struct Inner;
|
||||||
|
struct A$0 { inner: Inner }
|
||||||
|
|
||||||
|
impl A {
|
||||||
|
fn new(inner: Inner) -> A {
|
||||||
|
A { inner }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_with_default() -> A {
|
||||||
|
A::new(Inner)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_inner(self) -> Inner {
|
||||||
|
self.inner
|
||||||
|
}
|
||||||
|
}"#,
|
||||||
|
r#"
|
||||||
|
struct Inner;
|
||||||
|
struct A(Inner);
|
||||||
|
|
||||||
|
impl A {
|
||||||
|
fn new(inner: Inner) -> A {
|
||||||
|
A(inner)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_with_default() -> A {
|
||||||
|
A::new(Inner)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_inner(self) -> Inner {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn convert_struct_referenced_via_self_kw() {
|
||||||
|
check_assist(
|
||||||
|
convert_named_struct_to_tuple_struct,
|
||||||
|
r#"
|
||||||
|
struct Inner;
|
||||||
|
struct A$0 { inner: Inner }
|
||||||
|
|
||||||
|
impl A {
|
||||||
|
fn new(inner: Inner) -> Self {
|
||||||
|
Self { inner }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_with_default() -> Self {
|
||||||
|
Self::new(Inner)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_inner(self) -> Inner {
|
||||||
|
self.inner
|
||||||
|
}
|
||||||
|
}"#,
|
||||||
|
r#"
|
||||||
|
struct Inner;
|
||||||
|
struct A(Inner);
|
||||||
|
|
||||||
|
impl A {
|
||||||
|
fn new(inner: Inner) -> Self {
|
||||||
|
Self(inner)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_with_default() -> Self {
|
||||||
|
Self::new(Inner)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_inner(self) -> Inner {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn convert_destructured_struct() {
|
||||||
|
check_assist(
|
||||||
|
convert_named_struct_to_tuple_struct,
|
||||||
|
r#"
|
||||||
|
struct Inner;
|
||||||
|
struct A$0 { inner: Inner }
|
||||||
|
|
||||||
|
impl A {
|
||||||
|
fn into_inner(self) -> Inner {
|
||||||
|
let A { inner: a } = self;
|
||||||
|
a
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_inner_via_self(self) -> Inner {
|
||||||
|
let Self { inner } = self;
|
||||||
|
inner
|
||||||
|
}
|
||||||
|
}"#,
|
||||||
|
r#"
|
||||||
|
struct Inner;
|
||||||
|
struct A(Inner);
|
||||||
|
|
||||||
|
impl A {
|
||||||
|
fn into_inner(self) -> Inner {
|
||||||
|
let A(a) = self;
|
||||||
|
a
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_inner_via_self(self) -> Inner {
|
||||||
|
let Self(inner) = self;
|
||||||
|
inner
|
||||||
|
}
|
||||||
|
}"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn convert_struct_with_visibility() {
|
||||||
|
check_assist(
|
||||||
|
convert_named_struct_to_tuple_struct,
|
||||||
|
r#"
|
||||||
|
struct A$0 {
|
||||||
|
pub first: u32,
|
||||||
|
pub(crate) second: u64
|
||||||
|
}
|
||||||
|
|
||||||
|
impl A {
|
||||||
|
fn new() -> A {
|
||||||
|
A { first: 42, second: 42 }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_first(self) -> u32 {
|
||||||
|
self.first
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_second(self) -> u64 {
|
||||||
|
self.second
|
||||||
|
}
|
||||||
|
}"#,
|
||||||
|
r#"
|
||||||
|
struct A(pub u32, pub(crate) u64);
|
||||||
|
|
||||||
|
impl A {
|
||||||
|
fn new() -> A {
|
||||||
|
A(42, 42)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_first(self) -> u32 {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_second(self) -> u64 {
|
||||||
|
self.1
|
||||||
|
}
|
||||||
|
}"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn convert_struct_with_wrapped_references() {
|
||||||
|
check_assist(
|
||||||
|
convert_named_struct_to_tuple_struct,
|
||||||
|
r#"
|
||||||
|
struct Inner$0 { uint: u32 }
|
||||||
|
struct Outer { inner: Inner }
|
||||||
|
|
||||||
|
impl Outer {
|
||||||
|
fn new() -> Self {
|
||||||
|
Self { inner: Inner { uint: 42 } }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_inner(self) -> u32 {
|
||||||
|
self.inner.uint
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_inner_destructed(self) -> u32 {
|
||||||
|
let Outer { inner: Inner { uint: x } } = self;
|
||||||
|
x
|
||||||
|
}
|
||||||
|
}"#,
|
||||||
|
r#"
|
||||||
|
struct Inner(u32);
|
||||||
|
struct Outer { inner: Inner }
|
||||||
|
|
||||||
|
impl Outer {
|
||||||
|
fn new() -> Self {
|
||||||
|
Self { inner: Inner(42) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_inner(self) -> u32 {
|
||||||
|
self.inner.0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_inner_destructed(self) -> u32 {
|
||||||
|
let Outer { inner: Inner(x) } = self;
|
||||||
|
x
|
||||||
|
}
|
||||||
|
}"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
check_assist(
|
||||||
|
convert_named_struct_to_tuple_struct,
|
||||||
|
r#"
|
||||||
|
struct Inner { uint: u32 }
|
||||||
|
struct Outer$0 { inner: Inner }
|
||||||
|
|
||||||
|
impl Outer {
|
||||||
|
fn new() -> Self {
|
||||||
|
Self { inner: Inner { uint: 42 } }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_inner(self) -> u32 {
|
||||||
|
self.inner.uint
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_inner_destructed(self) -> u32 {
|
||||||
|
let Outer { inner: Inner { uint: x } } = self;
|
||||||
|
x
|
||||||
|
}
|
||||||
|
}"#,
|
||||||
|
r#"
|
||||||
|
struct Inner { uint: u32 }
|
||||||
|
struct Outer(Inner);
|
||||||
|
|
||||||
|
impl Outer {
|
||||||
|
fn new() -> Self {
|
||||||
|
Self(Inner { uint: 42 })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_inner(self) -> u32 {
|
||||||
|
self.0.uint
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_inner_destructed(self) -> u32 {
|
||||||
|
let Outer(Inner { uint: x }) = self;
|
||||||
|
x
|
||||||
|
}
|
||||||
|
}"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn convert_struct_with_multi_file_references() {
|
||||||
|
check_assist(
|
||||||
|
convert_named_struct_to_tuple_struct,
|
||||||
|
r#"
|
||||||
|
//- /main.rs
|
||||||
|
struct Inner;
|
||||||
|
struct A$0 { inner: Inner }
|
||||||
|
|
||||||
|
mod foo;
|
||||||
|
|
||||||
|
//- /foo.rs
|
||||||
|
use crate::{A, Inner};
|
||||||
|
fn f() {
|
||||||
|
let a = A { inner: Inner };
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
//- /main.rs
|
||||||
|
struct Inner;
|
||||||
|
struct A(Inner);
|
||||||
|
|
||||||
|
mod foo;
|
||||||
|
|
||||||
|
//- /foo.rs
|
||||||
|
use crate::{A, Inner};
|
||||||
|
fn f() {
|
||||||
|
let a = A(Inner);
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn convert_struct_with_where_clause() {
|
||||||
|
check_assist(
|
||||||
|
convert_named_struct_to_tuple_struct,
|
||||||
|
r#"
|
||||||
|
struct Wrap$0<T>
|
||||||
|
where
|
||||||
|
T: Display,
|
||||||
|
{ field1: T }
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
struct Wrap<T>(T)
|
||||||
|
where
|
||||||
|
T: Display;
|
||||||
|
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn not_applicable_other_than_record_variant() {
|
||||||
|
check_assist_not_applicable(
|
||||||
|
convert_named_struct_to_tuple_struct,
|
||||||
|
r#"enum Enum { Variant$0(usize) };"#,
|
||||||
|
);
|
||||||
|
check_assist_not_applicable(
|
||||||
|
convert_named_struct_to_tuple_struct,
|
||||||
|
r#"enum Enum { Variant$0 }"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn convert_simple_variant() {
|
||||||
|
check_assist(
|
||||||
|
convert_named_struct_to_tuple_struct,
|
||||||
|
r#"
|
||||||
|
enum A {
|
||||||
|
$0Variant { field1: usize },
|
||||||
|
}
|
||||||
|
|
||||||
|
impl A {
|
||||||
|
fn new(value: usize) -> A {
|
||||||
|
A::Variant { field1: value }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_with_default() -> A {
|
||||||
|
A::new(Default::default())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn value(self) -> usize {
|
||||||
|
match self {
|
||||||
|
A::Variant { field1: value } => value,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}"#,
|
||||||
|
r#"
|
||||||
|
enum A {
|
||||||
|
Variant(usize),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl A {
|
||||||
|
fn new(value: usize) -> A {
|
||||||
|
A::Variant(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_with_default() -> A {
|
||||||
|
A::new(Default::default())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn value(self) -> usize {
|
||||||
|
match self {
|
||||||
|
A::Variant(value) => value,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn convert_variant_referenced_via_self_kw() {
|
||||||
|
check_assist(
|
||||||
|
convert_named_struct_to_tuple_struct,
|
||||||
|
r#"
|
||||||
|
enum A {
|
||||||
|
$0Variant { field1: usize },
|
||||||
|
}
|
||||||
|
|
||||||
|
impl A {
|
||||||
|
fn new(value: usize) -> A {
|
||||||
|
Self::Variant { field1: value }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_with_default() -> A {
|
||||||
|
Self::new(Default::default())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn value(self) -> usize {
|
||||||
|
match self {
|
||||||
|
Self::Variant { field1: value } => value,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}"#,
|
||||||
|
r#"
|
||||||
|
enum A {
|
||||||
|
Variant(usize),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl A {
|
||||||
|
fn new(value: usize) -> A {
|
||||||
|
Self::Variant(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_with_default() -> A {
|
||||||
|
Self::new(Default::default())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn value(self) -> usize {
|
||||||
|
match self {
|
||||||
|
Self::Variant(value) => value,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn convert_destructured_variant() {
|
||||||
|
check_assist(
|
||||||
|
convert_named_struct_to_tuple_struct,
|
||||||
|
r#"
|
||||||
|
enum A {
|
||||||
|
$0Variant { field1: usize },
|
||||||
|
}
|
||||||
|
|
||||||
|
impl A {
|
||||||
|
fn into_inner(self) -> usize {
|
||||||
|
let A::Variant { field1: first } = self;
|
||||||
|
first
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_inner_via_self(self) -> usize {
|
||||||
|
let Self::Variant { field1: first } = self;
|
||||||
|
first
|
||||||
|
}
|
||||||
|
}"#,
|
||||||
|
r#"
|
||||||
|
enum A {
|
||||||
|
Variant(usize),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl A {
|
||||||
|
fn into_inner(self) -> usize {
|
||||||
|
let A::Variant(first) = self;
|
||||||
|
first
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_inner_via_self(self) -> usize {
|
||||||
|
let Self::Variant(first) = self;
|
||||||
|
first
|
||||||
|
}
|
||||||
|
}"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn convert_variant_with_wrapped_references() {
|
||||||
|
check_assist(
|
||||||
|
convert_named_struct_to_tuple_struct,
|
||||||
|
r#"
|
||||||
|
enum Inner {
|
||||||
|
$0Variant { field1: usize },
|
||||||
|
}
|
||||||
|
enum Outer {
|
||||||
|
Variant(Inner),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Outer {
|
||||||
|
fn new() -> Self {
|
||||||
|
Self::Variant(Inner::Variant { field1: 42 })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_inner_destructed(self) -> u32 {
|
||||||
|
let Outer::Variant(Inner::Variant { field1: x }) = self;
|
||||||
|
x
|
||||||
|
}
|
||||||
|
}"#,
|
||||||
|
r#"
|
||||||
|
enum Inner {
|
||||||
|
Variant(usize),
|
||||||
|
}
|
||||||
|
enum Outer {
|
||||||
|
Variant(Inner),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Outer {
|
||||||
|
fn new() -> Self {
|
||||||
|
Self::Variant(Inner::Variant(42))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_inner_destructed(self) -> u32 {
|
||||||
|
let Outer::Variant(Inner::Variant(x)) = self;
|
||||||
|
x
|
||||||
|
}
|
||||||
|
}"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
check_assist(
|
||||||
|
convert_named_struct_to_tuple_struct,
|
||||||
|
r#"
|
||||||
|
enum Inner {
|
||||||
|
Variant(usize),
|
||||||
|
}
|
||||||
|
enum Outer {
|
||||||
|
$0Variant { field1: Inner },
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Outer {
|
||||||
|
fn new() -> Self {
|
||||||
|
Self::Variant { field1: Inner::Variant(42) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_inner_destructed(self) -> u32 {
|
||||||
|
let Outer::Variant { field1: Inner::Variant(x) } = self;
|
||||||
|
x
|
||||||
|
}
|
||||||
|
}"#,
|
||||||
|
r#"
|
||||||
|
enum Inner {
|
||||||
|
Variant(usize),
|
||||||
|
}
|
||||||
|
enum Outer {
|
||||||
|
Variant(Inner),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Outer {
|
||||||
|
fn new() -> Self {
|
||||||
|
Self::Variant(Inner::Variant(42))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_inner_destructed(self) -> u32 {
|
||||||
|
let Outer::Variant(Inner::Variant(x)) = self;
|
||||||
|
x
|
||||||
|
}
|
||||||
|
}"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn convert_variant_with_multi_file_references() {
|
||||||
|
check_assist(
|
||||||
|
convert_named_struct_to_tuple_struct,
|
||||||
|
r#"
|
||||||
|
//- /main.rs
|
||||||
|
struct Inner;
|
||||||
|
enum A {
|
||||||
|
$0Variant { field1: Inner },
|
||||||
|
}
|
||||||
|
|
||||||
|
mod foo;
|
||||||
|
|
||||||
|
//- /foo.rs
|
||||||
|
use crate::{A, Inner};
|
||||||
|
fn f() {
|
||||||
|
let a = A::Variant { field1: Inner };
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
//- /main.rs
|
||||||
|
struct Inner;
|
||||||
|
enum A {
|
||||||
|
Variant(Inner),
|
||||||
|
}
|
||||||
|
|
||||||
|
mod foo;
|
||||||
|
|
||||||
|
//- /foo.rs
|
||||||
|
use crate::{A, Inner};
|
||||||
|
fn f() {
|
||||||
|
let a = A::Variant(Inner);
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn convert_directly_used_variant() {
|
||||||
|
check_assist(
|
||||||
|
convert_named_struct_to_tuple_struct,
|
||||||
|
r#"
|
||||||
|
//- /main.rs
|
||||||
|
struct Inner;
|
||||||
|
enum A {
|
||||||
|
$0Variant { field1: Inner },
|
||||||
|
}
|
||||||
|
|
||||||
|
mod foo;
|
||||||
|
|
||||||
|
//- /foo.rs
|
||||||
|
use crate::{A::Variant, Inner};
|
||||||
|
fn f() {
|
||||||
|
let a = Variant { field1: Inner };
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
//- /main.rs
|
||||||
|
struct Inner;
|
||||||
|
enum A {
|
||||||
|
Variant(Inner),
|
||||||
|
}
|
||||||
|
|
||||||
|
mod foo;
|
||||||
|
|
||||||
|
//- /foo.rs
|
||||||
|
use crate::{A::Variant, Inner};
|
||||||
|
fn f() {
|
||||||
|
let a = Variant(Inner);
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,7 +9,7 @@ use ide_db::{
|
||||||
search::FileReference,
|
search::FileReference,
|
||||||
FxHashSet, RootDatabase,
|
FxHashSet, RootDatabase,
|
||||||
};
|
};
|
||||||
use itertools::{Itertools, Position};
|
use itertools::Itertools;
|
||||||
use syntax::{
|
use syntax::{
|
||||||
ast::{
|
ast::{
|
||||||
self, edit::IndentLevel, edit_in_place::Indent, make, AstNode, HasAttrs, HasGenericParams,
|
self, edit::IndentLevel, edit_in_place::Indent, make, AstNode, HasAttrs, HasGenericParams,
|
||||||
|
@ -298,37 +298,7 @@ fn update_variant(variant: &ast::Variant, generics: Option<ast::GenericParamList
|
||||||
let name = variant.name()?;
|
let name = variant.name()?;
|
||||||
let ty = generics
|
let ty = generics
|
||||||
.filter(|generics| generics.generic_params().count() > 0)
|
.filter(|generics| generics.generic_params().count() > 0)
|
||||||
.map(|generics| {
|
.map(|generics| make::ty(&format!("{}{}", &name.text(), generics.to_generic_args())))
|
||||||
let mut generic_str = String::with_capacity(8);
|
|
||||||
|
|
||||||
for (p, more) in generics.generic_params().with_position().map(|p| match p {
|
|
||||||
Position::First(p) | Position::Middle(p) => (p, true),
|
|
||||||
Position::Last(p) | Position::Only(p) => (p, false),
|
|
||||||
}) {
|
|
||||||
match p {
|
|
||||||
ast::GenericParam::ConstParam(konst) => {
|
|
||||||
if let Some(name) = konst.name() {
|
|
||||||
generic_str.push_str(name.text().as_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ast::GenericParam::LifetimeParam(lt) => {
|
|
||||||
if let Some(lt) = lt.lifetime() {
|
|
||||||
generic_str.push_str(lt.text().as_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ast::GenericParam::TypeParam(ty) => {
|
|
||||||
if let Some(name) = ty.name() {
|
|
||||||
generic_str.push_str(name.text().as_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if more {
|
|
||||||
generic_str.push_str(", ");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
make::ty(&format!("{}<{}>", &name.text(), &generic_str))
|
|
||||||
})
|
|
||||||
.unwrap_or_else(|| make::ty(&name.text()));
|
.unwrap_or_else(|| make::ty(&name.text()));
|
||||||
|
|
||||||
// change from a record to a tuple field list
|
// change from a record to a tuple field list
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use hir::{HasSource, HirDisplay, Module, Semantics, TypeInfo};
|
use hir::{Adt, HasSource, HirDisplay, Module, Semantics, TypeInfo};
|
||||||
use ide_db::{
|
use ide_db::{
|
||||||
base_db::FileId,
|
base_db::FileId,
|
||||||
defs::{Definition, NameRefClass},
|
defs::{Definition, NameRefClass},
|
||||||
|
@ -145,7 +145,8 @@ fn gen_method(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let (impl_, file) = get_adt_source(ctx, &adt, fn_name.text().as_str())?;
|
let (impl_, file) = get_adt_source(ctx, &adt, fn_name.text().as_str())?;
|
||||||
let (target, insert_offset) = get_method_target(ctx, &target_module, &impl_)?;
|
let (target, insert_offset) = get_method_target(ctx, &impl_, &adt)?;
|
||||||
|
|
||||||
let function_builder =
|
let function_builder =
|
||||||
FunctionBuilder::from_method_call(ctx, &call, &fn_name, target_module, target)?;
|
FunctionBuilder::from_method_call(ctx, &call, &fn_name, target_module, target)?;
|
||||||
let text_range = call.syntax().text_range();
|
let text_range = call.syntax().text_range();
|
||||||
|
@ -174,10 +175,11 @@ fn add_func_to_accumulator(
|
||||||
label: String,
|
label: String,
|
||||||
) -> Option<()> {
|
) -> Option<()> {
|
||||||
acc.add(AssistId("generate_function", AssistKind::Generate), label, text_range, |builder| {
|
acc.add(AssistId("generate_function", AssistKind::Generate), label, text_range, |builder| {
|
||||||
let function_template = function_builder.render();
|
let indent = IndentLevel::from_node(function_builder.target.syntax());
|
||||||
|
let function_template = function_builder.render(adt_name.is_some());
|
||||||
let mut func = function_template.to_string(ctx.config.snippet_cap);
|
let mut func = function_template.to_string(ctx.config.snippet_cap);
|
||||||
if let Some(name) = adt_name {
|
if let Some(name) = adt_name {
|
||||||
func = format!("\nimpl {} {{\n{}\n}}", name, func);
|
func = format!("\n{}impl {} {{\n{}\n{}}}", indent, name, func, indent);
|
||||||
}
|
}
|
||||||
builder.edit_file(file);
|
builder.edit_file(file);
|
||||||
match ctx.config.snippet_cap {
|
match ctx.config.snippet_cap {
|
||||||
|
@ -307,7 +309,7 @@ impl FunctionBuilder {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render(self) -> FunctionTemplate {
|
fn render(self, is_method: bool) -> FunctionTemplate {
|
||||||
let placeholder_expr = make::ext::expr_todo();
|
let placeholder_expr = make::ext::expr_todo();
|
||||||
let fn_body = make::block_expr(vec![], Some(placeholder_expr));
|
let fn_body = make::block_expr(vec![], Some(placeholder_expr));
|
||||||
let visibility = if self.needs_pub { Some(make::visibility_pub_crate()) } else { None };
|
let visibility = if self.needs_pub { Some(make::visibility_pub_crate()) } else { None };
|
||||||
|
@ -325,8 +327,14 @@ impl FunctionBuilder {
|
||||||
|
|
||||||
match self.target {
|
match self.target {
|
||||||
GeneratedFunctionTarget::BehindItem(it) => {
|
GeneratedFunctionTarget::BehindItem(it) => {
|
||||||
let indent = IndentLevel::from_node(&it);
|
let mut indent = IndentLevel::from_node(&it);
|
||||||
|
if is_method {
|
||||||
|
indent = indent + 1;
|
||||||
|
leading_ws = format!("{}", indent);
|
||||||
|
} else {
|
||||||
leading_ws = format!("\n\n{}", indent);
|
leading_ws = format!("\n\n{}", indent);
|
||||||
|
}
|
||||||
|
|
||||||
fn_def = fn_def.indent(indent);
|
fn_def = fn_def.indent(indent);
|
||||||
trailing_ws = String::new();
|
trailing_ws = String::new();
|
||||||
}
|
}
|
||||||
|
@ -411,14 +419,13 @@ fn get_fn_target(
|
||||||
|
|
||||||
fn get_method_target(
|
fn get_method_target(
|
||||||
ctx: &AssistContext<'_>,
|
ctx: &AssistContext<'_>,
|
||||||
target_module: &Module,
|
|
||||||
impl_: &Option<ast::Impl>,
|
impl_: &Option<ast::Impl>,
|
||||||
|
adt: &Adt,
|
||||||
) -> Option<(GeneratedFunctionTarget, TextSize)> {
|
) -> Option<(GeneratedFunctionTarget, TextSize)> {
|
||||||
let target = match impl_ {
|
let target = match impl_ {
|
||||||
Some(impl_) => next_space_for_fn_in_impl(impl_)?,
|
Some(impl_) => next_space_for_fn_in_impl(impl_)?,
|
||||||
None => {
|
None => {
|
||||||
next_space_for_fn_in_module(ctx.sema.db, &target_module.definition_source(ctx.sema.db))?
|
GeneratedFunctionTarget::BehindItem(adt.source(ctx.sema.db)?.syntax().value.clone())
|
||||||
.1
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Some((target.clone(), get_insert_offset(&target)))
|
Some((target.clone(), get_insert_offset(&target)))
|
||||||
|
@ -437,7 +444,7 @@ fn assoc_fn_target_info(
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let (impl_, file) = get_adt_source(ctx, &adt, fn_name)?;
|
let (impl_, file) = get_adt_source(ctx, &adt, fn_name)?;
|
||||||
let (target, insert_offset) = get_method_target(ctx, &module, &impl_)?;
|
let (target, insert_offset) = get_method_target(ctx, &impl_, &adt)?;
|
||||||
let adt_name = if impl_.is_none() { Some(adt.name(ctx.sema.db)) } else { None };
|
let adt_name = if impl_.is_none() { Some(adt.name(ctx.sema.db)) } else { None };
|
||||||
Some(TargetInfo::new(target_module, adt_name, target, file, insert_offset))
|
Some(TargetInfo::new(target_module, adt_name, target, file, insert_offset))
|
||||||
}
|
}
|
||||||
|
@ -1468,14 +1475,12 @@ fn foo() {S.bar$0();}
|
||||||
",
|
",
|
||||||
r"
|
r"
|
||||||
struct S;
|
struct S;
|
||||||
fn foo() {S.bar();}
|
|
||||||
impl S {
|
impl S {
|
||||||
|
|
||||||
|
|
||||||
fn bar(&self) ${0:-> _} {
|
fn bar(&self) ${0:-> _} {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
fn foo() {S.bar();}
|
||||||
",
|
",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1517,8 +1522,6 @@ fn foo() {s::S.bar$0();}
|
||||||
mod s {
|
mod s {
|
||||||
pub struct S;
|
pub struct S;
|
||||||
impl S {
|
impl S {
|
||||||
|
|
||||||
|
|
||||||
pub(crate) fn bar(&self) ${0:-> _} {
|
pub(crate) fn bar(&self) ${0:-> _} {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
@ -1544,18 +1547,16 @@ mod s {
|
||||||
",
|
",
|
||||||
r"
|
r"
|
||||||
struct S;
|
struct S;
|
||||||
|
impl S {
|
||||||
|
fn bar(&self) ${0:-> _} {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
mod s {
|
mod s {
|
||||||
fn foo() {
|
fn foo() {
|
||||||
super::S.bar();
|
super::S.bar();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl S {
|
|
||||||
|
|
||||||
|
|
||||||
fn bar(&self) ${0:-> _} {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
",
|
",
|
||||||
)
|
)
|
||||||
|
@ -1571,14 +1572,12 @@ fn foo() {$0S.bar();}
|
||||||
",
|
",
|
||||||
r"
|
r"
|
||||||
struct S;
|
struct S;
|
||||||
fn foo() {S.bar();}
|
|
||||||
impl S {
|
impl S {
|
||||||
|
|
||||||
|
|
||||||
fn bar(&self) ${0:-> _} {
|
fn bar(&self) ${0:-> _} {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
fn foo() {S.bar();}
|
||||||
",
|
",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1593,14 +1592,12 @@ fn foo() {S::bar$0();}
|
||||||
",
|
",
|
||||||
r"
|
r"
|
||||||
struct S;
|
struct S;
|
||||||
fn foo() {S::bar();}
|
|
||||||
impl S {
|
impl S {
|
||||||
|
|
||||||
|
|
||||||
fn bar() ${0:-> _} {
|
fn bar() ${0:-> _} {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
fn foo() {S::bar();}
|
||||||
",
|
",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1642,8 +1639,6 @@ fn foo() {s::S::bar$0();}
|
||||||
mod s {
|
mod s {
|
||||||
pub struct S;
|
pub struct S;
|
||||||
impl S {
|
impl S {
|
||||||
|
|
||||||
|
|
||||||
pub(crate) fn bar() ${0:-> _} {
|
pub(crate) fn bar() ${0:-> _} {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
@ -1664,14 +1659,12 @@ fn foo() {$0S::bar();}
|
||||||
",
|
",
|
||||||
r"
|
r"
|
||||||
struct S;
|
struct S;
|
||||||
fn foo() {S::bar();}
|
|
||||||
impl S {
|
impl S {
|
||||||
|
|
||||||
|
|
||||||
fn bar() ${0:-> _} {
|
fn bar() ${0:-> _} {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
fn foo() {S::bar();}
|
||||||
",
|
",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1841,16 +1834,14 @@ fn main() {
|
||||||
",
|
",
|
||||||
r"
|
r"
|
||||||
enum Foo {}
|
enum Foo {}
|
||||||
fn main() {
|
|
||||||
Foo::new();
|
|
||||||
}
|
|
||||||
impl Foo {
|
impl Foo {
|
||||||
|
|
||||||
|
|
||||||
fn new() ${0:-> _} {
|
fn new() ${0:-> _} {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
fn main() {
|
||||||
|
Foo::new();
|
||||||
|
}
|
||||||
",
|
",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,6 +52,7 @@ mod tests {
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
// FIXME: break up into separate test fns
|
||||||
#[test]
|
#[test]
|
||||||
fn test_add_impl() {
|
fn test_add_impl() {
|
||||||
check_assist(
|
check_assist(
|
||||||
|
@ -134,6 +135,18 @@ mod tests {
|
||||||
}"#,
|
}"#,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
check_assist(
|
||||||
|
generate_impl,
|
||||||
|
r#"
|
||||||
|
struct Defaulted<const N: i32 = 0> {}$0"#,
|
||||||
|
r#"
|
||||||
|
struct Defaulted<const N: i32 = 0> {}
|
||||||
|
|
||||||
|
impl<const N: i32> Defaulted<N> {
|
||||||
|
$0
|
||||||
|
}"#,
|
||||||
|
);
|
||||||
|
|
||||||
check_assist(
|
check_assist(
|
||||||
generate_impl,
|
generate_impl,
|
||||||
r#"pub trait Trait {}
|
r#"pub trait Trait {}
|
||||||
|
|
|
@ -121,6 +121,7 @@ mod handlers {
|
||||||
mod convert_iter_for_each_to_for;
|
mod convert_iter_for_each_to_for;
|
||||||
mod convert_let_else_to_match;
|
mod convert_let_else_to_match;
|
||||||
mod convert_tuple_struct_to_named_struct;
|
mod convert_tuple_struct_to_named_struct;
|
||||||
|
mod convert_named_struct_to_tuple_struct;
|
||||||
mod convert_to_guarded_return;
|
mod convert_to_guarded_return;
|
||||||
mod convert_two_arm_bool_match_to_matches_macro;
|
mod convert_two_arm_bool_match_to_matches_macro;
|
||||||
mod convert_while_to_loop;
|
mod convert_while_to_loop;
|
||||||
|
@ -218,6 +219,7 @@ mod handlers {
|
||||||
convert_iter_for_each_to_for::convert_iter_for_each_to_for,
|
convert_iter_for_each_to_for::convert_iter_for_each_to_for,
|
||||||
convert_iter_for_each_to_for::convert_for_loop_with_for_each,
|
convert_iter_for_each_to_for::convert_for_loop_with_for_each,
|
||||||
convert_let_else_to_match::convert_let_else_to_match,
|
convert_let_else_to_match::convert_let_else_to_match,
|
||||||
|
convert_named_struct_to_tuple_struct::convert_named_struct_to_tuple_struct,
|
||||||
convert_to_guarded_return::convert_to_guarded_return,
|
convert_to_guarded_return::convert_to_guarded_return,
|
||||||
convert_tuple_struct_to_named_struct::convert_tuple_struct_to_named_struct,
|
convert_tuple_struct_to_named_struct::convert_tuple_struct_to_named_struct,
|
||||||
convert_two_arm_bool_match_to_matches_macro::convert_two_arm_bool_match_to_matches_macro,
|
convert_two_arm_bool_match_to_matches_macro::convert_two_arm_bool_match_to_matches_macro,
|
||||||
|
|
|
@ -232,6 +232,7 @@ fn assist_order_field_struct() {
|
||||||
assert_eq!(assists.next().expect("expected assist").label, "Generate a getter method");
|
assert_eq!(assists.next().expect("expected assist").label, "Generate a getter method");
|
||||||
assert_eq!(assists.next().expect("expected assist").label, "Generate a mut getter method");
|
assert_eq!(assists.next().expect("expected assist").label, "Generate a mut getter method");
|
||||||
assert_eq!(assists.next().expect("expected assist").label, "Generate a setter method");
|
assert_eq!(assists.next().expect("expected assist").label, "Generate a setter method");
|
||||||
|
assert_eq!(assists.next().expect("expected assist").label, "Convert to tuple struct");
|
||||||
assert_eq!(assists.next().expect("expected assist").label, "Add `#[derive]`");
|
assert_eq!(assists.next().expect("expected assist").label, "Add `#[derive]`");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -407,6 +407,47 @@ fn main() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn doctest_convert_named_struct_to_tuple_struct() {
|
||||||
|
check_doc_test(
|
||||||
|
"convert_named_struct_to_tuple_struct",
|
||||||
|
r#####"
|
||||||
|
struct Point$0 { x: f32, y: f32 }
|
||||||
|
|
||||||
|
impl Point {
|
||||||
|
pub fn new(x: f32, y: f32) -> Self {
|
||||||
|
Point { x, y }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn x(&self) -> f32 {
|
||||||
|
self.x
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn y(&self) -> f32 {
|
||||||
|
self.y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#####,
|
||||||
|
r#####"
|
||||||
|
struct Point(f32, f32);
|
||||||
|
|
||||||
|
impl Point {
|
||||||
|
pub fn new(x: f32, y: f32) -> Self {
|
||||||
|
Point(x, y)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn x(&self) -> f32 {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn y(&self) -> f32 {
|
||||||
|
self.1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#####,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn doctest_convert_to_guarded_return() {
|
fn doctest_convert_to_guarded_return() {
|
||||||
check_doc_test(
|
check_doc_test(
|
||||||
|
|
|
@ -2,8 +2,6 @@
|
||||||
|
|
||||||
use std::ops;
|
use std::ops;
|
||||||
|
|
||||||
use itertools::Itertools;
|
|
||||||
|
|
||||||
pub(crate) use gen_trait_fn_body::gen_trait_fn_body;
|
pub(crate) use gen_trait_fn_body::gen_trait_fn_body;
|
||||||
use hir::{db::HirDatabase, HirDisplay, Semantics};
|
use hir::{db::HirDatabase, HirDisplay, Semantics};
|
||||||
use ide_db::{famous_defs::FamousDefs, path_transform::PathTransform, RootDatabase, SnippetCap};
|
use ide_db::{famous_defs::FamousDefs, path_transform::PathTransform, RootDatabase, SnippetCap};
|
||||||
|
@ -15,7 +13,7 @@ use syntax::{
|
||||||
edit_in_place::{AttrsOwnerEdit, Removable},
|
edit_in_place::{AttrsOwnerEdit, Removable},
|
||||||
make, HasArgList, HasAttrs, HasGenericParams, HasName, HasTypeBounds, Whitespace,
|
make, HasArgList, HasAttrs, HasGenericParams, HasName, HasTypeBounds, Whitespace,
|
||||||
},
|
},
|
||||||
ted, AstNode, AstToken, Direction, SmolStr, SourceFile,
|
ted, AstNode, AstToken, Direction, SourceFile,
|
||||||
SyntaxKind::*,
|
SyntaxKind::*,
|
||||||
SyntaxNode, TextRange, TextSize, T,
|
SyntaxNode, TextRange, TextSize, T,
|
||||||
};
|
};
|
||||||
|
@ -424,34 +422,44 @@ pub(crate) fn generate_trait_impl_text(adt: &ast::Adt, trait_text: &str, code: &
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_impl_text_inner(adt: &ast::Adt, trait_text: Option<&str>, code: &str) -> String {
|
fn generate_impl_text_inner(adt: &ast::Adt, trait_text: Option<&str>, code: &str) -> String {
|
||||||
let generic_params = adt.generic_param_list();
|
// Ensure lifetime params are before type & const params
|
||||||
|
let generic_params = adt.generic_param_list().map(|generic_params| {
|
||||||
|
let lifetime_params =
|
||||||
|
generic_params.lifetime_params().map(ast::GenericParam::LifetimeParam);
|
||||||
|
let ty_or_const_params = generic_params.type_or_const_params().filter_map(|param| {
|
||||||
|
// remove defaults since they can't be specified in impls
|
||||||
|
match param {
|
||||||
|
ast::TypeOrConstParam::Type(param) => {
|
||||||
|
let param = param.clone_for_update();
|
||||||
|
param.remove_default();
|
||||||
|
Some(ast::GenericParam::TypeParam(param))
|
||||||
|
}
|
||||||
|
ast::TypeOrConstParam::Const(param) => {
|
||||||
|
let param = param.clone_for_update();
|
||||||
|
param.remove_default();
|
||||||
|
Some(ast::GenericParam::ConstParam(param))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
make::generic_param_list(itertools::chain(lifetime_params, ty_or_const_params))
|
||||||
|
});
|
||||||
|
|
||||||
|
// FIXME: use syntax::make & mutable AST apis instead
|
||||||
|
// `trait_text` and `code` can't be opaque blobs of text
|
||||||
let mut buf = String::with_capacity(code.len());
|
let mut buf = String::with_capacity(code.len());
|
||||||
|
|
||||||
|
// Copy any cfg attrs from the original adt
|
||||||
buf.push_str("\n\n");
|
buf.push_str("\n\n");
|
||||||
adt.attrs()
|
let cfg_attrs = adt
|
||||||
.filter(|attr| attr.as_simple_call().map(|(name, _arg)| name == "cfg").unwrap_or(false))
|
.attrs()
|
||||||
.for_each(|attr| buf.push_str(format!("{}\n", attr).as_str()));
|
.filter(|attr| attr.as_simple_call().map(|(name, _arg)| name == "cfg").unwrap_or(false));
|
||||||
|
cfg_attrs.for_each(|attr| buf.push_str(&format!("{attr}\n")));
|
||||||
|
|
||||||
|
// `impl{generic_params} {trait_text} for {name}{generic_params.to_generic_args()}`
|
||||||
buf.push_str("impl");
|
buf.push_str("impl");
|
||||||
if let Some(generic_params) = &generic_params {
|
if let Some(generic_params) = &generic_params {
|
||||||
let lifetimes = generic_params.lifetime_params().map(|lt| format!("{}", lt.syntax()));
|
format_to!(buf, "{generic_params}");
|
||||||
let toc_params = generic_params.type_or_const_params().map(|toc_param| {
|
|
||||||
let type_param = match toc_param {
|
|
||||||
ast::TypeOrConstParam::Type(x) => x,
|
|
||||||
ast::TypeOrConstParam::Const(x) => return x.syntax().to_string(),
|
|
||||||
};
|
|
||||||
let mut buf = String::new();
|
|
||||||
if let Some(it) = type_param.name() {
|
|
||||||
format_to!(buf, "{}", it.syntax());
|
|
||||||
}
|
|
||||||
if let Some(it) = type_param.colon_token() {
|
|
||||||
format_to!(buf, "{} ", it);
|
|
||||||
}
|
|
||||||
if let Some(it) = type_param.type_bound_list() {
|
|
||||||
format_to!(buf, "{}", it.syntax());
|
|
||||||
}
|
|
||||||
buf
|
|
||||||
});
|
|
||||||
let generics = lifetimes.chain(toc_params).format(", ");
|
|
||||||
format_to!(buf, "<{}>", generics);
|
|
||||||
}
|
}
|
||||||
buf.push(' ');
|
buf.push(' ');
|
||||||
if let Some(trait_text) = trait_text {
|
if let Some(trait_text) = trait_text {
|
||||||
|
@ -460,23 +468,15 @@ fn generate_impl_text_inner(adt: &ast::Adt, trait_text: Option<&str>, code: &str
|
||||||
}
|
}
|
||||||
buf.push_str(&adt.name().unwrap().text());
|
buf.push_str(&adt.name().unwrap().text());
|
||||||
if let Some(generic_params) = generic_params {
|
if let Some(generic_params) = generic_params {
|
||||||
let lifetime_params = generic_params
|
format_to!(buf, "{}", generic_params.to_generic_args());
|
||||||
.lifetime_params()
|
|
||||||
.filter_map(|it| it.lifetime())
|
|
||||||
.map(|it| SmolStr::from(it.text()));
|
|
||||||
let toc_params = generic_params
|
|
||||||
.type_or_const_params()
|
|
||||||
.filter_map(|it| it.name())
|
|
||||||
.map(|it| SmolStr::from(it.text()));
|
|
||||||
format_to!(buf, "<{}>", lifetime_params.chain(toc_params).format(", "))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
match adt.where_clause() {
|
match adt.where_clause() {
|
||||||
Some(where_clause) => {
|
Some(where_clause) => {
|
||||||
format_to!(buf, "\n{}\n{{\n{}\n}}", where_clause, code);
|
format_to!(buf, "\n{where_clause}\n{{\n{code}\n}}");
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
format_to!(buf, " {{\n{}\n}}", code);
|
format_to!(buf, " {{\n{code}\n}}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,10 +11,10 @@ doctest = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
cov-mark = "2.0.0-pre.1"
|
cov-mark = "2.0.0-pre.1"
|
||||||
itertools = "0.10.3"
|
itertools = "0.10.5"
|
||||||
|
|
||||||
once_cell = "1.12.0"
|
once_cell = "1.15.0"
|
||||||
smallvec = "1.9.0"
|
smallvec = "1.10.0"
|
||||||
|
|
||||||
stdx = { path = "../stdx", version = "0.0.0" }
|
stdx = { path = "../stdx", version = "0.0.0" }
|
||||||
syntax = { path = "../syntax", version = "0.0.0" }
|
syntax = { path = "../syntax", version = "0.0.0" }
|
||||||
|
|
|
@ -19,6 +19,7 @@ pub(crate) mod snippet;
|
||||||
pub(crate) mod r#type;
|
pub(crate) mod r#type;
|
||||||
pub(crate) mod use_;
|
pub(crate) mod use_;
|
||||||
pub(crate) mod vis;
|
pub(crate) mod vis;
|
||||||
|
pub(crate) mod env_vars;
|
||||||
|
|
||||||
use std::iter;
|
use std::iter;
|
||||||
|
|
||||||
|
|
150
crates/ide-completion/src/completions/env_vars.rs
Normal file
150
crates/ide-completion/src/completions/env_vars.rs
Normal file
|
@ -0,0 +1,150 @@
|
||||||
|
//! Completes environment variables defined by Cargo (https://doc.rust-lang.org/cargo/reference/environment-variables.html)
|
||||||
|
use hir::Semantics;
|
||||||
|
use ide_db::{syntax_helpers::node_ext::macro_call_for_string_token, RootDatabase};
|
||||||
|
use syntax::ast::{self, IsString};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
completions::Completions, context::CompletionContext, CompletionItem, CompletionItemKind,
|
||||||
|
};
|
||||||
|
|
||||||
|
const CARGO_DEFINED_VARS: &[(&str, &str)] = &[
|
||||||
|
("CARGO","Path to the cargo binary performing the build"),
|
||||||
|
("CARGO_MANIFEST_DIR","The directory containing the manifest of your package"),
|
||||||
|
("CARGO_PKG_VERSION","The full version of your package"),
|
||||||
|
("CARGO_PKG_VERSION_MAJOR","The major version of your package"),
|
||||||
|
("CARGO_PKG_VERSION_MINOR","The minor version of your package"),
|
||||||
|
("CARGO_PKG_VERSION_PATCH","The patch version of your package"),
|
||||||
|
("CARGO_PKG_VERSION_PRE","The pre-release version of your package"),
|
||||||
|
("CARGO_PKG_AUTHORS","Colon separated list of authors from the manifest of your package"),
|
||||||
|
("CARGO_PKG_NAME","The name of your package"),
|
||||||
|
("CARGO_PKG_DESCRIPTION","The description from the manifest of your package"),
|
||||||
|
("CARGO_PKG_HOMEPAGE","The home page from the manifest of your package"),
|
||||||
|
("CARGO_PKG_REPOSITORY","The repository from the manifest of your package"),
|
||||||
|
("CARGO_PKG_LICENSE","The license from the manifest of your package"),
|
||||||
|
("CARGO_PKG_LICENSE_FILE","The license file from the manifest of your package"),
|
||||||
|
("CARGO_PKG_RUST_VERSION","The Rust version from the manifest of your package. Note that this is the minimum Rust version supported by the package, not the current Rust version"),
|
||||||
|
("CARGO_CRATE_NAME","The name of the crate that is currently being compiled"),
|
||||||
|
("CARGO_BIN_NAME","The name of the binary that is currently being compiled (if it is a binary). This name does not include any file extension, such as .exe"),
|
||||||
|
("CARGO_PRIMARY_PACKAGE","This environment variable will be set if the package being built is primary. Primary packages are the ones the user selected on the command-line, either with -p flags or the defaults based on the current directory and the default workspace members. This environment variable will not be set when building dependencies. This is only set when compiling the package (not when running binaries or tests)"),
|
||||||
|
("CARGO_TARGET_TMPDIR","Only set when building integration test or benchmark code. This is a path to a directory inside the target directory where integration tests or benchmarks are free to put any data needed by the tests/benches. Cargo initially creates this directory but doesn't manage its content in any way, this is the responsibility of the test code")
|
||||||
|
];
|
||||||
|
|
||||||
|
pub(crate) fn complete_cargo_env_vars(
|
||||||
|
acc: &mut Completions,
|
||||||
|
ctx: &CompletionContext<'_>,
|
||||||
|
expanded: &ast::String,
|
||||||
|
) -> Option<()> {
|
||||||
|
guard_env_macro(expanded, &ctx.sema)?;
|
||||||
|
let range = expanded.text_range_between_quotes()?;
|
||||||
|
|
||||||
|
CARGO_DEFINED_VARS.iter().for_each(|(var, detail)| {
|
||||||
|
let mut item = CompletionItem::new(CompletionItemKind::Keyword, range, var);
|
||||||
|
item.detail(*detail);
|
||||||
|
item.add_to(acc);
|
||||||
|
});
|
||||||
|
|
||||||
|
Some(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn guard_env_macro(string: &ast::String, semantics: &Semantics<'_, RootDatabase>) -> Option<()> {
|
||||||
|
let call = macro_call_for_string_token(string)?;
|
||||||
|
let name = call.path()?.segment()?.name_ref()?;
|
||||||
|
let makro = semantics.resolve_macro_call(&call)?;
|
||||||
|
let db = semantics.db;
|
||||||
|
|
||||||
|
match name.text().as_str() {
|
||||||
|
"env" | "option_env" if makro.kind(db) == hir::MacroKind::BuiltIn => Some(()),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::tests::{check_edit, completion_list};
|
||||||
|
|
||||||
|
fn check(macro_name: &str) {
|
||||||
|
check_edit(
|
||||||
|
"CARGO_BIN_NAME",
|
||||||
|
&format!(
|
||||||
|
r#"
|
||||||
|
#[rustc_builtin_macro]
|
||||||
|
macro_rules! {} {{
|
||||||
|
($var:literal) => {{ 0 }}
|
||||||
|
}}
|
||||||
|
|
||||||
|
fn main() {{
|
||||||
|
let foo = {}!("CAR$0");
|
||||||
|
}}
|
||||||
|
"#,
|
||||||
|
macro_name, macro_name
|
||||||
|
),
|
||||||
|
&format!(
|
||||||
|
r#"
|
||||||
|
#[rustc_builtin_macro]
|
||||||
|
macro_rules! {} {{
|
||||||
|
($var:literal) => {{ 0 }}
|
||||||
|
}}
|
||||||
|
|
||||||
|
fn main() {{
|
||||||
|
let foo = {}!("CARGO_BIN_NAME");
|
||||||
|
}}
|
||||||
|
"#,
|
||||||
|
macro_name, macro_name
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn completes_env_variable_in_env() {
|
||||||
|
check("env")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn completes_env_variable_in_option_env() {
|
||||||
|
check("option_env");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn doesnt_complete_in_random_strings() {
|
||||||
|
let fixture = r#"
|
||||||
|
fn main() {
|
||||||
|
let foo = "CA$0";
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let completions = completion_list(fixture);
|
||||||
|
assert!(completions.is_empty(), "Completions weren't empty: {}", completions);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn doesnt_complete_in_random_macro() {
|
||||||
|
let fixture = r#"
|
||||||
|
macro_rules! bar {
|
||||||
|
($($arg:tt)*) => { 0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let foo = bar!("CA$0");
|
||||||
|
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let completions = completion_list(fixture);
|
||||||
|
assert!(completions.is_empty(), "Completions weren't empty: {}", completions);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn doesnt_complete_for_shadowed_macro() {
|
||||||
|
let fixture = r#"
|
||||||
|
macro_rules! env {
|
||||||
|
($var:literal) => { 0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let foo = env!("CA$0");
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let completions = completion_list(fixture);
|
||||||
|
assert!(completions.is_empty(), "Completions weren't empty: {}", completions)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
//! See `CompletionContext` structure.
|
//! See [`CompletionContext`] structure.
|
||||||
|
|
||||||
mod analysis;
|
mod analysis;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -23,7 +23,10 @@ use syntax::{
|
||||||
};
|
};
|
||||||
use text_edit::Indel;
|
use text_edit::Indel;
|
||||||
|
|
||||||
use crate::CompletionConfig;
|
use crate::{
|
||||||
|
context::analysis::{expand_and_analyze, AnalysisResult},
|
||||||
|
CompletionConfig,
|
||||||
|
};
|
||||||
|
|
||||||
const COMPLETION_MARKER: &str = "intellijRulezz";
|
const COMPLETION_MARKER: &str = "intellijRulezz";
|
||||||
|
|
||||||
|
@ -561,15 +564,27 @@ impl<'a> CompletionContext<'a> {
|
||||||
let edit = Indel::insert(offset, COMPLETION_MARKER.to_string());
|
let edit = Indel::insert(offset, COMPLETION_MARKER.to_string());
|
||||||
parse.reparse(&edit).tree()
|
parse.reparse(&edit).tree()
|
||||||
};
|
};
|
||||||
let fake_ident_token =
|
|
||||||
file_with_fake_ident.syntax().token_at_offset(offset).right_biased()?;
|
|
||||||
|
|
||||||
|
// always pick the token to the immediate left of the cursor, as that is what we are actually
|
||||||
|
// completing on
|
||||||
let original_token = original_file.syntax().token_at_offset(offset).left_biased()?;
|
let original_token = original_file.syntax().token_at_offset(offset).left_biased()?;
|
||||||
let token = sema.descend_into_macros_single(original_token.clone());
|
|
||||||
|
let AnalysisResult {
|
||||||
|
analysis,
|
||||||
|
expected: (expected_type, expected_name),
|
||||||
|
qualifier_ctx,
|
||||||
|
token,
|
||||||
|
offset,
|
||||||
|
} = expand_and_analyze(
|
||||||
|
&sema,
|
||||||
|
original_file.syntax().clone(),
|
||||||
|
file_with_fake_ident.syntax().clone(),
|
||||||
|
offset,
|
||||||
|
&original_token,
|
||||||
|
)?;
|
||||||
|
|
||||||
// adjust for macro input, this still fails if there is no token written yet
|
// adjust for macro input, this still fails if there is no token written yet
|
||||||
let scope_offset = if original_token == token { offset } else { token.text_range().end() };
|
let scope = sema.scope_at_offset(&token.parent()?, offset)?;
|
||||||
let scope = sema.scope_at_offset(&token.parent()?, scope_offset)?;
|
|
||||||
|
|
||||||
let krate = scope.krate();
|
let krate = scope.krate();
|
||||||
let module = scope.module();
|
let module = scope.module();
|
||||||
|
@ -583,7 +598,7 @@ impl<'a> CompletionContext<'a> {
|
||||||
|
|
||||||
let depth_from_crate_root = iter::successors(module.parent(db), |m| m.parent(db)).count();
|
let depth_from_crate_root = iter::successors(module.parent(db), |m| m.parent(db)).count();
|
||||||
|
|
||||||
let mut ctx = CompletionContext {
|
let ctx = CompletionContext {
|
||||||
sema,
|
sema,
|
||||||
scope,
|
scope,
|
||||||
db,
|
db,
|
||||||
|
@ -593,19 +608,13 @@ impl<'a> CompletionContext<'a> {
|
||||||
token,
|
token,
|
||||||
krate,
|
krate,
|
||||||
module,
|
module,
|
||||||
expected_name: None,
|
expected_name,
|
||||||
expected_type: None,
|
expected_type,
|
||||||
qualifier_ctx: Default::default(),
|
qualifier_ctx,
|
||||||
locals,
|
locals,
|
||||||
depth_from_crate_root,
|
depth_from_crate_root,
|
||||||
};
|
};
|
||||||
let ident_ctx = ctx.expand_and_analyze(
|
Some((ctx, analysis))
|
||||||
original_file.syntax().clone(),
|
|
||||||
file_with_fake_ident.syntax().clone(),
|
|
||||||
offset,
|
|
||||||
fake_ident_token,
|
|
||||||
)?;
|
|
||||||
Some((ctx, ident_ctx))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,25 +11,64 @@ use syntax::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::context::{
|
use crate::context::{
|
||||||
AttrCtx, CompletionAnalysis, CompletionContext, DotAccess, DotAccessKind, ExprCtx,
|
AttrCtx, CompletionAnalysis, DotAccess, DotAccessKind, ExprCtx, ItemListKind, LifetimeContext,
|
||||||
ItemListKind, LifetimeContext, LifetimeKind, NameContext, NameKind, NameRefContext,
|
LifetimeKind, NameContext, NameKind, NameRefContext, NameRefKind, ParamContext, ParamKind,
|
||||||
NameRefKind, ParamContext, ParamKind, PathCompletionCtx, PathKind, PatternContext,
|
PathCompletionCtx, PathKind, PatternContext, PatternRefutability, Qualified, QualifierCtx,
|
||||||
PatternRefutability, Qualified, QualifierCtx, TypeAscriptionTarget, TypeLocation,
|
TypeAscriptionTarget, TypeLocation, COMPLETION_MARKER,
|
||||||
COMPLETION_MARKER,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
impl<'a> CompletionContext<'a> {
|
struct ExpansionResult {
|
||||||
|
original_file: SyntaxNode,
|
||||||
|
speculative_file: SyntaxNode,
|
||||||
|
offset: TextSize,
|
||||||
|
fake_ident_token: SyntaxToken,
|
||||||
|
derive_ctx: Option<(SyntaxNode, SyntaxNode, TextSize, ast::Attr)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) struct AnalysisResult {
|
||||||
|
pub(super) analysis: CompletionAnalysis,
|
||||||
|
pub(super) expected: (Option<Type>, Option<ast::NameOrNameRef>),
|
||||||
|
pub(super) qualifier_ctx: QualifierCtx,
|
||||||
|
pub(super) token: SyntaxToken,
|
||||||
|
pub(super) offset: TextSize,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn expand_and_analyze(
|
||||||
|
sema: &Semantics<'_, RootDatabase>,
|
||||||
|
original_file: SyntaxNode,
|
||||||
|
speculative_file: SyntaxNode,
|
||||||
|
offset: TextSize,
|
||||||
|
original_token: &SyntaxToken,
|
||||||
|
) -> Option<AnalysisResult> {
|
||||||
|
// as we insert after the offset, right biased will *always* pick the identifier no matter
|
||||||
|
// if there is an ident already typed or not
|
||||||
|
let fake_ident_token = speculative_file.token_at_offset(offset).right_biased()?;
|
||||||
|
// the relative offset between the cursor and the *identifier* token we are completing on
|
||||||
|
let relative_offset = offset - fake_ident_token.text_range().start();
|
||||||
|
// make the offset point to the start of the original token, as that is what the
|
||||||
|
// intermediate offsets calculated in expansion always points to
|
||||||
|
let offset = offset - relative_offset;
|
||||||
|
let expansion = expand(sema, original_file, speculative_file, offset, fake_ident_token);
|
||||||
|
// add the relative offset back, so that left_biased finds the proper token
|
||||||
|
let offset = expansion.offset + relative_offset;
|
||||||
|
let token = expansion.original_file.token_at_offset(offset).left_biased()?;
|
||||||
|
|
||||||
|
analyze(sema, expansion, original_token, &token).map(|(analysis, expected, qualifier_ctx)| {
|
||||||
|
AnalysisResult { analysis, expected, qualifier_ctx, token, offset }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// Expand attributes and macro calls at the current cursor position for both the original file
|
/// Expand attributes and macro calls at the current cursor position for both the original file
|
||||||
/// and fake file repeatedly. As soon as one of the two expansions fail we stop so the original
|
/// and fake file repeatedly. As soon as one of the two expansions fail we stop so the original
|
||||||
/// and speculative states stay in sync.
|
/// and speculative states stay in sync.
|
||||||
pub(super) fn expand_and_analyze(
|
fn expand(
|
||||||
&mut self,
|
sema: &Semantics<'_, RootDatabase>,
|
||||||
mut original_file: SyntaxNode,
|
mut original_file: SyntaxNode,
|
||||||
mut speculative_file: SyntaxNode,
|
mut speculative_file: SyntaxNode,
|
||||||
mut offset: TextSize,
|
mut offset: TextSize,
|
||||||
mut fake_ident_token: SyntaxToken,
|
mut fake_ident_token: SyntaxToken,
|
||||||
) -> Option<CompletionAnalysis> {
|
) -> ExpansionResult {
|
||||||
let _p = profile::span("CompletionContext::expand_and_fill");
|
let _p = profile::span("CompletionContext::expand");
|
||||||
let mut derive_ctx = None;
|
let mut derive_ctx = None;
|
||||||
|
|
||||||
'expansion: loop {
|
'expansion: loop {
|
||||||
|
@ -46,8 +85,8 @@ impl<'a> CompletionContext<'a> {
|
||||||
// first try to expand attributes as these are always the outermost macro calls
|
// first try to expand attributes as these are always the outermost macro calls
|
||||||
'ancestors: for (actual_item, item_with_fake_ident) in ancestor_items {
|
'ancestors: for (actual_item, item_with_fake_ident) in ancestor_items {
|
||||||
match (
|
match (
|
||||||
self.sema.expand_attr_macro(&actual_item),
|
sema.expand_attr_macro(&actual_item),
|
||||||
self.sema.speculative_expand_attr_macro(
|
sema.speculative_expand_attr_macro(
|
||||||
&actual_item,
|
&actual_item,
|
||||||
&item_with_fake_ident,
|
&item_with_fake_ident,
|
||||||
fake_ident_token.clone(),
|
fake_ident_token.clone(),
|
||||||
|
@ -90,8 +129,8 @@ impl<'a> CompletionContext<'a> {
|
||||||
spec_tt.syntax().parent().and_then(ast::Meta::cast).and_then(|it| it.parent_attr()),
|
spec_tt.syntax().parent().and_then(ast::Meta::cast).and_then(|it| it.parent_attr()),
|
||||||
) {
|
) {
|
||||||
if let (Some(actual_expansion), Some((fake_expansion, fake_mapped_token))) = (
|
if let (Some(actual_expansion), Some((fake_expansion, fake_mapped_token))) = (
|
||||||
self.sema.expand_derive_as_pseudo_attr_macro(&orig_attr),
|
sema.expand_derive_as_pseudo_attr_macro(&orig_attr),
|
||||||
self.sema.speculative_expand_derive_as_pseudo_attr_macro(
|
sema.speculative_expand_derive_as_pseudo_attr_macro(
|
||||||
&orig_attr,
|
&orig_attr,
|
||||||
&spec_attr,
|
&spec_attr,
|
||||||
fake_ident_token.clone(),
|
fake_ident_token.clone(),
|
||||||
|
@ -127,8 +166,8 @@ impl<'a> CompletionContext<'a> {
|
||||||
};
|
};
|
||||||
|
|
||||||
match (
|
match (
|
||||||
self.sema.expand(&actual_macro_call),
|
sema.expand(&actual_macro_call),
|
||||||
self.sema.speculative_expand(
|
sema.speculative_expand(
|
||||||
&actual_macro_call,
|
&actual_macro_call,
|
||||||
&speculative_args,
|
&speculative_args,
|
||||||
fake_ident_token.clone(),
|
fake_ident_token.clone(),
|
||||||
|
@ -157,16 +196,115 @@ impl<'a> CompletionContext<'a> {
|
||||||
// none of our states have changed so stop the loop
|
// none of our states have changed so stop the loop
|
||||||
break 'expansion;
|
break 'expansion;
|
||||||
}
|
}
|
||||||
|
ExpansionResult { original_file, speculative_file, offset, fake_ident_token, derive_ctx }
|
||||||
|
}
|
||||||
|
|
||||||
self.analyze(&original_file, speculative_file, offset, derive_ctx)
|
/// Fill the completion context, this is what does semantic reasoning about the surrounding context
|
||||||
|
/// of the completion location.
|
||||||
|
fn analyze(
|
||||||
|
sema: &Semantics<'_, RootDatabase>,
|
||||||
|
expansion_result: ExpansionResult,
|
||||||
|
original_token: &SyntaxToken,
|
||||||
|
self_token: &SyntaxToken,
|
||||||
|
) -> Option<(CompletionAnalysis, (Option<Type>, Option<ast::NameOrNameRef>), QualifierCtx)> {
|
||||||
|
let _p = profile::span("CompletionContext::analyze");
|
||||||
|
let ExpansionResult { original_file, speculative_file, offset, fake_ident_token, derive_ctx } =
|
||||||
|
expansion_result;
|
||||||
|
let syntax_element = NodeOrToken::Token(fake_ident_token);
|
||||||
|
if is_in_token_of_for_loop(syntax_element.clone()) {
|
||||||
|
// for pat $0
|
||||||
|
// there is nothing to complete here except `in` keyword
|
||||||
|
// don't bother populating the context
|
||||||
|
// FIXME: the completion calculations should end up good enough
|
||||||
|
// such that this special case becomes unnecessary
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Overwrite the path kind for derives
|
||||||
|
if let Some((original_file, file_with_fake_ident, offset, origin_attr)) = derive_ctx {
|
||||||
|
if let Some(ast::NameLike::NameRef(name_ref)) =
|
||||||
|
find_node_at_offset(&file_with_fake_ident, offset)
|
||||||
|
{
|
||||||
|
let parent = name_ref.syntax().parent()?;
|
||||||
|
let (mut nameref_ctx, _) = classify_name_ref(&sema, &original_file, name_ref, parent)?;
|
||||||
|
if let NameRefKind::Path(path_ctx) = &mut nameref_ctx.kind {
|
||||||
|
path_ctx.kind = PathKind::Derive {
|
||||||
|
existing_derives: sema
|
||||||
|
.resolve_derive_macro(&origin_attr)
|
||||||
|
.into_iter()
|
||||||
|
.flatten()
|
||||||
|
.flatten()
|
||||||
|
.collect(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return Some((
|
||||||
|
CompletionAnalysis::NameRef(nameref_ctx),
|
||||||
|
(None, None),
|
||||||
|
QualifierCtx::default(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let name_like = match find_node_at_offset(&speculative_file, offset) {
|
||||||
|
Some(it) => it,
|
||||||
|
None => {
|
||||||
|
let analysis = if let Some(original) = ast::String::cast(original_token.clone()) {
|
||||||
|
CompletionAnalysis::String {
|
||||||
|
original,
|
||||||
|
expanded: ast::String::cast(self_token.clone()),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Fix up trailing whitespace problem
|
||||||
|
// #[attr(foo = $0
|
||||||
|
let token = syntax::algo::skip_trivia_token(self_token.clone(), Direction::Prev)?;
|
||||||
|
let p = token.parent()?;
|
||||||
|
if p.kind() == SyntaxKind::TOKEN_TREE
|
||||||
|
&& p.ancestors().any(|it| it.kind() == SyntaxKind::META)
|
||||||
|
{
|
||||||
|
let colon_prefix = previous_non_trivia_token(self_token.clone())
|
||||||
|
.map_or(false, |it| T![:] == it.kind());
|
||||||
|
CompletionAnalysis::UnexpandedAttrTT {
|
||||||
|
fake_attribute_under_caret: syntax_element
|
||||||
|
.ancestors()
|
||||||
|
.find_map(ast::Attr::cast),
|
||||||
|
colon_prefix,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return Some((analysis, (None, None), QualifierCtx::default()));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let expected = expected_type_and_name(sema, &self_token, &name_like);
|
||||||
|
let mut qual_ctx = QualifierCtx::default();
|
||||||
|
let analysis = match name_like {
|
||||||
|
ast::NameLike::Lifetime(lifetime) => {
|
||||||
|
CompletionAnalysis::Lifetime(classify_lifetime(sema, &original_file, lifetime)?)
|
||||||
|
}
|
||||||
|
ast::NameLike::NameRef(name_ref) => {
|
||||||
|
let parent = name_ref.syntax().parent()?;
|
||||||
|
let (nameref_ctx, qualifier_ctx) =
|
||||||
|
classify_name_ref(sema, &original_file, name_ref, parent.clone())?;
|
||||||
|
qual_ctx = qualifier_ctx;
|
||||||
|
CompletionAnalysis::NameRef(nameref_ctx)
|
||||||
|
}
|
||||||
|
ast::NameLike::Name(name) => {
|
||||||
|
let name_ctx = classify_name(sema, &original_file, name)?;
|
||||||
|
CompletionAnalysis::Name(name_ctx)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Some((analysis, expected, qual_ctx))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calculate the expected type and name of the cursor position.
|
/// Calculate the expected type and name of the cursor position.
|
||||||
fn expected_type_and_name(
|
fn expected_type_and_name(
|
||||||
&self,
|
sema: &Semantics<'_, RootDatabase>,
|
||||||
|
token: &SyntaxToken,
|
||||||
name_like: &ast::NameLike,
|
name_like: &ast::NameLike,
|
||||||
) -> (Option<Type>, Option<NameOrNameRef>) {
|
) -> (Option<Type>, Option<NameOrNameRef>) {
|
||||||
let mut node = match self.token.parent() {
|
let mut node = match token.parent() {
|
||||||
Some(it) => it,
|
Some(it) => it,
|
||||||
None => return (None, None),
|
None => return (None, None),
|
||||||
};
|
};
|
||||||
|
@ -215,8 +353,8 @@ impl<'a> CompletionContext<'a> {
|
||||||
cov_mark::hit!(expected_type_let_with_leading_char);
|
cov_mark::hit!(expected_type_let_with_leading_char);
|
||||||
cov_mark::hit!(expected_type_let_without_leading_char);
|
cov_mark::hit!(expected_type_let_without_leading_char);
|
||||||
let ty = it.pat()
|
let ty = it.pat()
|
||||||
.and_then(|pat| self.sema.type_of_pat(&pat))
|
.and_then(|pat| sema.type_of_pat(&pat))
|
||||||
.or_else(|| it.initializer().and_then(|it| self.sema.type_of_expr(&it)))
|
.or_else(|| it.initializer().and_then(|it| sema.type_of_expr(&it)))
|
||||||
.map(TypeInfo::original);
|
.map(TypeInfo::original);
|
||||||
let name = match it.pat() {
|
let name = match it.pat() {
|
||||||
Some(ast::Pat::IdentPat(ident)) => ident.name().map(NameOrNameRef::Name),
|
Some(ast::Pat::IdentPat(ident)) => ident.name().map(NameOrNameRef::Name),
|
||||||
|
@ -228,16 +366,16 @@ impl<'a> CompletionContext<'a> {
|
||||||
ast::LetExpr(it) => {
|
ast::LetExpr(it) => {
|
||||||
cov_mark::hit!(expected_type_if_let_without_leading_char);
|
cov_mark::hit!(expected_type_if_let_without_leading_char);
|
||||||
let ty = it.pat()
|
let ty = it.pat()
|
||||||
.and_then(|pat| self.sema.type_of_pat(&pat))
|
.and_then(|pat| sema.type_of_pat(&pat))
|
||||||
.or_else(|| it.expr().and_then(|it| self.sema.type_of_expr(&it)))
|
.or_else(|| it.expr().and_then(|it| sema.type_of_expr(&it)))
|
||||||
.map(TypeInfo::original);
|
.map(TypeInfo::original);
|
||||||
(ty, None)
|
(ty, None)
|
||||||
},
|
},
|
||||||
ast::ArgList(_) => {
|
ast::ArgList(_) => {
|
||||||
cov_mark::hit!(expected_type_fn_param);
|
cov_mark::hit!(expected_type_fn_param);
|
||||||
ActiveParameter::at_token(
|
ActiveParameter::at_token(
|
||||||
&self.sema,
|
&sema,
|
||||||
self.token.clone(),
|
token.clone(),
|
||||||
).map(|ap| {
|
).map(|ap| {
|
||||||
let name = ap.ident().map(NameOrNameRef::Name);
|
let name = ap.ident().map(NameOrNameRef::Name);
|
||||||
|
|
||||||
|
@ -249,22 +387,22 @@ impl<'a> CompletionContext<'a> {
|
||||||
ast::RecordExprFieldList(it) => {
|
ast::RecordExprFieldList(it) => {
|
||||||
// wouldn't try {} be nice...
|
// wouldn't try {} be nice...
|
||||||
(|| {
|
(|| {
|
||||||
if self.token.kind() == T![..]
|
if token.kind() == T![..]
|
||||||
|| self.token.prev_token().map(|t| t.kind()) == Some(T![..])
|
||token.prev_token().map(|t| t.kind()) == Some(T![..])
|
||||||
{
|
{
|
||||||
cov_mark::hit!(expected_type_struct_func_update);
|
cov_mark::hit!(expected_type_struct_func_update);
|
||||||
let record_expr = it.syntax().parent().and_then(ast::RecordExpr::cast)?;
|
let record_expr = it.syntax().parent().and_then(ast::RecordExpr::cast)?;
|
||||||
let ty = self.sema.type_of_expr(&record_expr.into())?;
|
let ty = sema.type_of_expr(&record_expr.into())?;
|
||||||
Some((
|
Some((
|
||||||
Some(ty.original),
|
Some(ty.original),
|
||||||
None
|
None
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
cov_mark::hit!(expected_type_struct_field_without_leading_char);
|
cov_mark::hit!(expected_type_struct_field_without_leading_char);
|
||||||
let expr_field = self.token.prev_sibling_or_token()?
|
let expr_field = token.prev_sibling_or_token()?
|
||||||
.into_node()
|
.into_node()
|
||||||
.and_then(ast::RecordExprField::cast)?;
|
.and_then(ast::RecordExprField::cast)?;
|
||||||
let (_, _, ty) = self.sema.resolve_record_field(&expr_field)?;
|
let (_, _, ty) = sema.resolve_record_field(&expr_field)?;
|
||||||
Some((
|
Some((
|
||||||
Some(ty),
|
Some(ty),
|
||||||
expr_field.field_name().map(NameOrNameRef::NameRef),
|
expr_field.field_name().map(NameOrNameRef::NameRef),
|
||||||
|
@ -276,12 +414,12 @@ impl<'a> CompletionContext<'a> {
|
||||||
if let Some(expr) = it.expr() {
|
if let Some(expr) = it.expr() {
|
||||||
cov_mark::hit!(expected_type_struct_field_with_leading_char);
|
cov_mark::hit!(expected_type_struct_field_with_leading_char);
|
||||||
(
|
(
|
||||||
self.sema.type_of_expr(&expr).map(TypeInfo::original),
|
sema.type_of_expr(&expr).map(TypeInfo::original),
|
||||||
it.field_name().map(NameOrNameRef::NameRef),
|
it.field_name().map(NameOrNameRef::NameRef),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
cov_mark::hit!(expected_type_struct_field_followed_by_comma);
|
cov_mark::hit!(expected_type_struct_field_followed_by_comma);
|
||||||
let ty = self.sema.resolve_record_field(&it)
|
let ty = sema.resolve_record_field(&it)
|
||||||
.map(|(_, _, ty)| ty);
|
.map(|(_, _, ty)| ty);
|
||||||
(
|
(
|
||||||
ty,
|
ty,
|
||||||
|
@ -292,41 +430,41 @@ impl<'a> CompletionContext<'a> {
|
||||||
// match foo { $0 }
|
// match foo { $0 }
|
||||||
// match foo { ..., pat => $0 }
|
// match foo { ..., pat => $0 }
|
||||||
ast::MatchExpr(it) => {
|
ast::MatchExpr(it) => {
|
||||||
let on_arrow = previous_non_trivia_token(self.token.clone()).map_or(false, |it| T![=>] == it.kind());
|
let on_arrow = previous_non_trivia_token(token.clone()).map_or(false, |it| T![=>] == it.kind());
|
||||||
|
|
||||||
let ty = if on_arrow {
|
let ty = if on_arrow {
|
||||||
// match foo { ..., pat => $0 }
|
// match foo { ..., pat => $0 }
|
||||||
cov_mark::hit!(expected_type_match_arm_body_without_leading_char);
|
cov_mark::hit!(expected_type_match_arm_body_without_leading_char);
|
||||||
cov_mark::hit!(expected_type_match_arm_body_with_leading_char);
|
cov_mark::hit!(expected_type_match_arm_body_with_leading_char);
|
||||||
self.sema.type_of_expr(&it.into())
|
sema.type_of_expr(&it.into())
|
||||||
} else {
|
} else {
|
||||||
// match foo { $0 }
|
// match foo { $0 }
|
||||||
cov_mark::hit!(expected_type_match_arm_without_leading_char);
|
cov_mark::hit!(expected_type_match_arm_without_leading_char);
|
||||||
it.expr().and_then(|e| self.sema.type_of_expr(&e))
|
it.expr().and_then(|e| sema.type_of_expr(&e))
|
||||||
}.map(TypeInfo::original);
|
}.map(TypeInfo::original);
|
||||||
(ty, None)
|
(ty, None)
|
||||||
},
|
},
|
||||||
ast::IfExpr(it) => {
|
ast::IfExpr(it) => {
|
||||||
let ty = it.condition()
|
let ty = it.condition()
|
||||||
.and_then(|e| self.sema.type_of_expr(&e))
|
.and_then(|e| sema.type_of_expr(&e))
|
||||||
.map(TypeInfo::original);
|
.map(TypeInfo::original);
|
||||||
(ty, None)
|
(ty, None)
|
||||||
},
|
},
|
||||||
ast::IdentPat(it) => {
|
ast::IdentPat(it) => {
|
||||||
cov_mark::hit!(expected_type_if_let_with_leading_char);
|
cov_mark::hit!(expected_type_if_let_with_leading_char);
|
||||||
cov_mark::hit!(expected_type_match_arm_with_leading_char);
|
cov_mark::hit!(expected_type_match_arm_with_leading_char);
|
||||||
let ty = self.sema.type_of_pat(&ast::Pat::from(it)).map(TypeInfo::original);
|
let ty = sema.type_of_pat(&ast::Pat::from(it)).map(TypeInfo::original);
|
||||||
(ty, None)
|
(ty, None)
|
||||||
},
|
},
|
||||||
ast::Fn(it) => {
|
ast::Fn(it) => {
|
||||||
cov_mark::hit!(expected_type_fn_ret_with_leading_char);
|
cov_mark::hit!(expected_type_fn_ret_with_leading_char);
|
||||||
cov_mark::hit!(expected_type_fn_ret_without_leading_char);
|
cov_mark::hit!(expected_type_fn_ret_without_leading_char);
|
||||||
let def = self.sema.to_def(&it);
|
let def = sema.to_def(&it);
|
||||||
(def.map(|def| def.ret_type(self.db)), None)
|
(def.map(|def| def.ret_type(sema.db)), None)
|
||||||
},
|
},
|
||||||
ast::ClosureExpr(it) => {
|
ast::ClosureExpr(it) => {
|
||||||
let ty = self.sema.type_of_expr(&it.into());
|
let ty = sema.type_of_expr(&it.into());
|
||||||
ty.and_then(|ty| ty.original.as_callable(self.db))
|
ty.and_then(|ty| ty.original.as_callable(sema.db))
|
||||||
.map(|c| (Some(c.return_type()), None))
|
.map(|c| (Some(c.return_type()), None))
|
||||||
.unwrap_or((None, None))
|
.unwrap_or((None, None))
|
||||||
},
|
},
|
||||||
|
@ -347,104 +485,6 @@ impl<'a> CompletionContext<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fill the completion context, this is what does semantic reasoning about the surrounding context
|
|
||||||
/// of the completion location.
|
|
||||||
fn analyze(
|
|
||||||
&mut self,
|
|
||||||
original_file: &SyntaxNode,
|
|
||||||
file_with_fake_ident: SyntaxNode,
|
|
||||||
offset: TextSize,
|
|
||||||
derive_ctx: Option<(SyntaxNode, SyntaxNode, TextSize, ast::Attr)>,
|
|
||||||
) -> Option<CompletionAnalysis> {
|
|
||||||
let fake_ident_token = file_with_fake_ident.token_at_offset(offset).right_biased()?;
|
|
||||||
let syntax_element = NodeOrToken::Token(fake_ident_token);
|
|
||||||
if is_in_token_of_for_loop(syntax_element.clone()) {
|
|
||||||
// for pat $0
|
|
||||||
// there is nothing to complete here except `in` keyword
|
|
||||||
// don't bother populating the context
|
|
||||||
// FIXME: the completion calculations should end up good enough
|
|
||||||
// such that this special case becomes unnecessary
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Overwrite the path kind for derives
|
|
||||||
if let Some((original_file, file_with_fake_ident, offset, origin_attr)) = derive_ctx {
|
|
||||||
if let Some(ast::NameLike::NameRef(name_ref)) =
|
|
||||||
find_node_at_offset(&file_with_fake_ident, offset)
|
|
||||||
{
|
|
||||||
let parent = name_ref.syntax().parent()?;
|
|
||||||
let (mut nameref_ctx, _) =
|
|
||||||
Self::classify_name_ref(&self.sema, &original_file, name_ref, parent)?;
|
|
||||||
if let NameRefKind::Path(path_ctx) = &mut nameref_ctx.kind {
|
|
||||||
path_ctx.kind = PathKind::Derive {
|
|
||||||
existing_derives: self
|
|
||||||
.sema
|
|
||||||
.resolve_derive_macro(&origin_attr)
|
|
||||||
.into_iter()
|
|
||||||
.flatten()
|
|
||||||
.flatten()
|
|
||||||
.collect(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return Some(CompletionAnalysis::NameRef(nameref_ctx));
|
|
||||||
}
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let name_like = match find_node_at_offset(&file_with_fake_ident, offset) {
|
|
||||||
Some(it) => it,
|
|
||||||
None => {
|
|
||||||
let analysis =
|
|
||||||
if let Some(original) = ast::String::cast(self.original_token.clone()) {
|
|
||||||
CompletionAnalysis::String {
|
|
||||||
original,
|
|
||||||
expanded: ast::String::cast(self.token.clone()),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Fix up trailing whitespace problem
|
|
||||||
// #[attr(foo = $0
|
|
||||||
let token =
|
|
||||||
syntax::algo::skip_trivia_token(self.token.clone(), Direction::Prev)?;
|
|
||||||
let p = token.parent()?;
|
|
||||||
if p.kind() == SyntaxKind::TOKEN_TREE
|
|
||||||
&& p.ancestors().any(|it| it.kind() == SyntaxKind::META)
|
|
||||||
{
|
|
||||||
let colon_prefix = previous_non_trivia_token(self.token.clone())
|
|
||||||
.map_or(false, |it| T![:] == it.kind());
|
|
||||||
CompletionAnalysis::UnexpandedAttrTT {
|
|
||||||
fake_attribute_under_caret: syntax_element
|
|
||||||
.ancestors()
|
|
||||||
.find_map(ast::Attr::cast),
|
|
||||||
colon_prefix,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return Some(analysis);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
(self.expected_type, self.expected_name) = self.expected_type_and_name(&name_like);
|
|
||||||
let analysis = match name_like {
|
|
||||||
ast::NameLike::Lifetime(lifetime) => CompletionAnalysis::Lifetime(
|
|
||||||
Self::classify_lifetime(&self.sema, original_file, lifetime)?,
|
|
||||||
),
|
|
||||||
ast::NameLike::NameRef(name_ref) => {
|
|
||||||
let parent = name_ref.syntax().parent()?;
|
|
||||||
let (nameref_ctx, qualifier_ctx) =
|
|
||||||
Self::classify_name_ref(&self.sema, &original_file, name_ref, parent.clone())?;
|
|
||||||
|
|
||||||
self.qualifier_ctx = qualifier_ctx;
|
|
||||||
CompletionAnalysis::NameRef(nameref_ctx)
|
|
||||||
}
|
|
||||||
ast::NameLike::Name(name) => {
|
|
||||||
let name_ctx = Self::classify_name(&self.sema, original_file, name)?;
|
|
||||||
CompletionAnalysis::Name(name_ctx)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Some(analysis)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn classify_lifetime(
|
fn classify_lifetime(
|
||||||
_sema: &Semantics<'_, RootDatabase>,
|
_sema: &Semantics<'_, RootDatabase>,
|
||||||
original_file: &SyntaxNode,
|
original_file: &SyntaxNode,
|
||||||
|
@ -520,8 +560,7 @@ impl<'a> CompletionContext<'a> {
|
||||||
) -> Option<(NameRefContext, QualifierCtx)> {
|
) -> Option<(NameRefContext, QualifierCtx)> {
|
||||||
let nameref = find_node_at_offset(&original_file, name_ref.syntax().text_range().start());
|
let nameref = find_node_at_offset(&original_file, name_ref.syntax().text_range().start());
|
||||||
|
|
||||||
let make_res =
|
let make_res = |kind| (NameRefContext { nameref: nameref.clone(), kind }, Default::default());
|
||||||
|kind| (NameRefContext { nameref: nameref.clone(), kind }, Default::default());
|
|
||||||
|
|
||||||
if let Some(record_field) = ast::RecordExprField::for_field_name(&name_ref) {
|
if let Some(record_field) = ast::RecordExprField::for_field_name(&name_ref) {
|
||||||
let dot_prefix = previous_non_trivia_token(name_ref.syntax().clone())
|
let dot_prefix = previous_non_trivia_token(name_ref.syntax().clone())
|
||||||
|
@ -748,9 +787,7 @@ impl<'a> CompletionContext<'a> {
|
||||||
let find_ret_ty = |it: SyntaxNode| {
|
let find_ret_ty = |it: SyntaxNode| {
|
||||||
if let Some(item) = ast::Item::cast(it.clone()) {
|
if let Some(item) = ast::Item::cast(it.clone()) {
|
||||||
match item {
|
match item {
|
||||||
ast::Item::Fn(f) => {
|
ast::Item::Fn(f) => Some(sema.to_def(&f).map(|it| it.ret_type(sema.db))),
|
||||||
Some(sema.to_def(&f).map(|it| it.ret_type(sema.db)))
|
|
||||||
}
|
|
||||||
ast::Item::MacroCall(_) => None,
|
ast::Item::MacroCall(_) => None,
|
||||||
_ => Some(None),
|
_ => Some(None),
|
||||||
}
|
}
|
||||||
|
@ -770,9 +807,7 @@ impl<'a> CompletionContext<'a> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let find_fn_self_param = |it| match it {
|
let find_fn_self_param = |it| match it {
|
||||||
ast::Item::Fn(fn_) => {
|
ast::Item::Fn(fn_) => Some(sema.to_def(&fn_).and_then(|it| it.self_param(sema.db))),
|
||||||
Some(sema.to_def(&fn_).and_then(|it| it.self_param(sema.db)))
|
|
||||||
}
|
|
||||||
ast::Item::MacroCall(_) => None,
|
ast::Item::MacroCall(_) => None,
|
||||||
_ => Some(None),
|
_ => Some(None),
|
||||||
};
|
};
|
||||||
|
@ -866,10 +901,8 @@ impl<'a> CompletionContext<'a> {
|
||||||
let kind = attr.kind();
|
let kind = attr.kind();
|
||||||
let attached = attr.syntax().parent()?;
|
let attached = attr.syntax().parent()?;
|
||||||
let is_trailing_outer_attr = kind != AttrKind::Inner
|
let is_trailing_outer_attr = kind != AttrKind::Inner
|
||||||
&& non_trivia_sibling(attr.syntax().clone().into(), syntax::Direction::Next)
|
&& non_trivia_sibling(attr.syntax().clone().into(), syntax::Direction::Next).is_none();
|
||||||
.is_none();
|
let annotated_item_kind = if is_trailing_outer_attr { None } else { Some(attached.kind()) };
|
||||||
let annotated_item_kind =
|
|
||||||
if is_trailing_outer_attr { None } else { Some(attached.kind()) };
|
|
||||||
Some(PathKind::Attr { attr_ctx: AttrCtx { kind, annotated_item_kind } })
|
Some(PathKind::Attr { attr_ctx: AttrCtx { kind, annotated_item_kind } })
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -967,10 +1000,11 @@ impl<'a> CompletionContext<'a> {
|
||||||
.map(|it| it.parent_path());
|
.map(|it| it.parent_path());
|
||||||
if let Some(qualifier) = qualifier {
|
if let Some(qualifier) = qualifier {
|
||||||
let type_anchor = match qualifier.segment().and_then(|it| it.kind()) {
|
let type_anchor = match qualifier.segment().and_then(|it| it.kind()) {
|
||||||
Some(ast::PathSegmentKind::Type {
|
Some(ast::PathSegmentKind::Type { type_ref: Some(type_ref), trait_ref })
|
||||||
type_ref: Some(type_ref),
|
if qualifier.qualifier().is_none() =>
|
||||||
trait_ref,
|
{
|
||||||
}) if qualifier.qualifier().is_none() => Some((type_ref, trait_ref)),
|
Some((type_ref, trait_ref))
|
||||||
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -987,8 +1021,7 @@ impl<'a> CompletionContext<'a> {
|
||||||
// For understanding how and why super_chain_len is calculated the way it
|
// For understanding how and why super_chain_len is calculated the way it
|
||||||
// is check the documentation at it's definition
|
// is check the documentation at it's definition
|
||||||
let mut segment_count = 0;
|
let mut segment_count = 0;
|
||||||
let super_count =
|
let super_count = iter::successors(Some(qualifier.clone()), |p| p.qualifier())
|
||||||
iter::successors(Some(qualifier.clone()), |p| p.qualifier())
|
|
||||||
.take_while(|p| {
|
.take_while(|p| {
|
||||||
p.segment()
|
p.segment()
|
||||||
.and_then(|s| {
|
.and_then(|s| {
|
||||||
|
@ -1042,8 +1075,7 @@ impl<'a> CompletionContext<'a> {
|
||||||
.children_with_tokens()
|
.children_with_tokens()
|
||||||
.filter_map(NodeOrToken::into_token)
|
.filter_map(NodeOrToken::into_token)
|
||||||
.find(|it| it.kind() == T![unsafe]);
|
.find(|it| it.kind() == T![unsafe]);
|
||||||
qualifier_ctx.vis_node =
|
qualifier_ctx.vis_node = error_node.children().find_map(ast::Visibility::cast);
|
||||||
error_node.children().find_map(ast::Visibility::cast);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1068,7 +1100,6 @@ impl<'a> CompletionContext<'a> {
|
||||||
}
|
}
|
||||||
Some((NameRefContext { nameref, kind: NameRefKind::Path(path_ctx) }, qualifier_ctx))
|
Some((NameRefContext { nameref, kind: NameRefKind::Path(path_ctx) }, qualifier_ctx))
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fn pattern_context_for(
|
fn pattern_context_for(
|
||||||
sema: &Semantics<'_, RootDatabase>,
|
sema: &Semantics<'_, RootDatabase>,
|
||||||
|
|
|
@ -183,6 +183,7 @@ pub fn completions(
|
||||||
CompletionAnalysis::String { original, expanded: Some(expanded) } => {
|
CompletionAnalysis::String { original, expanded: Some(expanded) } => {
|
||||||
completions::extern_abi::complete_extern_abi(acc, ctx, expanded);
|
completions::extern_abi::complete_extern_abi(acc, ctx, expanded);
|
||||||
completions::format_string::format_string(acc, ctx, original, expanded);
|
completions::format_string::format_string(acc, ctx, original, expanded);
|
||||||
|
completions::env_vars::complete_cargo_env_vars(acc, ctx, expanded);
|
||||||
}
|
}
|
||||||
CompletionAnalysis::UnexpandedAttrTT {
|
CompletionAnalysis::UnexpandedAttrTT {
|
||||||
colon_prefix,
|
colon_prefix,
|
||||||
|
|
|
@ -15,9 +15,9 @@ tracing = "0.1.35"
|
||||||
rayon = "1.5.3"
|
rayon = "1.5.3"
|
||||||
fst = { version = "0.4.7", default-features = false }
|
fst = { version = "0.4.7", default-features = false }
|
||||||
rustc-hash = "1.1.0"
|
rustc-hash = "1.1.0"
|
||||||
once_cell = "1.12.0"
|
once_cell = "1.15.0"
|
||||||
either = "1.7.0"
|
either = "1.7.0"
|
||||||
itertools = "0.10.3"
|
itertools = "0.10.5"
|
||||||
arrayvec = "0.7.2"
|
arrayvec = "0.7.2"
|
||||||
indexmap = "1.9.1"
|
indexmap = "1.9.1"
|
||||||
memchr = "2.5.0"
|
memchr = "2.5.0"
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
//! Tools to work with format string literals for the `format_args!` family of macros.
|
//! Tools to work with format string literals for the `format_args!` family of macros.
|
||||||
|
use crate::syntax_helpers::node_ext::macro_call_for_string_token;
|
||||||
use syntax::{
|
use syntax::{
|
||||||
ast::{self, IsString},
|
ast::{self, IsString},
|
||||||
AstNode, AstToken, TextRange, TextSize,
|
TextRange, TextSize,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn is_format_string(string: &ast::String) -> bool {
|
pub fn is_format_string(string: &ast::String) -> bool {
|
||||||
|
@ -14,8 +15,7 @@ pub fn is_format_string(string: &ast::String) -> bool {
|
||||||
// This setup lets us correctly highlight the components of `concat!("{}", "bla")` format
|
// This setup lets us correctly highlight the components of `concat!("{}", "bla")` format
|
||||||
// strings. It still fails for `concat!("{", "}")`, but that is rare.
|
// strings. It still fails for `concat!("{", "}")`, but that is rare.
|
||||||
(|| {
|
(|| {
|
||||||
let macro_call = string.syntax().parent_ancestors().find_map(ast::MacroCall::cast)?;
|
let name = macro_call_for_string_token(string)?.path()?.segment()?.name_ref()?;
|
||||||
let name = macro_call.path()?.segment()?.name_ref()?;
|
|
||||||
|
|
||||||
if !matches!(
|
if !matches!(
|
||||||
name.text().as_str(),
|
name.text().as_str(),
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use parser::T;
|
use parser::T;
|
||||||
use syntax::{
|
use syntax::{
|
||||||
ast::{self, HasLoopBody, PathSegmentKind, VisibilityKind},
|
ast::{self, HasLoopBody, MacroCall, PathSegmentKind, VisibilityKind},
|
||||||
AstNode, Preorder, RustLanguage, WalkEvent,
|
AstNode, AstToken, Preorder, RustLanguage, WalkEvent,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn expr_as_name_ref(expr: &ast::Expr) -> Option<ast::NameRef> {
|
pub fn expr_as_name_ref(expr: &ast::Expr) -> Option<ast::NameRef> {
|
||||||
|
@ -457,3 +457,8 @@ pub fn parse_tt_as_comma_sep_paths(input: ast::TokenTree) -> Option<Vec<ast::Pat
|
||||||
.collect();
|
.collect();
|
||||||
Some(paths)
|
Some(paths)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn macro_call_for_string_token(string: &ast::String) -> Option<MacroCall> {
|
||||||
|
let macro_call = string.syntax().parent_ancestors().find_map(ast::MacroCall::cast)?;
|
||||||
|
Some(macro_call)
|
||||||
|
}
|
||||||
|
|
|
@ -11,11 +11,9 @@ doctest = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
cov-mark = "2.0.0-pre.1"
|
cov-mark = "2.0.0-pre.1"
|
||||||
itertools = "0.10.3"
|
|
||||||
|
|
||||||
|
|
||||||
either = "1.7.0"
|
either = "1.7.0"
|
||||||
serde_json = "1.0.82"
|
itertools = "0.10.5"
|
||||||
|
serde_json = "1.0.86"
|
||||||
|
|
||||||
profile = { path = "../profile", version = "0.0.0" }
|
profile = { path = "../profile", version = "0.0.0" }
|
||||||
stdx = { path = "../stdx", version = "0.0.0" }
|
stdx = { path = "../stdx", version = "0.0.0" }
|
||||||
|
|
37
crates/ide-diagnostics/src/handlers/incorrect_try_expr.rs
Normal file
37
crates/ide-diagnostics/src/handlers/incorrect_try_expr.rs
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
use hir::InFile;
|
||||||
|
|
||||||
|
use crate::{Diagnostic, DiagnosticsContext};
|
||||||
|
|
||||||
|
// Diagnostic: incorrect-try-target
|
||||||
|
//
|
||||||
|
// This diagnostic is triggered if a question mark operator was used in a context where it is not applicable.
|
||||||
|
pub(crate) fn incorrect_try_expr(
|
||||||
|
ctx: &DiagnosticsContext<'_>,
|
||||||
|
d: &hir::IncorrectTryExpr,
|
||||||
|
) -> Diagnostic {
|
||||||
|
Diagnostic::new(
|
||||||
|
"incorrect-try-target",
|
||||||
|
format!("the return type of the containing function does not implement `FromResidual`"),
|
||||||
|
ctx.sema
|
||||||
|
.diagnostics_display_range(InFile::new(d.expr.file_id, d.expr.value.clone().into()))
|
||||||
|
.range,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::tests::check_diagnostics;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn try_ops_diag() {
|
||||||
|
check_diagnostics(
|
||||||
|
r#"
|
||||||
|
//- minicore: try
|
||||||
|
fn test() {
|
||||||
|
core::ops::ControlFlow::<u32, f32>::Continue(1.0)?;
|
||||||
|
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: the return type of the containing function does not implement `FromResidual`
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
35
crates/ide-diagnostics/src/handlers/not_implemented.rs
Normal file
35
crates/ide-diagnostics/src/handlers/not_implemented.rs
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
use hir::{db::DefDatabase, HirDisplay};
|
||||||
|
|
||||||
|
use crate::{Diagnostic, DiagnosticsContext};
|
||||||
|
|
||||||
|
// Diagnostic: not-implemented
|
||||||
|
//
|
||||||
|
// This diagnostic is triggered if a type doesn't implement a necessary trait.
|
||||||
|
pub(crate) fn not_implemented(ctx: &DiagnosticsContext<'_>, d: &hir::NotImplemented) -> Diagnostic {
|
||||||
|
Diagnostic::new(
|
||||||
|
"not-implemented",
|
||||||
|
format!(
|
||||||
|
"the trait `{}` is not implemented for `{}`",
|
||||||
|
ctx.sema.db.trait_data(d.trait_).name,
|
||||||
|
d.ty.display(ctx.sema.db)
|
||||||
|
),
|
||||||
|
ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::tests::check_diagnostics;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn missing_try_impl() {
|
||||||
|
check_diagnostics(
|
||||||
|
r#"
|
||||||
|
//- minicore: try
|
||||||
|
fn main() {
|
||||||
|
()?;
|
||||||
|
} //^^ error: the trait `Try` is not implemented for `()`
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -29,6 +29,7 @@ mod handlers {
|
||||||
pub(crate) mod break_outside_of_loop;
|
pub(crate) mod break_outside_of_loop;
|
||||||
pub(crate) mod inactive_code;
|
pub(crate) mod inactive_code;
|
||||||
pub(crate) mod incorrect_case;
|
pub(crate) mod incorrect_case;
|
||||||
|
pub(crate) mod incorrect_try_expr;
|
||||||
pub(crate) mod invalid_derive_target;
|
pub(crate) mod invalid_derive_target;
|
||||||
pub(crate) mod macro_error;
|
pub(crate) mod macro_error;
|
||||||
pub(crate) mod malformed_derive;
|
pub(crate) mod malformed_derive;
|
||||||
|
@ -36,6 +37,7 @@ mod handlers {
|
||||||
pub(crate) mod missing_fields;
|
pub(crate) mod missing_fields;
|
||||||
pub(crate) mod missing_match_arms;
|
pub(crate) mod missing_match_arms;
|
||||||
pub(crate) mod missing_unsafe;
|
pub(crate) mod missing_unsafe;
|
||||||
|
pub(crate) mod not_implemented;
|
||||||
pub(crate) mod no_such_field;
|
pub(crate) mod no_such_field;
|
||||||
pub(crate) mod replace_filter_map_next_with_find_map;
|
pub(crate) mod replace_filter_map_next_with_find_map;
|
||||||
pub(crate) mod type_mismatch;
|
pub(crate) mod type_mismatch;
|
||||||
|
@ -225,12 +227,14 @@ pub fn diagnostics(
|
||||||
let d = match diag {
|
let d = match diag {
|
||||||
AnyDiagnostic::BreakOutsideOfLoop(d) => handlers::break_outside_of_loop::break_outside_of_loop(&ctx, &d),
|
AnyDiagnostic::BreakOutsideOfLoop(d) => handlers::break_outside_of_loop::break_outside_of_loop(&ctx, &d),
|
||||||
AnyDiagnostic::IncorrectCase(d) => handlers::incorrect_case::incorrect_case(&ctx, &d),
|
AnyDiagnostic::IncorrectCase(d) => handlers::incorrect_case::incorrect_case(&ctx, &d),
|
||||||
|
AnyDiagnostic::IncorrectTryExpr(d) => handlers::incorrect_try_expr::incorrect_try_expr(&ctx, &d),
|
||||||
AnyDiagnostic::MacroError(d) => handlers::macro_error::macro_error(&ctx, &d),
|
AnyDiagnostic::MacroError(d) => handlers::macro_error::macro_error(&ctx, &d),
|
||||||
AnyDiagnostic::MalformedDerive(d) => handlers::malformed_derive::malformed_derive(&ctx, &d),
|
AnyDiagnostic::MalformedDerive(d) => handlers::malformed_derive::malformed_derive(&ctx, &d),
|
||||||
AnyDiagnostic::MismatchedArgCount(d) => handlers::mismatched_arg_count::mismatched_arg_count(&ctx, &d),
|
AnyDiagnostic::MismatchedArgCount(d) => handlers::mismatched_arg_count::mismatched_arg_count(&ctx, &d),
|
||||||
AnyDiagnostic::MissingFields(d) => handlers::missing_fields::missing_fields(&ctx, &d),
|
AnyDiagnostic::MissingFields(d) => handlers::missing_fields::missing_fields(&ctx, &d),
|
||||||
AnyDiagnostic::MissingMatchArms(d) => handlers::missing_match_arms::missing_match_arms(&ctx, &d),
|
AnyDiagnostic::MissingMatchArms(d) => handlers::missing_match_arms::missing_match_arms(&ctx, &d),
|
||||||
AnyDiagnostic::MissingUnsafe(d) => handlers::missing_unsafe::missing_unsafe(&ctx, &d),
|
AnyDiagnostic::MissingUnsafe(d) => handlers::missing_unsafe::missing_unsafe(&ctx, &d),
|
||||||
|
AnyDiagnostic::NotImplemented(d) => handlers::not_implemented::not_implemented(&ctx, &d),
|
||||||
AnyDiagnostic::NoSuchField(d) => handlers::no_such_field::no_such_field(&ctx, &d),
|
AnyDiagnostic::NoSuchField(d) => handlers::no_such_field::no_such_field(&ctx, &d),
|
||||||
AnyDiagnostic::ReplaceFilterMapNextWithFindMap(d) => handlers::replace_filter_map_next_with_find_map::replace_filter_map_next_with_find_map(&ctx, &d),
|
AnyDiagnostic::ReplaceFilterMapNextWithFindMap(d) => handlers::replace_filter_map_next_with_find_map::replace_filter_map_next_with_find_map(&ctx, &d),
|
||||||
AnyDiagnostic::TypeMismatch(d) => handlers::type_mismatch::type_mismatch(&ctx, &d),
|
AnyDiagnostic::TypeMismatch(d) => handlers::type_mismatch::type_mismatch(&ctx, &d),
|
||||||
|
|
|
@ -12,8 +12,7 @@ doctest = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
cov-mark = "2.0.0-pre.1"
|
cov-mark = "2.0.0-pre.1"
|
||||||
|
itertools = "0.10.5"
|
||||||
itertools = "0.10.3"
|
|
||||||
|
|
||||||
text-edit = { path = "../text-edit", version = "0.0.0" }
|
text-edit = { path = "../text-edit", version = "0.0.0" }
|
||||||
parser = { path = "../parser", version = "0.0.0" }
|
parser = { path = "../parser", version = "0.0.0" }
|
||||||
|
|
|
@ -13,12 +13,12 @@ doctest = false
|
||||||
cov-mark = "2.0.0-pre.1"
|
cov-mark = "2.0.0-pre.1"
|
||||||
crossbeam-channel = "0.5.5"
|
crossbeam-channel = "0.5.5"
|
||||||
either = "1.7.0"
|
either = "1.7.0"
|
||||||
itertools = "0.10.3"
|
itertools = "0.10.5"
|
||||||
tracing = "0.1.35"
|
tracing = "0.1.35"
|
||||||
oorandom = "11.1.3"
|
oorandom = "11.1.3"
|
||||||
pulldown-cmark-to-cmark = "10.0.1"
|
pulldown-cmark-to-cmark = "10.0.4"
|
||||||
pulldown-cmark = { version = "0.9.1", default-features = false }
|
pulldown-cmark = { version = "0.9.1", default-features = false }
|
||||||
url = "2.2.2"
|
url = "2.3.1"
|
||||||
dot = "0.1.4"
|
dot = "0.1.4"
|
||||||
|
|
||||||
stdx = { path = "../stdx", version = "0.0.0" }
|
stdx = { path = "../stdx", version = "0.0.0" }
|
||||||
|
|
|
@ -48,10 +48,14 @@ pub(crate) fn goto_definition(
|
||||||
_ => 1,
|
_ => 1,
|
||||||
})?;
|
})?;
|
||||||
if let Some(doc_comment) = token_as_doc_comment(&original_token) {
|
if let Some(doc_comment) = token_as_doc_comment(&original_token) {
|
||||||
return doc_comment.get_definition_with_descend_at(sema, position.offset, |def, _, _| {
|
return doc_comment.get_definition_with_descend_at(
|
||||||
|
sema,
|
||||||
|
position.offset,
|
||||||
|
|def, _, link_range| {
|
||||||
let nav = def.try_to_nav(db)?;
|
let nav = def.try_to_nav(db)?;
|
||||||
Some(RangeInfo::new(original_token.text_range(), vec![nav]))
|
Some(RangeInfo::new(link_range, vec![nav]))
|
||||||
});
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
let navs = sema
|
let navs = sema
|
||||||
.descend_into_macros(original_token.clone())
|
.descend_into_macros(original_token.clone())
|
||||||
|
|
|
@ -4913,6 +4913,22 @@ fn foo() -> NotResult<(), Short> {
|
||||||
```
|
```
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
|
check_hover_range(
|
||||||
|
r#"
|
||||||
|
//- minicore: try
|
||||||
|
use core::ops::ControlFlow;
|
||||||
|
fn foo() -> ControlFlow<()> {
|
||||||
|
$0ControlFlow::Break(())?$0;
|
||||||
|
ControlFlow::Continue(())
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
```text
|
||||||
|
Try Target Type: ControlFlow<(), {unknown}>
|
||||||
|
Propagated as: ControlFlow<(), ()>
|
||||||
|
```
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -4929,7 +4945,7 @@ fn foo() -> Option<()> {
|
||||||
"#,
|
"#,
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
```rust
|
```rust
|
||||||
<Option<i32> as Try>::Output
|
i32
|
||||||
```"#]],
|
```"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ doctest = false
|
||||||
[dependencies]
|
[dependencies]
|
||||||
cov-mark = "2.0.0-pre.1"
|
cov-mark = "2.0.0-pre.1"
|
||||||
rustc-hash = "1.1.0"
|
rustc-hash = "1.1.0"
|
||||||
smallvec = "1.9.0"
|
smallvec = "1.10.0"
|
||||||
tracing = "0.1.35"
|
tracing = "0.1.35"
|
||||||
|
|
||||||
syntax = { path = "../syntax", version = "0.0.0" }
|
syntax = { path = "../syntax", version = "0.0.0" }
|
||||||
|
|
|
@ -8,7 +8,7 @@ use syntax::{
|
||||||
use test_utils::{bench, bench_fixture, skip_slow_tests};
|
use test_utils::{bench, bench_fixture, skip_slow_tests};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
parser::{Op, RepeatKind, Separator},
|
parser::{MetaVarKind, Op, RepeatKind, Separator},
|
||||||
syntax_node_to_token_tree, DeclarativeMacro,
|
syntax_node_to_token_tree, DeclarativeMacro,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -111,35 +111,35 @@ fn invocation_fixtures(rules: &FxHashMap<String, DeclarativeMacro>) -> Vec<(Stri
|
||||||
|
|
||||||
fn collect_from_op(op: &Op, parent: &mut tt::Subtree, seed: &mut usize) {
|
fn collect_from_op(op: &Op, parent: &mut tt::Subtree, seed: &mut usize) {
|
||||||
return match op {
|
return match op {
|
||||||
Op::Var { kind, .. } => match kind.as_ref().map(|it| it.as_str()) {
|
Op::Var { kind, .. } => match kind.as_ref() {
|
||||||
Some("ident") => parent.token_trees.push(make_ident("foo")),
|
Some(MetaVarKind::Ident) => parent.token_trees.push(make_ident("foo")),
|
||||||
Some("ty") => parent.token_trees.push(make_ident("Foo")),
|
Some(MetaVarKind::Ty) => parent.token_trees.push(make_ident("Foo")),
|
||||||
Some("tt") => parent.token_trees.push(make_ident("foo")),
|
Some(MetaVarKind::Tt) => parent.token_trees.push(make_ident("foo")),
|
||||||
Some("vis") => parent.token_trees.push(make_ident("pub")),
|
Some(MetaVarKind::Vis) => parent.token_trees.push(make_ident("pub")),
|
||||||
Some("pat") => parent.token_trees.push(make_ident("foo")),
|
Some(MetaVarKind::Pat) => parent.token_trees.push(make_ident("foo")),
|
||||||
Some("path") => parent.token_trees.push(make_ident("foo")),
|
Some(MetaVarKind::Path) => parent.token_trees.push(make_ident("foo")),
|
||||||
Some("literal") => parent.token_trees.push(make_literal("1")),
|
Some(MetaVarKind::Literal) => parent.token_trees.push(make_literal("1")),
|
||||||
Some("expr") => parent.token_trees.push(make_ident("foo")),
|
Some(MetaVarKind::Expr) => parent.token_trees.push(make_ident("foo")),
|
||||||
Some("lifetime") => {
|
Some(MetaVarKind::Lifetime) => {
|
||||||
parent.token_trees.push(make_punct('\''));
|
parent.token_trees.push(make_punct('\''));
|
||||||
parent.token_trees.push(make_ident("a"));
|
parent.token_trees.push(make_ident("a"));
|
||||||
}
|
}
|
||||||
Some("block") => {
|
Some(MetaVarKind::Block) => {
|
||||||
parent.token_trees.push(make_subtree(tt::DelimiterKind::Brace, None))
|
parent.token_trees.push(make_subtree(tt::DelimiterKind::Brace, None))
|
||||||
}
|
}
|
||||||
Some("item") => {
|
Some(MetaVarKind::Item) => {
|
||||||
parent.token_trees.push(make_ident("fn"));
|
parent.token_trees.push(make_ident("fn"));
|
||||||
parent.token_trees.push(make_ident("foo"));
|
parent.token_trees.push(make_ident("foo"));
|
||||||
parent.token_trees.push(make_subtree(tt::DelimiterKind::Parenthesis, None));
|
parent.token_trees.push(make_subtree(tt::DelimiterKind::Parenthesis, None));
|
||||||
parent.token_trees.push(make_subtree(tt::DelimiterKind::Brace, None));
|
parent.token_trees.push(make_subtree(tt::DelimiterKind::Brace, None));
|
||||||
}
|
}
|
||||||
Some("meta") => {
|
Some(MetaVarKind::Meta) => {
|
||||||
parent.token_trees.push(make_ident("foo"));
|
parent.token_trees.push(make_ident("foo"));
|
||||||
parent.token_trees.push(make_subtree(tt::DelimiterKind::Parenthesis, None));
|
parent.token_trees.push(make_subtree(tt::DelimiterKind::Parenthesis, None));
|
||||||
}
|
}
|
||||||
|
|
||||||
None => (),
|
None => (),
|
||||||
Some(kind) => panic!("Unhandled kind {}", kind),
|
Some(kind) => panic!("Unhandled kind {:?}", kind),
|
||||||
},
|
},
|
||||||
Op::Leaf(leaf) => parent.token_trees.push(leaf.clone().into()),
|
Op::Leaf(leaf) => parent.token_trees.push(leaf.clone().into()),
|
||||||
Op::Repeat { tokens, kind, separator } => {
|
Op::Repeat { tokens, kind, separator } => {
|
||||||
|
|
|
@ -8,7 +8,7 @@ mod transcriber;
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
use syntax::SmolStr;
|
use syntax::SmolStr;
|
||||||
|
|
||||||
use crate::{ExpandError, ExpandResult};
|
use crate::{parser::MetaVarKind, ExpandError, ExpandResult};
|
||||||
|
|
||||||
pub(crate) fn expand_rules(
|
pub(crate) fn expand_rules(
|
||||||
rules: &[crate::Rule],
|
rules: &[crate::Rule],
|
||||||
|
@ -104,6 +104,7 @@ enum Binding {
|
||||||
Fragment(Fragment),
|
Fragment(Fragment),
|
||||||
Nested(Vec<Binding>),
|
Nested(Vec<Binding>),
|
||||||
Empty,
|
Empty,
|
||||||
|
Missing(MetaVarKind),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
|
|
@ -66,7 +66,7 @@ use syntax::SmolStr;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
expander::{Binding, Bindings, ExpandResult, Fragment},
|
expander::{Binding, Bindings, ExpandResult, Fragment},
|
||||||
parser::{Op, RepeatKind, Separator},
|
parser::{MetaVarKind, Op, RepeatKind, Separator},
|
||||||
tt_iter::TtIter,
|
tt_iter::TtIter,
|
||||||
ExpandError, MetaTemplate,
|
ExpandError, MetaTemplate,
|
||||||
};
|
};
|
||||||
|
@ -119,6 +119,7 @@ pub(super) fn match_(pattern: &MetaTemplate, input: &tt::Subtree) -> Match {
|
||||||
.map(|it| match it {
|
.map(|it| match it {
|
||||||
Binding::Fragment(_) => 1,
|
Binding::Fragment(_) => 1,
|
||||||
Binding::Empty => 1,
|
Binding::Empty => 1,
|
||||||
|
Binding::Missing(_) => 1,
|
||||||
Binding::Nested(it) => count(it.iter()),
|
Binding::Nested(it) => count(it.iter()),
|
||||||
})
|
})
|
||||||
.sum()
|
.sum()
|
||||||
|
@ -130,6 +131,7 @@ enum BindingKind {
|
||||||
Empty(SmolStr),
|
Empty(SmolStr),
|
||||||
Optional(SmolStr),
|
Optional(SmolStr),
|
||||||
Fragment(SmolStr, Fragment),
|
Fragment(SmolStr, Fragment),
|
||||||
|
Missing(SmolStr, MetaVarKind),
|
||||||
Nested(usize, usize),
|
Nested(usize, usize),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -190,6 +192,10 @@ impl BindingsBuilder {
|
||||||
.push(LinkNode::Node(Rc::new(BindingKind::Fragment(var.clone(), fragment))));
|
.push(LinkNode::Node(Rc::new(BindingKind::Fragment(var.clone(), fragment))));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn push_missing(&mut self, idx: &mut BindingsIdx, var: &SmolStr, kind: MetaVarKind) {
|
||||||
|
self.nodes[idx.0].push(LinkNode::Node(Rc::new(BindingKind::Missing(var.clone(), kind))));
|
||||||
|
}
|
||||||
|
|
||||||
fn push_nested(&mut self, parent: &mut BindingsIdx, child: &BindingsIdx) {
|
fn push_nested(&mut self, parent: &mut BindingsIdx, child: &BindingsIdx) {
|
||||||
let BindingsIdx(idx, nidx) = self.copy(child);
|
let BindingsIdx(idx, nidx) = self.copy(child);
|
||||||
self.nodes[parent.0].push(LinkNode::Node(Rc::new(BindingKind::Nested(idx, nidx))));
|
self.nodes[parent.0].push(LinkNode::Node(Rc::new(BindingKind::Nested(idx, nidx))));
|
||||||
|
@ -222,6 +228,9 @@ impl BindingsBuilder {
|
||||||
BindingKind::Fragment(name, fragment) => {
|
BindingKind::Fragment(name, fragment) => {
|
||||||
bindings.inner.insert(name.clone(), Binding::Fragment(fragment.clone()));
|
bindings.inner.insert(name.clone(), Binding::Fragment(fragment.clone()));
|
||||||
}
|
}
|
||||||
|
BindingKind::Missing(name, kind) => {
|
||||||
|
bindings.inner.insert(name.clone(), Binding::Missing(*kind));
|
||||||
|
}
|
||||||
BindingKind::Nested(idx, nested_idx) => {
|
BindingKind::Nested(idx, nested_idx) => {
|
||||||
let mut nested_nodes = Vec::new();
|
let mut nested_nodes = Vec::new();
|
||||||
self.collect_nested(*idx, *nested_idx, &mut nested_nodes);
|
self.collect_nested(*idx, *nested_idx, &mut nested_nodes);
|
||||||
|
@ -458,9 +467,9 @@ fn match_loop_inner<'t>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
OpDelimited::Op(Op::Var { kind, name, .. }) => {
|
OpDelimited::Op(Op::Var { kind, name, .. }) => {
|
||||||
if let Some(kind) = kind {
|
if let &Some(kind) = kind {
|
||||||
let mut fork = src.clone();
|
let mut fork = src.clone();
|
||||||
let match_res = match_meta_var(kind.as_str(), &mut fork);
|
let match_res = match_meta_var(kind, &mut fork);
|
||||||
match match_res.err {
|
match match_res.err {
|
||||||
None => {
|
None => {
|
||||||
// Some meta variables are optional (e.g. vis)
|
// Some meta variables are optional (e.g. vis)
|
||||||
|
@ -475,8 +484,15 @@ fn match_loop_inner<'t>(
|
||||||
}
|
}
|
||||||
Some(err) => {
|
Some(err) => {
|
||||||
res.add_err(err);
|
res.add_err(err);
|
||||||
if let Some(fragment) = match_res.value {
|
match match_res.value {
|
||||||
bindings_builder.push_fragment(&mut item.bindings, name, fragment);
|
Some(fragment) => bindings_builder.push_fragment(
|
||||||
|
&mut item.bindings,
|
||||||
|
name,
|
||||||
|
fragment,
|
||||||
|
),
|
||||||
|
None => {
|
||||||
|
bindings_builder.push_missing(&mut item.bindings, name, kind)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
item.is_error = true;
|
item.is_error = true;
|
||||||
error_items.push(item);
|
error_items.push(item);
|
||||||
|
@ -668,20 +684,20 @@ fn match_leaf(lhs: &tt::Leaf, src: &mut TtIter<'_>) -> Result<(), ExpandError> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn match_meta_var(kind: &str, input: &mut TtIter<'_>) -> ExpandResult<Option<Fragment>> {
|
fn match_meta_var(kind: MetaVarKind, input: &mut TtIter<'_>) -> ExpandResult<Option<Fragment>> {
|
||||||
let fragment = match kind {
|
let fragment = match kind {
|
||||||
"path" => parser::PrefixEntryPoint::Path,
|
MetaVarKind::Path => parser::PrefixEntryPoint::Path,
|
||||||
"ty" => parser::PrefixEntryPoint::Ty,
|
MetaVarKind::Ty => parser::PrefixEntryPoint::Ty,
|
||||||
// FIXME: These two should actually behave differently depending on the edition.
|
// FIXME: These two should actually behave differently depending on the edition.
|
||||||
//
|
//
|
||||||
// https://doc.rust-lang.org/edition-guide/rust-2021/or-patterns-macro-rules.html
|
// https://doc.rust-lang.org/edition-guide/rust-2021/or-patterns-macro-rules.html
|
||||||
"pat" | "pat_param" => parser::PrefixEntryPoint::Pat,
|
MetaVarKind::Pat | MetaVarKind::PatParam => parser::PrefixEntryPoint::Pat,
|
||||||
"stmt" => parser::PrefixEntryPoint::Stmt,
|
MetaVarKind::Stmt => parser::PrefixEntryPoint::Stmt,
|
||||||
"block" => parser::PrefixEntryPoint::Block,
|
MetaVarKind::Block => parser::PrefixEntryPoint::Block,
|
||||||
"meta" => parser::PrefixEntryPoint::MetaItem,
|
MetaVarKind::Meta => parser::PrefixEntryPoint::MetaItem,
|
||||||
"item" => parser::PrefixEntryPoint::Item,
|
MetaVarKind::Item => parser::PrefixEntryPoint::Item,
|
||||||
"vis" => parser::PrefixEntryPoint::Vis,
|
MetaVarKind::Vis => parser::PrefixEntryPoint::Vis,
|
||||||
"expr" => {
|
MetaVarKind::Expr => {
|
||||||
// `expr` should not match underscores.
|
// `expr` should not match underscores.
|
||||||
// HACK: Macro expansion should not be done using "rollback and try another alternative".
|
// HACK: Macro expansion should not be done using "rollback and try another alternative".
|
||||||
// rustc [explicitly checks the next token][0].
|
// rustc [explicitly checks the next token][0].
|
||||||
|
@ -698,17 +714,17 @@ fn match_meta_var(kind: &str, input: &mut TtIter<'_>) -> ExpandResult<Option<Fra
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
let tt_result = match kind {
|
let tt_result = match kind {
|
||||||
"ident" => input
|
MetaVarKind::Ident => input
|
||||||
.expect_ident()
|
.expect_ident()
|
||||||
.map(|ident| tt::Leaf::from(ident.clone()).into())
|
.map(|ident| tt::Leaf::from(ident.clone()).into())
|
||||||
.map_err(|()| ExpandError::binding_error("expected ident")),
|
.map_err(|()| ExpandError::binding_error("expected ident")),
|
||||||
"tt" => input
|
MetaVarKind::Tt => input
|
||||||
.expect_tt()
|
.expect_tt()
|
||||||
.map_err(|()| ExpandError::binding_error("expected token tree")),
|
.map_err(|()| ExpandError::binding_error("expected token tree")),
|
||||||
"lifetime" => input
|
MetaVarKind::Lifetime => input
|
||||||
.expect_lifetime()
|
.expect_lifetime()
|
||||||
.map_err(|()| ExpandError::binding_error("expected lifetime")),
|
.map_err(|()| ExpandError::binding_error("expected lifetime")),
|
||||||
"literal" => {
|
MetaVarKind::Literal => {
|
||||||
let neg = input.eat_char('-');
|
let neg = input.eat_char('-');
|
||||||
input
|
input
|
||||||
.expect_literal()
|
.expect_literal()
|
||||||
|
|
|
@ -6,7 +6,7 @@ use tt::{Delimiter, Subtree};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
expander::{Binding, Bindings, Fragment},
|
expander::{Binding, Bindings, Fragment},
|
||||||
parser::{Op, RepeatKind, Separator},
|
parser::{MetaVarKind, Op, RepeatKind, Separator},
|
||||||
ExpandError, ExpandResult, MetaTemplate,
|
ExpandError, ExpandResult, MetaTemplate,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ impl Bindings {
|
||||||
self.inner.contains_key(name)
|
self.inner.contains_key(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get(&self, name: &str, nesting: &mut [NestingState]) -> Result<&Fragment, ExpandError> {
|
fn get(&self, name: &str, nesting: &mut [NestingState]) -> Result<Fragment, ExpandError> {
|
||||||
macro_rules! binding_err {
|
macro_rules! binding_err {
|
||||||
($($arg:tt)*) => { ExpandError::binding_error(format!($($arg)*)) };
|
($($arg:tt)*) => { ExpandError::binding_error(format!($($arg)*)) };
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,7 @@ impl Bindings {
|
||||||
nesting_state.hit = true;
|
nesting_state.hit = true;
|
||||||
b = match b {
|
b = match b {
|
||||||
Binding::Fragment(_) => break,
|
Binding::Fragment(_) => break,
|
||||||
|
Binding::Missing(_) => break,
|
||||||
Binding::Nested(bs) => bs.get(nesting_state.idx).ok_or_else(|| {
|
Binding::Nested(bs) => bs.get(nesting_state.idx).ok_or_else(|| {
|
||||||
nesting_state.at_end = true;
|
nesting_state.at_end = true;
|
||||||
binding_err!("could not find nested binding `{name}`")
|
binding_err!("could not find nested binding `{name}`")
|
||||||
|
@ -37,7 +38,55 @@ impl Bindings {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
match b {
|
match b {
|
||||||
Binding::Fragment(it) => Ok(it),
|
Binding::Fragment(it) => Ok(it.clone()),
|
||||||
|
// emit some reasonable default expansion for missing bindings,
|
||||||
|
// this gives better recovery than emitting the `$fragment-name` verbatim
|
||||||
|
Binding::Missing(it) => Ok(match it {
|
||||||
|
MetaVarKind::Stmt => {
|
||||||
|
Fragment::Tokens(tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct {
|
||||||
|
id: tt::TokenId::unspecified(),
|
||||||
|
char: ';',
|
||||||
|
spacing: tt::Spacing::Alone,
|
||||||
|
})))
|
||||||
|
}
|
||||||
|
MetaVarKind::Block => Fragment::Tokens(tt::TokenTree::Subtree(tt::Subtree {
|
||||||
|
delimiter: Some(tt::Delimiter {
|
||||||
|
id: tt::TokenId::unspecified(),
|
||||||
|
kind: tt::DelimiterKind::Brace,
|
||||||
|
}),
|
||||||
|
token_trees: vec![],
|
||||||
|
})),
|
||||||
|
// FIXME: Meta and Item should get proper defaults
|
||||||
|
MetaVarKind::Meta | MetaVarKind::Item | MetaVarKind::Tt | MetaVarKind::Vis => {
|
||||||
|
Fragment::Tokens(tt::TokenTree::Subtree(tt::Subtree {
|
||||||
|
delimiter: None,
|
||||||
|
token_trees: vec![],
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
MetaVarKind::Path
|
||||||
|
| MetaVarKind::Ty
|
||||||
|
| MetaVarKind::Pat
|
||||||
|
| MetaVarKind::PatParam
|
||||||
|
| MetaVarKind::Expr
|
||||||
|
| MetaVarKind::Ident => {
|
||||||
|
Fragment::Tokens(tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident {
|
||||||
|
text: SmolStr::new_inline("missing"),
|
||||||
|
id: tt::TokenId::unspecified(),
|
||||||
|
})))
|
||||||
|
}
|
||||||
|
MetaVarKind::Lifetime => {
|
||||||
|
Fragment::Tokens(tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident {
|
||||||
|
text: SmolStr::new_inline("'missing"),
|
||||||
|
id: tt::TokenId::unspecified(),
|
||||||
|
})))
|
||||||
|
}
|
||||||
|
MetaVarKind::Literal => {
|
||||||
|
Fragment::Tokens(tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident {
|
||||||
|
text: SmolStr::new_inline("\"missing\""),
|
||||||
|
id: tt::TokenId::unspecified(),
|
||||||
|
})))
|
||||||
|
}
|
||||||
|
}),
|
||||||
Binding::Nested(_) => {
|
Binding::Nested(_) => {
|
||||||
Err(binding_err!("expected simple binding, found nested binding `{name}`"))
|
Err(binding_err!("expected simple binding, found nested binding `{name}`"))
|
||||||
}
|
}
|
||||||
|
@ -157,7 +206,7 @@ fn expand_var(ctx: &mut ExpandCtx<'_>, v: &SmolStr, id: tt::TokenId) -> ExpandRe
|
||||||
} else {
|
} else {
|
||||||
ctx.bindings.get(v, &mut ctx.nesting).map_or_else(
|
ctx.bindings.get(v, &mut ctx.nesting).map_or_else(
|
||||||
|e| ExpandResult { value: Fragment::Tokens(tt::TokenTree::empty()), err: Some(e) },
|
|e| ExpandResult { value: Fragment::Tokens(tt::TokenTree::empty()), err: Some(e) },
|
||||||
|b| ExpandResult::ok(b.clone()),
|
|it| ExpandResult::ok(it),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,7 @@ mod token_map;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
parser::{MetaTemplate, Op},
|
parser::{MetaTemplate, MetaVarKind, Op},
|
||||||
tt_iter::TtIter,
|
tt_iter::TtIter,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -291,9 +291,9 @@ fn validate(pattern: &MetaTemplate) -> Result<(), ParseError> {
|
||||||
// Checks that no repetition which could match an empty token
|
// Checks that no repetition which could match an empty token
|
||||||
// https://github.com/rust-lang/rust/blob/a58b1ed44f5e06976de2bdc4d7dc81c36a96934f/src/librustc_expand/mbe/macro_rules.rs#L558
|
// https://github.com/rust-lang/rust/blob/a58b1ed44f5e06976de2bdc4d7dc81c36a96934f/src/librustc_expand/mbe/macro_rules.rs#L558
|
||||||
let lsh_is_empty_seq = separator.is_none() && subtree.iter().all(|child_op| {
|
let lsh_is_empty_seq = separator.is_none() && subtree.iter().all(|child_op| {
|
||||||
match child_op {
|
match *child_op {
|
||||||
// vis is optional
|
// vis is optional
|
||||||
Op::Var { kind: Some(kind), .. } => kind == "vis",
|
Op::Var { kind: Some(kind), .. } => kind == MetaVarKind::Vis,
|
||||||
Op::Repeat {
|
Op::Repeat {
|
||||||
kind: parser::RepeatKind::ZeroOrMore | parser::RepeatKind::ZeroOrOne,
|
kind: parser::RepeatKind::ZeroOrMore | parser::RepeatKind::ZeroOrOne,
|
||||||
..
|
..
|
||||||
|
|
|
@ -50,7 +50,7 @@ impl MetaTemplate {
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
pub(crate) enum Op {
|
pub(crate) enum Op {
|
||||||
Var { name: SmolStr, kind: Option<SmolStr>, id: tt::TokenId },
|
Var { name: SmolStr, kind: Option<MetaVarKind>, id: tt::TokenId },
|
||||||
Ignore { name: SmolStr, id: tt::TokenId },
|
Ignore { name: SmolStr, id: tt::TokenId },
|
||||||
Index { depth: u32 },
|
Index { depth: u32 },
|
||||||
Repeat { tokens: MetaTemplate, kind: RepeatKind, separator: Option<Separator> },
|
Repeat { tokens: MetaTemplate, kind: RepeatKind, separator: Option<Separator> },
|
||||||
|
@ -65,6 +65,24 @@ pub(crate) enum RepeatKind {
|
||||||
ZeroOrOne,
|
ZeroOrOne,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub(crate) enum MetaVarKind {
|
||||||
|
Path,
|
||||||
|
Ty,
|
||||||
|
Pat,
|
||||||
|
PatParam,
|
||||||
|
Stmt,
|
||||||
|
Block,
|
||||||
|
Meta,
|
||||||
|
Item,
|
||||||
|
Vis,
|
||||||
|
Expr,
|
||||||
|
Ident,
|
||||||
|
Tt,
|
||||||
|
Lifetime,
|
||||||
|
Literal,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq)]
|
#[derive(Clone, Debug, Eq)]
|
||||||
pub(crate) enum Separator {
|
pub(crate) enum Separator {
|
||||||
Literal(tt::Literal),
|
Literal(tt::Literal),
|
||||||
|
@ -179,13 +197,30 @@ fn next_op<'a>(first: &tt::TokenTree, src: &mut TtIter<'a>, mode: Mode) -> Resul
|
||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eat_fragment_kind(src: &mut TtIter<'_>, mode: Mode) -> Result<Option<SmolStr>, ParseError> {
|
fn eat_fragment_kind(src: &mut TtIter<'_>, mode: Mode) -> Result<Option<MetaVarKind>, ParseError> {
|
||||||
if let Mode::Pattern = mode {
|
if let Mode::Pattern = mode {
|
||||||
src.expect_char(':').map_err(|()| ParseError::unexpected("missing fragment specifier"))?;
|
src.expect_char(':').map_err(|()| ParseError::unexpected("missing fragment specifier"))?;
|
||||||
let ident = src
|
let ident = src
|
||||||
.expect_ident()
|
.expect_ident()
|
||||||
.map_err(|()| ParseError::unexpected("missing fragment specifier"))?;
|
.map_err(|()| ParseError::unexpected("missing fragment specifier"))?;
|
||||||
return Ok(Some(ident.text.clone()));
|
let kind = match ident.text.as_str() {
|
||||||
|
"path" => MetaVarKind::Path,
|
||||||
|
"ty" => MetaVarKind::Ty,
|
||||||
|
"pat" => MetaVarKind::Pat,
|
||||||
|
"pat_param" => MetaVarKind::PatParam,
|
||||||
|
"stmt" => MetaVarKind::Stmt,
|
||||||
|
"block" => MetaVarKind::Block,
|
||||||
|
"meta" => MetaVarKind::Meta,
|
||||||
|
"item" => MetaVarKind::Item,
|
||||||
|
"vis" => MetaVarKind::Vis,
|
||||||
|
"expr" => MetaVarKind::Expr,
|
||||||
|
"ident" => MetaVarKind::Ident,
|
||||||
|
"tt" => MetaVarKind::Tt,
|
||||||
|
"lifetime" => MetaVarKind::Lifetime,
|
||||||
|
"literal" => MetaVarKind::Literal,
|
||||||
|
_ => return Ok(None),
|
||||||
|
};
|
||||||
|
return Ok(Some(kind));
|
||||||
};
|
};
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ object = { version = "0.29.0", default-features = false, features = [
|
||||||
] }
|
] }
|
||||||
serde = { version = "1.0.137", features = ["derive"] }
|
serde = { version = "1.0.137", features = ["derive"] }
|
||||||
serde_json = { version = "1.0.81", features = ["unbounded_depth"] }
|
serde_json = { version = "1.0.81", features = ["unbounded_depth"] }
|
||||||
tracing = "0.1.35"
|
tracing = "0.1.37"
|
||||||
memmap2 = "0.5.4"
|
memmap2 = "0.5.4"
|
||||||
snap = "1.0.5"
|
snap = "1.0.5"
|
||||||
|
|
||||||
|
|
|
@ -10,9 +10,9 @@ rust-version = "1.57"
|
||||||
doctest = false
|
doctest = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
once_cell = "1.12.0"
|
once_cell = "1.15.0"
|
||||||
cfg-if = "1.0.0"
|
cfg-if = "1.0.0"
|
||||||
libc = "0.2.126"
|
libc = "0.2.135"
|
||||||
la-arena = { version = "0.3.0", path = "../../lib/la-arena" }
|
la-arena = { version = "0.3.0", path = "../../lib/la-arena" }
|
||||||
countme = { version = "3.0.1", features = ["enable"] }
|
countme = { version = "3.0.1", features = ["enable"] }
|
||||||
jemalloc-ctl = { version = "0.5.0", package = "tikv-jemalloc-ctl", optional = true }
|
jemalloc-ctl = { version = "0.5.0", package = "tikv-jemalloc-ctl", optional = true }
|
||||||
|
|
|
@ -13,10 +13,10 @@ doctest = false
|
||||||
tracing = "0.1.35"
|
tracing = "0.1.35"
|
||||||
rustc-hash = "1.1.0"
|
rustc-hash = "1.1.0"
|
||||||
cargo_metadata = "0.15.0"
|
cargo_metadata = "0.15.0"
|
||||||
semver = "1.0.10"
|
semver = "1.0.14"
|
||||||
serde = { version = "1.0.137", features = ["derive"] }
|
serde = { version = "1.0.137", features = ["derive"] }
|
||||||
serde_json = "1.0.81"
|
serde_json = "1.0.86"
|
||||||
anyhow = "1.0.57"
|
anyhow = "1.0.62"
|
||||||
expect-test = "1.4.0"
|
expect-test = "1.4.0"
|
||||||
la-arena = { version = "0.3.0", path = "../../lib/la-arena" }
|
la-arena = { version = "0.3.0", path = "../../lib/la-arena" }
|
||||||
|
|
||||||
|
|
|
@ -154,6 +154,8 @@ impl WorkspaceBuildScripts {
|
||||||
Some(&it) => it,
|
Some(&it) => it,
|
||||||
None => return,
|
None => return,
|
||||||
};
|
};
|
||||||
|
progress(format!("running build-script: {}", workspace[package].name));
|
||||||
|
|
||||||
let cfgs = {
|
let cfgs = {
|
||||||
let mut acc = Vec::new();
|
let mut acc = Vec::new();
|
||||||
for cfg in message.cfgs {
|
for cfg in message.cfgs {
|
||||||
|
@ -189,7 +191,7 @@ impl WorkspaceBuildScripts {
|
||||||
None => return,
|
None => return,
|
||||||
};
|
};
|
||||||
|
|
||||||
progress(format!("metadata {}", message.target.name));
|
progress(format!("building proc-macros: {}", message.target.name));
|
||||||
|
|
||||||
if message.target.kind.iter().any(|k| k == "proc-macro") {
|
if message.target.kind.iter().any(|k| k == "proc-macro") {
|
||||||
// Skip rmeta file
|
// Skip rmeta file
|
||||||
|
|
|
@ -18,10 +18,10 @@ name = "rust-analyzer"
|
||||||
path = "src/bin/main.rs"
|
path = "src/bin/main.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0.57"
|
anyhow = "1.0.62"
|
||||||
crossbeam-channel = "0.5.5"
|
crossbeam-channel = "0.5.5"
|
||||||
dissimilar = "1.0.4"
|
dissimilar = "1.0.4"
|
||||||
itertools = "0.10.3"
|
itertools = "0.10.5"
|
||||||
scip = "0.1.1"
|
scip = "0.1.1"
|
||||||
lsp-types = { version = "0.93.1", features = ["proposed"] }
|
lsp-types = { version = "0.93.1", features = ["proposed"] }
|
||||||
parking_lot = "0.12.1"
|
parking_lot = "0.12.1"
|
||||||
|
@ -33,10 +33,10 @@ serde_json = { version = "1.0.81", features = ["preserve_order"] }
|
||||||
threadpool = "1.8.1"
|
threadpool = "1.8.1"
|
||||||
rayon = "1.5.3"
|
rayon = "1.5.3"
|
||||||
num_cpus = "1.13.1"
|
num_cpus = "1.13.1"
|
||||||
mimalloc = { version = "0.1.29", default-features = false, optional = true }
|
mimalloc = { version = "0.1.30", default-features = false, optional = true }
|
||||||
lsp-server = { version = "0.7.0", path = "../../lib/lsp-server" }
|
lsp-server = { version = "0.7.0", path = "../../lib/lsp-server" }
|
||||||
tracing = "0.1.35"
|
tracing = "0.1.35"
|
||||||
tracing-subscriber = { version = "0.3.14", default-features = false, features = [
|
tracing-subscriber = { version = "0.3.16", default-features = false, features = [
|
||||||
"env-filter",
|
"env-filter",
|
||||||
"registry",
|
"registry",
|
||||||
"fmt",
|
"fmt",
|
||||||
|
|
|
@ -177,6 +177,7 @@ fn check_licenses() {
|
||||||
let sh = &Shell::new().unwrap();
|
let sh = &Shell::new().unwrap();
|
||||||
|
|
||||||
let expected = "
|
let expected = "
|
||||||
|
(MIT OR Apache-2.0) AND Unicode-DFS-2016
|
||||||
0BSD OR MIT OR Apache-2.0
|
0BSD OR MIT OR Apache-2.0
|
||||||
Apache-2.0
|
Apache-2.0
|
||||||
Apache-2.0 OR BSL-1.0
|
Apache-2.0 OR BSL-1.0
|
||||||
|
|
|
@ -10,7 +10,7 @@ rust-version = "1.57"
|
||||||
doctest = false
|
doctest = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
libc = "0.2.126"
|
libc = "0.2.135"
|
||||||
backtrace = { version = "0.3.65", optional = true }
|
backtrace = { version = "0.3.65", optional = true }
|
||||||
always-assert = { version = "0.1.2", features = ["log"] }
|
always-assert = { version = "0.1.2", features = ["log"] }
|
||||||
# Think twice before adding anything here
|
# Think twice before adding anything here
|
||||||
|
|
|
@ -12,11 +12,11 @@ doctest = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
cov-mark = "2.0.0-pre.1"
|
cov-mark = "2.0.0-pre.1"
|
||||||
itertools = "0.10.3"
|
itertools = "0.10.5"
|
||||||
rowan = "0.15.8"
|
rowan = "0.15.10"
|
||||||
rustc_lexer = { version = "725.0.0", package = "rustc-ap-rustc_lexer" }
|
rustc_lexer = { version = "725.0.0", package = "rustc-ap-rustc_lexer" }
|
||||||
rustc-hash = "1.1.0"
|
rustc-hash = "1.1.0"
|
||||||
once_cell = "1.12.0"
|
once_cell = "1.15.0"
|
||||||
indexmap = "1.9.1"
|
indexmap = "1.9.1"
|
||||||
smol_str = "0.1.23"
|
smol_str = "0.1.23"
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ profile = { path = "../profile", version = "0.0.0" }
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
rayon = "1.5.3"
|
rayon = "1.5.3"
|
||||||
expect-test = "1.4.0"
|
expect-test = "1.4.0"
|
||||||
proc-macro2 = "1.0.39"
|
proc-macro2 = "1.0.47"
|
||||||
quote = "1.0.20"
|
quote = "1.0.20"
|
||||||
ungrammar = "1.16.1"
|
ungrammar = "1.16.1"
|
||||||
|
|
||||||
|
|
|
@ -235,6 +235,24 @@ impl ast::GenericParamList {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Constructs a matching [`ast::GenericArgList`]
|
||||||
|
pub fn to_generic_args(&self) -> ast::GenericArgList {
|
||||||
|
let args = self.generic_params().filter_map(|param| match param {
|
||||||
|
ast::GenericParam::LifetimeParam(it) => {
|
||||||
|
Some(ast::GenericArg::LifetimeArg(make::lifetime_arg(it.lifetime()?)))
|
||||||
|
}
|
||||||
|
ast::GenericParam::TypeParam(it) => {
|
||||||
|
Some(ast::GenericArg::TypeArg(make::type_arg(make::ext::ty_name(it.name()?))))
|
||||||
|
}
|
||||||
|
ast::GenericParam::ConstParam(it) => {
|
||||||
|
// Name-only const params get parsed as `TypeArg`s
|
||||||
|
Some(ast::GenericArg::TypeArg(make::type_arg(make::ext::ty_name(it.name()?))))
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
make::generic_arg_list(args)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ast::WhereClause {
|
impl ast::WhereClause {
|
||||||
|
@ -248,6 +266,42 @@ impl ast::WhereClause {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ast::TypeParam {
|
||||||
|
pub fn remove_default(&self) {
|
||||||
|
if let Some((eq, last)) = self
|
||||||
|
.syntax()
|
||||||
|
.children_with_tokens()
|
||||||
|
.find(|it| it.kind() == T![=])
|
||||||
|
.zip(self.syntax().last_child_or_token())
|
||||||
|
{
|
||||||
|
ted::remove_all(eq..=last);
|
||||||
|
|
||||||
|
// remove any trailing ws
|
||||||
|
if let Some(last) = self.syntax().last_token().filter(|it| it.kind() == WHITESPACE) {
|
||||||
|
last.detach();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ast::ConstParam {
|
||||||
|
pub fn remove_default(&self) {
|
||||||
|
if let Some((eq, last)) = self
|
||||||
|
.syntax()
|
||||||
|
.children_with_tokens()
|
||||||
|
.find(|it| it.kind() == T![=])
|
||||||
|
.zip(self.syntax().last_child_or_token())
|
||||||
|
{
|
||||||
|
ted::remove_all(eq..=last);
|
||||||
|
|
||||||
|
// remove any trailing ws
|
||||||
|
if let Some(last) = self.syntax().last_token().filter(|it| it.kind() == WHITESPACE) {
|
||||||
|
last.detach();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub trait Removable: AstNode {
|
pub trait Removable: AstNode {
|
||||||
fn remove(&self);
|
fn remove(&self);
|
||||||
}
|
}
|
||||||
|
@ -264,7 +318,7 @@ impl Removable for ast::TypeBoundList {
|
||||||
impl ast::PathSegment {
|
impl ast::PathSegment {
|
||||||
pub fn get_or_create_generic_arg_list(&self) -> ast::GenericArgList {
|
pub fn get_or_create_generic_arg_list(&self) -> ast::GenericArgList {
|
||||||
if self.generic_arg_list().is_none() {
|
if self.generic_arg_list().is_none() {
|
||||||
let arg_list = make::generic_arg_list().clone_for_update();
|
let arg_list = make::generic_arg_list(empty()).clone_for_update();
|
||||||
ted::append_child(self.syntax(), arg_list.syntax());
|
ted::append_child(self.syntax(), arg_list.syntax());
|
||||||
}
|
}
|
||||||
self.generic_arg_list().unwrap()
|
self.generic_arg_list().unwrap()
|
||||||
|
|
|
@ -88,6 +88,9 @@ pub mod ext {
|
||||||
block_expr(None, None)
|
block_expr(None, None)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn ty_name(name: ast::Name) -> ast::Type {
|
||||||
|
ty_path(ident_path(&name.to_string()))
|
||||||
|
}
|
||||||
pub fn ty_bool() -> ast::Type {
|
pub fn ty_bool() -> ast::Type {
|
||||||
ty_path(ident_path("bool"))
|
ty_path(ident_path("bool"))
|
||||||
}
|
}
|
||||||
|
@ -160,6 +163,7 @@ pub fn assoc_item_list() -> ast::AssocItemList {
|
||||||
ast_from_text("impl C for D {}")
|
ast_from_text("impl C for D {}")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: `ty_params` should be `ast::GenericArgList`
|
||||||
pub fn impl_(
|
pub fn impl_(
|
||||||
ty: ast::Path,
|
ty: ast::Path,
|
||||||
params: Option<ast::GenericParamList>,
|
params: Option<ast::GenericParamList>,
|
||||||
|
@ -185,10 +189,6 @@ pub fn impl_trait(
|
||||||
ast_from_text(&format!("impl{ty_params} {trait_} for {ty}{ty_params} {{}}"))
|
ast_from_text(&format!("impl{ty_params} {trait_} for {ty}{ty_params} {{}}"))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn generic_arg_list() -> ast::GenericArgList {
|
|
||||||
ast_from_text("const S: T<> = ();")
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn path_segment(name_ref: ast::NameRef) -> ast::PathSegment {
|
pub fn path_segment(name_ref: ast::NameRef) -> ast::PathSegment {
|
||||||
ast_from_text(&format!("type __ = {name_ref};"))
|
ast_from_text(&format!("type __ = {name_ref};"))
|
||||||
}
|
}
|
||||||
|
@ -718,6 +718,21 @@ pub fn generic_param_list(
|
||||||
ast_from_text(&format!("fn f<{args}>() {{ }}"))
|
ast_from_text(&format!("fn f<{args}>() {{ }}"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn type_arg(ty: ast::Type) -> ast::TypeArg {
|
||||||
|
ast_from_text(&format!("const S: T<{ty}> = ();"))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lifetime_arg(lifetime: ast::Lifetime) -> ast::LifetimeArg {
|
||||||
|
ast_from_text(&format!("const S: T<{lifetime}> = ();"))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn generic_arg_list(
|
||||||
|
args: impl IntoIterator<Item = ast::GenericArg>,
|
||||||
|
) -> ast::GenericArgList {
|
||||||
|
let args = args.into_iter().join(", ");
|
||||||
|
ast_from_text(&format!("const S: T<{args}> = ();"))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn visibility_pub_crate() -> ast::Visibility {
|
pub fn visibility_pub_crate() -> ast::Visibility {
|
||||||
ast_from_text("pub(crate) struct S")
|
ast_from_text("pub(crate) struct S")
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,6 +61,8 @@
|
||||||
//! "
|
//! "
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
|
use std::iter;
|
||||||
|
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
use stdx::trim_indent;
|
use stdx::trim_indent;
|
||||||
|
|
||||||
|
@ -259,7 +261,7 @@ impl MiniCore {
|
||||||
if res.has_flag(entry) {
|
if res.has_flag(entry) {
|
||||||
panic!("duplicate minicore flag: {:?}", entry);
|
panic!("duplicate minicore flag: {:?}", entry);
|
||||||
}
|
}
|
||||||
res.activated_flags.push(entry.to_string());
|
res.activated_flags.push(entry.to_owned());
|
||||||
}
|
}
|
||||||
|
|
||||||
res
|
res
|
||||||
|
@ -273,35 +275,34 @@ impl MiniCore {
|
||||||
let raw_mini_core = include_str!("./minicore.rs");
|
let raw_mini_core = include_str!("./minicore.rs");
|
||||||
let mut lines = raw_mini_core.split_inclusive('\n');
|
let mut lines = raw_mini_core.split_inclusive('\n');
|
||||||
|
|
||||||
let mut parsing_flags = false;
|
|
||||||
let mut implications = Vec::new();
|
let mut implications = Vec::new();
|
||||||
|
|
||||||
// Parse `//!` preamble and extract flags and dependencies.
|
// Parse `//!` preamble and extract flags and dependencies.
|
||||||
for line in lines.by_ref() {
|
let trim_doc: fn(&str) -> Option<&str> = |line| match line.strip_prefix("//!") {
|
||||||
let line = match line.strip_prefix("//!") {
|
Some(it) => Some(it),
|
||||||
Some(it) => it,
|
|
||||||
None => {
|
None => {
|
||||||
assert!(line.trim().is_empty());
|
assert!(line.trim().is_empty(), "expected empty line after minicore header");
|
||||||
break;
|
None
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
for line in lines
|
||||||
if parsing_flags {
|
.by_ref()
|
||||||
|
.map_while(trim_doc)
|
||||||
|
.skip_while(|line| !line.contains("Available flags:"))
|
||||||
|
.skip(1)
|
||||||
|
{
|
||||||
let (flag, deps) = line.split_once(':').unwrap();
|
let (flag, deps) = line.split_once(':').unwrap();
|
||||||
let flag = flag.trim();
|
let flag = flag.trim();
|
||||||
|
|
||||||
self.valid_flags.push(flag.to_string());
|
self.valid_flags.push(flag.to_string());
|
||||||
for dep in deps.split(", ") {
|
implications.extend(
|
||||||
let dep = dep.trim();
|
iter::repeat(flag)
|
||||||
if !dep.is_empty() {
|
.zip(deps.split(", ").map(str::trim).filter(|dep| !dep.is_empty())),
|
||||||
self.assert_valid_flag(dep);
|
);
|
||||||
implications.push((flag, dep));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if line.contains("Available flags:") {
|
for (_, dep) in &implications {
|
||||||
parsing_flags = true;
|
self.assert_valid_flag(dep);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for flag in &self.activated_flags {
|
for flag in &self.activated_flags {
|
||||||
|
@ -332,7 +333,7 @@ impl MiniCore {
|
||||||
}
|
}
|
||||||
if let Some(region) = trimmed.strip_prefix("// endregion:") {
|
if let Some(region) = trimmed.strip_prefix("// endregion:") {
|
||||||
let prev = active_regions.pop().unwrap();
|
let prev = active_regions.pop().unwrap();
|
||||||
assert_eq!(prev, region);
|
assert_eq!(prev, region, "unbalanced region pairs");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,36 +8,37 @@
|
||||||
//! We then strip all the code marked with other flags.
|
//! We then strip all the code marked with other flags.
|
||||||
//!
|
//!
|
||||||
//! Available flags:
|
//! Available flags:
|
||||||
//! sized:
|
|
||||||
//! unsize: sized
|
|
||||||
//! coerce_unsized: unsize
|
|
||||||
//! slice:
|
|
||||||
//! range:
|
|
||||||
//! deref: sized
|
|
||||||
//! deref_mut: deref
|
|
||||||
//! index: sized
|
|
||||||
//! fn:
|
|
||||||
//! try:
|
|
||||||
//! pin:
|
|
||||||
//! future: pin
|
|
||||||
//! option:
|
|
||||||
//! result:
|
|
||||||
//! iterator: option
|
|
||||||
//! iterators: iterator, fn
|
|
||||||
//! default: sized
|
|
||||||
//! hash:
|
|
||||||
//! clone: sized
|
|
||||||
//! copy: clone
|
|
||||||
//! from: sized
|
|
||||||
//! eq: sized
|
|
||||||
//! ord: eq, option
|
|
||||||
//! derive:
|
|
||||||
//! fmt: result
|
|
||||||
//! bool_impl: option, fn
|
|
||||||
//! add:
|
//! add:
|
||||||
//! as_ref: sized
|
//! as_ref: sized
|
||||||
|
//! bool_impl: option, fn
|
||||||
|
//! clone: sized
|
||||||
|
//! coerce_unsized: unsize
|
||||||
|
//! copy: clone
|
||||||
|
//! default: sized
|
||||||
|
//! deref_mut: deref
|
||||||
|
//! deref: sized
|
||||||
|
//! derive:
|
||||||
//! drop:
|
//! drop:
|
||||||
|
//! eq: sized
|
||||||
|
//! fmt: result
|
||||||
|
//! fn:
|
||||||
|
//! from: sized
|
||||||
|
//! future: pin
|
||||||
//! generator: pin
|
//! generator: pin
|
||||||
|
//! hash:
|
||||||
|
//! index: sized
|
||||||
|
//! infallible:
|
||||||
|
//! iterator: option
|
||||||
|
//! iterators: iterator, fn
|
||||||
|
//! option:
|
||||||
|
//! ord: eq, option
|
||||||
|
//! pin:
|
||||||
|
//! range:
|
||||||
|
//! result:
|
||||||
|
//! sized:
|
||||||
|
//! slice:
|
||||||
|
//! try: infallible
|
||||||
|
//! unsize: sized
|
||||||
|
|
||||||
pub mod marker {
|
pub mod marker {
|
||||||
// region:sized
|
// region:sized
|
||||||
|
@ -150,6 +151,9 @@ pub mod convert {
|
||||||
fn as_ref(&self) -> &T;
|
fn as_ref(&self) -> &T;
|
||||||
}
|
}
|
||||||
// endregion:as_ref
|
// endregion:as_ref
|
||||||
|
// region:infallible
|
||||||
|
pub enum Infallible {}
|
||||||
|
// endregion:infallible
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod ops {
|
pub mod ops {
|
||||||
|
@ -326,7 +330,7 @@ pub mod ops {
|
||||||
Continue(C),
|
Continue(C),
|
||||||
Break(B),
|
Break(B),
|
||||||
}
|
}
|
||||||
pub trait FromResidual<R = Self::Residual> {
|
pub trait FromResidual<R = <Self as Try>::Residual> {
|
||||||
#[lang = "from_residual"]
|
#[lang = "from_residual"]
|
||||||
fn from_residual(residual: R) -> Self;
|
fn from_residual(residual: R) -> Self;
|
||||||
}
|
}
|
||||||
|
@ -342,13 +346,13 @@ pub mod ops {
|
||||||
|
|
||||||
impl<B, C> Try for ControlFlow<B, C> {
|
impl<B, C> Try for ControlFlow<B, C> {
|
||||||
type Output = C;
|
type Output = C;
|
||||||
type Residual = ControlFlow<B, convert::Infallible>;
|
type Residual = ControlFlow<B, crate::convert::Infallible>;
|
||||||
fn from_output(output: Self::Output) -> Self {}
|
fn from_output(output: Self::Output) -> Self {}
|
||||||
fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {}
|
fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<B, C> FromResidual for ControlFlow<B, C> {
|
impl<B, C> FromResidual for ControlFlow<B, C> {
|
||||||
fn from_residual(residual: ControlFlow<B, convert::Infallible>) -> Self {}
|
fn from_residual(residual: ControlFlow<B, crate::convert::Infallible>) -> Self {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub use self::try_::{ControlFlow, FromResidual, Try};
|
pub use self::try_::{ControlFlow, FromResidual, Try};
|
||||||
|
@ -469,6 +473,33 @@ pub mod option {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// region:try
|
||||||
|
impl<T> crate::ops::Try for Option<T> {
|
||||||
|
type Output = T;
|
||||||
|
type Residual = Option<crate::convert::Infallible>;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn from_output(output: Self::Output) -> Self {
|
||||||
|
Some(output)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn branch(self) -> crate::ops::ControlFlow<Self::Residual, Self::Output> {
|
||||||
|
match self {
|
||||||
|
Some(v) => crate::ops::ControlFlow::Continue(v),
|
||||||
|
None => crate::ops::ControlFlow::Break(None),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<T> crate::ops::FromResidual for Option<T> {
|
||||||
|
#[inline]
|
||||||
|
fn from_residual(residual: Option<crate::convert::Infallible>) -> Self {
|
||||||
|
match residual {
|
||||||
|
None => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// endregion:try
|
||||||
}
|
}
|
||||||
// endregion:option
|
// endregion:option
|
||||||
|
|
||||||
|
@ -584,7 +615,7 @@ pub mod iter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub use self::adapters::{Take, FilterMap};
|
pub use self::adapters::{FilterMap, Take};
|
||||||
|
|
||||||
mod sources {
|
mod sources {
|
||||||
mod repeat {
|
mod repeat {
|
||||||
|
|
|
@ -10,5 +10,5 @@ rust-version = "1.57"
|
||||||
doctest = false
|
doctest = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
itertools = "0.10.3"
|
itertools = "0.10.5"
|
||||||
text-size = "1.1.0"
|
text-size = "1.1.0"
|
||||||
|
|
|
@ -10,4 +10,4 @@ rust-version = "1.57"
|
||||||
doctest = false
|
doctest = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
home = "0.5.3"
|
home = "0.5.4"
|
||||||
|
|
|
@ -14,7 +14,7 @@ tracing = "0.1.35"
|
||||||
jod-thread = "0.1.2"
|
jod-thread = "0.1.2"
|
||||||
walkdir = "2.3.2"
|
walkdir = "2.3.2"
|
||||||
crossbeam-channel = "0.5.5"
|
crossbeam-channel = "0.5.5"
|
||||||
notify = "=5.0.0-pre.16"
|
notify = "5.0"
|
||||||
|
|
||||||
vfs = { path = "../vfs", version = "0.0.0" }
|
vfs = { path = "../vfs", version = "0.0.0" }
|
||||||
paths = { path = "../paths", version = "0.0.0" }
|
paths = { path = "../paths", version = "0.0.0" }
|
||||||
|
|
|
@ -88,9 +88,8 @@ is lower than Cargo's model of packages: each Cargo package consists of several
|
||||||
targets, each of which is a separate crate (or several crates, if you try
|
targets, each of which is a separate crate (or several crates, if you try
|
||||||
different feature combinations).
|
different feature combinations).
|
||||||
|
|
||||||
Procedural macros should become inputs as well, but currently they are not
|
Procedural macros are inputs as well, roughly modeled as a crate with a bunch of
|
||||||
supported. Procedural macro will be a black box `Box<dyn Fn(TokenStream) -> TokenStream>`
|
additional black box `dyn Fn(TokenStream) -> TokenStream` functions.
|
||||||
function, and will be inserted into the crate graph just like dependencies.
|
|
||||||
|
|
||||||
Soon we'll talk how we build an LSP server on top of `Analysis`, but first,
|
Soon we'll talk how we build an LSP server on top of `Analysis`, but first,
|
||||||
let's deal with that paths issue.
|
let's deal with that paths issue.
|
||||||
|
|
|
@ -8,10 +8,10 @@ This guide describes the current state of syntax trees and parsing in rust-analy
|
||||||
|
|
||||||
The things described are implemented in three places
|
The things described are implemented in three places
|
||||||
|
|
||||||
* [rowan](https://github.com/rust-analyzer/rowan/tree/v0.9.0) -- a generic library for rowan syntax trees.
|
* [rowan](https://github.com/rust-analyzer/rowan/tree/v0.15.10) -- a generic library for rowan syntax trees.
|
||||||
* [ra_syntax](https://github.com/rust-lang/rust-analyzer/tree/cf5bdf464cad7ceb9a67e07985a3f4d3799ec0b6/crates/ra_syntax) crate inside rust-analyzer which wraps `rowan` into rust-analyzer specific API.
|
* [syntax](https://github.com/rust-lang/rust-analyzer/tree/36a70b7435c48837018c71576d7bb4e8f763f501/crates/syntax) crate inside rust-analyzer which wraps `rowan` into rust-analyzer specific API.
|
||||||
Nothing in rust-analyzer except this crate knows about `rowan`.
|
Nothing in rust-analyzer except this crate knows about `rowan`.
|
||||||
* [parser](https://github.com/rust-lang/rust-analyzer/tree/cf5bdf464cad7ceb9a67e07985a3f4d3799ec0b6/crates/parser) crate parses input tokens into an `ra_syntax` tree
|
* [parser](https://github.com/rust-lang/rust-analyzer/tree/36a70b7435c48837018c71576d7bb4e8f763f501/crates/parser) crate parses input tokens into a `syntax` tree
|
||||||
|
|
||||||
## Design Goals
|
## Design Goals
|
||||||
|
|
||||||
|
|
|
@ -174,14 +174,25 @@ On Unix, running the editor from a shell or changing the `.desktop` file to set
|
||||||
|
|
||||||
==== `rustup`
|
==== `rustup`
|
||||||
|
|
||||||
`rust-analyzer` is available in `rustup`, but only in the nightly toolchain:
|
`rust-analyzer` is available in `rustup`:
|
||||||
|
|
||||||
[source,bash]
|
[source,bash]
|
||||||
----
|
----
|
||||||
$ rustup +nightly component add rust-analyzer-preview
|
$ rustup component add rust-analyzer
|
||||||
----
|
----
|
||||||
|
|
||||||
However, in contrast to `component add clippy` or `component add rustfmt`, this does not actually place a `rust-analyzer` binary in `~/.cargo/bin`, see https://github.com/rust-lang/rustup/issues/2411[this issue].
|
However, in contrast to `component add clippy` or `component add rustfmt`, this does not actually place a `rust-analyzer` binary in `~/.cargo/bin`, see https://github.com/rust-lang/rustup/issues/2411[this issue]. You can find the path to the binary using:
|
||||||
|
[source,bash]
|
||||||
|
----
|
||||||
|
$ rustup which --toolchain stable rust-analyzer
|
||||||
|
----
|
||||||
|
You can link to there from `~/.cargo/bin` or configure your editor to use the full path.
|
||||||
|
|
||||||
|
Alternatively you might be able to configure your editor to start `rust-analyzer` using the command:
|
||||||
|
[source,bash]
|
||||||
|
----
|
||||||
|
$ rustup run stable rust-analyzer
|
||||||
|
----
|
||||||
|
|
||||||
==== Arch Linux
|
==== Arch Linux
|
||||||
|
|
||||||
|
|
|
@ -133,7 +133,21 @@ export class Config {
|
||||||
}
|
}
|
||||||
|
|
||||||
get runnableEnv() {
|
get runnableEnv() {
|
||||||
return this.get<RunnableEnvCfg>("runnableEnv");
|
const item = this.get<any>("runnableEnv");
|
||||||
|
if (!item) return item;
|
||||||
|
const fixRecord = (r: Record<string, any>) => {
|
||||||
|
for (const key in r) {
|
||||||
|
if (typeof r[key] !== "string") {
|
||||||
|
r[key] = String(r[key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (item instanceof Array) {
|
||||||
|
item.forEach((x) => fixRecord(x.env));
|
||||||
|
} else {
|
||||||
|
fixRecord(item);
|
||||||
|
}
|
||||||
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
get restartServerOnConfigChange() {
|
get restartServerOnConfigChange() {
|
||||||
|
|
|
@ -8,7 +8,7 @@ edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
log = "0.4.17"
|
log = "0.4.17"
|
||||||
serde_json = "1.0.85"
|
serde_json = "1.0.86"
|
||||||
serde = { version = "1.0.144", features = ["derive"] }
|
serde = { version = "1.0.144", features = ["derive"] }
|
||||||
crossbeam-channel = "0.5.6"
|
crossbeam-channel = "0.5.6"
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ edition = "2021"
|
||||||
rust-version = "1.57"
|
rust-version = "1.57"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0.57"
|
anyhow = "1.0.62"
|
||||||
flate2 = "1.0.24"
|
flate2 = "1.0.24"
|
||||||
write-json = "0.1.2"
|
write-json = "0.1.2"
|
||||||
xshell = "0.2.2"
|
xshell = "0.2.2"
|
||||||
|
|
Loading…
Reference in a new issue