mirror of
https://github.com/rust-lang/rust.git
synced 2025-05-14 02:49:40 +00:00
Rollup merge of #138305 - lnicola:sync-from-ra, r=lnicola
Subtree update of `rust-analyzer` r? `@ghost`
This commit is contained in:
commit
2abeceeb97
@ -15,3 +15,6 @@ indent_size = 2
|
||||
|
||||
[*.{yml,yaml}]
|
||||
indent_size = 2
|
||||
|
||||
[COMMIT_EDITMSG]
|
||||
max_line_length = unset
|
||||
|
@ -35,19 +35,48 @@ jobs:
|
||||
typescript:
|
||||
- 'editors/code/**'
|
||||
proc_macros:
|
||||
- 'crates/tt/**'
|
||||
- 'crates/proc-macro-api/**'
|
||||
- 'crates/proc-macro-srv/**'
|
||||
- 'crates/proc-macro-srv-cli/**'
|
||||
|
||||
rust:
|
||||
proc-macro-srv:
|
||||
needs: changes
|
||||
if: github.repository == 'rust-lang/rust-analyzer' && needs.changes.outputs.proc_macros == 'true'
|
||||
name: proc-macro-srv
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
|
||||
- name: Install Rust toolchain
|
||||
run: |
|
||||
rustup update --no-self-update nightly
|
||||
rustup default nightly
|
||||
rustup component add --toolchain nightly rust-src rustfmt
|
||||
# https://github.com/actions-rust-lang/setup-rust-toolchain/blob/main/rust.json
|
||||
- name: Install Rust Problem Matcher
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: echo "::add-matcher::.github/rust.json"
|
||||
|
||||
- name: Cache Dependencies
|
||||
uses: Swatinem/rust-cache@9bdad043e88c75890e36ad3bbc8d27f0090dd609
|
||||
|
||||
- name: Bump opt-level
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: sed -i '/\[profile.dev]/a opt-level=1' Cargo.toml
|
||||
|
||||
- name: Test
|
||||
run: cargo test --features sysroot-abi -p rust-analyzer -p proc-macro-srv -p proc-macro-srv-cli -p proc-macro-api -- --quiet
|
||||
|
||||
rust:
|
||||
if: github.repository == 'rust-lang/rust-analyzer'
|
||||
name: Rust
|
||||
runs-on: ${{ matrix.os }}
|
||||
env:
|
||||
CC: deny_c
|
||||
RUST_CHANNEL: "${{ needs.changes.outputs.proc_macros == 'true' && 'nightly' || 'stable' }}"
|
||||
USE_SYSROOT_ABI: "${{ needs.changes.outputs.proc_macros == 'true' && '--features sysroot-abi' || '' }}"
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@ -62,13 +91,12 @@ jobs:
|
||||
|
||||
- name: Install Rust toolchain
|
||||
run: |
|
||||
rustup update --no-self-update ${{ env.RUST_CHANNEL }}
|
||||
rustup default ${{ env.RUST_CHANNEL }}
|
||||
rustup component add --toolchain ${{ env.RUST_CHANNEL }} rust-src
|
||||
rustup update --no-self-update stable
|
||||
rustup default stable
|
||||
rustup component add --toolchain stable rust-src
|
||||
# We always use a nightly rustfmt, regardless of channel, because we need
|
||||
# --file-lines.
|
||||
rustup toolchain add nightly --profile minimal
|
||||
rustup component add --toolchain nightly rustfmt
|
||||
rustup toolchain install nightly --profile minimal --component rustfmt
|
||||
# https://github.com/actions-rust-lang/setup-rust-toolchain/blob/main/rust.json
|
||||
- name: Install Rust Problem Matcher
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
@ -76,8 +104,6 @@ jobs:
|
||||
|
||||
- name: Cache Dependencies
|
||||
uses: Swatinem/rust-cache@9bdad043e88c75890e36ad3bbc8d27f0090dd609
|
||||
with:
|
||||
key: ${{ env.RUST_CHANNEL }}
|
||||
|
||||
- name: Bump opt-level
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
@ -87,16 +113,16 @@ jobs:
|
||||
run: cargo codegen --check
|
||||
|
||||
- name: Compile (tests)
|
||||
run: cargo test --no-run --locked ${{ env.USE_SYSROOT_ABI }}
|
||||
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 ${{ env.USE_SYSROOT_ABI }}
|
||||
run: cargo build --quiet
|
||||
|
||||
- name: Test
|
||||
if: matrix.os == 'ubuntu-latest' || matrix.os == 'windows-latest' || github.event_name == 'push'
|
||||
run: cargo test ${{ env.USE_SYSROOT_ABI }} -- --nocapture --quiet
|
||||
run: cargo test -- --quiet
|
||||
|
||||
- name: Switch to stable toolchain
|
||||
run: |
|
||||
@ -157,7 +183,7 @@ jobs:
|
||||
|
||||
typescript:
|
||||
needs: changes
|
||||
if: github.repository == 'rust-lang/rust-analyzer'
|
||||
if: github.repository == 'rust-lang/rust-analyzer' && needs.changes.outputs.typescript == 'true'
|
||||
name: TypeScript
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@ -169,21 +195,18 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
if: needs.changes.outputs.typescript == 'true'
|
||||
|
||||
- name: Install Nodejs
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22
|
||||
if: needs.changes.outputs.typescript == 'true'
|
||||
|
||||
- name: Install xvfb
|
||||
if: matrix.os == 'ubuntu-latest' && needs.changes.outputs.typescript == 'true'
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: sudo apt-get install -y xvfb
|
||||
|
||||
- run: npm ci
|
||||
working-directory: ./editors/code
|
||||
if: needs.changes.outputs.typescript == 'true'
|
||||
|
||||
# - run: npm audit || { sleep 10 && npm audit; } || { sleep 30 && npm audit; }
|
||||
# if: runner.os == 'Linux'
|
||||
@ -192,27 +215,24 @@ jobs:
|
||||
# If this steps fails, your code's type integrity might be wrong at some places at TypeScript level.
|
||||
- run: npm run typecheck
|
||||
working-directory: ./editors/code
|
||||
if: needs.changes.outputs.typescript == 'true'
|
||||
|
||||
# You may fix the code automatically by running `npm run lint:fix` if this steps fails.
|
||||
- run: npm run lint
|
||||
working-directory: ./editors/code
|
||||
if: needs.changes.outputs.typescript == 'true'
|
||||
|
||||
# To fix this steps, please run `npm run format`.
|
||||
- run: npm run format:check
|
||||
working-directory: ./editors/code
|
||||
if: needs.changes.outputs.typescript == 'true'
|
||||
|
||||
- name: Run VS Code tests (Linux)
|
||||
if: matrix.os == 'ubuntu-latest' && needs.changes.outputs.typescript == 'true'
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
env:
|
||||
VSCODE_CLI: 1
|
||||
run: xvfb-run npm test
|
||||
working-directory: ./editors/code
|
||||
|
||||
- name: Run VS Code tests (Windows)
|
||||
if: matrix.os == 'windows-latest' && needs.changes.outputs.typescript == 'true'
|
||||
if: matrix.os == 'windows-latest'
|
||||
env:
|
||||
VSCODE_CLI: 1
|
||||
run: npm test
|
||||
@ -220,7 +240,6 @@ jobs:
|
||||
|
||||
- run: npm run package --scripts-prepend-node-path
|
||||
working-directory: ./editors/code
|
||||
if: needs.changes.outputs.typescript == 'true'
|
||||
|
||||
typo-check:
|
||||
name: Typo Check
|
||||
@ -242,7 +261,7 @@ jobs:
|
||||
run: typos
|
||||
|
||||
conclusion:
|
||||
needs: [rust, rust-cross, typescript, typo-check]
|
||||
needs: [rust, rust-cross, typescript, typo-check, proc-macro-srv]
|
||||
# We need to ensure this job does *not* get skipped if its dependencies fail,
|
||||
# because a skipped job is considered a success by GitHub. So we have to
|
||||
# overwrite `if:`. We use `!cancelled()` to ensure the job does still not get run
|
||||
@ -257,5 +276,5 @@ jobs:
|
||||
run: |
|
||||
# Print the dependent jobs to see them in the CI log
|
||||
jq -C <<< '${{ toJson(needs) }}'
|
||||
# Check if all jobs that we depend on (in the needs array) were successful.
|
||||
jq --exit-status 'all(.result == "success")' <<< '${{ toJson(needs) }}'
|
||||
# Check if all jobs that we depend on (in the needs array) were successful (or have been skipped).
|
||||
jq --exit-status 'all(.result == "success" or .result == "skipped")' <<< '${{ toJson(needs) }}'
|
||||
|
@ -191,9 +191,9 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
|
||||
|
||||
[[package]]
|
||||
name = "chalk-derive"
|
||||
version = "0.99.0"
|
||||
version = "0.100.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "572583d9b97f9d277e5c7607f8239a30e2e04d3ed3b47c87d1cb2152ae724073"
|
||||
checksum = "ab2d131019373f0d0d1f2af0abd4f719739f6583c1b33965112455f643a910af"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -203,9 +203,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "chalk-ir"
|
||||
version = "0.99.0"
|
||||
version = "0.100.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e60e0ef9c81dce1336a9ed3c76f08775f5b623151d96d85ba45f7b10de76d1c7"
|
||||
checksum = "4f114996bda14c0213f014a4ef31a7867dcf5f539a3900477fc6b20138e7a17b"
|
||||
dependencies = [
|
||||
"bitflags 2.7.0",
|
||||
"chalk-derive",
|
||||
@ -213,9 +213,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "chalk-recursive"
|
||||
version = "0.99.0"
|
||||
version = "0.100.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a06350d614e22b03a69b8105e3541614450a7ea48bc58ecc6c6bd92731a3995"
|
||||
checksum = "551e956e031c09057c7b21f17d48d91de99c9b6b6e34bceaf5e7202d71021268"
|
||||
dependencies = [
|
||||
"chalk-derive",
|
||||
"chalk-ir",
|
||||
@ -226,9 +226,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "chalk-solve"
|
||||
version = "0.99.0"
|
||||
version = "0.100.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0e428761e9b55bee516bfe2457caed8b6d1b86353f92ae825bbe438a36ce91e8"
|
||||
checksum = "cd7ca50181156ce649efe8e5dd00580f573651554e4dcd11afa4e2ac93f53324"
|
||||
dependencies = [
|
||||
"chalk-derive",
|
||||
"chalk-ir",
|
||||
@ -626,7 +626,6 @@ dependencies = [
|
||||
"oorandom",
|
||||
"project-model",
|
||||
"ra-ap-rustc_abi",
|
||||
"ra-ap-rustc_hashes",
|
||||
"ra-ap-rustc_index",
|
||||
"ra-ap-rustc_pattern_analysis",
|
||||
"rustc-hash 2.0.0",
|
||||
@ -1504,9 +1503,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ra-ap-rustc_abi"
|
||||
version = "0.98.0"
|
||||
version = "0.100.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4b42cccfff8091a4c3397736518774dbad619e82f8def6f70d8e46dbbe396007"
|
||||
checksum = "f1651b0f7e8c3eb7c27a88f39d277e69c32bfe58e3be174d286c1a24d6a7a4d8"
|
||||
dependencies = [
|
||||
"bitflags 2.7.0",
|
||||
"ra-ap-rustc_hashes",
|
||||
@ -1516,18 +1515,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ra-ap-rustc_hashes"
|
||||
version = "0.98.0"
|
||||
version = "0.100.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46d8bd34ed6552c8cac1764106ef5adbeef3e5c7700e0ceb4c83a47a631894fe"
|
||||
checksum = "2bcd85e93dc0ea850bcfe7957a115957df799ccbc9eea488bdee5ec6780d212b"
|
||||
dependencies = [
|
||||
"rustc-stable-hash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ra-ap-rustc_index"
|
||||
version = "0.98.0"
|
||||
version = "0.100.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93799e4dccbbd47f8b66bc0aa42effc1b7077aaee09d8a40b86b8d659b80c7b7"
|
||||
checksum = "62b295fc0640cd9fe0ecab872ee4a17a96f90a3998ec9f0c4765e9b8415c12cc"
|
||||
dependencies = [
|
||||
"ra-ap-rustc_index_macros",
|
||||
"smallvec",
|
||||
@ -1535,9 +1534,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ra-ap-rustc_index_macros"
|
||||
version = "0.98.0"
|
||||
version = "0.100.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "30baa5d00f94ba437a9dcaf7ae074ebe4f367bb05a4c2835e0aa2e7af3463aac"
|
||||
checksum = "c675f4257023aa933882906f13802cae287e88cc39ab13cbb96809083db0c801"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -1546,9 +1545,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ra-ap-rustc_lexer"
|
||||
version = "0.98.0"
|
||||
version = "0.100.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3004d1d1b50afe3e1f9cdd428a282da7ffbf5f26dd8bf04af0d651d44e4873d8"
|
||||
checksum = "c8358702c2a510ea84ba5801ddc047d9ad9520902cfb0e6173277610cdce2c9c"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"unicode-properties",
|
||||
@ -1557,9 +1556,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ra-ap-rustc_parse_format"
|
||||
version = "0.98.0"
|
||||
version = "0.100.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb57e5124a64aaaf92c06130fbc1b8e1d547b5a2a96081f1f848e31c211df5d2"
|
||||
checksum = "b98f402011d46732c35c47bfd111dec0495747fef2ec900ddee7fe15d78449a7"
|
||||
dependencies = [
|
||||
"ra-ap-rustc_index",
|
||||
"ra-ap-rustc_lexer",
|
||||
@ -1567,9 +1566,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ra-ap-rustc_pattern_analysis"
|
||||
version = "0.98.0"
|
||||
version = "0.100.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e427c3d30e4bdff28abd6b0ef3e6f4dfab44acd9468a4954eeff8717d8df8819"
|
||||
checksum = "bef3ff73fa4653252ffe1d1e9177a446f49ef46d97140e4816b7ff2dad59ed53"
|
||||
dependencies = [
|
||||
"ra-ap-rustc_index",
|
||||
"rustc-hash 2.0.0",
|
||||
|
@ -85,12 +85,12 @@ vfs-notify = { path = "./crates/vfs-notify", version = "0.0.0" }
|
||||
vfs = { path = "./crates/vfs", version = "0.0.0" }
|
||||
edition = { path = "./crates/edition", version = "0.0.0" }
|
||||
|
||||
ra-ap-rustc_hashes = { version = "0.98", default-features = false }
|
||||
ra-ap-rustc_lexer = { version = "0.98", default-features = false }
|
||||
ra-ap-rustc_parse_format = { version = "0.98", default-features = false }
|
||||
ra-ap-rustc_index = { version = "0.98", default-features = false }
|
||||
ra-ap-rustc_abi = { version = "0.98", default-features = false }
|
||||
ra-ap-rustc_pattern_analysis = { version = "0.98", default-features = false }
|
||||
ra-ap-rustc_hashes = { version = "0.100", default-features = false }
|
||||
ra-ap-rustc_lexer = { version = "0.100", default-features = false }
|
||||
ra-ap-rustc_parse_format = { version = "0.100", default-features = false }
|
||||
ra-ap-rustc_index = { version = "0.100", default-features = false }
|
||||
ra-ap-rustc_abi = { version = "0.100", default-features = false }
|
||||
ra-ap-rustc_pattern_analysis = { version = "0.100", default-features = false }
|
||||
|
||||
# local crates that aren't published to crates.io. These should not have versions.
|
||||
|
||||
@ -105,10 +105,10 @@ arrayvec = "0.7.4"
|
||||
bitflags = "2.4.1"
|
||||
cargo_metadata = "0.18.1"
|
||||
camino = "1.1.6"
|
||||
chalk-solve = { version = "0.99.0", default-features = false }
|
||||
chalk-ir = "0.99.0"
|
||||
chalk-recursive = { version = "0.99.0", default-features = false }
|
||||
chalk-derive = "0.99.0"
|
||||
chalk-solve = { version = "0.100.0", default-features = false }
|
||||
chalk-ir = "0.100.0"
|
||||
chalk-recursive = { version = "0.100.0", default-features = false }
|
||||
chalk-derive = "0.100.0"
|
||||
crossbeam-channel = "0.5.8"
|
||||
dissimilar = "1.0.7"
|
||||
dot = "0.1.4"
|
||||
|
@ -18,6 +18,25 @@ pub enum CfgAtom {
|
||||
KeyValue { key: Symbol, value: Symbol },
|
||||
}
|
||||
|
||||
impl PartialOrd for CfgAtom {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for CfgAtom {
|
||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||
match (self, other) {
|
||||
(CfgAtom::Flag(a), CfgAtom::Flag(b)) => a.as_str().cmp(b.as_str()),
|
||||
(CfgAtom::Flag(_), CfgAtom::KeyValue { .. }) => std::cmp::Ordering::Less,
|
||||
(CfgAtom::KeyValue { .. }, CfgAtom::Flag(_)) => std::cmp::Ordering::Greater,
|
||||
(CfgAtom::KeyValue { key, value }, CfgAtom::KeyValue { key: key2, value: value2 }) => {
|
||||
key.as_str().cmp(key2.as_str()).then(value.as_str().cmp(value2.as_str()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for CfgAtom {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
|
@ -66,9 +66,9 @@ impl DnfExpr {
|
||||
}
|
||||
}
|
||||
|
||||
res.enabled.sort_unstable_by(compare);
|
||||
res.enabled.sort_unstable();
|
||||
res.enabled.dedup();
|
||||
res.disabled.sort_unstable_by(compare);
|
||||
res.disabled.sort_unstable();
|
||||
res.disabled.dedup();
|
||||
Some(res)
|
||||
}
|
||||
@ -114,25 +114,14 @@ impl DnfExpr {
|
||||
};
|
||||
|
||||
// Undo the FxHashMap randomization for consistent output.
|
||||
diff.enable.sort_unstable_by(compare);
|
||||
diff.disable.sort_unstable_by(compare);
|
||||
diff.enable.sort_unstable();
|
||||
diff.disable.sort_unstable();
|
||||
|
||||
Some(diff)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn compare(a: &CfgAtom, b: &CfgAtom) -> std::cmp::Ordering {
|
||||
match (a, b) {
|
||||
(CfgAtom::Flag(a), CfgAtom::Flag(b)) => a.as_str().cmp(b.as_str()),
|
||||
(CfgAtom::Flag(_), CfgAtom::KeyValue { .. }) => std::cmp::Ordering::Less,
|
||||
(CfgAtom::KeyValue { .. }, CfgAtom::Flag(_)) => std::cmp::Ordering::Greater,
|
||||
(CfgAtom::KeyValue { key, value }, CfgAtom::KeyValue { key: key2, value: value2 }) => {
|
||||
key.as_str().cmp(key2.as_str()).then(value.as_str().cmp(value2.as_str()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for DnfExpr {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
if self.conjunctions.len() != 1 {
|
||||
|
@ -148,16 +148,20 @@ pub struct CfgDiff {
|
||||
}
|
||||
|
||||
impl CfgDiff {
|
||||
/// Create a new CfgDiff. Will return None if the same item appears more than once in the set
|
||||
/// of both.
|
||||
pub fn new(enable: Vec<CfgAtom>, disable: Vec<CfgAtom>) -> Option<CfgDiff> {
|
||||
let mut occupied = FxHashSet::default();
|
||||
if enable.iter().chain(disable.iter()).any(|item| !occupied.insert(item)) {
|
||||
// was present
|
||||
return None;
|
||||
/// Create a new CfgDiff.
|
||||
pub fn new(mut enable: Vec<CfgAtom>, mut disable: Vec<CfgAtom>) -> CfgDiff {
|
||||
enable.sort();
|
||||
enable.dedup();
|
||||
disable.sort();
|
||||
disable.dedup();
|
||||
for i in (0..enable.len()).rev() {
|
||||
if let Some(j) = disable.iter().position(|atom| *atom == enable[i]) {
|
||||
enable.remove(i);
|
||||
disable.remove(j);
|
||||
}
|
||||
}
|
||||
|
||||
Some(CfgDiff { enable, disable })
|
||||
CfgDiff { enable, disable }
|
||||
}
|
||||
|
||||
/// Returns the total number of atoms changed by this diff.
|
||||
|
@ -260,7 +260,7 @@ pub enum TypeBound {
|
||||
}
|
||||
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
const _: [(); 24] = [(); ::std::mem::size_of::<TypeBound>()];
|
||||
const _: [(); 24] = [(); size_of::<TypeBound>()];
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub enum UseArgRef {
|
||||
|
@ -93,15 +93,15 @@ fn broken_parenthesis_sequence() {
|
||||
macro_rules! m1 { ($x:ident) => { ($x } }
|
||||
macro_rules! m2 { ($x:ident) => {} }
|
||||
|
||||
m1!();
|
||||
m2!(x
|
||||
fn f1() { m1!(x); }
|
||||
fn f2() { m2!(x }
|
||||
"#,
|
||||
expect![[r#"
|
||||
macro_rules! m1 { ($x:ident) => { ($x } }
|
||||
macro_rules! m2 { ($x:ident) => {} }
|
||||
|
||||
/* error: macro definition has parse errors */
|
||||
/* error: expected ident */
|
||||
fn f1() { (x); }
|
||||
fn f2() { /* error: expected ident */ }
|
||||
"#]],
|
||||
)
|
||||
}
|
||||
|
@ -57,7 +57,7 @@ pub enum Path {
|
||||
/// or type anchor, it is `Path::Normal` with the generics filled with `None` even if there are none (practically
|
||||
/// this is not a problem since many more paths have generics than a type anchor).
|
||||
BarePath(Interned<ModPath>),
|
||||
/// `Path::Normal` may have empty generics and type anchor (but generic args will be filled with `None`).
|
||||
/// `Path::Normal` will always have either generics or type anchor.
|
||||
Normal(NormalPath),
|
||||
/// A link to a lang item. It is used in desugaring of things like `it?`. We can show these
|
||||
/// links via a normal path since they might be private and not accessible in the usage place.
|
||||
@ -208,11 +208,15 @@ impl Path {
|
||||
mod_path.segments()[..mod_path.segments().len() - 1].iter().cloned(),
|
||||
));
|
||||
let qualifier_generic_args = &generic_args[..generic_args.len() - 1];
|
||||
Some(Path::Normal(NormalPath::new(
|
||||
type_anchor,
|
||||
qualifier_mod_path,
|
||||
qualifier_generic_args.iter().cloned(),
|
||||
)))
|
||||
if type_anchor.is_none() && qualifier_generic_args.iter().all(|it| it.is_none()) {
|
||||
Some(Path::BarePath(qualifier_mod_path))
|
||||
} else {
|
||||
Some(Path::Normal(NormalPath::new(
|
||||
type_anchor,
|
||||
qualifier_mod_path,
|
||||
qualifier_generic_args.iter().cloned(),
|
||||
)))
|
||||
}
|
||||
}
|
||||
Path::LangItem(..) => None,
|
||||
}
|
||||
|
@ -3,10 +3,11 @@ use std::{fmt, iter, mem};
|
||||
|
||||
use base_db::CrateId;
|
||||
use hir_expand::{name::Name, MacroDefId};
|
||||
use intern::sym;
|
||||
use intern::{sym, Symbol};
|
||||
use itertools::Itertools as _;
|
||||
use rustc_hash::FxHashSet;
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use span::SyntaxContextId;
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{
|
||||
@ -343,15 +344,7 @@ impl Resolver {
|
||||
}
|
||||
|
||||
if n_segments <= 1 {
|
||||
let mut hygiene_info = if !hygiene_id.is_root() {
|
||||
let ctx = hygiene_id.lookup(db);
|
||||
ctx.outer_expn.map(|expansion| {
|
||||
let expansion = db.lookup_intern_macro_call(expansion);
|
||||
(ctx.parent, expansion.def)
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let mut hygiene_info = hygiene_info(db, hygiene_id);
|
||||
for scope in self.scopes() {
|
||||
match scope {
|
||||
Scope::ExprScope(scope) => {
|
||||
@ -371,19 +364,7 @@ impl Resolver {
|
||||
}
|
||||
}
|
||||
Scope::MacroDefScope(macro_id) => {
|
||||
if let Some((parent_ctx, label_macro_id)) = hygiene_info {
|
||||
if label_macro_id == **macro_id {
|
||||
// A macro is allowed to refer to variables from before its declaration.
|
||||
// Therefore, if we got to the rib of its declaration, give up its hygiene
|
||||
// and use its parent expansion.
|
||||
let parent_ctx = db.lookup_intern_syntax_context(parent_ctx);
|
||||
hygiene_id = HygieneId::new(parent_ctx.opaque_and_semitransparent);
|
||||
hygiene_info = parent_ctx.outer_expn.map(|expansion| {
|
||||
let expansion = db.lookup_intern_macro_call(expansion);
|
||||
(parent_ctx.parent, expansion.def)
|
||||
});
|
||||
}
|
||||
}
|
||||
handle_macro_def_scope(db, &mut hygiene_id, &mut hygiene_info, macro_id)
|
||||
}
|
||||
Scope::GenericParams { params, def } => {
|
||||
if let Some(id) = params.find_const_by_name(first_name, *def) {
|
||||
@ -730,6 +711,107 @@ impl Resolver {
|
||||
})
|
||||
}
|
||||
|
||||
/// Checks if we rename `renamed` (currently named `current_name`) to `new_name`, will the meaning of this reference
|
||||
/// (that contains `current_name` path) change from `renamed` to some another variable (returned as `Some`).
|
||||
pub fn rename_will_conflict_with_another_variable(
|
||||
&self,
|
||||
db: &dyn DefDatabase,
|
||||
current_name: &Name,
|
||||
current_name_as_path: &ModPath,
|
||||
mut hygiene_id: HygieneId,
|
||||
new_name: &Symbol,
|
||||
to_be_renamed: BindingId,
|
||||
) -> Option<BindingId> {
|
||||
let mut hygiene_info = hygiene_info(db, hygiene_id);
|
||||
let mut will_be_resolved_to = None;
|
||||
for scope in self.scopes() {
|
||||
match scope {
|
||||
Scope::ExprScope(scope) => {
|
||||
for entry in scope.expr_scopes.entries(scope.scope_id) {
|
||||
if entry.hygiene() == hygiene_id {
|
||||
if entry.binding() == to_be_renamed {
|
||||
// This currently resolves to our renamed variable, now `will_be_resolved_to`
|
||||
// contains `Some` if the meaning will change or `None` if not.
|
||||
return will_be_resolved_to;
|
||||
} else if entry.name().symbol() == new_name {
|
||||
will_be_resolved_to = Some(entry.binding());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Scope::MacroDefScope(macro_id) => {
|
||||
handle_macro_def_scope(db, &mut hygiene_id, &mut hygiene_info, macro_id)
|
||||
}
|
||||
Scope::GenericParams { params, def } => {
|
||||
if params.find_const_by_name(current_name, *def).is_some() {
|
||||
// It does not resolve to our renamed variable.
|
||||
return None;
|
||||
}
|
||||
}
|
||||
Scope::AdtScope(_) | Scope::ImplDefScope(_) => continue,
|
||||
Scope::BlockScope(m) => {
|
||||
if m.resolve_path_in_value_ns(db, current_name_as_path).is_some() {
|
||||
// It does not resolve to our renamed variable.
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// It does not resolve to our renamed variable.
|
||||
None
|
||||
}
|
||||
|
||||
/// Checks if we rename `renamed` to `name`, will the meaning of this reference (that contains `name` path) change
|
||||
/// from some other variable (returned as `Some`) to `renamed`.
|
||||
pub fn rename_will_conflict_with_renamed(
|
||||
&self,
|
||||
db: &dyn DefDatabase,
|
||||
name: &Name,
|
||||
name_as_path: &ModPath,
|
||||
mut hygiene_id: HygieneId,
|
||||
to_be_renamed: BindingId,
|
||||
) -> Option<BindingId> {
|
||||
let mut hygiene_info = hygiene_info(db, hygiene_id);
|
||||
let mut will_resolve_to_renamed = false;
|
||||
for scope in self.scopes() {
|
||||
match scope {
|
||||
Scope::ExprScope(scope) => {
|
||||
for entry in scope.expr_scopes.entries(scope.scope_id) {
|
||||
if entry.binding() == to_be_renamed {
|
||||
will_resolve_to_renamed = true;
|
||||
} else if entry.hygiene() == hygiene_id && entry.name() == name {
|
||||
if will_resolve_to_renamed {
|
||||
// This will resolve to the renamed variable before it resolves to the original variable.
|
||||
return Some(entry.binding());
|
||||
} else {
|
||||
// This will resolve to the original variable.
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Scope::MacroDefScope(macro_id) => {
|
||||
handle_macro_def_scope(db, &mut hygiene_id, &mut hygiene_info, macro_id)
|
||||
}
|
||||
Scope::GenericParams { params, def } => {
|
||||
if params.find_const_by_name(name, *def).is_some() {
|
||||
// Here and below, it might actually resolve to our renamed variable - in which case it'll
|
||||
// hide the generic parameter or some other thing (not a variable). We don't check for that
|
||||
// because due to naming conventions, it is rare that variable will shadow a non-variable.
|
||||
return None;
|
||||
}
|
||||
}
|
||||
Scope::AdtScope(_) | Scope::ImplDefScope(_) => continue,
|
||||
Scope::BlockScope(m) => {
|
||||
if m.resolve_path_in_value_ns(db, name_as_path).is_some() {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// `expr_id` is required to be an expression id that comes after the top level expression scope in the given resolver
|
||||
#[must_use]
|
||||
pub fn update_to_inner_scope(
|
||||
@ -795,6 +877,44 @@ impl Resolver {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn handle_macro_def_scope(
|
||||
db: &dyn DefDatabase,
|
||||
hygiene_id: &mut HygieneId,
|
||||
hygiene_info: &mut Option<(SyntaxContextId, MacroDefId)>,
|
||||
macro_id: &MacroDefId,
|
||||
) {
|
||||
if let Some((parent_ctx, label_macro_id)) = hygiene_info {
|
||||
if label_macro_id == macro_id {
|
||||
// A macro is allowed to refer to variables from before its declaration.
|
||||
// Therefore, if we got to the rib of its declaration, give up its hygiene
|
||||
// and use its parent expansion.
|
||||
let parent_ctx = db.lookup_intern_syntax_context(*parent_ctx);
|
||||
*hygiene_id = HygieneId::new(parent_ctx.opaque_and_semitransparent);
|
||||
*hygiene_info = parent_ctx.outer_expn.map(|expansion| {
|
||||
let expansion = db.lookup_intern_macro_call(expansion);
|
||||
(parent_ctx.parent, expansion.def)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn hygiene_info(
|
||||
db: &dyn DefDatabase,
|
||||
hygiene_id: HygieneId,
|
||||
) -> Option<(SyntaxContextId, MacroDefId)> {
|
||||
if !hygiene_id.is_root() {
|
||||
let ctx = hygiene_id.lookup(db);
|
||||
ctx.outer_expn.map(|expansion| {
|
||||
let expansion = db.lookup_intern_macro_call(expansion);
|
||||
(ctx.parent, expansion.def)
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub struct UpdateGuard(usize);
|
||||
|
||||
impl Resolver {
|
||||
|
@ -148,7 +148,6 @@ pub(crate) fn fixup_syntax(
|
||||
}
|
||||
if it.then_branch().is_none() {
|
||||
append.insert(node.clone().into(), vec![
|
||||
// FIXME: THis should be a subtree no?
|
||||
Leaf::Punct(Punct {
|
||||
char: '{',
|
||||
spacing: Spacing::Alone,
|
||||
@ -179,7 +178,6 @@ pub(crate) fn fixup_syntax(
|
||||
}
|
||||
if it.loop_body().is_none() {
|
||||
append.insert(node.clone().into(), vec![
|
||||
// FIXME: THis should be a subtree no?
|
||||
Leaf::Punct(Punct {
|
||||
char: '{',
|
||||
spacing: Spacing::Alone,
|
||||
@ -196,7 +194,6 @@ pub(crate) fn fixup_syntax(
|
||||
ast::LoopExpr(it) => {
|
||||
if it.loop_body().is_none() {
|
||||
append.insert(node.clone().into(), vec![
|
||||
// FIXME: THis should be a subtree no?
|
||||
Leaf::Punct(Punct {
|
||||
char: '{',
|
||||
spacing: Spacing::Alone,
|
||||
@ -228,7 +225,6 @@ pub(crate) fn fixup_syntax(
|
||||
if it.match_arm_list().is_none() {
|
||||
// No match arms
|
||||
append.insert(node.clone().into(), vec![
|
||||
// FIXME: THis should be a subtree no?
|
||||
Leaf::Punct(Punct {
|
||||
char: '{',
|
||||
spacing: Spacing::Alone,
|
||||
@ -269,7 +265,6 @@ pub(crate) fn fixup_syntax(
|
||||
|
||||
if it.loop_body().is_none() {
|
||||
append.insert(node.clone().into(), vec![
|
||||
// FIXME: THis should be a subtree no?
|
||||
Leaf::Punct(Punct {
|
||||
char: '{',
|
||||
spacing: Spacing::Alone,
|
||||
@ -309,28 +304,6 @@ pub(crate) fn fixup_syntax(
|
||||
}
|
||||
}
|
||||
},
|
||||
ast::ArgList(it) => {
|
||||
if it.r_paren_token().is_none() {
|
||||
append.insert(node.into(), vec![
|
||||
Leaf::Punct(Punct {
|
||||
span: fake_span(node_range),
|
||||
char: ')',
|
||||
spacing: Spacing::Alone
|
||||
})
|
||||
]);
|
||||
}
|
||||
},
|
||||
ast::ArgList(it) => {
|
||||
if it.r_paren_token().is_none() {
|
||||
append.insert(node.into(), vec![
|
||||
Leaf::Punct(Punct {
|
||||
span: fake_span(node_range),
|
||||
char: ')',
|
||||
spacing: Spacing::Alone
|
||||
})
|
||||
]);
|
||||
}
|
||||
},
|
||||
ast::ClosureExpr(it) => {
|
||||
if it.body().is_none() {
|
||||
append.insert(node.into(), vec![
|
||||
@ -476,12 +449,12 @@ fn reverse_fixups_(tt: &mut TopSubtree, undo_info: &[TopSubtree]) {
|
||||
}
|
||||
}
|
||||
tt::TokenTree::Subtree(tt) => {
|
||||
// fixup should only create matching delimiters, but proc macros
|
||||
// could just copy the span to one of the delimiters. We don't want
|
||||
// to leak the dummy ID, so we remove both.
|
||||
if tt.delimiter.close.anchor.ast_id == FIXUP_DUMMY_AST_ID
|
||||
|| tt.delimiter.open.anchor.ast_id == FIXUP_DUMMY_AST_ID
|
||||
{
|
||||
// Even though fixup never creates subtrees with fixup spans, the old proc-macro server
|
||||
// might copy them if the proc-macro asks for it, so we need to filter those out
|
||||
// here as well.
|
||||
return TransformTtAction::remove();
|
||||
}
|
||||
TransformTtAction::Keep
|
||||
@ -571,6 +544,17 @@ mod tests {
|
||||
parse.syntax_node()
|
||||
);
|
||||
|
||||
// the fixed-up tree should not contain braces as punct
|
||||
// FIXME: should probably instead check that it's a valid punctuation character
|
||||
for x in tt.token_trees().flat_tokens() {
|
||||
match x {
|
||||
::tt::TokenTree::Leaf(::tt::Leaf::Punct(punct)) => {
|
||||
assert!(!matches!(punct.char, '{' | '}' | '(' | ')' | '[' | ']'))
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
reverse_fixups(&mut tt, &fixups.undo_info);
|
||||
|
||||
// the fixed-up + reversed version should be equivalent to the original input
|
||||
@ -596,7 +580,7 @@ fn foo() {
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
fn foo () {for _ in __ra_fixup { }}
|
||||
fn foo () {for _ in __ra_fixup {}}
|
||||
"#]],
|
||||
)
|
||||
}
|
||||
@ -624,7 +608,7 @@ fn foo() {
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
fn foo () {for bar in qux { }}
|
||||
fn foo () {for bar in qux {}}
|
||||
"#]],
|
||||
)
|
||||
}
|
||||
@ -655,7 +639,7 @@ fn foo() {
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
fn foo () {match __ra_fixup { }}
|
||||
fn foo () {match __ra_fixup {}}
|
||||
"#]],
|
||||
)
|
||||
}
|
||||
@ -687,7 +671,7 @@ fn foo() {
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
fn foo () {match __ra_fixup { }}
|
||||
fn foo () {match __ra_fixup {}}
|
||||
"#]],
|
||||
)
|
||||
}
|
||||
@ -802,7 +786,7 @@ fn foo() {
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
fn foo () {if a { }}
|
||||
fn foo () {if a {}}
|
||||
"#]],
|
||||
)
|
||||
}
|
||||
@ -816,7 +800,7 @@ fn foo() {
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
fn foo () {if __ra_fixup { }}
|
||||
fn foo () {if __ra_fixup {}}
|
||||
"#]],
|
||||
)
|
||||
}
|
||||
@ -830,7 +814,7 @@ fn foo() {
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
fn foo () {if __ra_fixup {} { }}
|
||||
fn foo () {if __ra_fixup {} {}}
|
||||
"#]],
|
||||
)
|
||||
}
|
||||
@ -844,7 +828,7 @@ fn foo() {
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
fn foo () {while __ra_fixup { }}
|
||||
fn foo () {while __ra_fixup {}}
|
||||
"#]],
|
||||
)
|
||||
}
|
||||
@ -858,7 +842,7 @@ fn foo() {
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
fn foo () {while foo { }}
|
||||
fn foo () {while foo {}}
|
||||
"#]],
|
||||
)
|
||||
}
|
||||
@ -885,7 +869,7 @@ fn foo() {
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
fn foo () {loop { }}
|
||||
fn foo () {loop {}}
|
||||
"#]],
|
||||
)
|
||||
}
|
||||
@ -941,7 +925,7 @@ fn foo() {
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
fn foo () { foo ( a ) }
|
||||
fn foo () {foo (a)}
|
||||
"#]],
|
||||
);
|
||||
check(
|
||||
@ -951,7 +935,7 @@ fn foo() {
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
fn foo () { bar . foo ( a ) }
|
||||
fn foo () {bar . foo (a)}
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
@ -5,6 +5,8 @@
|
||||
//! expansion.
|
||||
#![cfg_attr(feature = "in-rust-tree", feature(rustc_private))]
|
||||
|
||||
pub use intern;
|
||||
|
||||
pub mod attrs;
|
||||
pub mod builtin;
|
||||
pub mod change;
|
||||
|
@ -388,7 +388,7 @@ macro_rules! __path {
|
||||
($start:ident $(:: $seg:ident)*) => ({
|
||||
$crate::__known_path!($start $(:: $seg)*);
|
||||
$crate::mod_path::ModPath::from_segments($crate::mod_path::PathKind::Abs, vec![
|
||||
$crate::name::Name::new_symbol_root(intern::sym::$start.clone()), $($crate::name::Name::new_symbol_root(intern::sym::$seg.clone()),)*
|
||||
$crate::name::Name::new_symbol_root($crate::intern::sym::$start.clone()), $($crate::name::Name::new_symbol_root($crate::intern::sym::$seg.clone()),)*
|
||||
])
|
||||
});
|
||||
}
|
||||
@ -399,7 +399,7 @@ pub use crate::__path as path;
|
||||
macro_rules! __tool_path {
|
||||
($start:ident $(:: $seg:ident)*) => ({
|
||||
$crate::mod_path::ModPath::from_segments($crate::mod_path::PathKind::Plain, vec![
|
||||
$crate::name::Name::new_symbol_root(intern::sym::rust_analyzer.clone()), $crate::name::Name::new_symbol_root(intern::sym::$start.clone()), $($crate::name::Name::new_symbol_root(intern::sym::$seg.clone()),)*
|
||||
$crate::name::Name::new_symbol_root($crate::intern::sym::rust_analyzer.clone()), $crate::name::Name::new_symbol_root($crate::intern::sym::$start.clone()), $($crate::name::Name::new_symbol_root($crate::intern::sym::$seg.clone()),)*
|
||||
])
|
||||
});
|
||||
}
|
||||
|
@ -35,7 +35,6 @@ indexmap.workspace = true
|
||||
rustc_apfloat = "0.2.0"
|
||||
|
||||
ra-ap-rustc_abi.workspace = true
|
||||
ra-ap-rustc_hashes.workspace = true
|
||||
ra-ap-rustc_index.workspace = true
|
||||
ra-ap-rustc_pattern_analysis.workspace = true
|
||||
|
||||
|
@ -708,6 +708,9 @@ fn well_known_trait_from_lang_item(item: LangItem) -> Option<WellKnownTrait> {
|
||||
LangItem::Fn => WellKnownTrait::Fn,
|
||||
LangItem::FnMut => WellKnownTrait::FnMut,
|
||||
LangItem::FnOnce => WellKnownTrait::FnOnce,
|
||||
LangItem::AsyncFn => WellKnownTrait::AsyncFn,
|
||||
LangItem::AsyncFnMut => WellKnownTrait::AsyncFnMut,
|
||||
LangItem::AsyncFnOnce => WellKnownTrait::AsyncFnOnce,
|
||||
LangItem::Coroutine => WellKnownTrait::Coroutine,
|
||||
LangItem::Sized => WellKnownTrait::Sized,
|
||||
LangItem::Unpin => WellKnownTrait::Unpin,
|
||||
@ -715,6 +718,7 @@ fn well_known_trait_from_lang_item(item: LangItem) -> Option<WellKnownTrait> {
|
||||
LangItem::Tuple => WellKnownTrait::Tuple,
|
||||
LangItem::PointeeTrait => WellKnownTrait::Pointee,
|
||||
LangItem::FnPtrTrait => WellKnownTrait::FnPtr,
|
||||
LangItem::Future => WellKnownTrait::Future,
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
@ -730,6 +734,9 @@ fn lang_item_from_well_known_trait(trait_: WellKnownTrait) -> LangItem {
|
||||
WellKnownTrait::Fn => LangItem::Fn,
|
||||
WellKnownTrait::FnMut => LangItem::FnMut,
|
||||
WellKnownTrait::FnOnce => LangItem::FnOnce,
|
||||
WellKnownTrait::AsyncFn => LangItem::AsyncFn,
|
||||
WellKnownTrait::AsyncFnMut => LangItem::AsyncFnMut,
|
||||
WellKnownTrait::AsyncFnOnce => LangItem::AsyncFnOnce,
|
||||
WellKnownTrait::Coroutine => LangItem::Coroutine,
|
||||
WellKnownTrait::Sized => LangItem::Sized,
|
||||
WellKnownTrait::Tuple => LangItem::Tuple,
|
||||
@ -737,6 +744,7 @@ fn lang_item_from_well_known_trait(trait_: WellKnownTrait) -> LangItem {
|
||||
WellKnownTrait::Unsize => LangItem::Unsize,
|
||||
WellKnownTrait::Pointee => LangItem::PointeeTrait,
|
||||
WellKnownTrait::FnPtr => LangItem::FnPtrTrait,
|
||||
WellKnownTrait::Future => LangItem::Future,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -15,9 +15,10 @@ use stdx::never;
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{
|
||||
db::HirDatabase, generics::Generics, infer::InferenceContext, lower::ParamLoweringMode,
|
||||
mir::monomorphize_mir_body_bad, to_placeholder_idx, Const, ConstData, ConstScalar, ConstValue,
|
||||
GenericArg, Interner, MemoryMap, Substitution, TraitEnvironment, Ty, TyBuilder,
|
||||
db::HirDatabase, display::DisplayTarget, generics::Generics, infer::InferenceContext,
|
||||
lower::ParamLoweringMode, mir::monomorphize_mir_body_bad, to_placeholder_idx, Const, ConstData,
|
||||
ConstScalar, ConstValue, GenericArg, Interner, MemoryMap, Substitution, TraitEnvironment, Ty,
|
||||
TyBuilder,
|
||||
};
|
||||
|
||||
use super::mir::{interpret_mir, lower_to_mir, pad16, MirEvalError, MirLowerError};
|
||||
@ -62,11 +63,15 @@ impl ConstEvalError {
|
||||
f: &mut String,
|
||||
db: &dyn HirDatabase,
|
||||
span_formatter: impl Fn(span::FileId, span::TextRange) -> String,
|
||||
edition: span::Edition,
|
||||
display_target: DisplayTarget,
|
||||
) -> std::result::Result<(), std::fmt::Error> {
|
||||
match self {
|
||||
ConstEvalError::MirLowerError(e) => e.pretty_print(f, db, span_formatter, edition),
|
||||
ConstEvalError::MirEvalError(e) => e.pretty_print(f, db, span_formatter, edition),
|
||||
ConstEvalError::MirLowerError(e) => {
|
||||
e.pretty_print(f, db, span_formatter, display_target)
|
||||
}
|
||||
ConstEvalError::MirEvalError(e) => {
|
||||
e.pretty_print(f, db, span_formatter, display_target)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,8 +10,8 @@ use test_fixture::WithFixture;
|
||||
use test_utils::skip_slow_tests;
|
||||
|
||||
use crate::{
|
||||
consteval::try_const_usize, db::HirDatabase, mir::pad16, test_db::TestDB, Const, ConstScalar,
|
||||
Interner, MemoryMap,
|
||||
consteval::try_const_usize, db::HirDatabase, display::DisplayTarget, mir::pad16,
|
||||
test_db::TestDB, Const, ConstScalar, Interner, MemoryMap,
|
||||
};
|
||||
|
||||
use super::{
|
||||
@ -101,11 +101,17 @@ fn check_answer(
|
||||
fn pretty_print_err(e: ConstEvalError, db: TestDB) -> String {
|
||||
let mut err = String::new();
|
||||
let span_formatter = |file, range| format!("{file:?} {range:?}");
|
||||
let edition =
|
||||
db.crate_graph()[*db.crate_graph().crates_in_topological_order().last().unwrap()].edition;
|
||||
let display_target = DisplayTarget::from_crate(
|
||||
&db,
|
||||
*db.crate_graph().crates_in_topological_order().last().unwrap(),
|
||||
);
|
||||
match e {
|
||||
ConstEvalError::MirLowerError(e) => e.pretty_print(&mut err, &db, span_formatter, edition),
|
||||
ConstEvalError::MirEvalError(e) => e.pretty_print(&mut err, &db, span_formatter, edition),
|
||||
ConstEvalError::MirLowerError(e) => {
|
||||
e.pretty_print(&mut err, &db, span_formatter, display_target)
|
||||
}
|
||||
ConstEvalError::MirEvalError(e) => {
|
||||
e.pretty_print(&mut err, &db, span_formatter, display_target)
|
||||
}
|
||||
}
|
||||
.unwrap();
|
||||
err
|
||||
@ -2713,12 +2719,11 @@ fn const_trait_assoc() {
|
||||
r#"
|
||||
//- minicore: size_of, fn
|
||||
//- /a/lib.rs crate:a
|
||||
use core::mem::size_of;
|
||||
pub struct S<T>(T);
|
||||
impl<T> S<T> {
|
||||
pub const X: usize = {
|
||||
let k: T;
|
||||
let f = || core::mem::size_of::<T>();
|
||||
let f = || size_of::<T>();
|
||||
f()
|
||||
};
|
||||
}
|
||||
|
@ -9,5 +9,8 @@ pub use crate::diagnostics::{
|
||||
expr::{
|
||||
record_literal_missing_fields, record_pattern_missing_fields, BodyValidationDiagnostic,
|
||||
},
|
||||
unsafe_check::{missing_unsafe, unsafe_expressions, InsideUnsafeBlock, UnsafetyReason},
|
||||
unsafe_check::{
|
||||
missing_unsafe, unsafe_operations, unsafe_operations_for_body, InsideUnsafeBlock,
|
||||
UnsafetyReason,
|
||||
},
|
||||
};
|
||||
|
@ -16,7 +16,6 @@ use intern::sym;
|
||||
use itertools::Itertools;
|
||||
use rustc_hash::FxHashSet;
|
||||
use rustc_pattern_analysis::constructor::Constructor;
|
||||
use span::Edition;
|
||||
use syntax::{
|
||||
ast::{self, UnaryOp},
|
||||
AstNode,
|
||||
@ -31,7 +30,7 @@ use crate::{
|
||||
self,
|
||||
pat_analysis::{self, DeconstructedPat, MatchCheckCtx, WitnessPat},
|
||||
},
|
||||
display::HirDisplay,
|
||||
display::{DisplayTarget, HirDisplay},
|
||||
Adjust, InferenceResult, Interner, Ty, TyExt, TyKind,
|
||||
};
|
||||
|
||||
@ -633,24 +632,24 @@ fn missing_match_arms<'p>(
|
||||
arms_is_empty: bool,
|
||||
krate: CrateId,
|
||||
) -> String {
|
||||
struct DisplayWitness<'a, 'p>(&'a WitnessPat<'p>, &'a MatchCheckCtx<'p>, Edition);
|
||||
struct DisplayWitness<'a, 'p>(&'a WitnessPat<'p>, &'a MatchCheckCtx<'p>, DisplayTarget);
|
||||
impl fmt::Display for DisplayWitness<'_, '_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let DisplayWitness(witness, cx, edition) = *self;
|
||||
let DisplayWitness(witness, cx, display_target) = *self;
|
||||
let pat = cx.hoist_witness_pat(witness);
|
||||
write!(f, "{}", pat.display(cx.db, edition))
|
||||
write!(f, "{}", pat.display(cx.db, display_target))
|
||||
}
|
||||
}
|
||||
|
||||
let edition = cx.db.crate_graph()[krate].edition;
|
||||
let non_empty_enum = match scrut_ty.as_adt() {
|
||||
Some((AdtId::EnumId(e), _)) => !cx.db.enum_data(e).variants.is_empty(),
|
||||
_ => false,
|
||||
};
|
||||
let display_target = DisplayTarget::from_crate(cx.db, krate);
|
||||
if arms_is_empty && !non_empty_enum {
|
||||
format!("type `{}` is non-empty", scrut_ty.display(cx.db, edition))
|
||||
format!("type `{}` is non-empty", scrut_ty.display(cx.db, display_target))
|
||||
} else {
|
||||
let pat_display = |witness| DisplayWitness(witness, cx, edition);
|
||||
let pat_display = |witness| DisplayWitness(witness, cx, display_target);
|
||||
const LIMIT: usize = 3;
|
||||
match &*witnesses {
|
||||
[witness] => format!("`{}` not covered", pat_display(witness)),
|
||||
|
@ -95,7 +95,26 @@ enum UnsafeDiagnostic {
|
||||
DeprecatedSafe2024 { node: ExprId, inside_unsafe_block: InsideUnsafeBlock },
|
||||
}
|
||||
|
||||
pub fn unsafe_expressions(
|
||||
pub fn unsafe_operations_for_body(
|
||||
db: &dyn HirDatabase,
|
||||
infer: &InferenceResult,
|
||||
def: DefWithBodyId,
|
||||
body: &Body,
|
||||
callback: &mut dyn FnMut(ExprOrPatId),
|
||||
) {
|
||||
let mut visitor_callback = |diag| {
|
||||
if let UnsafeDiagnostic::UnsafeOperation { node, .. } = diag {
|
||||
callback(node);
|
||||
}
|
||||
};
|
||||
let mut visitor = UnsafeVisitor::new(db, infer, body, def, &mut visitor_callback);
|
||||
visitor.walk_expr(body.body_expr);
|
||||
for ¶m in &body.params {
|
||||
visitor.walk_pat(param);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unsafe_operations(
|
||||
db: &dyn HirDatabase,
|
||||
infer: &InferenceResult,
|
||||
def: DefWithBodyId,
|
||||
@ -281,13 +300,6 @@ impl<'a> UnsafeVisitor<'a> {
|
||||
self.on_unsafe_op(current.into(), UnsafetyReason::RawPtrDeref);
|
||||
}
|
||||
}
|
||||
Expr::Unsafe { .. } => {
|
||||
let old_inside_unsafe_block =
|
||||
mem::replace(&mut self.inside_unsafe_block, InsideUnsafeBlock::Yes);
|
||||
self.body.walk_child_exprs_without_pats(current, |child| self.walk_expr(child));
|
||||
self.inside_unsafe_block = old_inside_unsafe_block;
|
||||
return;
|
||||
}
|
||||
&Expr::Assignment { target, value: _ } => {
|
||||
let old_inside_assignment = mem::replace(&mut self.inside_assignment, true);
|
||||
self.walk_pats_top(std::iter::once(target), current);
|
||||
@ -306,6 +318,20 @@ impl<'a> UnsafeVisitor<'a> {
|
||||
}
|
||||
}
|
||||
}
|
||||
Expr::Unsafe { statements, .. } => {
|
||||
let old_inside_unsafe_block =
|
||||
mem::replace(&mut self.inside_unsafe_block, InsideUnsafeBlock::Yes);
|
||||
self.walk_pats_top(
|
||||
statements.iter().filter_map(|statement| match statement {
|
||||
&Statement::Let { pat, .. } => Some(pat),
|
||||
_ => None,
|
||||
}),
|
||||
current,
|
||||
);
|
||||
self.body.walk_child_exprs_without_pats(current, |child| self.walk_expr(child));
|
||||
self.inside_unsafe_block = old_inside_unsafe_block;
|
||||
return;
|
||||
}
|
||||
Expr::Block { statements, .. } | Expr::Async { statements, .. } => {
|
||||
self.walk_pats_top(
|
||||
statements.iter().filter_map(|statement| match statement {
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
use std::{
|
||||
fmt::{self, Debug},
|
||||
mem::{self, size_of},
|
||||
mem,
|
||||
};
|
||||
|
||||
use base_db::CrateId;
|
||||
@ -45,6 +45,7 @@ use crate::{
|
||||
db::{HirDatabase, InternedClosure},
|
||||
from_assoc_type_id, from_foreign_def_id, from_placeholder_idx,
|
||||
generics::generics,
|
||||
infer::normalize,
|
||||
layout::Layout,
|
||||
lt_from_placeholder_idx,
|
||||
mapping::from_chalk,
|
||||
@ -87,6 +88,7 @@ pub struct HirFormatter<'a> {
|
||||
show_container_bounds: bool,
|
||||
omit_verbose_types: bool,
|
||||
closure_style: ClosureStyle,
|
||||
display_kind: DisplayKind,
|
||||
display_target: DisplayTarget,
|
||||
bounds_formatting_ctx: BoundsFormattingCtx,
|
||||
}
|
||||
@ -164,6 +166,7 @@ pub trait HirDisplay {
|
||||
limited_size: Option<usize>,
|
||||
omit_verbose_types: bool,
|
||||
display_target: DisplayTarget,
|
||||
display_kind: DisplayKind,
|
||||
closure_style: ClosureStyle,
|
||||
show_container_bounds: bool,
|
||||
) -> HirDisplayWrapper<'a, Self>
|
||||
@ -171,7 +174,7 @@ pub trait HirDisplay {
|
||||
Self: Sized,
|
||||
{
|
||||
assert!(
|
||||
!matches!(display_target, DisplayTarget::SourceCode { .. }),
|
||||
!matches!(display_kind, DisplayKind::SourceCode { .. }),
|
||||
"HirDisplayWrapper cannot fail with DisplaySourceCodeError, use HirDisplay::hir_fmt directly instead"
|
||||
);
|
||||
HirDisplayWrapper {
|
||||
@ -181,6 +184,7 @@ pub trait HirDisplay {
|
||||
limited_size,
|
||||
omit_verbose_types,
|
||||
display_target,
|
||||
display_kind,
|
||||
closure_style,
|
||||
show_container_bounds,
|
||||
}
|
||||
@ -191,7 +195,7 @@ pub trait HirDisplay {
|
||||
fn display<'a>(
|
||||
&'a self,
|
||||
db: &'a dyn HirDatabase,
|
||||
edition: Edition,
|
||||
display_target: DisplayTarget,
|
||||
) -> HirDisplayWrapper<'a, Self>
|
||||
where
|
||||
Self: Sized,
|
||||
@ -203,7 +207,8 @@ pub trait HirDisplay {
|
||||
limited_size: None,
|
||||
omit_verbose_types: false,
|
||||
closure_style: ClosureStyle::ImplFn,
|
||||
display_target: DisplayTarget::Diagnostics { edition },
|
||||
display_target,
|
||||
display_kind: DisplayKind::Diagnostics,
|
||||
show_container_bounds: false,
|
||||
}
|
||||
}
|
||||
@ -214,7 +219,7 @@ pub trait HirDisplay {
|
||||
&'a self,
|
||||
db: &'a dyn HirDatabase,
|
||||
max_size: Option<usize>,
|
||||
edition: Edition,
|
||||
display_target: DisplayTarget,
|
||||
) -> HirDisplayWrapper<'a, Self>
|
||||
where
|
||||
Self: Sized,
|
||||
@ -226,7 +231,8 @@ pub trait HirDisplay {
|
||||
limited_size: None,
|
||||
omit_verbose_types: true,
|
||||
closure_style: ClosureStyle::ImplFn,
|
||||
display_target: DisplayTarget::Diagnostics { edition },
|
||||
display_target,
|
||||
display_kind: DisplayKind::Diagnostics,
|
||||
show_container_bounds: false,
|
||||
}
|
||||
}
|
||||
@ -237,7 +243,7 @@ pub trait HirDisplay {
|
||||
&'a self,
|
||||
db: &'a dyn HirDatabase,
|
||||
limited_size: Option<usize>,
|
||||
edition: Edition,
|
||||
display_target: DisplayTarget,
|
||||
) -> HirDisplayWrapper<'a, Self>
|
||||
where
|
||||
Self: Sized,
|
||||
@ -249,7 +255,8 @@ pub trait HirDisplay {
|
||||
limited_size,
|
||||
omit_verbose_types: true,
|
||||
closure_style: ClosureStyle::ImplFn,
|
||||
display_target: DisplayTarget::Diagnostics { edition },
|
||||
display_target,
|
||||
display_kind: DisplayKind::Diagnostics,
|
||||
show_container_bounds: false,
|
||||
}
|
||||
}
|
||||
@ -272,7 +279,8 @@ pub trait HirDisplay {
|
||||
entity_limit: None,
|
||||
omit_verbose_types: false,
|
||||
closure_style: ClosureStyle::ImplFn,
|
||||
display_target: DisplayTarget::SourceCode { module_id, allow_opaque },
|
||||
display_target: DisplayTarget::from_crate(db, module_id.krate()),
|
||||
display_kind: DisplayKind::SourceCode { target_module_id: module_id, allow_opaque },
|
||||
show_container_bounds: false,
|
||||
bounds_formatting_ctx: Default::default(),
|
||||
}) {
|
||||
@ -284,29 +292,10 @@ pub trait HirDisplay {
|
||||
}
|
||||
|
||||
/// Returns a String representation of `self` for test purposes
|
||||
fn display_test<'a>(&'a self, db: &'a dyn HirDatabase) -> HirDisplayWrapper<'a, Self>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
HirDisplayWrapper {
|
||||
db,
|
||||
t: self,
|
||||
max_size: None,
|
||||
limited_size: None,
|
||||
omit_verbose_types: false,
|
||||
closure_style: ClosureStyle::ImplFn,
|
||||
display_target: DisplayTarget::Test,
|
||||
show_container_bounds: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a String representation of `self` that shows the constraint from
|
||||
/// the container for functions
|
||||
fn display_with_container_bounds<'a>(
|
||||
fn display_test<'a>(
|
||||
&'a self,
|
||||
db: &'a dyn HirDatabase,
|
||||
show_container_bounds: bool,
|
||||
edition: Edition,
|
||||
display_target: DisplayTarget,
|
||||
) -> HirDisplayWrapper<'a, Self>
|
||||
where
|
||||
Self: Sized,
|
||||
@ -318,21 +307,44 @@ pub trait HirDisplay {
|
||||
limited_size: None,
|
||||
omit_verbose_types: false,
|
||||
closure_style: ClosureStyle::ImplFn,
|
||||
display_target: DisplayTarget::Diagnostics { edition },
|
||||
display_target,
|
||||
display_kind: DisplayKind::Test,
|
||||
show_container_bounds: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a String representation of `self` that shows the constraint from
|
||||
/// the container for functions
|
||||
fn display_with_container_bounds<'a>(
|
||||
&'a self,
|
||||
db: &'a dyn HirDatabase,
|
||||
show_container_bounds: bool,
|
||||
display_target: DisplayTarget,
|
||||
) -> HirDisplayWrapper<'a, Self>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
HirDisplayWrapper {
|
||||
db,
|
||||
t: self,
|
||||
max_size: None,
|
||||
limited_size: None,
|
||||
omit_verbose_types: false,
|
||||
closure_style: ClosureStyle::ImplFn,
|
||||
display_target,
|
||||
display_kind: DisplayKind::Diagnostics,
|
||||
show_container_bounds,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl HirFormatter<'_> {
|
||||
pub fn krate(&self) -> CrateId {
|
||||
self.display_target.krate
|
||||
}
|
||||
|
||||
pub fn edition(&self) -> Edition {
|
||||
match self.display_target {
|
||||
DisplayTarget::Diagnostics { edition } => edition,
|
||||
DisplayTarget::SourceCode { module_id, .. } => {
|
||||
self.db.crate_graph()[module_id.krate()].edition
|
||||
}
|
||||
DisplayTarget::Test => Edition::CURRENT,
|
||||
}
|
||||
self.display_target.edition
|
||||
}
|
||||
|
||||
pub fn write_joined<T: HirDisplay>(
|
||||
@ -394,20 +406,33 @@ impl HirFormatter<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct DisplayTarget {
|
||||
krate: CrateId,
|
||||
pub edition: Edition,
|
||||
}
|
||||
|
||||
impl DisplayTarget {
|
||||
pub fn from_crate(db: &dyn HirDatabase, krate: CrateId) -> Self {
|
||||
let edition = db.crate_graph()[krate].edition;
|
||||
Self { krate, edition }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum DisplayTarget {
|
||||
pub enum DisplayKind {
|
||||
/// Display types for inlays, doc popups, autocompletion, etc...
|
||||
/// Showing `{unknown}` or not qualifying paths is fine here.
|
||||
/// There's no reason for this to fail.
|
||||
Diagnostics { edition: Edition },
|
||||
Diagnostics,
|
||||
/// Display types for inserting them in source files.
|
||||
/// The generated code should compile, so paths need to be qualified.
|
||||
SourceCode { module_id: ModuleId, allow_opaque: bool },
|
||||
SourceCode { target_module_id: ModuleId, allow_opaque: bool },
|
||||
/// Only for test purpose to keep real types
|
||||
Test,
|
||||
}
|
||||
|
||||
impl DisplayTarget {
|
||||
impl DisplayKind {
|
||||
fn is_source_code(self) -> bool {
|
||||
matches!(self, Self::SourceCode { .. })
|
||||
}
|
||||
@ -450,6 +475,7 @@ pub struct HirDisplayWrapper<'a, T> {
|
||||
limited_size: Option<usize>,
|
||||
omit_verbose_types: bool,
|
||||
closure_style: ClosureStyle,
|
||||
display_kind: DisplayKind,
|
||||
display_target: DisplayTarget,
|
||||
show_container_bounds: bool,
|
||||
}
|
||||
@ -479,6 +505,7 @@ impl<T: HirDisplay> HirDisplayWrapper<'_, T> {
|
||||
max_size: self.max_size,
|
||||
entity_limit: self.limited_size,
|
||||
omit_verbose_types: self.omit_verbose_types,
|
||||
display_kind: self.display_kind,
|
||||
display_target: self.display_target,
|
||||
closure_style: self.closure_style,
|
||||
show_container_bounds: self.show_container_bounds,
|
||||
@ -533,7 +560,7 @@ impl HirDisplay for ProjectionTy {
|
||||
// if we are projection on a type parameter, check if the projection target has bounds
|
||||
// itself, if so, we render them directly as `impl Bound` instead of the less useful
|
||||
// `<Param as Trait>::Assoc`
|
||||
if !f.display_target.is_source_code() {
|
||||
if !f.display_kind.is_source_code() {
|
||||
if let TyKind::Placeholder(idx) = self_ty.kind(Interner) {
|
||||
if !f.bounds_formatting_ctx.contains(self) {
|
||||
let db = f.db;
|
||||
@ -653,10 +680,8 @@ fn render_const_scalar(
|
||||
memory_map: &MemoryMap,
|
||||
ty: &Ty,
|
||||
) -> Result<(), HirDisplayError> {
|
||||
// FIXME: We need to get krate from the final callers of the hir display
|
||||
// infrastructure and have it here as a field on `f`.
|
||||
let trait_env =
|
||||
TraitEnvironment::empty(*f.db.crate_graph().crates_in_topological_order().last().unwrap());
|
||||
let trait_env = TraitEnvironment::empty(f.krate());
|
||||
let ty = normalize(f.db, trait_env.clone(), ty.clone());
|
||||
match ty.kind(Interner) {
|
||||
TyKind::Scalar(s) => match s {
|
||||
Scalar::Bool => write!(f, "{}", b[0] != 0),
|
||||
@ -1109,7 +1134,7 @@ impl HirDisplay for Ty {
|
||||
let def = from_chalk(db, *def);
|
||||
let sig = db.callable_item_signature(def).substitute(Interner, parameters);
|
||||
|
||||
if f.display_target.is_source_code() {
|
||||
if f.display_kind.is_source_code() {
|
||||
// `FnDef` is anonymous and there's no surface syntax for it. Show it as a
|
||||
// function pointer type.
|
||||
return sig.hir_fmt(f);
|
||||
@ -1198,8 +1223,8 @@ impl HirDisplay for Ty {
|
||||
}
|
||||
TyKind::Adt(AdtId(def_id), parameters) => {
|
||||
f.start_location_link((*def_id).into());
|
||||
match f.display_target {
|
||||
DisplayTarget::Diagnostics { .. } | DisplayTarget::Test => {
|
||||
match f.display_kind {
|
||||
DisplayKind::Diagnostics { .. } | DisplayKind::Test { .. } => {
|
||||
let name = match *def_id {
|
||||
hir_def::AdtId::StructId(it) => db.struct_data(it).name.clone(),
|
||||
hir_def::AdtId::UnionId(it) => db.union_data(it).name.clone(),
|
||||
@ -1207,7 +1232,7 @@ impl HirDisplay for Ty {
|
||||
};
|
||||
write!(f, "{}", name.display(f.db.upcast(), f.edition()))?;
|
||||
}
|
||||
DisplayTarget::SourceCode { module_id, allow_opaque: _ } => {
|
||||
DisplayKind::SourceCode { target_module_id: module_id, allow_opaque: _ } => {
|
||||
if let Some(path) = find_path::find_path(
|
||||
db.upcast(),
|
||||
ItemInNs::Types((*def_id).into()),
|
||||
@ -1246,7 +1271,7 @@ impl HirDisplay for Ty {
|
||||
let type_alias_data = db.type_alias_data(type_alias);
|
||||
|
||||
// Use placeholder associated types when the target is test (https://rust-lang.github.io/chalk/book/clauses/type_equality.html#placeholder-associated-types)
|
||||
if f.display_target.is_test() {
|
||||
if f.display_kind.is_test() {
|
||||
f.start_location_link(trait_.into());
|
||||
write!(f, "{}", trait_data.name.display(f.db.upcast(), f.edition()))?;
|
||||
f.end_location_link();
|
||||
@ -1275,7 +1300,7 @@ impl HirDisplay for Ty {
|
||||
f.end_location_link();
|
||||
}
|
||||
TyKind::OpaqueType(opaque_ty_id, parameters) => {
|
||||
if !f.display_target.allows_opaque() {
|
||||
if !f.display_kind.allows_opaque() {
|
||||
return Err(HirDisplayError::DisplaySourceCodeError(
|
||||
DisplaySourceCodeError::OpaqueType,
|
||||
));
|
||||
@ -1344,8 +1369,8 @@ impl HirDisplay for Ty {
|
||||
}
|
||||
}
|
||||
TyKind::Closure(id, substs) => {
|
||||
if f.display_target.is_source_code() {
|
||||
if !f.display_target.allows_opaque() {
|
||||
if f.display_kind.is_source_code() {
|
||||
if !f.display_kind.allows_opaque() {
|
||||
return Err(HirDisplayError::DisplaySourceCodeError(
|
||||
DisplaySourceCodeError::OpaqueType,
|
||||
));
|
||||
@ -1465,7 +1490,7 @@ impl HirDisplay for Ty {
|
||||
}
|
||||
TyKind::Alias(AliasTy::Projection(p_ty)) => p_ty.hir_fmt(f)?,
|
||||
TyKind::Alias(AliasTy::Opaque(opaque_ty)) => {
|
||||
if !f.display_target.allows_opaque() {
|
||||
if !f.display_kind.allows_opaque() {
|
||||
return Err(HirDisplayError::DisplaySourceCodeError(
|
||||
DisplaySourceCodeError::OpaqueType,
|
||||
));
|
||||
@ -1508,7 +1533,7 @@ impl HirDisplay for Ty {
|
||||
};
|
||||
}
|
||||
TyKind::Error => {
|
||||
if f.display_target.is_source_code() {
|
||||
if f.display_kind.is_source_code() {
|
||||
f.write_char('_')?;
|
||||
} else {
|
||||
write!(f, "{{unknown}}")?;
|
||||
@ -1516,7 +1541,7 @@ impl HirDisplay for Ty {
|
||||
}
|
||||
TyKind::InferenceVar(..) => write!(f, "_")?,
|
||||
TyKind::Coroutine(_, subst) => {
|
||||
if f.display_target.is_source_code() {
|
||||
if f.display_kind.is_source_code() {
|
||||
return Err(HirDisplayError::DisplaySourceCodeError(
|
||||
DisplaySourceCodeError::Coroutine,
|
||||
));
|
||||
@ -1573,7 +1598,7 @@ fn generic_args_sans_defaults<'ga>(
|
||||
generic_def: Option<hir_def::GenericDefId>,
|
||||
parameters: &'ga [GenericArg],
|
||||
) -> &'ga [GenericArg] {
|
||||
if f.display_target.is_source_code() || f.omit_verbose_types() {
|
||||
if f.display_kind.is_source_code() || f.omit_verbose_types() {
|
||||
match generic_def
|
||||
.map(|generic_def_id| f.db.generic_defaults(generic_def_id))
|
||||
.filter(|it| !it.is_empty())
|
||||
@ -1958,7 +1983,7 @@ impl HirDisplay for LifetimeData {
|
||||
write!(f, "{}", param_data.name.display(f.db.upcast(), f.edition()))?;
|
||||
Ok(())
|
||||
}
|
||||
_ if f.display_target.is_source_code() => write!(f, "'_"),
|
||||
_ if f.display_kind.is_source_code() => write!(f, "'_"),
|
||||
LifetimeData::BoundVar(idx) => idx.hir_fmt(f),
|
||||
LifetimeData::InferenceVar(_) => write!(f, "_"),
|
||||
LifetimeData::Static => write!(f, "'static"),
|
||||
|
@ -6,9 +6,8 @@ use base_db::ra_salsa::Cycle;
|
||||
use chalk_ir::{AdtId, FloatTy, IntTy, TyKind, UintTy};
|
||||
use hir_def::{
|
||||
layout::{
|
||||
Float, Integer, LayoutCalculator, LayoutCalculatorError,
|
||||
LayoutData, Primitive, ReprOptions, Scalar, StructKind, TargetDataLayout,
|
||||
WrappingRange,
|
||||
Float, Integer, LayoutCalculator, LayoutCalculatorError, LayoutData, Primitive,
|
||||
ReprOptions, Scalar, StructKind, TargetDataLayout, WrappingRange,
|
||||
},
|
||||
LocalFieldId, StructId,
|
||||
};
|
||||
@ -192,43 +191,52 @@ pub fn layout_of_ty_query(
|
||||
valid_range: WrappingRange { start: 0, end: 0x10FFFF },
|
||||
},
|
||||
),
|
||||
chalk_ir::Scalar::Int(i) => Layout::scalar(dl, scalar_unit(
|
||||
chalk_ir::Scalar::Int(i) => Layout::scalar(
|
||||
dl,
|
||||
Primitive::Int(
|
||||
match i {
|
||||
IntTy::Isize => dl.ptr_sized_integer(),
|
||||
IntTy::I8 => Integer::I8,
|
||||
IntTy::I16 => Integer::I16,
|
||||
IntTy::I32 => Integer::I32,
|
||||
IntTy::I64 => Integer::I64,
|
||||
IntTy::I128 => Integer::I128,
|
||||
},
|
||||
true,
|
||||
scalar_unit(
|
||||
dl,
|
||||
Primitive::Int(
|
||||
match i {
|
||||
IntTy::Isize => dl.ptr_sized_integer(),
|
||||
IntTy::I8 => Integer::I8,
|
||||
IntTy::I16 => Integer::I16,
|
||||
IntTy::I32 => Integer::I32,
|
||||
IntTy::I64 => Integer::I64,
|
||||
IntTy::I128 => Integer::I128,
|
||||
},
|
||||
true,
|
||||
),
|
||||
),
|
||||
)),
|
||||
chalk_ir::Scalar::Uint(i) => Layout::scalar(dl, scalar_unit(
|
||||
),
|
||||
chalk_ir::Scalar::Uint(i) => Layout::scalar(
|
||||
dl,
|
||||
Primitive::Int(
|
||||
match i {
|
||||
UintTy::Usize => dl.ptr_sized_integer(),
|
||||
UintTy::U8 => Integer::I8,
|
||||
UintTy::U16 => Integer::I16,
|
||||
UintTy::U32 => Integer::I32,
|
||||
UintTy::U64 => Integer::I64,
|
||||
UintTy::U128 => Integer::I128,
|
||||
},
|
||||
false,
|
||||
scalar_unit(
|
||||
dl,
|
||||
Primitive::Int(
|
||||
match i {
|
||||
UintTy::Usize => dl.ptr_sized_integer(),
|
||||
UintTy::U8 => Integer::I8,
|
||||
UintTy::U16 => Integer::I16,
|
||||
UintTy::U32 => Integer::I32,
|
||||
UintTy::U64 => Integer::I64,
|
||||
UintTy::U128 => Integer::I128,
|
||||
},
|
||||
false,
|
||||
),
|
||||
),
|
||||
)),
|
||||
chalk_ir::Scalar::Float(f) => Layout::scalar(dl, scalar_unit(
|
||||
),
|
||||
chalk_ir::Scalar::Float(f) => Layout::scalar(
|
||||
dl,
|
||||
Primitive::Float(match f {
|
||||
FloatTy::F16 => Float::F16,
|
||||
FloatTy::F32 => Float::F32,
|
||||
FloatTy::F64 => Float::F64,
|
||||
FloatTy::F128 => Float::F128,
|
||||
}),
|
||||
)),
|
||||
scalar_unit(
|
||||
dl,
|
||||
Primitive::Float(match f {
|
||||
FloatTy::F16 => Float::F16,
|
||||
FloatTy::F32 => Float::F32,
|
||||
FloatTy::F64 => Float::F64,
|
||||
FloatTy::F128 => Float::F128,
|
||||
}),
|
||||
),
|
||||
),
|
||||
},
|
||||
TyKind::Tuple(len, tys) => {
|
||||
let kind = if *len == 0 { StructKind::AlwaysSized } else { StructKind::MaybeUnsized };
|
||||
@ -341,6 +349,9 @@ pub fn layout_of_ty_query(
|
||||
TyKind::Error => return Err(LayoutError::HasErrorType),
|
||||
TyKind::AssociatedType(id, subst) => {
|
||||
// Try again with `TyKind::Alias` to normalize the associated type.
|
||||
// Usually we should not try to normalize `TyKind::AssociatedType`, but layout calculation is used
|
||||
// in monomorphized MIR where this is okay. If outside monomorphization, this will lead to cycle,
|
||||
// which we will recover from with an error.
|
||||
let ty = TyKind::Alias(chalk_ir::AliasTy::Projection(ProjectionTy {
|
||||
associated_ty_id: *id,
|
||||
substitution: subst.clone(),
|
||||
|
@ -342,7 +342,7 @@ fn simd_types() {
|
||||
check_size_and_align(
|
||||
r#"
|
||||
#[repr(simd)]
|
||||
struct SimdType(i64, i64);
|
||||
struct SimdType([i64; 2]);
|
||||
struct Goal(SimdType);
|
||||
"#,
|
||||
"",
|
||||
|
@ -21,9 +21,6 @@ extern crate rustc_pattern_analysis;
|
||||
#[cfg(not(feature = "in-rust-tree"))]
|
||||
extern crate ra_ap_rustc_pattern_analysis as rustc_pattern_analysis;
|
||||
|
||||
#[cfg(not(feature = "in-rust-tree"))]
|
||||
extern crate ra_ap_rustc_hashes as rustc_hashes;
|
||||
|
||||
mod builder;
|
||||
mod chalk_db;
|
||||
mod chalk_ext;
|
||||
@ -73,13 +70,15 @@ use intern::{sym, Symbol};
|
||||
use la_arena::{Arena, Idx};
|
||||
use mir::{MirEvalError, VTableMap};
|
||||
use rustc_hash::{FxBuildHasher, FxHashMap, FxHashSet};
|
||||
use span::Edition;
|
||||
use syntax::ast::{make, ConstArg};
|
||||
use traits::FnTrait;
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{
|
||||
consteval::unknown_const, db::HirDatabase, display::HirDisplay, generics::Generics,
|
||||
consteval::unknown_const,
|
||||
db::HirDatabase,
|
||||
display::{DisplayTarget, HirDisplay},
|
||||
generics::Generics,
|
||||
infer::unify::InferenceTable,
|
||||
};
|
||||
|
||||
@ -1041,7 +1040,7 @@ where
|
||||
pub fn known_const_to_ast(
|
||||
konst: &Const,
|
||||
db: &dyn HirDatabase,
|
||||
edition: Edition,
|
||||
display_target: DisplayTarget,
|
||||
) -> Option<ConstArg> {
|
||||
if let ConstValue::Concrete(c) = &konst.interned().value {
|
||||
match c.interned {
|
||||
@ -1052,7 +1051,7 @@ pub fn known_const_to_ast(
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
Some(make::expr_const_value(konst.display(db, edition).to_string().as_str()))
|
||||
Some(make::expr_const_value(konst.display(db, display_target).to_string().as_str()))
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
|
@ -5,7 +5,7 @@ use std::{collections::hash_map::Entry, fmt::Display, iter};
|
||||
use crate::{
|
||||
consteval::usize_const,
|
||||
db::HirDatabase,
|
||||
display::HirDisplay,
|
||||
display::{DisplayTarget, HirDisplay},
|
||||
infer::{normalize, PointerCast},
|
||||
lang_items::is_box,
|
||||
mapping::ToChalk,
|
||||
@ -168,7 +168,7 @@ impl<V, T> ProjectionElem<V, T> {
|
||||
_ => {
|
||||
never!(
|
||||
"Overloaded deref on type {} is not a projection",
|
||||
base.display(db, db.crate_graph()[krate].edition)
|
||||
base.display(db, DisplayTarget::from_crate(db, krate))
|
||||
);
|
||||
TyKind::Error.intern(Interner)
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ use triomphe::Arc;
|
||||
|
||||
use crate::{
|
||||
db::{HirDatabase, InternedClosure},
|
||||
display::DisplayTarget,
|
||||
mir::Operand,
|
||||
utils::ClosureSubst,
|
||||
ClosureId, Interner, Substitution, Ty, TyExt, TypeFlags,
|
||||
@ -422,7 +423,10 @@ fn ever_initialized_map(
|
||||
let Some(terminator) = &block.terminator else {
|
||||
never!(
|
||||
"Terminator should be none only in construction.\nThe body:\n{}",
|
||||
body.pretty_print(db)
|
||||
body.pretty_print(
|
||||
db,
|
||||
DisplayTarget::from_crate(db, body.owner.krate(db.upcast()))
|
||||
)
|
||||
);
|
||||
return;
|
||||
};
|
||||
|
@ -24,7 +24,7 @@ use rustc_apfloat::{
|
||||
Float,
|
||||
};
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use span::{Edition, FileId};
|
||||
use span::FileId;
|
||||
use stdx::never;
|
||||
use syntax::{SyntaxNodePtr, TextRange};
|
||||
use triomphe::Arc;
|
||||
@ -32,7 +32,7 @@ use triomphe::Arc;
|
||||
use crate::{
|
||||
consteval::{intern_const_scalar, try_const_usize, ConstEvalError},
|
||||
db::{HirDatabase, InternedClosure},
|
||||
display::{ClosureStyle, HirDisplay},
|
||||
display::{ClosureStyle, DisplayTarget, HirDisplay},
|
||||
infer::PointerCast,
|
||||
layout::{Layout, LayoutError, RustcEnumVariantIdx},
|
||||
mapping::from_chalk,
|
||||
@ -302,7 +302,7 @@ impl Address {
|
||||
}
|
||||
}
|
||||
|
||||
fn to_bytes(&self) -> [u8; mem::size_of::<usize>()] {
|
||||
fn to_bytes(&self) -> [u8; size_of::<usize>()] {
|
||||
usize::to_le_bytes(self.to_usize())
|
||||
}
|
||||
|
||||
@ -359,7 +359,7 @@ impl MirEvalError {
|
||||
f: &mut String,
|
||||
db: &dyn HirDatabase,
|
||||
span_formatter: impl Fn(FileId, TextRange) -> String,
|
||||
edition: Edition,
|
||||
display_target: DisplayTarget,
|
||||
) -> std::result::Result<(), std::fmt::Error> {
|
||||
writeln!(f, "Mir eval error:")?;
|
||||
let mut err = self;
|
||||
@ -372,7 +372,7 @@ impl MirEvalError {
|
||||
writeln!(
|
||||
f,
|
||||
"In function {} ({:?})",
|
||||
function_name.name.display(db.upcast(), edition),
|
||||
function_name.name.display(db.upcast(), display_target.edition),
|
||||
func
|
||||
)?;
|
||||
}
|
||||
@ -417,7 +417,7 @@ impl MirEvalError {
|
||||
write!(
|
||||
f,
|
||||
"Layout for type `{}` is not available due {err:?}",
|
||||
ty.display(db, edition).with_closure_style(ClosureStyle::ClosureWithId)
|
||||
ty.display(db, display_target).with_closure_style(ClosureStyle::ClosureWithId)
|
||||
)?;
|
||||
}
|
||||
MirEvalError::MirLowerError(func, err) => {
|
||||
@ -428,12 +428,15 @@ impl MirEvalError {
|
||||
let substs = generics.placeholder_subst(db);
|
||||
db.impl_self_ty(impl_id)
|
||||
.substitute(Interner, &substs)
|
||||
.display(db, edition)
|
||||
.display(db, display_target)
|
||||
.to_string()
|
||||
}),
|
||||
ItemContainerId::TraitId(it) => {
|
||||
Some(db.trait_data(it).name.display(db.upcast(), edition).to_string())
|
||||
}
|
||||
ItemContainerId::TraitId(it) => Some(
|
||||
db.trait_data(it)
|
||||
.name
|
||||
.display(db.upcast(), display_target.edition)
|
||||
.to_string(),
|
||||
),
|
||||
_ => None,
|
||||
};
|
||||
writeln!(
|
||||
@ -441,17 +444,17 @@ impl MirEvalError {
|
||||
"MIR lowering for function `{}{}{}` ({:?}) failed due:",
|
||||
self_.as_deref().unwrap_or_default(),
|
||||
if self_.is_some() { "::" } else { "" },
|
||||
function_name.name.display(db.upcast(), edition),
|
||||
function_name.name.display(db.upcast(), display_target.edition),
|
||||
func
|
||||
)?;
|
||||
err.pretty_print(f, db, span_formatter, edition)?;
|
||||
err.pretty_print(f, db, span_formatter, display_target)?;
|
||||
}
|
||||
MirEvalError::ConstEvalError(name, err) => {
|
||||
MirLowerError::ConstEvalError((**name).into(), err.clone()).pretty_print(
|
||||
f,
|
||||
db,
|
||||
span_formatter,
|
||||
edition,
|
||||
display_target,
|
||||
)?;
|
||||
}
|
||||
MirEvalError::UndefinedBehavior(_)
|
||||
@ -589,7 +592,7 @@ pub fn interpret_mir(
|
||||
let ty = body.locals[return_slot()].ty.clone();
|
||||
let mut evaluator = Evaluator::new(db, body.owner, assert_placeholder_ty_is_unused, trait_env)?;
|
||||
let it: Result<Const> = (|| {
|
||||
if evaluator.ptr_size() != std::mem::size_of::<usize>() {
|
||||
if evaluator.ptr_size() != size_of::<usize>() {
|
||||
not_supported!("targets with different pointer size from host");
|
||||
}
|
||||
let interval = evaluator.interpret_mir(body.clone(), None.into_iter())?;
|
||||
|
@ -14,6 +14,7 @@ use intern::{sym, Symbol};
|
||||
use stdx::never;
|
||||
|
||||
use crate::{
|
||||
display::DisplayTarget,
|
||||
error_lifetime,
|
||||
mir::eval::{
|
||||
pad16, Address, AdtId, Arc, BuiltinType, Evaluator, FunctionId, HasModule, HirDisplay,
|
||||
@ -835,8 +836,7 @@ impl Evaluator<'_> {
|
||||
// render full paths.
|
||||
Err(_) => {
|
||||
let krate = locals.body.owner.krate(self.db.upcast());
|
||||
let edition = self.db.crate_graph()[krate].edition;
|
||||
ty.display(self.db, edition).to_string()
|
||||
ty.display(self.db, DisplayTarget::from_crate(self.db, krate)).to_string()
|
||||
}
|
||||
};
|
||||
let len = ty_name.len();
|
||||
|
@ -3,6 +3,7 @@ use span::{Edition, EditionedFileId};
|
||||
use syntax::{TextRange, TextSize};
|
||||
use test_fixture::WithFixture;
|
||||
|
||||
use crate::display::DisplayTarget;
|
||||
use crate::{db::HirDatabase, mir::MirLowerError, test_db::TestDB, Interner, Substitution};
|
||||
|
||||
use super::{interpret_mir, MirEvalError};
|
||||
@ -67,7 +68,9 @@ fn check_pass_and_stdio(
|
||||
let span_formatter = |file, range: TextRange| {
|
||||
format!("{:?} {:?}..{:?}", file, line_index(range.start()), line_index(range.end()))
|
||||
};
|
||||
e.pretty_print(&mut err, &db, span_formatter, Edition::CURRENT).unwrap();
|
||||
let krate = db.module_for_file(file_id).krate();
|
||||
e.pretty_print(&mut err, &db, span_formatter, DisplayTarget::from_crate(&db, krate))
|
||||
.unwrap();
|
||||
panic!("Error in interpreting: {err}");
|
||||
}
|
||||
Ok((stdout, stderr)) => {
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
use std::{fmt::Write, iter, mem};
|
||||
|
||||
use base_db::ra_salsa::Cycle;
|
||||
use base_db::{ra_salsa::Cycle, CrateId};
|
||||
use chalk_ir::{BoundVar, ConstData, DebruijnIndex, TyKind};
|
||||
use hir_def::{
|
||||
data::adt::{StructKind, VariantData},
|
||||
@ -29,7 +29,7 @@ use triomphe::Arc;
|
||||
use crate::{
|
||||
consteval::ConstEvalError,
|
||||
db::{HirDatabase, InternedClosure},
|
||||
display::{hir_display_with_types_map, HirDisplay},
|
||||
display::{hir_display_with_types_map, DisplayTarget, HirDisplay},
|
||||
error_lifetime,
|
||||
generics::generics,
|
||||
infer::{cast::CastTy, unify::InferenceTable, CaptureKind, CapturedItem, TypeMismatch},
|
||||
@ -160,17 +160,17 @@ impl MirLowerError {
|
||||
f: &mut String,
|
||||
db: &dyn HirDatabase,
|
||||
span_formatter: impl Fn(FileId, TextRange) -> String,
|
||||
edition: Edition,
|
||||
display_target: DisplayTarget,
|
||||
) -> std::result::Result<(), std::fmt::Error> {
|
||||
match self {
|
||||
MirLowerError::ConstEvalError(name, e) => {
|
||||
writeln!(f, "In evaluating constant {name}")?;
|
||||
match &**e {
|
||||
ConstEvalError::MirLowerError(e) => {
|
||||
e.pretty_print(f, db, span_formatter, edition)?
|
||||
e.pretty_print(f, db, span_formatter, display_target)?
|
||||
}
|
||||
ConstEvalError::MirEvalError(e) => {
|
||||
e.pretty_print(f, db, span_formatter, edition)?
|
||||
e.pretty_print(f, db, span_formatter, display_target)?
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -179,15 +179,15 @@ impl MirLowerError {
|
||||
writeln!(
|
||||
f,
|
||||
"Missing function definition for {}",
|
||||
body.pretty_print_expr(db.upcast(), *owner, *it, edition)
|
||||
body.pretty_print_expr(db.upcast(), *owner, *it, display_target.edition)
|
||||
)?;
|
||||
}
|
||||
MirLowerError::HasErrors => writeln!(f, "Type inference result contains errors")?,
|
||||
MirLowerError::TypeMismatch(e) => writeln!(
|
||||
f,
|
||||
"Type mismatch: Expected {}, found {}",
|
||||
e.expected.display(db, edition),
|
||||
e.actual.display(db, edition),
|
||||
e.expected.display(db, display_target),
|
||||
e.actual.display(db, display_target),
|
||||
)?,
|
||||
MirLowerError::GenericArgNotProvided(id, subst) => {
|
||||
let parent = id.parent;
|
||||
@ -195,11 +195,14 @@ impl MirLowerError {
|
||||
writeln!(
|
||||
f,
|
||||
"Generic arg not provided for {}",
|
||||
param.name().unwrap_or(&Name::missing()).display(db.upcast(), edition)
|
||||
param
|
||||
.name()
|
||||
.unwrap_or(&Name::missing())
|
||||
.display(db.upcast(), display_target.edition)
|
||||
)?;
|
||||
writeln!(f, "Provided args: [")?;
|
||||
for g in subst.iter(Interner) {
|
||||
write!(f, " {},", g.display(db, edition))?;
|
||||
write!(f, " {},", g.display(db, display_target))?;
|
||||
}
|
||||
writeln!(f, "]")?;
|
||||
}
|
||||
@ -251,11 +254,11 @@ impl MirLowerError {
|
||||
fn unresolved_path(
|
||||
db: &dyn HirDatabase,
|
||||
p: &Path,
|
||||
edition: Edition,
|
||||
display_target: DisplayTarget,
|
||||
types_map: &TypesMap,
|
||||
) -> Self {
|
||||
Self::UnresolvedName(
|
||||
hir_display_with_types_map(p, types_map).display(db, edition).to_string(),
|
||||
hir_display_with_types_map(p, types_map).display(db, display_target).to_string(),
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -462,7 +465,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
||||
MirLowerError::unresolved_path(
|
||||
self.db,
|
||||
p,
|
||||
self.edition(),
|
||||
DisplayTarget::from_crate(self.db, self.krate()),
|
||||
&self.body.types,
|
||||
)
|
||||
})?;
|
||||
@ -838,7 +841,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
||||
self.infer.variant_resolution_for_expr(expr_id).ok_or_else(|| match path {
|
||||
Some(p) => MirLowerError::UnresolvedName(
|
||||
hir_display_with_types_map(&**p, &self.body.types)
|
||||
.display(self.db, self.edition())
|
||||
.display(self.db, self.display_target())
|
||||
.to_string(),
|
||||
),
|
||||
None => MirLowerError::RecordLiteralWithoutPath,
|
||||
@ -1362,9 +1365,16 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
||||
match &self.body.exprs[*loc] {
|
||||
Expr::Literal(l) => self.lower_literal_to_operand(ty, l),
|
||||
Expr::Path(c) => {
|
||||
let edition = self.edition();
|
||||
let unresolved_name =
|
||||
|| MirLowerError::unresolved_path(self.db, c, edition, &self.body.types);
|
||||
let owner = self.owner;
|
||||
let db = self.db;
|
||||
let unresolved_name = || {
|
||||
MirLowerError::unresolved_path(
|
||||
self.db,
|
||||
c,
|
||||
DisplayTarget::from_crate(db, owner.krate(db.upcast())),
|
||||
&self.body.types,
|
||||
)
|
||||
};
|
||||
let pr = self
|
||||
.resolver
|
||||
.resolve_path_in_value_ns(self.db.upcast(), c, HygieneId::ROOT)
|
||||
@ -1394,7 +1404,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
||||
.layout_of_ty(ty.clone(), self.db.trait_environment_for_body(self.owner))
|
||||
.map(|it| it.size.bytes_usize())
|
||||
};
|
||||
const USIZE_SIZE: usize = mem::size_of::<usize>();
|
||||
const USIZE_SIZE: usize = size_of::<usize>();
|
||||
let bytes: Box<[_]> = match l {
|
||||
hir_def::hir::Literal::String(b) => {
|
||||
let b = b.as_str();
|
||||
@ -1910,8 +1920,15 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
||||
}
|
||||
|
||||
fn edition(&self) -> Edition {
|
||||
let krate = self.owner.krate(self.db.upcast());
|
||||
self.db.crate_graph()[krate].edition
|
||||
self.db.crate_graph()[self.krate()].edition
|
||||
}
|
||||
|
||||
fn krate(&self) -> CrateId {
|
||||
self.owner.krate(self.db.upcast())
|
||||
}
|
||||
|
||||
fn display_target(&self) -> DisplayTarget {
|
||||
DisplayTarget::from_crate(self.db, self.krate())
|
||||
}
|
||||
|
||||
fn drop_until_scope(
|
||||
|
@ -350,7 +350,12 @@ impl MirLowerCtx<'_> {
|
||||
)?,
|
||||
None => {
|
||||
let unresolved_name = || {
|
||||
MirLowerError::unresolved_path(self.db, p, self.edition(), &self.body.types)
|
||||
MirLowerError::unresolved_path(
|
||||
self.db,
|
||||
p,
|
||||
self.display_target(),
|
||||
&self.body.types,
|
||||
)
|
||||
};
|
||||
let hygiene = self.body.pat_path_hygiene(pattern);
|
||||
let pr = self
|
||||
|
@ -9,11 +9,10 @@ use either::Either;
|
||||
use hir_def::{expr_store::Body, hir::BindingId};
|
||||
use hir_expand::{name::Name, Lookup};
|
||||
use la_arena::ArenaMap;
|
||||
use span::Edition;
|
||||
|
||||
use crate::{
|
||||
db::HirDatabase,
|
||||
display::{ClosureStyle, HirDisplay},
|
||||
display::{ClosureStyle, DisplayTarget, HirDisplay},
|
||||
mir::{PlaceElem, ProjectionElem, StatementKind, TerminatorKind},
|
||||
ClosureId,
|
||||
};
|
||||
@ -39,17 +38,21 @@ macro_rules! wln {
|
||||
}
|
||||
|
||||
impl MirBody {
|
||||
pub fn pretty_print(&self, db: &dyn HirDatabase) -> String {
|
||||
pub fn pretty_print(&self, db: &dyn HirDatabase, display_target: DisplayTarget) -> String {
|
||||
let hir_body = db.body(self.owner);
|
||||
let mut ctx = MirPrettyCtx::new(self, &hir_body, db);
|
||||
let mut ctx = MirPrettyCtx::new(self, &hir_body, db, display_target);
|
||||
ctx.for_body(|this| match ctx.body.owner {
|
||||
hir_def::DefWithBodyId::FunctionId(id) => {
|
||||
let data = db.function_data(id);
|
||||
w!(this, "fn {}() ", data.name.display(db.upcast(), Edition::LATEST));
|
||||
w!(this, "fn {}() ", data.name.display(db.upcast(), this.display_target.edition));
|
||||
}
|
||||
hir_def::DefWithBodyId::StaticId(id) => {
|
||||
let data = db.static_data(id);
|
||||
w!(this, "static {}: _ = ", data.name.display(db.upcast(), Edition::LATEST));
|
||||
w!(
|
||||
this,
|
||||
"static {}: _ = ",
|
||||
data.name.display(db.upcast(), this.display_target.edition)
|
||||
);
|
||||
}
|
||||
hir_def::DefWithBodyId::ConstId(id) => {
|
||||
let data = db.const_data(id);
|
||||
@ -59,7 +62,7 @@ impl MirBody {
|
||||
data.name
|
||||
.as_ref()
|
||||
.unwrap_or(&Name::missing())
|
||||
.display(db.upcast(), Edition::LATEST)
|
||||
.display(db.upcast(), this.display_target.edition)
|
||||
);
|
||||
}
|
||||
hir_def::DefWithBodyId::VariantId(id) => {
|
||||
@ -70,10 +73,10 @@ impl MirBody {
|
||||
"enum {}::{} = ",
|
||||
enum_loc.id.item_tree(db.upcast())[enum_loc.id.value]
|
||||
.name
|
||||
.display(db.upcast(), Edition::LATEST),
|
||||
.display(db.upcast(), this.display_target.edition),
|
||||
loc.id.item_tree(db.upcast())[loc.id.value]
|
||||
.name
|
||||
.display(db.upcast(), Edition::LATEST),
|
||||
.display(db.upcast(), this.display_target.edition),
|
||||
)
|
||||
}
|
||||
hir_def::DefWithBodyId::InTypeConstId(id) => {
|
||||
@ -85,14 +88,14 @@ impl MirBody {
|
||||
|
||||
// String with lines is rendered poorly in `dbg` macros, which I use very much, so this
|
||||
// function exists to solve that.
|
||||
pub fn dbg(&self, db: &dyn HirDatabase) -> impl Debug {
|
||||
pub fn dbg(&self, db: &dyn HirDatabase, display_target: DisplayTarget) -> impl Debug {
|
||||
struct StringDbg(String);
|
||||
impl Debug for StringDbg {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str(&self.0)
|
||||
}
|
||||
}
|
||||
StringDbg(self.pretty_print(db))
|
||||
StringDbg(self.pretty_print(db, display_target))
|
||||
}
|
||||
}
|
||||
|
||||
@ -103,6 +106,7 @@ struct MirPrettyCtx<'a> {
|
||||
result: String,
|
||||
indent: String,
|
||||
local_to_binding: ArenaMap<LocalId, BindingId>,
|
||||
display_target: DisplayTarget,
|
||||
}
|
||||
|
||||
impl Write for MirPrettyCtx<'_> {
|
||||
@ -182,7 +186,12 @@ impl<'a> MirPrettyCtx<'a> {
|
||||
wln!(self, "}}");
|
||||
}
|
||||
|
||||
fn new(body: &'a MirBody, hir_body: &'a Body, db: &'a dyn HirDatabase) -> Self {
|
||||
fn new(
|
||||
body: &'a MirBody,
|
||||
hir_body: &'a Body,
|
||||
db: &'a dyn HirDatabase,
|
||||
display_target: DisplayTarget,
|
||||
) -> Self {
|
||||
let local_to_binding = body.local_to_binding_map();
|
||||
MirPrettyCtx {
|
||||
body,
|
||||
@ -191,6 +200,7 @@ impl<'a> MirPrettyCtx<'a> {
|
||||
indent: String::new(),
|
||||
local_to_binding,
|
||||
hir_body,
|
||||
display_target,
|
||||
}
|
||||
}
|
||||
|
||||
@ -208,7 +218,7 @@ impl<'a> MirPrettyCtx<'a> {
|
||||
wln!(
|
||||
self,
|
||||
"let {}: {};",
|
||||
self.local_name(id).display_test(self.db),
|
||||
self.local_name(id).display_test(self.db, self.display_target),
|
||||
self.hir_display(&local.ty)
|
||||
);
|
||||
}
|
||||
@ -242,14 +252,14 @@ impl<'a> MirPrettyCtx<'a> {
|
||||
wln!(
|
||||
this,
|
||||
"StorageDead({})",
|
||||
this.local_name(*p).display_test(self.db)
|
||||
this.local_name(*p).display_test(this.db, this.display_target)
|
||||
);
|
||||
}
|
||||
StatementKind::StorageLive(p) => {
|
||||
wln!(
|
||||
this,
|
||||
"StorageLive({})",
|
||||
this.local_name(*p).display_test(self.db)
|
||||
this.local_name(*p).display_test(this.db, this.display_target)
|
||||
);
|
||||
}
|
||||
StatementKind::Deinit(p) => {
|
||||
@ -313,7 +323,7 @@ impl<'a> MirPrettyCtx<'a> {
|
||||
fn f(this: &mut MirPrettyCtx<'_>, local: LocalId, projections: &[PlaceElem]) {
|
||||
let Some((last, head)) = projections.split_last() else {
|
||||
// no projection
|
||||
w!(this, "{}", this.local_name(local).display_test(this.db));
|
||||
w!(this, "{}", this.local_name(local).display_test(this.db, this.display_target));
|
||||
return;
|
||||
};
|
||||
match last {
|
||||
@ -333,13 +343,17 @@ impl<'a> MirPrettyCtx<'a> {
|
||||
w!(
|
||||
this,
|
||||
" as {}).{}",
|
||||
variant_name.display(this.db.upcast(), Edition::LATEST),
|
||||
name.display(this.db.upcast(), Edition::LATEST)
|
||||
variant_name.display(this.db.upcast(), this.display_target.edition),
|
||||
name.display(this.db.upcast(), this.display_target.edition)
|
||||
);
|
||||
}
|
||||
hir_def::VariantId::StructId(_) | hir_def::VariantId::UnionId(_) => {
|
||||
f(this, local, head);
|
||||
w!(this, ".{}", name.display(this.db.upcast(), Edition::LATEST));
|
||||
w!(
|
||||
this,
|
||||
".{}",
|
||||
name.display(this.db.upcast(), this.display_target.edition)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -353,7 +367,11 @@ impl<'a> MirPrettyCtx<'a> {
|
||||
}
|
||||
ProjectionElem::Index(l) => {
|
||||
f(this, local, head);
|
||||
w!(this, "[{}]", this.local_name(*l).display_test(this.db));
|
||||
w!(
|
||||
this,
|
||||
"[{}]",
|
||||
this.local_name(*l).display_test(this.db, this.display_target)
|
||||
);
|
||||
}
|
||||
it => {
|
||||
f(this, local, head);
|
||||
@ -403,7 +421,7 @@ impl<'a> MirPrettyCtx<'a> {
|
||||
Rvalue::Repeat(op, len) => {
|
||||
w!(self, "[");
|
||||
self.operand(op);
|
||||
w!(self, "; {}]", len.display_test(self.db));
|
||||
w!(self, "; {}]", len.display_test(self.db, self.display_target));
|
||||
}
|
||||
Rvalue::Aggregate(AggregateKind::Adt(_, _), it) => {
|
||||
w!(self, "Adt(");
|
||||
@ -478,6 +496,7 @@ impl<'a> MirPrettyCtx<'a> {
|
||||
}
|
||||
|
||||
fn hir_display<T: HirDisplay>(&self, ty: &'a T) -> impl Display + 'a {
|
||||
ty.display_test(self.db).with_closure_style(ClosureStyle::ClosureWithSubst)
|
||||
ty.display_test(self.db, self.display_target)
|
||||
.with_closure_style(ClosureStyle::ClosureWithSubst)
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ mod type_alias_impl_traits;
|
||||
use std::env;
|
||||
use std::sync::LazyLock;
|
||||
|
||||
use base_db::SourceDatabaseFileInputExt as _;
|
||||
use base_db::{CrateId, SourceDatabaseFileInputExt as _};
|
||||
use expect_test::Expect;
|
||||
use hir_def::{
|
||||
db::DefDatabase,
|
||||
@ -41,7 +41,7 @@ use triomphe::Arc;
|
||||
|
||||
use crate::{
|
||||
db::HirDatabase,
|
||||
display::HirDisplay,
|
||||
display::{DisplayTarget, HirDisplay},
|
||||
infer::{Adjustment, TypeMismatch},
|
||||
test_db::TestDB,
|
||||
InferenceResult, Ty,
|
||||
@ -124,7 +124,7 @@ fn check_impl(
|
||||
}
|
||||
assert!(had_annotations || allow_none, "no `//^` annotations found");
|
||||
|
||||
let mut defs: Vec<DefWithBodyId> = Vec::new();
|
||||
let mut defs: Vec<(DefWithBodyId, CrateId)> = Vec::new();
|
||||
for file_id in files {
|
||||
let module = db.module_for_file_opt(file_id);
|
||||
let module = match module {
|
||||
@ -133,16 +133,17 @@ fn check_impl(
|
||||
};
|
||||
let def_map = module.def_map(&db);
|
||||
visit_module(&db, &def_map, module.local_id, &mut |it| {
|
||||
defs.push(match it {
|
||||
let def = match it {
|
||||
ModuleDefId::FunctionId(it) => it.into(),
|
||||
ModuleDefId::EnumVariantId(it) => it.into(),
|
||||
ModuleDefId::ConstId(it) => it.into(),
|
||||
ModuleDefId::StaticId(it) => it.into(),
|
||||
_ => return,
|
||||
})
|
||||
};
|
||||
defs.push((def, module.krate()))
|
||||
});
|
||||
}
|
||||
defs.sort_by_key(|def| match def {
|
||||
defs.sort_by_key(|(def, _)| match def {
|
||||
DefWithBodyId::FunctionId(it) => {
|
||||
let loc = it.lookup(&db);
|
||||
loc.source(&db).value.syntax().text_range().start()
|
||||
@ -162,7 +163,8 @@ fn check_impl(
|
||||
DefWithBodyId::InTypeConstId(it) => it.source(&db).syntax().text_range().start(),
|
||||
});
|
||||
let mut unexpected_type_mismatches = String::new();
|
||||
for def in defs {
|
||||
for (def, krate) in defs {
|
||||
let display_target = DisplayTarget::from_crate(&db, krate);
|
||||
let (body, body_source_map) = db.body_with_source_map(def);
|
||||
let inference_result = db.infer(def);
|
||||
|
||||
@ -179,7 +181,7 @@ fn check_impl(
|
||||
let actual = if display_source {
|
||||
ty.display_source_code(&db, def.module(&db), true).unwrap()
|
||||
} else {
|
||||
ty.display_test(&db).to_string()
|
||||
ty.display_test(&db, display_target).to_string()
|
||||
};
|
||||
assert_eq!(actual, expected, "type annotation differs at {:#?}", range.range);
|
||||
}
|
||||
@ -195,7 +197,7 @@ fn check_impl(
|
||||
let actual = if display_source {
|
||||
ty.display_source_code(&db, def.module(&db), true).unwrap()
|
||||
} else {
|
||||
ty.display_test(&db).to_string()
|
||||
ty.display_test(&db, display_target).to_string()
|
||||
};
|
||||
assert_eq!(actual, expected, "type annotation differs at {:#?}", range.range);
|
||||
}
|
||||
@ -224,8 +226,8 @@ fn check_impl(
|
||||
let range = node.as_ref().original_file_range_rooted(&db);
|
||||
let actual = format!(
|
||||
"expected {}, got {}",
|
||||
mismatch.expected.display_test(&db),
|
||||
mismatch.actual.display_test(&db)
|
||||
mismatch.expected.display_test(&db, display_target),
|
||||
mismatch.actual.display_test(&db, display_target)
|
||||
);
|
||||
match mismatches.remove(&range) {
|
||||
Some(annotation) => assert_eq!(actual, annotation),
|
||||
@ -299,7 +301,9 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String {
|
||||
|
||||
let mut infer_def = |inference_result: Arc<InferenceResult>,
|
||||
body: Arc<Body>,
|
||||
body_source_map: Arc<BodySourceMap>| {
|
||||
body_source_map: Arc<BodySourceMap>,
|
||||
krate: CrateId| {
|
||||
let display_target = DisplayTarget::from_crate(&db, krate);
|
||||
let mut types: Vec<(InFile<SyntaxNode>, &Ty)> = Vec::new();
|
||||
let mut mismatches: Vec<(InFile<SyntaxNode>, &TypeMismatch)> = Vec::new();
|
||||
|
||||
@ -361,7 +365,7 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String {
|
||||
macro_prefix,
|
||||
range,
|
||||
ellipsize(text, 15),
|
||||
ty.display_test(&db)
|
||||
ty.display_test(&db, display_target)
|
||||
);
|
||||
}
|
||||
if include_mismatches {
|
||||
@ -377,8 +381,8 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String {
|
||||
"{}{:?}: expected {}, got {}\n",
|
||||
macro_prefix,
|
||||
range,
|
||||
mismatch.expected.display_test(&db),
|
||||
mismatch.actual.display_test(&db),
|
||||
mismatch.expected.display_test(&db, display_target),
|
||||
mismatch.actual.display_test(&db, display_target),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -387,17 +391,18 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String {
|
||||
let module = db.module_for_file(file_id);
|
||||
let def_map = module.def_map(&db);
|
||||
|
||||
let mut defs: Vec<DefWithBodyId> = Vec::new();
|
||||
let mut defs: Vec<(DefWithBodyId, CrateId)> = Vec::new();
|
||||
visit_module(&db, &def_map, module.local_id, &mut |it| {
|
||||
defs.push(match it {
|
||||
let def = match it {
|
||||
ModuleDefId::FunctionId(it) => it.into(),
|
||||
ModuleDefId::EnumVariantId(it) => it.into(),
|
||||
ModuleDefId::ConstId(it) => it.into(),
|
||||
ModuleDefId::StaticId(it) => it.into(),
|
||||
_ => return,
|
||||
})
|
||||
};
|
||||
defs.push((def, module.krate()))
|
||||
});
|
||||
defs.sort_by_key(|def| match def {
|
||||
defs.sort_by_key(|(def, _)| match def {
|
||||
DefWithBodyId::FunctionId(it) => {
|
||||
let loc = it.lookup(&db);
|
||||
loc.source(&db).value.syntax().text_range().start()
|
||||
@ -416,10 +421,10 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String {
|
||||
}
|
||||
DefWithBodyId::InTypeConstId(it) => it.source(&db).syntax().text_range().start(),
|
||||
});
|
||||
for def in defs {
|
||||
for (def, krate) in defs {
|
||||
let (body, source_map) = db.body_with_source_map(def);
|
||||
let infer = db.infer(def);
|
||||
infer_def(infer, body, source_map);
|
||||
infer_def(infer, body, source_map, krate);
|
||||
}
|
||||
|
||||
buf.truncate(buf.trim_end().len());
|
||||
|
@ -8,7 +8,7 @@ use syntax::{AstNode, AstPtr};
|
||||
use test_fixture::WithFixture;
|
||||
|
||||
use crate::db::{HirDatabase, InternedClosureId};
|
||||
use crate::display::HirDisplay;
|
||||
use crate::display::{DisplayTarget, HirDisplay};
|
||||
use crate::mir::MirSpan;
|
||||
use crate::test_db::TestDB;
|
||||
|
||||
@ -66,7 +66,11 @@ fn check_closure_captures(#[rust_analyzer::rust_fixture] ra_fixture: &str, expec
|
||||
.join(", "),
|
||||
};
|
||||
let place = capture.display_place(closure.0, db);
|
||||
let capture_ty = capture.ty.skip_binders().display_test(db).to_string();
|
||||
let capture_ty = capture
|
||||
.ty
|
||||
.skip_binders()
|
||||
.display_test(db, DisplayTarget::from_crate(db, module.krate()))
|
||||
.to_string();
|
||||
let spans = capture
|
||||
.spans()
|
||||
.iter()
|
||||
|
@ -3861,3 +3861,42 @@ fn main() {
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn regression_19196() {
|
||||
check_infer(
|
||||
r#"
|
||||
//- minicore: async_fn
|
||||
fn async_closure<F: AsyncFnOnce(i32)>(f: F) {}
|
||||
fn closure<F: FnOnce(i32)>(f: F) {}
|
||||
|
||||
fn main() {
|
||||
async_closure(async |arg| {
|
||||
arg;
|
||||
});
|
||||
closure(|arg| {
|
||||
arg;
|
||||
});
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
38..39 'f': F
|
||||
44..46 '{}': ()
|
||||
74..75 'f': F
|
||||
80..82 '{}': ()
|
||||
94..191 '{ ... }); }': ()
|
||||
100..113 'async_closure': fn async_closure<impl AsyncFnOnce(i32) -> impl Future<Output = ()>>(impl AsyncFnOnce(i32) -> impl Future<Output = ()>)
|
||||
100..147 'async_... })': ()
|
||||
114..146 'async ... }': impl AsyncFnOnce(i32) -> impl Future<Output = ()>
|
||||
121..124 'arg': i32
|
||||
126..146 '{ ... }': ()
|
||||
136..139 'arg': i32
|
||||
153..160 'closure': fn closure<impl FnOnce(i32)>(impl FnOnce(i32))
|
||||
153..188 'closur... })': ()
|
||||
161..187 '|arg| ... }': impl FnOnce(i32)
|
||||
162..165 'arg': i32
|
||||
167..187 '{ ... }': ()
|
||||
177..180 'arg': i32
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ use tt::TextRange;
|
||||
use crate::{
|
||||
db::HirDatabase, Adt, Callee, Const, Enum, ExternCrateDecl, Field, FieldSource, Function, Impl,
|
||||
InlineAsmOperand, Label, LifetimeParam, LocalSource, Macro, Module, Param, SelfParam, Static,
|
||||
Struct, Trait, TraitAlias, TypeAlias, TypeOrConstParam, Union, Variant,
|
||||
Struct, Trait, TraitAlias, TypeAlias, TypeOrConstParam, Union, Variant, VariantDef,
|
||||
};
|
||||
|
||||
pub trait HasSource {
|
||||
@ -110,6 +110,16 @@ impl HasSource for Adt {
|
||||
}
|
||||
}
|
||||
}
|
||||
impl HasSource for VariantDef {
|
||||
type Ast = ast::VariantDef;
|
||||
fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
|
||||
match self {
|
||||
VariantDef::Struct(s) => Some(s.source(db)?.map(ast::VariantDef::Struct)),
|
||||
VariantDef::Union(u) => Some(u.source(db)?.map(ast::VariantDef::Union)),
|
||||
VariantDef::Variant(v) => Some(v.source(db)?.map(ast::VariantDef::Variant)),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl HasSource for Struct {
|
||||
type Ast = ast::Struct;
|
||||
fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
|
||||
|
@ -39,7 +39,7 @@ use std::{
|
||||
};
|
||||
|
||||
use arrayvec::ArrayVec;
|
||||
use base_db::{CrateDisplayName, CrateId, CrateOrigin};
|
||||
use base_db::{CrateDisplayName, CrateId, CrateOrigin, LangCrateOrigin};
|
||||
use either::Either;
|
||||
use hir_def::{
|
||||
data::{adt::VariantData, TraitFlags},
|
||||
@ -139,6 +139,7 @@ pub use {
|
||||
},
|
||||
hygiene::{marks_rev, SyntaxContextExt},
|
||||
inert_attr_macro::AttributeTemplate,
|
||||
mod_path::tool_path,
|
||||
name::Name,
|
||||
prettify_macro_expansion,
|
||||
proc_macro::{ProcMacros, ProcMacrosBuilder},
|
||||
@ -147,7 +148,7 @@ pub use {
|
||||
hir_ty::{
|
||||
consteval::ConstEvalError,
|
||||
diagnostics::UnsafetyReason,
|
||||
display::{ClosureStyle, HirDisplay, HirDisplayError, HirWrite},
|
||||
display::{ClosureStyle, DisplayTarget, HirDisplay, HirDisplayError, HirWrite},
|
||||
dyn_compatibility::{DynCompatibilityViolation, MethodViolationCode},
|
||||
layout::LayoutError,
|
||||
method_resolution::TyFingerprint,
|
||||
@ -282,6 +283,21 @@ impl Crate {
|
||||
let data = &db.crate_graph()[self.id];
|
||||
data.potential_cfg_options.clone().unwrap_or_else(|| data.cfg_options.clone())
|
||||
}
|
||||
|
||||
pub fn to_display_target(self, db: &dyn HirDatabase) -> DisplayTarget {
|
||||
DisplayTarget::from_crate(db, self.id)
|
||||
}
|
||||
|
||||
fn core(db: &dyn HirDatabase) -> Option<Crate> {
|
||||
let crate_graph = db.crate_graph();
|
||||
let result = crate_graph
|
||||
.iter()
|
||||
.find(|&krate| {
|
||||
matches!(crate_graph[krate].origin, CrateOrigin::Lang(LangCrateOrigin::Core))
|
||||
})
|
||||
.map(Crate::from);
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
@ -296,6 +312,7 @@ pub enum ModuleDef {
|
||||
Function(Function),
|
||||
Adt(Adt),
|
||||
// Can't be directly declared, but can be imported.
|
||||
// FIXME: Rename to `EnumVariant`
|
||||
Variant(Variant),
|
||||
Const(Const),
|
||||
Static(Static),
|
||||
@ -469,6 +486,17 @@ impl ModuleDef {
|
||||
}
|
||||
}
|
||||
|
||||
impl HasCrate for ModuleDef {
|
||||
fn krate(&self, db: &dyn HirDatabase) -> Crate {
|
||||
match self.module(db) {
|
||||
Some(module) => module.krate(),
|
||||
None => Crate::core(db).unwrap_or_else(|| {
|
||||
(*db.crate_graph().crates_in_topological_order().last().unwrap()).into()
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl HasVisibility for ModuleDef {
|
||||
fn visibility(&self, db: &dyn HirDatabase) -> Visibility {
|
||||
match *self {
|
||||
@ -1564,6 +1592,7 @@ impl From<&Variant> for DefWithBodyId {
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: Rename to `EnumVariant`
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct Variant {
|
||||
pub(crate) id: EnumVariantId,
|
||||
@ -1863,7 +1892,7 @@ impl DefWithBody {
|
||||
pub fn debug_mir(self, db: &dyn HirDatabase) -> String {
|
||||
let body = db.mir_body(self.id());
|
||||
match body {
|
||||
Ok(body) => body.pretty_print(db),
|
||||
Ok(body) => body.pretty_print(db, self.module(db).krate().to_display_target(db)),
|
||||
Err(e) => format!("error:\n{e:?}"),
|
||||
}
|
||||
}
|
||||
@ -2449,8 +2478,6 @@ impl Function {
|
||||
db: &dyn HirDatabase,
|
||||
span_formatter: impl Fn(FileId, TextRange) -> String,
|
||||
) -> Result<String, ConstEvalError> {
|
||||
let krate = HasModule::krate(&self.id, db.upcast());
|
||||
let edition = db.crate_graph()[krate].edition;
|
||||
let body = db.monomorphized_mir_body(
|
||||
self.id.into(),
|
||||
Substitution::empty(Interner),
|
||||
@ -2461,7 +2488,12 @@ impl Function {
|
||||
Ok(_) => "pass".to_owned(),
|
||||
Err(e) => {
|
||||
let mut r = String::new();
|
||||
_ = e.pretty_print(&mut r, db, &span_formatter, edition);
|
||||
_ = e.pretty_print(
|
||||
&mut r,
|
||||
db,
|
||||
&span_formatter,
|
||||
self.krate(db).to_display_target(db),
|
||||
);
|
||||
r
|
||||
}
|
||||
};
|
||||
@ -2725,8 +2757,8 @@ pub struct EvaluatedConst {
|
||||
}
|
||||
|
||||
impl EvaluatedConst {
|
||||
pub fn render(&self, db: &dyn HirDatabase, edition: Edition) -> String {
|
||||
format!("{}", self.const_.display(db, edition))
|
||||
pub fn render(&self, db: &dyn HirDatabase, display_target: DisplayTarget) -> String {
|
||||
format!("{}", self.const_.display(db, display_target))
|
||||
}
|
||||
|
||||
pub fn render_debug(&self, db: &dyn HirDatabase) -> Result<String, MirEvalError> {
|
||||
@ -4195,9 +4227,13 @@ impl ConstParam {
|
||||
Type::new(db, self.id.parent(), db.const_param_ty(self.id))
|
||||
}
|
||||
|
||||
pub fn default(self, db: &dyn HirDatabase, edition: Edition) -> Option<ast::ConstArg> {
|
||||
pub fn default(
|
||||
self,
|
||||
db: &dyn HirDatabase,
|
||||
display_target: DisplayTarget,
|
||||
) -> Option<ast::ConstArg> {
|
||||
let arg = generic_arg_from_param(db, self.id.into())?;
|
||||
known_const_to_ast(arg.constant(Interner)?, db, edition)
|
||||
known_const_to_ast(arg.constant(Interner)?, db, display_target)
|
||||
}
|
||||
}
|
||||
|
||||
@ -4505,18 +4541,18 @@ impl Closure {
|
||||
TyKind::Closure(self.id, self.subst).intern(Interner)
|
||||
}
|
||||
|
||||
pub fn display_with_id(&self, db: &dyn HirDatabase, edition: Edition) -> String {
|
||||
pub fn display_with_id(&self, db: &dyn HirDatabase, display_target: DisplayTarget) -> String {
|
||||
self.clone()
|
||||
.as_ty()
|
||||
.display(db, edition)
|
||||
.display(db, display_target)
|
||||
.with_closure_style(ClosureStyle::ClosureWithId)
|
||||
.to_string()
|
||||
}
|
||||
|
||||
pub fn display_with_impl(&self, db: &dyn HirDatabase, edition: Edition) -> String {
|
||||
pub fn display_with_impl(&self, db: &dyn HirDatabase, display_target: DisplayTarget) -> String {
|
||||
self.clone()
|
||||
.as_ty()
|
||||
.display(db, edition)
|
||||
.display(db, display_target)
|
||||
.with_closure_style(ClosureStyle::ImplFn)
|
||||
.to_string()
|
||||
}
|
||||
@ -5321,7 +5357,7 @@ impl Type {
|
||||
pub fn type_and_const_arguments<'a>(
|
||||
&'a self,
|
||||
db: &'a dyn HirDatabase,
|
||||
edition: Edition,
|
||||
display_target: DisplayTarget,
|
||||
) -> impl Iterator<Item = SmolStr> + 'a {
|
||||
self.ty
|
||||
.strip_references()
|
||||
@ -5331,10 +5367,10 @@ impl Type {
|
||||
.filter_map(move |arg| {
|
||||
// arg can be either a `Ty` or `constant`
|
||||
if let Some(ty) = arg.ty(Interner) {
|
||||
Some(format_smolstr!("{}", ty.display(db, edition)))
|
||||
Some(format_smolstr!("{}", ty.display(db, display_target)))
|
||||
} else {
|
||||
arg.constant(Interner)
|
||||
.map(|const_| format_smolstr!("{}", const_.display(db, edition)))
|
||||
.map(|const_| format_smolstr!("{}", const_.display(db, display_target)))
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -5343,7 +5379,7 @@ impl Type {
|
||||
pub fn generic_parameters<'a>(
|
||||
&'a self,
|
||||
db: &'a dyn HirDatabase,
|
||||
edition: Edition,
|
||||
display_target: DisplayTarget,
|
||||
) -> impl Iterator<Item = SmolStr> + 'a {
|
||||
// iterate the lifetime
|
||||
self.as_adt()
|
||||
@ -5353,7 +5389,7 @@ impl Type {
|
||||
})
|
||||
.into_iter()
|
||||
// add the type and const parameters
|
||||
.chain(self.type_and_const_arguments(db, edition))
|
||||
.chain(self.type_and_const_arguments(db, display_target))
|
||||
}
|
||||
|
||||
pub fn iterate_method_candidates_with_traits<T>(
|
||||
|
@ -12,7 +12,8 @@ use std::{
|
||||
|
||||
use either::Either;
|
||||
use hir_def::{
|
||||
hir::{Expr, ExprOrPatId},
|
||||
expr_store::{Body, ExprOrPatSource},
|
||||
hir::{BindingId, Expr, ExprId, ExprOrPatId, Pat},
|
||||
lower::LowerCtx,
|
||||
nameres::{MacroSubNs, ModuleOrigin},
|
||||
path::ModPath,
|
||||
@ -30,6 +31,7 @@ use hir_expand::{
|
||||
name::AsName,
|
||||
ExpandResult, FileRange, InMacroFile, MacroCallId, MacroFileId, MacroFileIdExt,
|
||||
};
|
||||
use hir_ty::diagnostics::unsafe_operations_for_body;
|
||||
use intern::{sym, Symbol};
|
||||
use itertools::Itertools;
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
@ -48,8 +50,8 @@ use crate::{
|
||||
db::HirDatabase,
|
||||
semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx},
|
||||
source_analyzer::{name_hygiene, resolve_hir_path, SourceAnalyzer},
|
||||
Access, Adjust, Adjustment, Adt, AutoBorrow, BindingMode, BuiltinAttr, Callable, Const,
|
||||
ConstParam, Crate, DeriveHelper, Enum, Field, Function, GenericSubstitution, HasSource,
|
||||
Adjust, Adjustment, Adt, AutoBorrow, BindingMode, BuiltinAttr, Callable, Const, ConstParam,
|
||||
Crate, DefWithBody, DeriveHelper, Enum, Field, Function, GenericSubstitution, HasSource,
|
||||
HirFileId, Impl, InFile, InlineAsmOperand, ItemInNs, Label, LifetimeParam, Local, Macro,
|
||||
Module, ModuleDef, Name, OverloadedDeref, Path, ScopeDef, Static, Struct, ToolModule, Trait,
|
||||
TraitAlias, TupleField, Type, TypeAlias, TypeParam, Union, Variant, VariantDef,
|
||||
@ -311,6 +313,14 @@ impl<'db> SemanticsImpl<'db> {
|
||||
tree
|
||||
}
|
||||
|
||||
/// If not crate is found for the file, returns the last crate in topological order.
|
||||
pub fn first_crate_or_default(&self, file: FileId) -> Crate {
|
||||
match self.file_to_module_defs(file).next() {
|
||||
Some(module) => module.krate(),
|
||||
None => (*self.db.crate_graph().crates_in_topological_order().last().unwrap()).into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn attach_first_edition(&self, file: FileId) -> Option<EditionedFileId> {
|
||||
Some(EditionedFileId::new(
|
||||
file,
|
||||
@ -627,6 +637,31 @@ impl<'db> SemanticsImpl<'db> {
|
||||
)
|
||||
}
|
||||
|
||||
/// Checks if renaming `renamed` to `new_name` may introduce conflicts with other locals,
|
||||
/// and returns the conflicting locals.
|
||||
pub fn rename_conflicts(&self, to_be_renamed: &Local, new_name: &str) -> Vec<Local> {
|
||||
let body = self.db.body(to_be_renamed.parent);
|
||||
let resolver = to_be_renamed.parent.resolver(self.db.upcast());
|
||||
let starting_expr =
|
||||
body.binding_owners.get(&to_be_renamed.binding_id).copied().unwrap_or(body.body_expr);
|
||||
let mut visitor = RenameConflictsVisitor {
|
||||
body: &body,
|
||||
conflicts: FxHashSet::default(),
|
||||
db: self.db,
|
||||
new_name: Symbol::intern(new_name),
|
||||
old_name: to_be_renamed.name(self.db).symbol().clone(),
|
||||
owner: to_be_renamed.parent,
|
||||
to_be_renamed: to_be_renamed.binding_id,
|
||||
resolver,
|
||||
};
|
||||
visitor.rename_conflicts(starting_expr);
|
||||
visitor
|
||||
.conflicts
|
||||
.into_iter()
|
||||
.map(|binding_id| Local { parent: to_be_renamed.parent, binding_id })
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Retrieves all the formatting parts of the format_args! (or `asm!`) template string.
|
||||
pub fn as_format_args_parts(
|
||||
&self,
|
||||
@ -1555,6 +1590,19 @@ impl<'db> SemanticsImpl<'db> {
|
||||
.matched_arm
|
||||
}
|
||||
|
||||
pub fn get_unsafe_ops(&self, def: DefWithBody) -> FxHashSet<ExprOrPatSource> {
|
||||
let def = DefWithBodyId::from(def);
|
||||
let (body, source_map) = self.db.body_with_source_map(def);
|
||||
let infer = self.db.infer(def);
|
||||
let mut res = FxHashSet::default();
|
||||
unsafe_operations_for_body(self.db, &infer, def, &body, &mut |node| {
|
||||
if let Ok(node) = source_map.expr_or_pat_syntax(node) {
|
||||
res.insert(node);
|
||||
}
|
||||
});
|
||||
res
|
||||
}
|
||||
|
||||
pub fn is_unsafe_macro_call(&self, macro_call: &ast::MacroCall) -> bool {
|
||||
let Some(mac) = self.resolve_macro_call(macro_call) else { return false };
|
||||
if mac.is_asm_or_global_asm(self.db) {
|
||||
@ -1682,6 +1730,15 @@ impl<'db> SemanticsImpl<'db> {
|
||||
Some(res)
|
||||
}
|
||||
|
||||
pub fn body_for(&self, node: InFile<&SyntaxNode>) -> Option<DefWithBody> {
|
||||
let container = self.with_ctx(|ctx| ctx.find_container(node))?;
|
||||
|
||||
match container {
|
||||
ChildContainer::DefWithBodyId(def) => Some(def.into()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns none if the file of the node is not part of a crate.
|
||||
fn analyze(&self, node: &SyntaxNode) -> Option<SourceAnalyzer> {
|
||||
let node = self.find_file(node);
|
||||
@ -1783,91 +1840,6 @@ impl<'db> SemanticsImpl<'db> {
|
||||
InFile::new(file_id, node)
|
||||
}
|
||||
|
||||
pub fn is_unsafe_method_call(&self, method_call_expr: &ast::MethodCallExpr) -> bool {
|
||||
method_call_expr
|
||||
.receiver()
|
||||
.and_then(|expr| {
|
||||
let field_expr = match expr {
|
||||
ast::Expr::FieldExpr(field_expr) => field_expr,
|
||||
_ => return None,
|
||||
};
|
||||
let ty = self.type_of_expr(&field_expr.expr()?)?.original;
|
||||
if !ty.is_packed(self.db) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let func = self.resolve_method_call(method_call_expr)?;
|
||||
let res = match func.self_param(self.db)?.access(self.db) {
|
||||
Access::Shared | Access::Exclusive => true,
|
||||
Access::Owned => false,
|
||||
};
|
||||
Some(res)
|
||||
})
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
pub fn is_unsafe_ref_expr(&self, ref_expr: &ast::RefExpr) -> bool {
|
||||
ref_expr
|
||||
.expr()
|
||||
.and_then(|expr| {
|
||||
let field_expr = match expr {
|
||||
ast::Expr::FieldExpr(field_expr) => field_expr,
|
||||
_ => return None,
|
||||
};
|
||||
let expr = field_expr.expr()?;
|
||||
self.type_of_expr(&expr)
|
||||
})
|
||||
// Binding a reference to a packed type is possibly unsafe.
|
||||
.map(|ty| ty.original.is_packed(self.db))
|
||||
.unwrap_or(false)
|
||||
|
||||
// FIXME This needs layout computation to be correct. It will highlight
|
||||
// more than it should with the current implementation.
|
||||
}
|
||||
|
||||
pub fn is_unsafe_ident_pat(&self, ident_pat: &ast::IdentPat) -> bool {
|
||||
if ident_pat.ref_token().is_none() {
|
||||
return false;
|
||||
}
|
||||
|
||||
ident_pat
|
||||
.syntax()
|
||||
.parent()
|
||||
.and_then(|parent| {
|
||||
// `IdentPat` can live under `RecordPat` directly under `RecordPatField` or
|
||||
// `RecordPatFieldList`. `RecordPatField` also lives under `RecordPatFieldList`,
|
||||
// so this tries to lookup the `IdentPat` anywhere along that structure to the
|
||||
// `RecordPat` so we can get the containing type.
|
||||
let record_pat = ast::RecordPatField::cast(parent.clone())
|
||||
.and_then(|record_pat| record_pat.syntax().parent())
|
||||
.or_else(|| Some(parent.clone()))
|
||||
.and_then(|parent| {
|
||||
ast::RecordPatFieldList::cast(parent)?
|
||||
.syntax()
|
||||
.parent()
|
||||
.and_then(ast::RecordPat::cast)
|
||||
});
|
||||
|
||||
// If this doesn't match a `RecordPat`, fallback to a `LetStmt` to see if
|
||||
// this is initialized from a `FieldExpr`.
|
||||
if let Some(record_pat) = record_pat {
|
||||
self.type_of_pat(&ast::Pat::RecordPat(record_pat))
|
||||
} else if let Some(let_stmt) = ast::LetStmt::cast(parent) {
|
||||
let field_expr = match let_stmt.initializer()? {
|
||||
ast::Expr::FieldExpr(field_expr) => field_expr,
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
self.type_of_expr(&field_expr.expr()?)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
// Binding a reference to a packed type is possibly unsafe.
|
||||
.map(|ty| ty.original.is_packed(self.db))
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
/// Returns `true` if the `node` is inside an `unsafe` context.
|
||||
pub fn is_inside_unsafe(&self, expr: &ast::Expr) -> bool {
|
||||
let Some(enclosing_item) =
|
||||
@ -2155,3 +2127,69 @@ impl ops::Deref for VisibleTraits {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
struct RenameConflictsVisitor<'a> {
|
||||
db: &'a dyn HirDatabase,
|
||||
owner: DefWithBodyId,
|
||||
resolver: Resolver,
|
||||
body: &'a Body,
|
||||
to_be_renamed: BindingId,
|
||||
new_name: Symbol,
|
||||
old_name: Symbol,
|
||||
conflicts: FxHashSet<BindingId>,
|
||||
}
|
||||
|
||||
impl RenameConflictsVisitor<'_> {
|
||||
fn resolve_path(&mut self, node: ExprOrPatId, path: &Path) {
|
||||
if let Path::BarePath(path) = path {
|
||||
if let Some(name) = path.as_ident() {
|
||||
if *name.symbol() == self.new_name {
|
||||
if let Some(conflicting) = self.resolver.rename_will_conflict_with_renamed(
|
||||
self.db.upcast(),
|
||||
name,
|
||||
path,
|
||||
self.body.expr_or_pat_path_hygiene(node),
|
||||
self.to_be_renamed,
|
||||
) {
|
||||
self.conflicts.insert(conflicting);
|
||||
}
|
||||
} else if *name.symbol() == self.old_name {
|
||||
if let Some(conflicting) =
|
||||
self.resolver.rename_will_conflict_with_another_variable(
|
||||
self.db.upcast(),
|
||||
name,
|
||||
path,
|
||||
self.body.expr_or_pat_path_hygiene(node),
|
||||
&self.new_name,
|
||||
self.to_be_renamed,
|
||||
)
|
||||
{
|
||||
self.conflicts.insert(conflicting);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn rename_conflicts(&mut self, expr: ExprId) {
|
||||
match &self.body[expr] {
|
||||
Expr::Path(path) => {
|
||||
let guard = self.resolver.update_to_inner_scope(self.db.upcast(), self.owner, expr);
|
||||
self.resolve_path(expr.into(), path);
|
||||
self.resolver.reset_to_guard(guard);
|
||||
}
|
||||
&Expr::Assignment { target, .. } => {
|
||||
let guard = self.resolver.update_to_inner_scope(self.db.upcast(), self.owner, expr);
|
||||
self.body.walk_pats(target, &mut |pat| {
|
||||
if let Pat::Path(path) = &self.body[pat] {
|
||||
self.resolve_path(pat.into(), path);
|
||||
}
|
||||
});
|
||||
self.resolver.reset_to_guard(guard);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
self.body.walk_child_exprs(expr, |expr| self.rename_conflicts(expr));
|
||||
}
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ use hir_expand::{
|
||||
};
|
||||
use hir_ty::{
|
||||
diagnostics::{
|
||||
record_literal_missing_fields, record_pattern_missing_fields, unsafe_expressions,
|
||||
record_literal_missing_fields, record_pattern_missing_fields, unsafe_operations,
|
||||
InsideUnsafeBlock,
|
||||
},
|
||||
from_assoc_type_id,
|
||||
@ -1160,7 +1160,7 @@ impl SourceAnalyzer {
|
||||
if let Some(expanded_expr) = sm.macro_expansion_expr(macro_expr) {
|
||||
let mut is_unsafe = false;
|
||||
let mut walk_expr = |expr_id| {
|
||||
unsafe_expressions(db, infer, *def, body, expr_id, &mut |inside_unsafe_block| {
|
||||
unsafe_operations(db, infer, *def, body, expr_id, &mut |inside_unsafe_block| {
|
||||
is_unsafe |= inside_unsafe_block == InsideUnsafeBlock::No
|
||||
})
|
||||
};
|
||||
|
@ -13,11 +13,10 @@ use hir_def::{
|
||||
use hir_expand::{name::Name, HirFileId};
|
||||
use hir_ty::{
|
||||
db::HirDatabase,
|
||||
display::{hir_display_with_types_map, HirDisplay},
|
||||
display::{hir_display_with_types_map, DisplayTarget, HirDisplay},
|
||||
};
|
||||
use intern::Symbol;
|
||||
use rustc_hash::FxHashMap;
|
||||
use span::Edition;
|
||||
use syntax::{ast::HasName, AstNode, AstPtr, SmolStr, SyntaxNode, SyntaxNodePtr, ToSmolStr};
|
||||
|
||||
use crate::{Module, ModuleDef, Semantics};
|
||||
@ -66,7 +65,7 @@ pub struct SymbolCollector<'a> {
|
||||
symbols: FxIndexSet<FileSymbol>,
|
||||
work: Vec<SymbolCollectorWork>,
|
||||
current_container_name: Option<SmolStr>,
|
||||
edition: Edition,
|
||||
display_target: DisplayTarget,
|
||||
}
|
||||
|
||||
/// Given a [`ModuleId`] and a [`HirDatabase`], use the DefMap for the module's crate to collect
|
||||
@ -78,7 +77,10 @@ impl<'a> SymbolCollector<'a> {
|
||||
symbols: Default::default(),
|
||||
work: Default::default(),
|
||||
current_container_name: None,
|
||||
edition: Edition::Edition2015,
|
||||
display_target: DisplayTarget::from_crate(
|
||||
db,
|
||||
*db.crate_graph().crates_in_topological_order().last().unwrap(),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
@ -91,7 +93,7 @@ impl<'a> SymbolCollector<'a> {
|
||||
pub fn collect(&mut self, module: Module) {
|
||||
let _p = tracing::info_span!("SymbolCollector::collect", ?module).entered();
|
||||
tracing::info!(?module, "SymbolCollector::collect",);
|
||||
self.edition = module.krate().edition(self.db);
|
||||
self.display_target = module.krate().to_display_target(self.db);
|
||||
|
||||
// The initial work is the root module we're collecting, additional work will
|
||||
// be populated as we traverse the module's definitions.
|
||||
@ -307,7 +309,7 @@ impl<'a> SymbolCollector<'a> {
|
||||
let impl_data = self.db.impl_data(impl_id);
|
||||
let impl_name = Some(
|
||||
hir_display_with_types_map(impl_data.self_ty, &impl_data.types_map)
|
||||
.display(self.db, self.edition)
|
||||
.display(self.db, self.display_target)
|
||||
.to_smolstr(),
|
||||
);
|
||||
self.with_container_name(impl_name, |s| {
|
||||
|
@ -4,7 +4,7 @@ use hir_def::ImportPathConfig;
|
||||
use hir_expand::mod_path::ModPath;
|
||||
use hir_ty::{
|
||||
db::HirDatabase,
|
||||
display::{DisplaySourceCodeError, HirDisplay},
|
||||
display::{DisplaySourceCodeError, DisplayTarget, HirDisplay},
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use span::Edition;
|
||||
@ -99,14 +99,16 @@ impl Expr {
|
||||
sema_scope: &SemanticsScope<'_>,
|
||||
many_formatter: &mut dyn FnMut(&Type) -> String,
|
||||
cfg: ImportPathConfig,
|
||||
edition: Edition,
|
||||
display_target: DisplayTarget,
|
||||
) -> Result<String, DisplaySourceCodeError> {
|
||||
let db = sema_scope.db;
|
||||
let edition = display_target.edition;
|
||||
let mod_item_path_str = |s, def| mod_item_path_str(s, def, cfg, edition);
|
||||
match self {
|
||||
Expr::Const(it) => match it.as_assoc_item(db).map(|it| it.container(db)) {
|
||||
Some(container) => {
|
||||
let container_name = container_name(container, sema_scope, cfg, edition)?;
|
||||
let container_name =
|
||||
container_name(container, sema_scope, cfg, edition, display_target)?;
|
||||
let const_name = it
|
||||
.name(db)
|
||||
.map(|c| c.display(db.upcast(), edition).to_string())
|
||||
@ -122,14 +124,15 @@ impl Expr {
|
||||
Expr::Function { func, params, .. } => {
|
||||
let args = params
|
||||
.iter()
|
||||
.map(|f| f.gen_source_code(sema_scope, many_formatter, cfg, edition))
|
||||
.map(|f| f.gen_source_code(sema_scope, many_formatter, cfg, display_target))
|
||||
.collect::<Result<Vec<String>, DisplaySourceCodeError>>()?
|
||||
.into_iter()
|
||||
.join(", ");
|
||||
|
||||
match func.as_assoc_item(db).map(|it| it.container(db)) {
|
||||
Some(container) => {
|
||||
let container_name = container_name(container, sema_scope, cfg, edition)?;
|
||||
let container_name =
|
||||
container_name(container, sema_scope, cfg, edition, display_target)?;
|
||||
let fn_name = func.name(db).display(db.upcast(), edition).to_string();
|
||||
Ok(format!("{container_name}::{fn_name}({args})"))
|
||||
}
|
||||
@ -147,10 +150,10 @@ impl Expr {
|
||||
let func_name = func.name(db).display(db.upcast(), edition).to_string();
|
||||
let self_param = func.self_param(db).unwrap();
|
||||
let target_str =
|
||||
target.gen_source_code(sema_scope, many_formatter, cfg, edition)?;
|
||||
target.gen_source_code(sema_scope, many_formatter, cfg, display_target)?;
|
||||
let args = params
|
||||
.iter()
|
||||
.map(|f| f.gen_source_code(sema_scope, many_formatter, cfg, edition))
|
||||
.map(|f| f.gen_source_code(sema_scope, many_formatter, cfg, display_target))
|
||||
.collect::<Result<Vec<String>, DisplaySourceCodeError>>()?
|
||||
.into_iter()
|
||||
.join(", ");
|
||||
@ -180,7 +183,9 @@ impl Expr {
|
||||
StructKind::Tuple => {
|
||||
let args = params
|
||||
.iter()
|
||||
.map(|f| f.gen_source_code(sema_scope, many_formatter, cfg, edition))
|
||||
.map(|f| {
|
||||
f.gen_source_code(sema_scope, many_formatter, cfg, display_target)
|
||||
})
|
||||
.collect::<Result<Vec<String>, DisplaySourceCodeError>>()?
|
||||
.into_iter()
|
||||
.join(", ");
|
||||
@ -195,7 +200,12 @@ impl Expr {
|
||||
let tmp = format!(
|
||||
"{}: {}",
|
||||
f.name(db).display(db.upcast(), edition),
|
||||
a.gen_source_code(sema_scope, many_formatter, cfg, edition)?
|
||||
a.gen_source_code(
|
||||
sema_scope,
|
||||
many_formatter,
|
||||
cfg,
|
||||
display_target
|
||||
)?
|
||||
);
|
||||
Ok(tmp)
|
||||
})
|
||||
@ -215,7 +225,9 @@ impl Expr {
|
||||
StructKind::Tuple => {
|
||||
let args = params
|
||||
.iter()
|
||||
.map(|a| a.gen_source_code(sema_scope, many_formatter, cfg, edition))
|
||||
.map(|a| {
|
||||
a.gen_source_code(sema_scope, many_formatter, cfg, display_target)
|
||||
})
|
||||
.collect::<Result<Vec<String>, DisplaySourceCodeError>>()?
|
||||
.into_iter()
|
||||
.join(", ");
|
||||
@ -230,7 +242,12 @@ impl Expr {
|
||||
let tmp = format!(
|
||||
"{}: {}",
|
||||
f.name(db).display(db.upcast(), edition),
|
||||
a.gen_source_code(sema_scope, many_formatter, cfg, edition)?
|
||||
a.gen_source_code(
|
||||
sema_scope,
|
||||
many_formatter,
|
||||
cfg,
|
||||
display_target
|
||||
)?
|
||||
);
|
||||
Ok(tmp)
|
||||
})
|
||||
@ -248,7 +265,7 @@ impl Expr {
|
||||
Expr::Tuple { params, .. } => {
|
||||
let args = params
|
||||
.iter()
|
||||
.map(|a| a.gen_source_code(sema_scope, many_formatter, cfg, edition))
|
||||
.map(|a| a.gen_source_code(sema_scope, many_formatter, cfg, display_target))
|
||||
.collect::<Result<Vec<String>, DisplaySourceCodeError>>()?
|
||||
.into_iter()
|
||||
.join(", ");
|
||||
@ -260,7 +277,8 @@ impl Expr {
|
||||
return Ok(many_formatter(&expr.ty(db)));
|
||||
}
|
||||
|
||||
let strukt = expr.gen_source_code(sema_scope, many_formatter, cfg, edition)?;
|
||||
let strukt =
|
||||
expr.gen_source_code(sema_scope, many_formatter, cfg, display_target)?;
|
||||
let field = field.name(db).display(db.upcast(), edition).to_string();
|
||||
Ok(format!("{strukt}.{field}"))
|
||||
}
|
||||
@ -269,7 +287,8 @@ impl Expr {
|
||||
return Ok(many_formatter(&expr.ty(db)));
|
||||
}
|
||||
|
||||
let inner = expr.gen_source_code(sema_scope, many_formatter, cfg, edition)?;
|
||||
let inner =
|
||||
expr.gen_source_code(sema_scope, many_formatter, cfg, display_target)?;
|
||||
Ok(format!("&{inner}"))
|
||||
}
|
||||
Expr::Many(ty) => Ok(many_formatter(ty)),
|
||||
@ -358,6 +377,7 @@ fn container_name(
|
||||
sema_scope: &SemanticsScope<'_>,
|
||||
cfg: ImportPathConfig,
|
||||
edition: Edition,
|
||||
display_target: DisplayTarget,
|
||||
) -> Result<String, DisplaySourceCodeError> {
|
||||
let container_name = match container {
|
||||
crate::AssocItemContainer::Trait(trait_) => {
|
||||
@ -368,7 +388,7 @@ fn container_name(
|
||||
// Should it be guaranteed that `mod_item_path` always exists?
|
||||
match self_ty.as_adt().and_then(|adt| mod_item_path(sema_scope, &adt.into(), cfg)) {
|
||||
Some(path) => path.display(sema_scope.db.upcast(), edition).to_string(),
|
||||
None => self_ty.display(sema_scope.db, edition).to_string(),
|
||||
None => self_ty.display(sema_scope.db, display_target).to_string(),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -8,7 +8,7 @@ use syntax::{ast, AstNode};
|
||||
|
||||
use crate::{AssistContext, Assists};
|
||||
|
||||
// Assist: explicit_enum_discriminant
|
||||
// Assist: add_explicit_enum_discriminant
|
||||
//
|
||||
// Adds explicit discriminant to all enum variants.
|
||||
//
|
||||
@ -29,7 +29,10 @@ use crate::{AssistContext, Assists};
|
||||
// Quux = 43,
|
||||
// }
|
||||
// ```
|
||||
pub(crate) fn explicit_enum_discriminant(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
|
||||
pub(crate) fn add_explicit_enum_discriminant(
|
||||
acc: &mut Assists,
|
||||
ctx: &AssistContext<'_>,
|
||||
) -> Option<()> {
|
||||
let enum_node = ctx.find_node_at_offset::<ast::Enum>()?;
|
||||
let enum_def = ctx.sema.to_def(&enum_node)?;
|
||||
|
||||
@ -50,7 +53,7 @@ pub(crate) fn explicit_enum_discriminant(acc: &mut Assists, ctx: &AssistContext<
|
||||
}
|
||||
|
||||
acc.add(
|
||||
AssistId("explicit_enum_discriminant", AssistKind::RefactorRewrite),
|
||||
AssistId("add_explicit_enum_discriminant", AssistKind::RefactorRewrite),
|
||||
"Add explicit enum discriminants",
|
||||
enum_node.syntax().text_range(),
|
||||
|builder| {
|
||||
@ -88,12 +91,12 @@ fn add_variant_discriminant(
|
||||
mod tests {
|
||||
use crate::tests::{check_assist, check_assist_not_applicable};
|
||||
|
||||
use super::explicit_enum_discriminant;
|
||||
use super::add_explicit_enum_discriminant;
|
||||
|
||||
#[test]
|
||||
fn non_primitive_repr_non_data_bearing_add_discriminant() {
|
||||
check_assist(
|
||||
explicit_enum_discriminant,
|
||||
add_explicit_enum_discriminant,
|
||||
r#"
|
||||
enum TheEnum$0 {
|
||||
Foo,
|
||||
@ -120,7 +123,7 @@ enum TheEnum {
|
||||
#[test]
|
||||
fn primitive_repr_data_bearing_add_discriminant() {
|
||||
check_assist(
|
||||
explicit_enum_discriminant,
|
||||
add_explicit_enum_discriminant,
|
||||
r#"
|
||||
#[repr(u8)]
|
||||
$0enum TheEnum {
|
||||
@ -145,7 +148,7 @@ enum TheEnum {
|
||||
#[test]
|
||||
fn non_primitive_repr_data_bearing_not_applicable() {
|
||||
check_assist_not_applicable(
|
||||
explicit_enum_discriminant,
|
||||
add_explicit_enum_discriminant,
|
||||
r#"
|
||||
enum TheEnum$0 {
|
||||
Foo,
|
||||
@ -159,7 +162,7 @@ enum TheEnum$0 {
|
||||
#[test]
|
||||
fn primitive_repr_non_data_bearing_add_discriminant() {
|
||||
check_assist(
|
||||
explicit_enum_discriminant,
|
||||
add_explicit_enum_discriminant,
|
||||
r#"
|
||||
#[repr(i64)]
|
||||
enum TheEnum {
|
||||
@ -184,7 +187,7 @@ enum TheEnum {
|
||||
#[test]
|
||||
fn discriminants_already_explicit_not_applicable() {
|
||||
check_assist_not_applicable(
|
||||
explicit_enum_discriminant,
|
||||
add_explicit_enum_discriminant,
|
||||
r#"
|
||||
enum TheEnum$0 {
|
||||
Foo = 0,
|
||||
@ -197,7 +200,7 @@ enum TheEnum$0 {
|
||||
#[test]
|
||||
fn empty_enum_not_applicable() {
|
||||
check_assist_not_applicable(
|
||||
explicit_enum_discriminant,
|
||||
add_explicit_enum_discriminant,
|
||||
r#"
|
||||
enum TheEnum$0 {}
|
||||
"#,
|
@ -128,7 +128,9 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
|
||||
let parent = neg_expr.syntax().parent();
|
||||
editor = builder.make_editor(neg_expr.syntax());
|
||||
|
||||
if parent.is_some_and(|parent| demorganed.needs_parens_in(&parent)) {
|
||||
if parent.is_some_and(|parent| {
|
||||
demorganed.needs_parens_in_place_of(&parent, neg_expr.syntax())
|
||||
}) {
|
||||
cov_mark::hit!(demorgan_keep_parens_for_op_precedence2);
|
||||
editor.replace(neg_expr.syntax(), make.expr_paren(demorganed).syntax());
|
||||
} else {
|
||||
@ -392,15 +394,19 @@ fn f() { !(S <= S || S < S) }
|
||||
|
||||
#[test]
|
||||
fn demorgan_keep_pars_for_op_precedence3() {
|
||||
check_assist(apply_demorgan, "fn f() { (a || !(b &&$0 c); }", "fn f() { (a || !b || !c; }");
|
||||
check_assist(
|
||||
apply_demorgan,
|
||||
"fn f() { (a || !(b &&$0 c); }",
|
||||
"fn f() { (a || (!b || !c); }",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn demorgan_removes_pars_in_eq_precedence() {
|
||||
fn demorgan_keeps_pars_in_eq_precedence() {
|
||||
check_assist(
|
||||
apply_demorgan,
|
||||
"fn() { let x = a && !(!b |$0| !c); }",
|
||||
"fn() { let x = a && b && c; }",
|
||||
"fn() { let x = a && (b && c); }",
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -26,7 +26,7 @@ use crate::{
|
||||
utils,
|
||||
};
|
||||
|
||||
// Assist: bool_to_enum
|
||||
// Assist: convert_bool_to_enum
|
||||
//
|
||||
// This converts boolean local variables, fields, constants, and statics into a new
|
||||
// enum with two variants `Bool::True` and `Bool::False`, as well as replacing
|
||||
@ -55,14 +55,14 @@ use crate::{
|
||||
// }
|
||||
// }
|
||||
// ```
|
||||
pub(crate) fn bool_to_enum(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
|
||||
pub(crate) fn convert_bool_to_enum(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
|
||||
let BoolNodeData { target_node, name, ty_annotation, initializer, definition } =
|
||||
find_bool_node(ctx)?;
|
||||
let target_module = ctx.sema.scope(&target_node)?.module().nearest_non_block_module(ctx.db());
|
||||
|
||||
let target = name.syntax().text_range();
|
||||
acc.add(
|
||||
AssistId("bool_to_enum", AssistKind::RefactorRewrite),
|
||||
AssistId("convert_bool_to_enum", AssistKind::RefactorRewrite),
|
||||
"Convert boolean to enum",
|
||||
target,
|
||||
|edit| {
|
||||
@ -549,7 +549,7 @@ mod tests {
|
||||
#[test]
|
||||
fn parameter_with_first_param_usage() {
|
||||
check_assist(
|
||||
bool_to_enum,
|
||||
convert_bool_to_enum,
|
||||
r#"
|
||||
fn function($0foo: bool, bar: bool) {
|
||||
if foo {
|
||||
@ -573,7 +573,7 @@ fn function(foo: Bool, bar: bool) {
|
||||
#[test]
|
||||
fn no_duplicate_enums() {
|
||||
check_assist(
|
||||
bool_to_enum,
|
||||
convert_bool_to_enum,
|
||||
r#"
|
||||
#[derive(PartialEq, Eq)]
|
||||
enum Bool { True, False }
|
||||
@ -600,7 +600,7 @@ fn function(foo: bool, bar: Bool) {
|
||||
#[test]
|
||||
fn parameter_with_last_param_usage() {
|
||||
check_assist(
|
||||
bool_to_enum,
|
||||
convert_bool_to_enum,
|
||||
r#"
|
||||
fn function(foo: bool, $0bar: bool) {
|
||||
if bar {
|
||||
@ -624,7 +624,7 @@ fn function(foo: bool, bar: Bool) {
|
||||
#[test]
|
||||
fn parameter_with_middle_param_usage() {
|
||||
check_assist(
|
||||
bool_to_enum,
|
||||
convert_bool_to_enum,
|
||||
r#"
|
||||
fn function(foo: bool, $0bar: bool, baz: bool) {
|
||||
if bar {
|
||||
@ -648,7 +648,7 @@ fn function(foo: bool, bar: Bool, baz: bool) {
|
||||
#[test]
|
||||
fn parameter_with_closure_usage() {
|
||||
check_assist(
|
||||
bool_to_enum,
|
||||
convert_bool_to_enum,
|
||||
r#"
|
||||
fn main() {
|
||||
let foo = |$0bar: bool| bar;
|
||||
@ -668,7 +668,7 @@ fn main() {
|
||||
#[test]
|
||||
fn local_variable_with_usage() {
|
||||
check_assist(
|
||||
bool_to_enum,
|
||||
convert_bool_to_enum,
|
||||
r#"
|
||||
fn main() {
|
||||
let $0foo = true;
|
||||
@ -697,7 +697,7 @@ fn main() {
|
||||
fn local_variable_with_usage_negated() {
|
||||
cov_mark::check!(replaces_negation);
|
||||
check_assist(
|
||||
bool_to_enum,
|
||||
convert_bool_to_enum,
|
||||
r#"
|
||||
fn main() {
|
||||
let $0foo = true;
|
||||
@ -726,7 +726,7 @@ fn main() {
|
||||
fn local_variable_with_type_annotation() {
|
||||
cov_mark::check!(replaces_ty_annotation);
|
||||
check_assist(
|
||||
bool_to_enum,
|
||||
convert_bool_to_enum,
|
||||
r#"
|
||||
fn main() {
|
||||
let $0foo: bool = false;
|
||||
@ -746,7 +746,7 @@ fn main() {
|
||||
#[test]
|
||||
fn local_variable_with_non_literal_initializer() {
|
||||
check_assist(
|
||||
bool_to_enum,
|
||||
convert_bool_to_enum,
|
||||
r#"
|
||||
fn main() {
|
||||
let $0foo = 1 == 2;
|
||||
@ -766,7 +766,7 @@ fn main() {
|
||||
#[test]
|
||||
fn local_variable_binexpr_usage() {
|
||||
check_assist(
|
||||
bool_to_enum,
|
||||
convert_bool_to_enum,
|
||||
r#"
|
||||
fn main() {
|
||||
let $0foo = false;
|
||||
@ -796,7 +796,7 @@ fn main() {
|
||||
#[test]
|
||||
fn local_variable_unop_usage() {
|
||||
check_assist(
|
||||
bool_to_enum,
|
||||
convert_bool_to_enum,
|
||||
r#"
|
||||
fn main() {
|
||||
let $0foo = true;
|
||||
@ -825,7 +825,7 @@ fn main() {
|
||||
fn local_variable_assigned_later() {
|
||||
cov_mark::check!(replaces_assignment);
|
||||
check_assist(
|
||||
bool_to_enum,
|
||||
convert_bool_to_enum,
|
||||
r#"
|
||||
fn main() {
|
||||
let $0foo: bool;
|
||||
@ -847,7 +847,7 @@ fn main() {
|
||||
#[test]
|
||||
fn local_variable_does_not_apply_recursively() {
|
||||
check_assist(
|
||||
bool_to_enum,
|
||||
convert_bool_to_enum,
|
||||
r#"
|
||||
fn main() {
|
||||
let $0foo = true;
|
||||
@ -878,7 +878,7 @@ fn main() {
|
||||
fn local_variable_nested_in_negation() {
|
||||
cov_mark::check!(dont_overwrite_expression_inside_negation);
|
||||
check_assist(
|
||||
bool_to_enum,
|
||||
convert_bool_to_enum,
|
||||
r#"
|
||||
fn main() {
|
||||
if !"foo".chars().any(|c| {
|
||||
@ -909,7 +909,7 @@ fn main() {
|
||||
fn local_variable_non_bool() {
|
||||
cov_mark::check!(not_applicable_non_bool_local);
|
||||
check_assist_not_applicable(
|
||||
bool_to_enum,
|
||||
convert_bool_to_enum,
|
||||
r#"
|
||||
fn main() {
|
||||
let $0foo = 1;
|
||||
@ -921,7 +921,7 @@ fn main() {
|
||||
#[test]
|
||||
fn local_variable_cursor_not_on_ident() {
|
||||
check_assist_not_applicable(
|
||||
bool_to_enum,
|
||||
convert_bool_to_enum,
|
||||
r#"
|
||||
fn main() {
|
||||
let foo = $0true;
|
||||
@ -933,7 +933,7 @@ fn main() {
|
||||
#[test]
|
||||
fn local_variable_non_ident_pat() {
|
||||
check_assist_not_applicable(
|
||||
bool_to_enum,
|
||||
convert_bool_to_enum,
|
||||
r#"
|
||||
fn main() {
|
||||
let ($0foo, bar) = (true, false);
|
||||
@ -945,7 +945,7 @@ fn main() {
|
||||
#[test]
|
||||
fn local_var_init_struct_usage() {
|
||||
check_assist(
|
||||
bool_to_enum,
|
||||
convert_bool_to_enum,
|
||||
r#"
|
||||
struct Foo {
|
||||
foo: bool,
|
||||
@ -975,7 +975,7 @@ fn main() {
|
||||
#[test]
|
||||
fn local_var_init_struct_usage_in_macro() {
|
||||
check_assist(
|
||||
bool_to_enum,
|
||||
convert_bool_to_enum,
|
||||
r#"
|
||||
struct Struct {
|
||||
boolean: bool,
|
||||
@ -1018,7 +1018,7 @@ fn new() -> Struct {
|
||||
fn field_struct_basic() {
|
||||
cov_mark::check!(replaces_record_expr);
|
||||
check_assist(
|
||||
bool_to_enum,
|
||||
convert_bool_to_enum,
|
||||
r#"
|
||||
struct Foo {
|
||||
$0bar: bool,
|
||||
@ -1057,7 +1057,7 @@ fn main() {
|
||||
fn field_enum_basic() {
|
||||
cov_mark::check!(replaces_record_pat);
|
||||
check_assist(
|
||||
bool_to_enum,
|
||||
convert_bool_to_enum,
|
||||
r#"
|
||||
enum Foo {
|
||||
Foo,
|
||||
@ -1100,7 +1100,7 @@ fn main() {
|
||||
fn field_enum_cross_file() {
|
||||
// FIXME: The import is missing
|
||||
check_assist(
|
||||
bool_to_enum,
|
||||
convert_bool_to_enum,
|
||||
r#"
|
||||
//- /foo.rs
|
||||
pub enum Foo {
|
||||
@ -1151,7 +1151,7 @@ fn main() {
|
||||
fn field_enum_shorthand() {
|
||||
cov_mark::check!(replaces_record_pat_shorthand);
|
||||
check_assist(
|
||||
bool_to_enum,
|
||||
convert_bool_to_enum,
|
||||
r#"
|
||||
enum Foo {
|
||||
Foo,
|
||||
@ -1200,7 +1200,7 @@ fn main() {
|
||||
fn field_enum_replaces_literal_patterns() {
|
||||
cov_mark::check!(replaces_literal_pat);
|
||||
check_assist(
|
||||
bool_to_enum,
|
||||
convert_bool_to_enum,
|
||||
r#"
|
||||
enum Foo {
|
||||
Foo,
|
||||
@ -1238,7 +1238,7 @@ fn main() {
|
||||
#[test]
|
||||
fn field_enum_keeps_wildcard_patterns() {
|
||||
check_assist(
|
||||
bool_to_enum,
|
||||
convert_bool_to_enum,
|
||||
r#"
|
||||
enum Foo {
|
||||
Foo,
|
||||
@ -1276,7 +1276,7 @@ fn main() {
|
||||
#[test]
|
||||
fn field_union_basic() {
|
||||
check_assist(
|
||||
bool_to_enum,
|
||||
convert_bool_to_enum,
|
||||
r#"
|
||||
union Foo {
|
||||
$0foo: bool,
|
||||
@ -1314,7 +1314,7 @@ fn main() {
|
||||
#[test]
|
||||
fn field_negated() {
|
||||
check_assist(
|
||||
bool_to_enum,
|
||||
convert_bool_to_enum,
|
||||
r#"
|
||||
struct Foo {
|
||||
$0bar: bool,
|
||||
@ -1350,7 +1350,7 @@ fn main() {
|
||||
#[test]
|
||||
fn field_in_mod_properly_indented() {
|
||||
check_assist(
|
||||
bool_to_enum,
|
||||
convert_bool_to_enum,
|
||||
r#"
|
||||
mod foo {
|
||||
struct Bar {
|
||||
@ -1386,7 +1386,7 @@ mod foo {
|
||||
#[test]
|
||||
fn field_multiple_initializations() {
|
||||
check_assist(
|
||||
bool_to_enum,
|
||||
convert_bool_to_enum,
|
||||
r#"
|
||||
struct Foo {
|
||||
$0bar: bool,
|
||||
@ -1427,7 +1427,7 @@ fn main() {
|
||||
fn field_assigned_to_another() {
|
||||
cov_mark::check!(dont_assign_incorrect_ref);
|
||||
check_assist(
|
||||
bool_to_enum,
|
||||
convert_bool_to_enum,
|
||||
r#"
|
||||
struct Foo {
|
||||
$0foo: bool,
|
||||
@ -1469,7 +1469,7 @@ fn main() {
|
||||
#[test]
|
||||
fn field_initialized_with_other() {
|
||||
check_assist(
|
||||
bool_to_enum,
|
||||
convert_bool_to_enum,
|
||||
r#"
|
||||
struct Foo {
|
||||
$0foo: bool,
|
||||
@ -1507,7 +1507,7 @@ fn main() {
|
||||
#[test]
|
||||
fn field_method_chain_usage() {
|
||||
check_assist(
|
||||
bool_to_enum,
|
||||
convert_bool_to_enum,
|
||||
r#"
|
||||
struct Foo {
|
||||
$0bool: bool,
|
||||
@ -1539,7 +1539,7 @@ fn main() {
|
||||
#[test]
|
||||
fn field_in_macro() {
|
||||
check_assist(
|
||||
bool_to_enum,
|
||||
convert_bool_to_enum,
|
||||
r#"
|
||||
struct Struct {
|
||||
$0boolean: bool,
|
||||
@ -1580,7 +1580,7 @@ fn new() -> Struct {
|
||||
fn field_non_bool() {
|
||||
cov_mark::check!(not_applicable_non_bool_field);
|
||||
check_assist_not_applicable(
|
||||
bool_to_enum,
|
||||
convert_bool_to_enum,
|
||||
r#"
|
||||
struct Foo {
|
||||
$0bar: usize,
|
||||
@ -1596,7 +1596,7 @@ fn main() {
|
||||
#[test]
|
||||
fn const_basic() {
|
||||
check_assist(
|
||||
bool_to_enum,
|
||||
convert_bool_to_enum,
|
||||
r#"
|
||||
const $0FOO: bool = false;
|
||||
|
||||
@ -1624,7 +1624,7 @@ fn main() {
|
||||
#[test]
|
||||
fn const_in_module() {
|
||||
check_assist(
|
||||
bool_to_enum,
|
||||
convert_bool_to_enum,
|
||||
r#"
|
||||
fn main() {
|
||||
if foo::FOO {
|
||||
@ -1658,7 +1658,7 @@ mod foo {
|
||||
#[test]
|
||||
fn const_in_module_with_import() {
|
||||
check_assist(
|
||||
bool_to_enum,
|
||||
convert_bool_to_enum,
|
||||
r#"
|
||||
fn main() {
|
||||
use foo::FOO;
|
||||
@ -1696,7 +1696,7 @@ mod foo {
|
||||
#[test]
|
||||
fn const_cross_file() {
|
||||
check_assist(
|
||||
bool_to_enum,
|
||||
convert_bool_to_enum,
|
||||
r#"
|
||||
//- /main.rs
|
||||
mod foo;
|
||||
@ -1734,7 +1734,7 @@ pub const FOO: Bool = Bool::True;
|
||||
#[test]
|
||||
fn const_cross_file_and_module() {
|
||||
check_assist(
|
||||
bool_to_enum,
|
||||
convert_bool_to_enum,
|
||||
r#"
|
||||
//- /main.rs
|
||||
mod foo;
|
||||
@ -1780,7 +1780,7 @@ pub mod bar {
|
||||
#[test]
|
||||
fn const_in_impl_cross_file() {
|
||||
check_assist(
|
||||
bool_to_enum,
|
||||
convert_bool_to_enum,
|
||||
r#"
|
||||
//- /main.rs
|
||||
mod foo;
|
||||
@ -1824,7 +1824,7 @@ fn foo() -> bool {
|
||||
#[test]
|
||||
fn const_in_trait() {
|
||||
check_assist(
|
||||
bool_to_enum,
|
||||
convert_bool_to_enum,
|
||||
r#"
|
||||
trait Foo {
|
||||
const $0BOOL: bool;
|
||||
@ -1865,7 +1865,7 @@ fn main() {
|
||||
fn const_non_bool() {
|
||||
cov_mark::check!(not_applicable_non_bool_const);
|
||||
check_assist_not_applicable(
|
||||
bool_to_enum,
|
||||
convert_bool_to_enum,
|
||||
r#"
|
||||
const $0FOO: &str = "foo";
|
||||
|
||||
@ -1879,7 +1879,7 @@ fn main() {
|
||||
#[test]
|
||||
fn static_basic() {
|
||||
check_assist(
|
||||
bool_to_enum,
|
||||
convert_bool_to_enum,
|
||||
r#"
|
||||
static mut $0BOOL: bool = true;
|
||||
|
||||
@ -1910,7 +1910,7 @@ fn main() {
|
||||
fn static_non_bool() {
|
||||
cov_mark::check!(not_applicable_non_bool_static);
|
||||
check_assist_not_applicable(
|
||||
bool_to_enum,
|
||||
convert_bool_to_enum,
|
||||
r#"
|
||||
static mut $0FOO: usize = 0;
|
||||
|
||||
@ -1925,6 +1925,6 @@ fn main() {
|
||||
|
||||
#[test]
|
||||
fn not_applicable_to_other_names() {
|
||||
check_assist_not_applicable(bool_to_enum, "fn $0main() {}")
|
||||
check_assist_not_applicable(convert_bool_to_enum, "fn $0main() {}")
|
||||
}
|
||||
}
|
@ -1,11 +1,29 @@
|
||||
use hir::{PathResolution, StructKind};
|
||||
use ide_db::syntax_helpers::suggest_name::NameGenerator;
|
||||
use syntax::{
|
||||
ast::{self, make},
|
||||
AstNode, ToSmolStr,
|
||||
match_ast, AstNode, ToSmolStr,
|
||||
};
|
||||
|
||||
use crate::{AssistContext, AssistId, Assists};
|
||||
|
||||
// Assist: fill_record_pattern_fields
|
||||
pub(crate) fn expand_rest_pattern(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
|
||||
let rest_pat = ctx.find_node_at_offset::<ast::RestPat>()?;
|
||||
let parent = rest_pat.syntax().parent()?;
|
||||
match_ast! {
|
||||
match parent {
|
||||
ast::RecordPatFieldList(it) => expand_record_rest_pattern(acc, ctx, it.syntax().parent().and_then(ast::RecordPat::cast)?, rest_pat),
|
||||
ast::TupleStructPat(it) => expand_tuple_struct_rest_pattern(acc, ctx, it, rest_pat),
|
||||
// FIXME
|
||||
// ast::TuplePat(it) => (),
|
||||
// FIXME
|
||||
// ast::SlicePat(it) => (),
|
||||
_ => return None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Assist: expand_record_rest_pattern
|
||||
//
|
||||
// Fills fields by replacing rest pattern in record patterns.
|
||||
//
|
||||
@ -24,16 +42,12 @@ use crate::{AssistContext, AssistId, Assists};
|
||||
// let Bar { y, z } = bar;
|
||||
// }
|
||||
// ```
|
||||
pub(crate) fn fill_record_pattern_fields(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
|
||||
let record_pat = ctx.find_node_at_offset::<ast::RecordPat>()?;
|
||||
|
||||
let ellipsis = record_pat.record_pat_field_list().and_then(|r| r.rest_pat())?;
|
||||
if !ellipsis.syntax().text_range().contains_inclusive(ctx.offset()) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let target_range = ellipsis.syntax().text_range();
|
||||
|
||||
fn expand_record_rest_pattern(
|
||||
acc: &mut Assists,
|
||||
ctx: &AssistContext<'_>,
|
||||
record_pat: ast::RecordPat,
|
||||
rest_pat: ast::RestPat,
|
||||
) -> Option<()> {
|
||||
let missing_fields = ctx.sema.record_pattern_missing_fields(&record_pat);
|
||||
|
||||
if missing_fields.is_empty() {
|
||||
@ -42,6 +56,11 @@ pub(crate) fn fill_record_pattern_fields(acc: &mut Assists, ctx: &AssistContext<
|
||||
}
|
||||
|
||||
let old_field_list = record_pat.record_pat_field_list()?;
|
||||
let old_range = ctx.sema.original_range_opt(old_field_list.syntax())?;
|
||||
if old_range.file_id != ctx.file_id() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let new_field_list =
|
||||
make::record_pat_field_list(old_field_list.fields(), None).clone_for_update();
|
||||
for (f, _) in missing_fields.iter() {
|
||||
@ -52,16 +71,93 @@ pub(crate) fn fill_record_pattern_fields(acc: &mut Assists, ctx: &AssistContext<
|
||||
new_field_list.add_field(field.clone_for_update());
|
||||
}
|
||||
|
||||
let old_range = ctx.sema.original_range_opt(old_field_list.syntax())?;
|
||||
let target_range = rest_pat.syntax().text_range();
|
||||
acc.add(
|
||||
AssistId("expand_record_rest_pattern", crate::AssistKind::RefactorRewrite),
|
||||
"Fill struct fields",
|
||||
target_range,
|
||||
move |builder| builder.replace_ast(old_field_list, new_field_list),
|
||||
)
|
||||
}
|
||||
// Assist: expand_tuple_struct_rest_pattern
|
||||
//
|
||||
// Fills fields by replacing rest pattern in tuple struct patterns.
|
||||
//
|
||||
// ```
|
||||
// struct Bar(Y, Z);
|
||||
//
|
||||
// fn foo(bar: Bar) {
|
||||
// let Bar(..$0) = bar;
|
||||
// }
|
||||
// ```
|
||||
// ->
|
||||
// ```
|
||||
// struct Bar(Y, Z);
|
||||
//
|
||||
// fn foo(bar: Bar) {
|
||||
// let Bar(_0, _1) = bar;
|
||||
// }
|
||||
// ```
|
||||
fn expand_tuple_struct_rest_pattern(
|
||||
acc: &mut Assists,
|
||||
ctx: &AssistContext<'_>,
|
||||
pat: ast::TupleStructPat,
|
||||
rest_pat: ast::RestPat,
|
||||
) -> Option<()> {
|
||||
let path = pat.path()?;
|
||||
let fields = match ctx.sema.type_of_pat(&pat.clone().into())?.original.as_adt()? {
|
||||
hir::Adt::Struct(s) if s.kind(ctx.sema.db) == StructKind::Tuple => s.fields(ctx.sema.db),
|
||||
hir::Adt::Enum(_) => match ctx.sema.resolve_path(&path)? {
|
||||
PathResolution::Def(hir::ModuleDef::Variant(v))
|
||||
if v.kind(ctx.sema.db) == StructKind::Tuple =>
|
||||
{
|
||||
v.fields(ctx.sema.db)
|
||||
}
|
||||
_ => return None,
|
||||
},
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
let rest_pat = rest_pat.into();
|
||||
let mut pats = pat.fields();
|
||||
let prefix_count = pats.by_ref().position(|p| p == rest_pat)?;
|
||||
let suffix_count = pats.count();
|
||||
|
||||
if fields.len().saturating_sub(prefix_count).saturating_sub(suffix_count) == 0 {
|
||||
cov_mark::hit!(no_missing_fields_tuple_struct);
|
||||
return None;
|
||||
}
|
||||
|
||||
let old_range = ctx.sema.original_range_opt(pat.syntax())?;
|
||||
if old_range.file_id != ctx.file_id() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut name_gen = NameGenerator::new_from_scope_locals(ctx.sema.scope(pat.syntax()));
|
||||
let new_pat = make::tuple_struct_pat(
|
||||
path,
|
||||
pat.fields()
|
||||
.take(prefix_count)
|
||||
.chain(fields[prefix_count..fields.len() - suffix_count].iter().map(|f| {
|
||||
make::ident_pat(
|
||||
false,
|
||||
false,
|
||||
match name_gen.for_type(&f.ty(ctx.sema.db), ctx.sema.db, ctx.edition()) {
|
||||
Some(name) => make::name(&name),
|
||||
None => make::name(&format!("_{}", f.index())),
|
||||
},
|
||||
)
|
||||
.into()
|
||||
}))
|
||||
.chain(pat.fields().skip(prefix_count + 1)),
|
||||
);
|
||||
|
||||
let target_range = rest_pat.syntax().text_range();
|
||||
acc.add(
|
||||
AssistId("fill_record_pattern_fields", crate::AssistKind::RefactorRewrite),
|
||||
"Fill structure fields",
|
||||
AssistId("expand_tuple_struct_rest_pattern", crate::AssistKind::RefactorRewrite),
|
||||
"Fill tuple struct fields",
|
||||
target_range,
|
||||
move |builder| builder.replace_ast(old_field_list, new_field_list),
|
||||
move |builder| builder.replace_ast(pat, new_pat),
|
||||
)
|
||||
}
|
||||
|
||||
@ -73,7 +169,7 @@ mod tests {
|
||||
#[test]
|
||||
fn fill_fields_enum_with_only_ellipsis() {
|
||||
check_assist(
|
||||
fill_record_pattern_fields,
|
||||
expand_rest_pattern,
|
||||
r#"
|
||||
enum Foo {
|
||||
A(X),
|
||||
@ -106,7 +202,7 @@ fn bar(foo: Foo) {
|
||||
#[test]
|
||||
fn fill_fields_enum_with_fields() {
|
||||
check_assist(
|
||||
fill_record_pattern_fields,
|
||||
expand_rest_pattern,
|
||||
r#"
|
||||
enum Foo {
|
||||
A(X),
|
||||
@ -139,7 +235,7 @@ fn bar(foo: Foo) {
|
||||
#[test]
|
||||
fn fill_fields_struct_with_only_ellipsis() {
|
||||
check_assist(
|
||||
fill_record_pattern_fields,
|
||||
expand_rest_pattern,
|
||||
r#"
|
||||
struct Bar {
|
||||
y: Y,
|
||||
@ -159,6 +255,27 @@ struct Bar {
|
||||
fn foo(bar: Bar) {
|
||||
let Bar { y, z } = bar;
|
||||
}
|
||||
"#,
|
||||
);
|
||||
check_assist(
|
||||
expand_rest_pattern,
|
||||
r#"
|
||||
struct Y;
|
||||
struct Z;
|
||||
struct Bar(Y, Z)
|
||||
|
||||
fn foo(bar: Bar) {
|
||||
let Bar(..$0) = bar;
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
struct Y;
|
||||
struct Z;
|
||||
struct Bar(Y, Z)
|
||||
|
||||
fn foo(bar: Bar) {
|
||||
let Bar(y, z) = bar;
|
||||
}
|
||||
"#,
|
||||
)
|
||||
}
|
||||
@ -166,7 +283,7 @@ fn foo(bar: Bar) {
|
||||
#[test]
|
||||
fn fill_fields_struct_with_fields() {
|
||||
check_assist(
|
||||
fill_record_pattern_fields,
|
||||
expand_rest_pattern,
|
||||
r#"
|
||||
struct Bar {
|
||||
y: Y,
|
||||
@ -186,6 +303,29 @@ struct Bar {
|
||||
fn foo(bar: Bar) {
|
||||
let Bar { y, z } = bar;
|
||||
}
|
||||
"#,
|
||||
);
|
||||
check_assist(
|
||||
expand_rest_pattern,
|
||||
r#"
|
||||
struct X;
|
||||
struct Y;
|
||||
struct Z;
|
||||
struct Bar(X, Y, Z)
|
||||
|
||||
fn foo(bar: Bar) {
|
||||
let Bar(x, ..$0, z) = bar;
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
struct X;
|
||||
struct Y;
|
||||
struct Z;
|
||||
struct Bar(X, Y, Z)
|
||||
|
||||
fn foo(bar: Bar) {
|
||||
let Bar(x, y, z) = bar;
|
||||
}
|
||||
"#,
|
||||
)
|
||||
}
|
||||
@ -193,7 +333,7 @@ fn foo(bar: Bar) {
|
||||
#[test]
|
||||
fn fill_fields_struct_generated_by_macro() {
|
||||
check_assist(
|
||||
fill_record_pattern_fields,
|
||||
expand_rest_pattern,
|
||||
r#"
|
||||
macro_rules! position {
|
||||
($t: ty) => {
|
||||
@ -226,7 +366,7 @@ fn macro_call(pos: Pos) {
|
||||
#[test]
|
||||
fn fill_fields_enum_generated_by_macro() {
|
||||
check_assist(
|
||||
fill_record_pattern_fields,
|
||||
expand_rest_pattern,
|
||||
r#"
|
||||
macro_rules! enum_gen {
|
||||
($t: ty) => {
|
||||
@ -271,7 +411,7 @@ fn macro_call(foo: Foo) {
|
||||
#[test]
|
||||
fn not_applicable_when_not_in_ellipsis() {
|
||||
check_assist_not_applicable(
|
||||
fill_record_pattern_fields,
|
||||
expand_rest_pattern,
|
||||
r#"
|
||||
enum Foo {
|
||||
A(X),
|
||||
@ -287,7 +427,7 @@ fn bar(foo: Foo) {
|
||||
"#,
|
||||
);
|
||||
check_assist_not_applicable(
|
||||
fill_record_pattern_fields,
|
||||
expand_rest_pattern,
|
||||
r#"
|
||||
enum Foo {
|
||||
A(X),
|
||||
@ -303,7 +443,7 @@ fn bar(foo: Foo) {
|
||||
"#,
|
||||
);
|
||||
check_assist_not_applicable(
|
||||
fill_record_pattern_fields,
|
||||
expand_rest_pattern,
|
||||
r#"
|
||||
enum Foo {
|
||||
A(X),
|
||||
@ -324,8 +464,9 @@ fn bar(foo: Foo) {
|
||||
fn not_applicable_when_no_missing_fields() {
|
||||
// This is still possible even though it's meaningless
|
||||
cov_mark::check!(no_missing_fields);
|
||||
cov_mark::check!(no_missing_fields_tuple_struct);
|
||||
check_assist_not_applicable(
|
||||
fill_record_pattern_fields,
|
||||
expand_rest_pattern,
|
||||
r#"
|
||||
enum Foo {
|
||||
A(X),
|
||||
@ -341,7 +482,7 @@ fn bar(foo: Foo) {
|
||||
"#,
|
||||
);
|
||||
check_assist_not_applicable(
|
||||
fill_record_pattern_fields,
|
||||
expand_rest_pattern,
|
||||
r#"
|
||||
struct Bar {
|
||||
y: Y,
|
||||
@ -351,6 +492,16 @@ struct Bar {
|
||||
fn foo(bar: Bar) {
|
||||
let Bar { y, z, ..$0 } = bar;
|
||||
}
|
||||
"#,
|
||||
);
|
||||
check_assist_not_applicable(
|
||||
expand_rest_pattern,
|
||||
r#"
|
||||
struct Bar(Y, Z)
|
||||
|
||||
fn foo(bar: Bar) {
|
||||
let Bar(y, ..$0, z) = bar;
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
@ -750,7 +750,10 @@ impl FunctionBody {
|
||||
ast::Stmt::Item(_) => (),
|
||||
ast::Stmt::LetStmt(stmt) => {
|
||||
if let Some(pat) = stmt.pat() {
|
||||
walk_pat(&pat, cb);
|
||||
walk_pat(&pat, &mut |pat| {
|
||||
cb(pat);
|
||||
std::ops::ControlFlow::<(), ()>::Continue(())
|
||||
});
|
||||
}
|
||||
if let Some(expr) = stmt.initializer() {
|
||||
walk_patterns_in_expr(&expr, cb);
|
||||
|
@ -724,9 +724,9 @@ fn fn_generic_params(
|
||||
filter_unnecessary_bounds(&mut generic_params, &mut where_preds, necessary_params);
|
||||
filter_bounds_in_scope(&mut generic_params, &mut where_preds, ctx, target);
|
||||
|
||||
let generic_params: Vec<_> =
|
||||
let generic_params: Vec<ast::GenericParam> =
|
||||
generic_params.into_iter().map(|it| it.node.clone_for_update()).collect();
|
||||
let where_preds: Vec<_> =
|
||||
let where_preds: Vec<ast::WherePred> =
|
||||
where_preds.into_iter().map(|it| it.node.clone_for_update()).collect();
|
||||
|
||||
// 4. Rewrite paths
|
||||
@ -1116,9 +1116,12 @@ fn fn_arg_type(
|
||||
|
||||
if ty.is_reference() || ty.is_mutable_reference() {
|
||||
let famous_defs = &FamousDefs(&ctx.sema, ctx.sema.scope(fn_arg.syntax())?.krate());
|
||||
let target_edition = target_module.krate().edition(ctx.db());
|
||||
convert_reference_type(ty.strip_references(), ctx.db(), famous_defs)
|
||||
.map(|conversion| conversion.convert_type(ctx.db(), target_edition).to_string())
|
||||
.map(|conversion| {
|
||||
conversion
|
||||
.convert_type(ctx.db(), target_module.krate().to_display_target(ctx.db()))
|
||||
.to_string()
|
||||
})
|
||||
.or_else(|| ty.display_source_code(ctx.db(), target_module.into(), true).ok())
|
||||
} else {
|
||||
ty.display_source_code(ctx.db(), target_module.into(), true).ok()
|
||||
|
@ -233,7 +233,7 @@ fn generate_getter_from_info(
|
||||
.map(|conversion| {
|
||||
cov_mark::hit!(convert_reference_type);
|
||||
(
|
||||
conversion.convert_type(ctx.db(), krate.edition(ctx.db())),
|
||||
conversion.convert_type(ctx.db(), krate.to_display_target(ctx.db())),
|
||||
conversion.getter(record_field_info.field_name.to_string()),
|
||||
)
|
||||
})
|
||||
|
@ -42,7 +42,7 @@ pub(crate) fn inline_const_as_literal(acc: &mut Assists, ctx: &AssistContext<'_>
|
||||
let value = konst
|
||||
.eval(ctx.sema.db)
|
||||
.ok()?
|
||||
.render(ctx.sema.db, konst.krate(ctx.sema.db).edition(ctx.sema.db));
|
||||
.render(ctx.sema.db, konst.krate(ctx.sema.db).to_display_target(ctx.sema.db));
|
||||
|
||||
let id = AssistId("inline_const_as_literal", AssistKind::RefactorInline);
|
||||
|
||||
|
@ -5,7 +5,7 @@ use ide_db::{
|
||||
EditionedFileId, RootDatabase,
|
||||
};
|
||||
use syntax::{
|
||||
ast::{self, AstNode, AstToken, HasName},
|
||||
ast::{self, syntax_factory::SyntaxFactory, AstNode, AstToken, HasName},
|
||||
SyntaxElement, TextRange,
|
||||
};
|
||||
|
||||
@ -43,22 +43,6 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext<'_>)
|
||||
}?;
|
||||
let initializer_expr = let_stmt.initializer()?;
|
||||
|
||||
let delete_range = delete_let.then(|| {
|
||||
if let Some(whitespace) = let_stmt
|
||||
.syntax()
|
||||
.next_sibling_or_token()
|
||||
.and_then(SyntaxElement::into_token)
|
||||
.and_then(ast::Whitespace::cast)
|
||||
{
|
||||
TextRange::new(
|
||||
let_stmt.syntax().text_range().start(),
|
||||
whitespace.syntax().text_range().end(),
|
||||
)
|
||||
} else {
|
||||
let_stmt.syntax().text_range()
|
||||
}
|
||||
});
|
||||
|
||||
let wrap_in_parens = references
|
||||
.into_iter()
|
||||
.filter_map(|FileReference { range, name, .. }| match name {
|
||||
@ -73,40 +57,60 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext<'_>)
|
||||
}
|
||||
let usage_node =
|
||||
name_ref.syntax().ancestors().find(|it| ast::PathExpr::can_cast(it.kind()));
|
||||
let usage_parent_option = usage_node.and_then(|it| it.parent());
|
||||
let usage_parent_option = usage_node.as_ref().and_then(|it| it.parent());
|
||||
let usage_parent = match usage_parent_option {
|
||||
Some(u) => u,
|
||||
None => return Some((range, name_ref, false)),
|
||||
None => return Some((name_ref, false)),
|
||||
};
|
||||
Some((range, name_ref, initializer_expr.needs_parens_in(&usage_parent)))
|
||||
let should_wrap = initializer_expr
|
||||
.needs_parens_in_place_of(&usage_parent, usage_node.as_ref().unwrap());
|
||||
Some((name_ref, should_wrap))
|
||||
})
|
||||
.collect::<Option<Vec<_>>>()?;
|
||||
|
||||
let init_str = initializer_expr.syntax().text().to_string();
|
||||
let init_in_paren = format!("({init_str})");
|
||||
|
||||
let target = match target {
|
||||
ast::NameOrNameRef::Name(it) => it.syntax().text_range(),
|
||||
ast::NameOrNameRef::NameRef(it) => it.syntax().text_range(),
|
||||
ast::NameOrNameRef::Name(it) => it.syntax().clone(),
|
||||
ast::NameOrNameRef::NameRef(it) => it.syntax().clone(),
|
||||
};
|
||||
|
||||
acc.add(
|
||||
AssistId("inline_local_variable", AssistKind::RefactorInline),
|
||||
"Inline variable",
|
||||
target,
|
||||
target.text_range(),
|
||||
move |builder| {
|
||||
if let Some(range) = delete_range {
|
||||
builder.delete(range);
|
||||
}
|
||||
for (range, name, should_wrap) in wrap_in_parens {
|
||||
let replacement = if should_wrap { &init_in_paren } else { &init_str };
|
||||
if ast::RecordExprField::for_field_name(&name).is_some() {
|
||||
cov_mark::hit!(inline_field_shorthand);
|
||||
builder.insert(range.end(), format!(": {replacement}"));
|
||||
} else {
|
||||
builder.replace(range, replacement.clone())
|
||||
let mut editor = builder.make_editor(&target);
|
||||
if delete_let {
|
||||
editor.delete(let_stmt.syntax());
|
||||
if let Some(whitespace) = let_stmt
|
||||
.syntax()
|
||||
.next_sibling_or_token()
|
||||
.and_then(SyntaxElement::into_token)
|
||||
.and_then(ast::Whitespace::cast)
|
||||
{
|
||||
editor.delete(whitespace.syntax());
|
||||
}
|
||||
}
|
||||
|
||||
let make = SyntaxFactory::new();
|
||||
|
||||
for (name, should_wrap) in wrap_in_parens {
|
||||
let replacement = if should_wrap {
|
||||
make.expr_paren(initializer_expr.clone()).into()
|
||||
} else {
|
||||
initializer_expr.clone()
|
||||
};
|
||||
|
||||
if let Some(record_field) = ast::RecordExprField::for_field_name(&name) {
|
||||
cov_mark::hit!(inline_field_shorthand);
|
||||
let replacement = make.record_expr_field(name, Some(replacement));
|
||||
editor.replace(record_field.syntax(), replacement.syntax());
|
||||
} else {
|
||||
editor.replace(name.syntax(), replacement.syntax());
|
||||
}
|
||||
}
|
||||
|
||||
editor.add_mappings(make.finish_with_mappings());
|
||||
builder.add_file_edits(ctx.file_id(), editor);
|
||||
},
|
||||
)
|
||||
}
|
||||
@ -939,6 +943,54 @@ fn main() {
|
||||
fn main() {
|
||||
let _ = (|| 2)();
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_wrap_in_parens() {
|
||||
check_assist(
|
||||
inline_local_variable,
|
||||
r#"
|
||||
fn main() {
|
||||
let $0a = 123 < 456;
|
||||
let b = !a;
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
fn main() {
|
||||
let b = !(123 < 456);
|
||||
}
|
||||
"#,
|
||||
);
|
||||
check_assist(
|
||||
inline_local_variable,
|
||||
r#"
|
||||
trait Foo {
|
||||
fn foo(&self);
|
||||
}
|
||||
|
||||
impl Foo for bool {
|
||||
fn foo(&self) {}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let $0a = 123 < 456;
|
||||
let b = a.foo();
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
trait Foo {
|
||||
fn foo(&self);
|
||||
}
|
||||
|
||||
impl Foo for bool {
|
||||
fn foo(&self) {}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let b = (123 < 456).foo();
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ use syntax::ast::{self, syntax_factory::SyntaxFactory, AstNode, HasGenericParams
|
||||
|
||||
use crate::{AssistContext, AssistId, AssistKind, Assists};
|
||||
|
||||
// Assist: introduce_named_generic
|
||||
// Assist: introduce_named_type_parameter
|
||||
//
|
||||
// Replaces `impl Trait` function argument with the named generic.
|
||||
//
|
||||
@ -15,18 +15,20 @@ use crate::{AssistContext, AssistId, AssistKind, Assists};
|
||||
// ```
|
||||
// fn foo<$0B: Bar>(bar: B) {}
|
||||
// ```
|
||||
pub(crate) fn introduce_named_generic(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
|
||||
pub(crate) fn introduce_named_type_parameter(
|
||||
acc: &mut Assists,
|
||||
ctx: &AssistContext<'_>,
|
||||
) -> Option<()> {
|
||||
let impl_trait_type = ctx.find_node_at_offset::<ast::ImplTraitType>()?;
|
||||
let param = impl_trait_type.syntax().ancestors().find_map(ast::Param::cast)?;
|
||||
let fn_ = param.syntax().ancestors().find_map(ast::Fn::cast)?;
|
||||
|
||||
let fn_ = param.syntax().ancestors().nth(2).and_then(ast::Fn::cast)?;
|
||||
let type_bound_list = impl_trait_type.type_bound_list()?;
|
||||
|
||||
let make = SyntaxFactory::new();
|
||||
let target = fn_.syntax().text_range();
|
||||
acc.add(
|
||||
AssistId("introduce_named_generic", AssistKind::RefactorRewrite),
|
||||
"Replace impl trait with generic",
|
||||
AssistId("introduce_named_type_parameter", AssistKind::RefactorRewrite),
|
||||
"Replace impl trait with type parameter",
|
||||
target,
|
||||
|builder| {
|
||||
let mut editor = builder.make_editor(fn_.syntax());
|
||||
@ -71,7 +73,7 @@ mod tests {
|
||||
#[test]
|
||||
fn introduce_named_generic_params() {
|
||||
check_assist(
|
||||
introduce_named_generic,
|
||||
introduce_named_type_parameter,
|
||||
r#"fn foo<G>(bar: $0impl Bar) {}"#,
|
||||
r#"fn foo<G, $0B: Bar>(bar: B) {}"#,
|
||||
);
|
||||
@ -80,7 +82,7 @@ mod tests {
|
||||
#[test]
|
||||
fn replace_impl_trait_without_generic_params() {
|
||||
check_assist(
|
||||
introduce_named_generic,
|
||||
introduce_named_type_parameter,
|
||||
r#"fn foo(bar: $0impl Bar) {}"#,
|
||||
r#"fn foo<$0B: Bar>(bar: B) {}"#,
|
||||
);
|
||||
@ -89,7 +91,7 @@ mod tests {
|
||||
#[test]
|
||||
fn replace_two_impl_trait_with_generic_params() {
|
||||
check_assist(
|
||||
introduce_named_generic,
|
||||
introduce_named_type_parameter,
|
||||
r#"fn foo<G>(foo: impl Foo, bar: $0impl Bar) {}"#,
|
||||
r#"fn foo<G, $0B: Bar>(foo: impl Foo, bar: B) {}"#,
|
||||
);
|
||||
@ -98,7 +100,7 @@ mod tests {
|
||||
#[test]
|
||||
fn replace_impl_trait_with_empty_generic_params() {
|
||||
check_assist(
|
||||
introduce_named_generic,
|
||||
introduce_named_type_parameter,
|
||||
r#"fn foo<>(bar: $0impl Bar) {}"#,
|
||||
r#"fn foo<$0B: Bar>(bar: B) {}"#,
|
||||
);
|
||||
@ -107,7 +109,7 @@ mod tests {
|
||||
#[test]
|
||||
fn replace_impl_trait_with_empty_multiline_generic_params() {
|
||||
check_assist(
|
||||
introduce_named_generic,
|
||||
introduce_named_type_parameter,
|
||||
r#"
|
||||
fn foo<
|
||||
>(bar: $0impl Bar) {}
|
||||
@ -122,7 +124,7 @@ fn foo<$0B: Bar
|
||||
#[test]
|
||||
fn replace_impl_trait_with_exist_generic_letter() {
|
||||
check_assist(
|
||||
introduce_named_generic,
|
||||
introduce_named_type_parameter,
|
||||
r#"fn foo<B>(bar: $0impl Bar) {}"#,
|
||||
r#"fn foo<B, $0B1: Bar>(bar: B1) {}"#,
|
||||
);
|
||||
@ -131,7 +133,7 @@ fn foo<$0B: Bar
|
||||
#[test]
|
||||
fn replace_impl_trait_with_more_exist_generic_letter() {
|
||||
check_assist(
|
||||
introduce_named_generic,
|
||||
introduce_named_type_parameter,
|
||||
r#"fn foo<B, B0, B1, B3>(bar: $0impl Bar) {}"#,
|
||||
r#"fn foo<B, B0, B1, B3, $0B4: Bar>(bar: B4) {}"#,
|
||||
);
|
||||
@ -140,7 +142,7 @@ fn foo<$0B: Bar
|
||||
#[test]
|
||||
fn replace_impl_trait_with_multiline_generic_params() {
|
||||
check_assist(
|
||||
introduce_named_generic,
|
||||
introduce_named_type_parameter,
|
||||
r#"
|
||||
fn foo<
|
||||
G: Foo,
|
||||
@ -161,7 +163,7 @@ fn foo<
|
||||
#[test]
|
||||
fn replace_impl_trait_multiple() {
|
||||
check_assist(
|
||||
introduce_named_generic,
|
||||
introduce_named_type_parameter,
|
||||
r#"fn foo(bar: $0impl Foo + Bar) {}"#,
|
||||
r#"fn foo<$0F: Foo + Bar>(bar: F) {}"#,
|
||||
);
|
||||
@ -170,7 +172,7 @@ fn foo<
|
||||
#[test]
|
||||
fn replace_impl_with_mut() {
|
||||
check_assist(
|
||||
introduce_named_generic,
|
||||
introduce_named_type_parameter,
|
||||
r#"fn f(iter: &mut $0impl Iterator<Item = i32>) {}"#,
|
||||
r#"fn f<$0I: Iterator<Item = i32>>(iter: &mut I) {}"#,
|
||||
);
|
||||
@ -179,7 +181,7 @@ fn foo<
|
||||
#[test]
|
||||
fn replace_impl_inside() {
|
||||
check_assist(
|
||||
introduce_named_generic,
|
||||
introduce_named_type_parameter,
|
||||
r#"fn f(x: &mut Vec<$0impl Iterator<Item = i32>>) {}"#,
|
||||
r#"fn f<$0I: Iterator<Item = i32>>(x: &mut Vec<I>) {}"#,
|
||||
);
|
@ -52,8 +52,13 @@ pub(crate) fn term_search(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<
|
||||
let paths = paths
|
||||
.into_iter()
|
||||
.filter_map(|path| {
|
||||
path.gen_source_code(&scope, &mut formatter, ctx.config.import_path_config(), edition)
|
||||
.ok()
|
||||
path.gen_source_code(
|
||||
&scope,
|
||||
&mut formatter,
|
||||
ctx.config.import_path_config(),
|
||||
scope.krate().to_display_target(ctx.db()),
|
||||
)
|
||||
.ok()
|
||||
})
|
||||
.unique();
|
||||
|
||||
|
@ -105,6 +105,7 @@ mod handlers {
|
||||
pub(crate) type Handler = fn(&mut Assists, &AssistContext<'_>) -> Option<()>;
|
||||
|
||||
mod add_braces;
|
||||
mod add_explicit_enum_discriminant;
|
||||
mod add_explicit_type;
|
||||
mod add_label_to_loop;
|
||||
mod add_lifetime_to_type;
|
||||
@ -115,9 +116,9 @@ mod handlers {
|
||||
mod apply_demorgan;
|
||||
mod auto_import;
|
||||
mod bind_unused_param;
|
||||
mod bool_to_enum;
|
||||
mod change_visibility;
|
||||
mod convert_bool_then;
|
||||
mod convert_bool_to_enum;
|
||||
mod convert_closure_to_fn;
|
||||
mod convert_comment_block;
|
||||
mod convert_comment_from_or_to_doc;
|
||||
@ -138,14 +139,13 @@ mod handlers {
|
||||
mod destructure_tuple_binding;
|
||||
mod desugar_doc_comment;
|
||||
mod expand_glob_import;
|
||||
mod explicit_enum_discriminant;
|
||||
mod expand_rest_pattern;
|
||||
mod extract_expressions_from_format_string;
|
||||
mod extract_function;
|
||||
mod extract_module;
|
||||
mod extract_struct_from_enum_variant;
|
||||
mod extract_type_alias;
|
||||
mod extract_variable;
|
||||
mod fill_record_pattern_fields;
|
||||
mod fix_visibility;
|
||||
mod flip_binexpr;
|
||||
mod flip_comma;
|
||||
@ -177,8 +177,8 @@ mod handlers {
|
||||
mod inline_macro;
|
||||
mod inline_type_alias;
|
||||
mod into_to_qualified_from;
|
||||
mod introduce_named_generic;
|
||||
mod introduce_named_lifetime;
|
||||
mod introduce_named_type_parameter;
|
||||
mod invert_if;
|
||||
mod merge_imports;
|
||||
mod merge_match_arms;
|
||||
@ -234,49 +234,47 @@ mod handlers {
|
||||
&[
|
||||
// These are alphabetic for the foolish consistency
|
||||
add_braces::add_braces,
|
||||
add_explicit_enum_discriminant::add_explicit_enum_discriminant,
|
||||
add_explicit_type::add_explicit_type,
|
||||
add_label_to_loop::add_label_to_loop,
|
||||
add_missing_match_arms::add_missing_match_arms,
|
||||
add_lifetime_to_type::add_lifetime_to_type,
|
||||
add_missing_match_arms::add_missing_match_arms,
|
||||
add_return_type::add_return_type,
|
||||
add_turbo_fish::add_turbo_fish,
|
||||
apply_demorgan::apply_demorgan,
|
||||
apply_demorgan::apply_demorgan_iterator,
|
||||
apply_demorgan::apply_demorgan,
|
||||
auto_import::auto_import,
|
||||
bind_unused_param::bind_unused_param,
|
||||
bool_to_enum::bool_to_enum,
|
||||
change_visibility::change_visibility,
|
||||
convert_bool_then::convert_bool_then_to_if,
|
||||
convert_bool_then::convert_if_to_bool_then,
|
||||
toggle_async_sugar::desugar_async_into_impl_future,
|
||||
toggle_async_sugar::sugar_impl_future_into_async,
|
||||
convert_bool_to_enum::convert_bool_to_enum,
|
||||
convert_closure_to_fn::convert_closure_to_fn,
|
||||
convert_comment_block::convert_comment_block,
|
||||
convert_comment_from_or_to_doc::convert_comment_from_or_to_doc,
|
||||
convert_closure_to_fn::convert_closure_to_fn,
|
||||
convert_from_to_tryfrom::convert_from_to_tryfrom,
|
||||
convert_integer_literal::convert_integer_literal,
|
||||
convert_into_to_from::convert_into_to_from,
|
||||
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_iter_for_each_to_for,
|
||||
convert_let_else_to_match::convert_let_else_to_match,
|
||||
convert_match_to_let_else::convert_match_to_let_else,
|
||||
convert_tuple_return_type_to_struct::convert_tuple_return_type_to_struct,
|
||||
convert_named_struct_to_tuple_struct::convert_named_struct_to_tuple_struct,
|
||||
convert_nested_function_to_closure::convert_nested_function_to_closure,
|
||||
convert_to_guarded_return::convert_to_guarded_return,
|
||||
convert_tuple_return_type_to_struct::convert_tuple_return_type_to_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_while_to_loop::convert_while_to_loop,
|
||||
desugar_doc_comment::desugar_doc_comment,
|
||||
destructure_tuple_binding::destructure_tuple_binding,
|
||||
destructure_struct_binding::destructure_struct_binding,
|
||||
destructure_tuple_binding::destructure_tuple_binding,
|
||||
desugar_doc_comment::desugar_doc_comment,
|
||||
expand_glob_import::expand_glob_import,
|
||||
expand_glob_import::expand_glob_reexport,
|
||||
explicit_enum_discriminant::explicit_enum_discriminant,
|
||||
expand_rest_pattern::expand_rest_pattern,
|
||||
extract_expressions_from_format_string::extract_expressions_from_format_string,
|
||||
extract_struct_from_enum_variant::extract_struct_from_enum_variant,
|
||||
extract_type_alias::extract_type_alias,
|
||||
fill_record_pattern_fields::fill_record_pattern_fields,
|
||||
fix_visibility::fix_visibility,
|
||||
flip_binexpr::flip_binexpr,
|
||||
flip_comma::flip_comma,
|
||||
@ -287,8 +285,8 @@ mod handlers {
|
||||
generate_default_from_new::generate_default_from_new,
|
||||
generate_delegate_trait::generate_delegate_trait,
|
||||
generate_derive::generate_derive,
|
||||
generate_documentation_template::generate_documentation_template,
|
||||
generate_documentation_template::generate_doc_example,
|
||||
generate_documentation_template::generate_documentation_template,
|
||||
generate_enum_is_method::generate_enum_is_method,
|
||||
generate_enum_projection_method::generate_enum_as_method,
|
||||
generate_enum_projection_method::generate_enum_try_into_method,
|
||||
@ -298,8 +296,8 @@ mod handlers {
|
||||
generate_function::generate_function,
|
||||
generate_impl::generate_impl,
|
||||
generate_impl::generate_trait_impl,
|
||||
generate_mut_trait_impl::generate_mut_trait_impl,
|
||||
generate_is_empty_from_len::generate_is_empty_from_len,
|
||||
generate_mut_trait_impl::generate_mut_trait_impl,
|
||||
generate_new::generate_new,
|
||||
generate_trait_from_impl::generate_trait_from_impl,
|
||||
inline_call::inline_call,
|
||||
@ -307,39 +305,41 @@ mod handlers {
|
||||
inline_const_as_literal::inline_const_as_literal,
|
||||
inline_local_variable::inline_local_variable,
|
||||
inline_macro::inline_macro,
|
||||
inline_type_alias::inline_type_alias,
|
||||
inline_type_alias::inline_type_alias_uses,
|
||||
inline_type_alias::inline_type_alias,
|
||||
into_to_qualified_from::into_to_qualified_from,
|
||||
introduce_named_generic::introduce_named_generic,
|
||||
introduce_named_lifetime::introduce_named_lifetime,
|
||||
introduce_named_type_parameter::introduce_named_type_parameter,
|
||||
invert_if::invert_if,
|
||||
merge_imports::merge_imports,
|
||||
merge_match_arms::merge_match_arms,
|
||||
merge_nested_if::merge_nested_if,
|
||||
move_bounds::move_bounds_to_where_clause,
|
||||
move_const_to_impl::move_const_to_impl,
|
||||
move_from_mod_rs::move_from_mod_rs,
|
||||
move_guard::move_arm_cond_to_match_guard,
|
||||
move_guard::move_guard_to_arm_body,
|
||||
move_module_to_file::move_module_to_file,
|
||||
move_to_mod_rs::move_to_mod_rs,
|
||||
move_from_mod_rs::move_from_mod_rs,
|
||||
normalize_import::normalize_import,
|
||||
number_representation::reformat_number_literal,
|
||||
pull_assignment_up::pull_assignment_up,
|
||||
promote_local_to_const::promote_local_to_const,
|
||||
qualify_path::qualify_path,
|
||||
pull_assignment_up::pull_assignment_up,
|
||||
qualify_method_call::qualify_method_call,
|
||||
qualify_path::qualify_path,
|
||||
raw_string::add_hash,
|
||||
raw_string::make_usual_string,
|
||||
raw_string::remove_hash,
|
||||
remove_dbg::remove_dbg,
|
||||
remove_mut::remove_mut,
|
||||
remove_parentheses::remove_parentheses,
|
||||
remove_unused_imports::remove_unused_imports,
|
||||
remove_unused_param::remove_unused_param,
|
||||
remove_parentheses::remove_parentheses,
|
||||
reorder_fields::reorder_fields,
|
||||
reorder_impl_items::reorder_impl_items,
|
||||
replace_try_expr_with_match::replace_try_expr_with_match,
|
||||
replace_arith_op::replace_arith_with_checked,
|
||||
replace_arith_op::replace_arith_with_saturating,
|
||||
replace_arith_op::replace_arith_with_wrapping,
|
||||
replace_derive_with_manual_impl::replace_derive_with_manual_impl,
|
||||
replace_if_let_with_match::replace_if_let_with_match,
|
||||
replace_if_let_with_match::replace_match_with_if_let,
|
||||
@ -348,23 +348,23 @@ mod handlers {
|
||||
replace_method_eager_lazy::replace_with_eager_method,
|
||||
replace_method_eager_lazy::replace_with_lazy_method,
|
||||
replace_named_generic_with_impl::replace_named_generic_with_impl,
|
||||
replace_turbofish_with_explicit_type::replace_turbofish_with_explicit_type,
|
||||
replace_qualified_name_with_use::replace_qualified_name_with_use,
|
||||
replace_arith_op::replace_arith_with_wrapping,
|
||||
replace_arith_op::replace_arith_with_checked,
|
||||
replace_arith_op::replace_arith_with_saturating,
|
||||
replace_try_expr_with_match::replace_try_expr_with_match,
|
||||
replace_turbofish_with_explicit_type::replace_turbofish_with_explicit_type,
|
||||
sort_items::sort_items,
|
||||
split_import::split_import,
|
||||
term_search::term_search,
|
||||
toggle_async_sugar::desugar_async_into_impl_future,
|
||||
toggle_async_sugar::sugar_impl_future_into_async,
|
||||
toggle_ignore::toggle_ignore,
|
||||
toggle_macro_delimiter::toggle_macro_delimiter,
|
||||
unmerge_match_arm::unmerge_match_arm,
|
||||
unmerge_use::unmerge_use,
|
||||
unnecessary_async::unnecessary_async,
|
||||
unqualify_method_call::unqualify_method_call,
|
||||
unwrap_block::unwrap_block,
|
||||
unwrap_return_type::unwrap_return_type,
|
||||
unwrap_tuple::unwrap_tuple,
|
||||
unqualify_method_call::unqualify_method_call,
|
||||
wrap_return_type::wrap_return_type,
|
||||
wrap_unwrap_cfg_attr::wrap_unwrap_cfg_attr,
|
||||
|
||||
|
@ -710,18 +710,22 @@ pub fn test_some_range(a: int) -> bool {
|
||||
Indel {
|
||||
insert: "let",
|
||||
delete: 45..47,
|
||||
annotation: None,
|
||||
},
|
||||
Indel {
|
||||
insert: "var_name",
|
||||
delete: 48..60,
|
||||
annotation: None,
|
||||
},
|
||||
Indel {
|
||||
insert: "=",
|
||||
delete: 61..81,
|
||||
annotation: None,
|
||||
},
|
||||
Indel {
|
||||
insert: "5;\n if let 2..6 = var_name {\n true\n } else {\n false\n }",
|
||||
delete: 82..108,
|
||||
annotation: None,
|
||||
},
|
||||
],
|
||||
},
|
||||
@ -739,6 +743,8 @@ pub fn test_some_range(a: int) -> bool {
|
||||
},
|
||||
file_system_edits: [],
|
||||
is_snippet: true,
|
||||
annotations: {},
|
||||
next_annotation_id: 0,
|
||||
},
|
||||
),
|
||||
command: Some(
|
||||
@ -839,18 +845,22 @@ pub fn test_some_range(a: int) -> bool {
|
||||
Indel {
|
||||
insert: "let",
|
||||
delete: 45..47,
|
||||
annotation: None,
|
||||
},
|
||||
Indel {
|
||||
insert: "var_name",
|
||||
delete: 48..60,
|
||||
annotation: None,
|
||||
},
|
||||
Indel {
|
||||
insert: "=",
|
||||
delete: 61..81,
|
||||
annotation: None,
|
||||
},
|
||||
Indel {
|
||||
insert: "5;\n if let 2..6 = var_name {\n true\n } else {\n false\n }",
|
||||
delete: 82..108,
|
||||
annotation: None,
|
||||
},
|
||||
],
|
||||
},
|
||||
@ -868,6 +878,8 @@ pub fn test_some_range(a: int) -> bool {
|
||||
},
|
||||
file_system_edits: [],
|
||||
is_snippet: true,
|
||||
annotations: {},
|
||||
next_annotation_id: 0,
|
||||
},
|
||||
),
|
||||
command: Some(
|
||||
@ -902,22 +914,27 @@ pub fn test_some_range(a: int) -> bool {
|
||||
Indel {
|
||||
insert: "const",
|
||||
delete: 45..47,
|
||||
annotation: None,
|
||||
},
|
||||
Indel {
|
||||
insert: "VAR_NAME:",
|
||||
delete: 48..60,
|
||||
annotation: None,
|
||||
},
|
||||
Indel {
|
||||
insert: "i32",
|
||||
delete: 61..81,
|
||||
annotation: None,
|
||||
},
|
||||
Indel {
|
||||
insert: "=",
|
||||
delete: 82..86,
|
||||
annotation: None,
|
||||
},
|
||||
Indel {
|
||||
insert: "5;\n if let 2..6 = VAR_NAME {\n true\n } else {\n false\n }",
|
||||
delete: 87..108,
|
||||
annotation: None,
|
||||
},
|
||||
],
|
||||
},
|
||||
@ -935,6 +952,8 @@ pub fn test_some_range(a: int) -> bool {
|
||||
},
|
||||
file_system_edits: [],
|
||||
is_snippet: true,
|
||||
annotations: {},
|
||||
next_annotation_id: 0,
|
||||
},
|
||||
),
|
||||
command: Some(
|
||||
@ -969,22 +988,27 @@ pub fn test_some_range(a: int) -> bool {
|
||||
Indel {
|
||||
insert: "static",
|
||||
delete: 45..47,
|
||||
annotation: None,
|
||||
},
|
||||
Indel {
|
||||
insert: "VAR_NAME:",
|
||||
delete: 48..60,
|
||||
annotation: None,
|
||||
},
|
||||
Indel {
|
||||
insert: "i32",
|
||||
delete: 61..81,
|
||||
annotation: None,
|
||||
},
|
||||
Indel {
|
||||
insert: "=",
|
||||
delete: 82..86,
|
||||
annotation: None,
|
||||
},
|
||||
Indel {
|
||||
insert: "5;\n if let 2..6 = VAR_NAME {\n true\n } else {\n false\n }",
|
||||
delete: 87..108,
|
||||
annotation: None,
|
||||
},
|
||||
],
|
||||
},
|
||||
@ -1002,6 +1026,8 @@ pub fn test_some_range(a: int) -> bool {
|
||||
},
|
||||
file_system_edits: [],
|
||||
is_snippet: true,
|
||||
annotations: {},
|
||||
next_annotation_id: 0,
|
||||
},
|
||||
),
|
||||
command: Some(
|
||||
@ -1036,10 +1062,12 @@ pub fn test_some_range(a: int) -> bool {
|
||||
Indel {
|
||||
insert: "fun_name()",
|
||||
delete: 59..60,
|
||||
annotation: None,
|
||||
},
|
||||
Indel {
|
||||
insert: "\n\nfn fun_name() -> i32 {\n 5\n}",
|
||||
delete: 110..110,
|
||||
annotation: None,
|
||||
},
|
||||
],
|
||||
},
|
||||
@ -1057,6 +1085,8 @@ pub fn test_some_range(a: int) -> bool {
|
||||
},
|
||||
file_system_edits: [],
|
||||
is_snippet: true,
|
||||
annotations: {},
|
||||
next_annotation_id: 0,
|
||||
},
|
||||
),
|
||||
command: None,
|
||||
|
@ -27,6 +27,29 @@ fn foo(n: i32) -> i32 {
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doctest_add_explicit_enum_discriminant() {
|
||||
check_doc_test(
|
||||
"add_explicit_enum_discriminant",
|
||||
r#####"
|
||||
enum TheEnum$0 {
|
||||
Foo,
|
||||
Bar,
|
||||
Baz = 42,
|
||||
Quux,
|
||||
}
|
||||
"#####,
|
||||
r#####"
|
||||
enum TheEnum {
|
||||
Foo = 0,
|
||||
Bar = 1,
|
||||
Baz = 42,
|
||||
Quux = 43,
|
||||
}
|
||||
"#####,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doctest_add_explicit_type() {
|
||||
check_doc_test(
|
||||
@ -304,34 +327,6 @@ fn some_function(x: i32) {
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doctest_bool_to_enum() {
|
||||
check_doc_test(
|
||||
"bool_to_enum",
|
||||
r#####"
|
||||
fn main() {
|
||||
let $0bool = true;
|
||||
|
||||
if bool {
|
||||
println!("foo");
|
||||
}
|
||||
}
|
||||
"#####,
|
||||
r#####"
|
||||
#[derive(PartialEq, Eq)]
|
||||
enum Bool { True, False }
|
||||
|
||||
fn main() {
|
||||
let bool = Bool::True;
|
||||
|
||||
if bool == Bool::True {
|
||||
println!("foo");
|
||||
}
|
||||
}
|
||||
"#####,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doctest_change_visibility() {
|
||||
check_doc_test(
|
||||
@ -382,6 +377,34 @@ fn main() {
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doctest_convert_bool_to_enum() {
|
||||
check_doc_test(
|
||||
"convert_bool_to_enum",
|
||||
r#####"
|
||||
fn main() {
|
||||
let $0bool = true;
|
||||
|
||||
if bool {
|
||||
println!("foo");
|
||||
}
|
||||
}
|
||||
"#####,
|
||||
r#####"
|
||||
#[derive(PartialEq, Eq)]
|
||||
enum Bool { True, False }
|
||||
|
||||
fn main() {
|
||||
let bool = Bool::True;
|
||||
|
||||
if bool == Bool::True {
|
||||
println!("foo");
|
||||
}
|
||||
}
|
||||
"#####,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doctest_convert_closure_to_fn() {
|
||||
check_doc_test(
|
||||
@ -933,23 +956,42 @@ pub use foo::{Bar, Baz};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doctest_explicit_enum_discriminant() {
|
||||
fn doctest_expand_record_rest_pattern() {
|
||||
check_doc_test(
|
||||
"explicit_enum_discriminant",
|
||||
"expand_record_rest_pattern",
|
||||
r#####"
|
||||
enum TheEnum$0 {
|
||||
Foo,
|
||||
Bar,
|
||||
Baz = 42,
|
||||
Quux,
|
||||
struct Bar { y: Y, z: Z }
|
||||
|
||||
fn foo(bar: Bar) {
|
||||
let Bar { ..$0 } = bar;
|
||||
}
|
||||
"#####,
|
||||
r#####"
|
||||
enum TheEnum {
|
||||
Foo = 0,
|
||||
Bar = 1,
|
||||
Baz = 42,
|
||||
Quux = 43,
|
||||
struct Bar { y: Y, z: Z }
|
||||
|
||||
fn foo(bar: Bar) {
|
||||
let Bar { y, z } = bar;
|
||||
}
|
||||
"#####,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doctest_expand_tuple_struct_rest_pattern() {
|
||||
check_doc_test(
|
||||
"expand_tuple_struct_rest_pattern",
|
||||
r#####"
|
||||
struct Bar(Y, Z);
|
||||
|
||||
fn foo(bar: Bar) {
|
||||
let Bar(..$0) = bar;
|
||||
}
|
||||
"#####,
|
||||
r#####"
|
||||
struct Bar(Y, Z);
|
||||
|
||||
fn foo(bar: Bar) {
|
||||
let Bar(_0, _1) = bar;
|
||||
}
|
||||
"#####,
|
||||
)
|
||||
@ -1117,27 +1159,6 @@ fn main() {
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doctest_fill_record_pattern_fields() {
|
||||
check_doc_test(
|
||||
"fill_record_pattern_fields",
|
||||
r#####"
|
||||
struct Bar { y: Y, z: Z }
|
||||
|
||||
fn foo(bar: Bar) {
|
||||
let Bar { ..$0 } = bar;
|
||||
}
|
||||
"#####,
|
||||
r#####"
|
||||
struct Bar { y: Y, z: Z }
|
||||
|
||||
fn foo(bar: Bar) {
|
||||
let Bar { y, z } = bar;
|
||||
}
|
||||
"#####,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doctest_fix_visibility() {
|
||||
check_doc_test(
|
||||
@ -2193,19 +2214,6 @@ fn main() -> () {
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doctest_introduce_named_generic() {
|
||||
check_doc_test(
|
||||
"introduce_named_generic",
|
||||
r#####"
|
||||
fn foo(bar: $0impl Bar) {}
|
||||
"#####,
|
||||
r#####"
|
||||
fn foo<$0B: Bar>(bar: B) {}
|
||||
"#####,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doctest_introduce_named_lifetime() {
|
||||
check_doc_test(
|
||||
@ -2231,6 +2239,19 @@ impl<'a> Cursor<'a> {
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doctest_introduce_named_type_parameter() {
|
||||
check_doc_test(
|
||||
"introduce_named_type_parameter",
|
||||
r#####"
|
||||
fn foo(bar: $0impl Bar) {}
|
||||
"#####,
|
||||
r#####"
|
||||
fn foo<$0B: Bar>(bar: B) {}
|
||||
"#####,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doctest_invert_if() {
|
||||
check_doc_test(
|
||||
|
@ -3,7 +3,8 @@
|
||||
pub(crate) use gen_trait_fn_body::gen_trait_fn_body;
|
||||
use hir::{
|
||||
db::{ExpandDatabase, HirDatabase},
|
||||
HasAttrs as HirHasAttrs, HirDisplay, InFile, ModuleDef, PathResolution, Semantics,
|
||||
DisplayTarget, HasAttrs as HirHasAttrs, HirDisplay, InFile, ModuleDef, PathResolution,
|
||||
Semantics,
|
||||
};
|
||||
use ide_db::{
|
||||
famous_defs::FamousDefs,
|
||||
@ -21,7 +22,7 @@ use syntax::{
|
||||
syntax_factory::SyntaxFactory,
|
||||
HasArgList, HasAttrs, HasGenericParams, HasName, HasTypeBounds, Whitespace,
|
||||
},
|
||||
ted, AstNode, AstToken, Direction, Edition, NodeOrToken, SourceFile,
|
||||
ted, AstNode, AstToken, Direction, NodeOrToken, SourceFile,
|
||||
SyntaxKind::*,
|
||||
SyntaxNode, SyntaxToken, TextRange, TextSize, WalkEvent, T,
|
||||
};
|
||||
@ -793,31 +794,50 @@ enum ReferenceConversionType {
|
||||
}
|
||||
|
||||
impl ReferenceConversion {
|
||||
pub(crate) fn convert_type(&self, db: &dyn HirDatabase, edition: Edition) -> ast::Type {
|
||||
pub(crate) fn convert_type(
|
||||
&self,
|
||||
db: &dyn HirDatabase,
|
||||
display_target: DisplayTarget,
|
||||
) -> ast::Type {
|
||||
let ty = match self.conversion {
|
||||
ReferenceConversionType::Copy => self.ty.display(db, edition).to_string(),
|
||||
ReferenceConversionType::Copy => self.ty.display(db, display_target).to_string(),
|
||||
ReferenceConversionType::AsRefStr => "&str".to_owned(),
|
||||
ReferenceConversionType::AsRefSlice => {
|
||||
let type_argument_name =
|
||||
self.ty.type_arguments().next().unwrap().display(db, edition).to_string();
|
||||
let type_argument_name = self
|
||||
.ty
|
||||
.type_arguments()
|
||||
.next()
|
||||
.unwrap()
|
||||
.display(db, display_target)
|
||||
.to_string();
|
||||
format!("&[{type_argument_name}]")
|
||||
}
|
||||
ReferenceConversionType::Dereferenced => {
|
||||
let type_argument_name =
|
||||
self.ty.type_arguments().next().unwrap().display(db, edition).to_string();
|
||||
let type_argument_name = self
|
||||
.ty
|
||||
.type_arguments()
|
||||
.next()
|
||||
.unwrap()
|
||||
.display(db, display_target)
|
||||
.to_string();
|
||||
format!("&{type_argument_name}")
|
||||
}
|
||||
ReferenceConversionType::Option => {
|
||||
let type_argument_name =
|
||||
self.ty.type_arguments().next().unwrap().display(db, edition).to_string();
|
||||
let type_argument_name = self
|
||||
.ty
|
||||
.type_arguments()
|
||||
.next()
|
||||
.unwrap()
|
||||
.display(db, display_target)
|
||||
.to_string();
|
||||
format!("Option<&{type_argument_name}>")
|
||||
}
|
||||
ReferenceConversionType::Result => {
|
||||
let mut type_arguments = self.ty.type_arguments();
|
||||
let first_type_argument_name =
|
||||
type_arguments.next().unwrap().display(db, edition).to_string();
|
||||
type_arguments.next().unwrap().display(db, display_target).to_string();
|
||||
let second_type_argument_name =
|
||||
type_arguments.next().unwrap().display(db, edition).to_string();
|
||||
type_arguments.next().unwrap().display(db, display_target).to_string();
|
||||
format!("Result<&{first_type_argument_name}, &{second_type_argument_name}>")
|
||||
}
|
||||
};
|
||||
|
@ -365,7 +365,8 @@ pub(crate) fn complete_expr_path(
|
||||
add_keyword("false", "false");
|
||||
|
||||
if in_condition || in_block_expr {
|
||||
add_keyword("let", "let");
|
||||
add_keyword("letm", "let mut $0");
|
||||
add_keyword("let", "let $0");
|
||||
}
|
||||
|
||||
if after_if_expr {
|
||||
|
@ -330,4 +330,34 @@ fn main() {
|
||||
",
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn completes_let_with_space() {
|
||||
check_edit(
|
||||
"let",
|
||||
r#"
|
||||
fn main() {
|
||||
$0
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
fn main() {
|
||||
let $0
|
||||
}
|
||||
"#,
|
||||
);
|
||||
check_edit(
|
||||
"letm",
|
||||
r#"
|
||||
fn main() {
|
||||
$0
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
fn main() {
|
||||
let mut $0
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -7,8 +7,8 @@ mod tests;
|
||||
use std::{iter, ops::ControlFlow};
|
||||
|
||||
use hir::{
|
||||
HasAttrs, Local, ModPath, ModuleDef, ModuleSource, Name, PathResolution, ScopeDef, Semantics,
|
||||
SemanticsScope, Symbol, Type, TypeInfo,
|
||||
DisplayTarget, HasAttrs, Local, ModPath, ModuleDef, ModuleSource, Name, PathResolution,
|
||||
ScopeDef, Semantics, SemanticsScope, Symbol, Type, TypeInfo,
|
||||
};
|
||||
use ide_db::{
|
||||
base_db::SourceDatabase, famous_defs::FamousDefs, helpers::is_editable_crate, FilePosition,
|
||||
@ -440,6 +440,7 @@ pub(crate) struct CompletionContext<'a> {
|
||||
pub(crate) token: SyntaxToken,
|
||||
/// The crate of the current file.
|
||||
pub(crate) krate: hir::Crate,
|
||||
pub(crate) display_target: DisplayTarget,
|
||||
/// The module of the `scope`.
|
||||
pub(crate) module: hir::Module,
|
||||
/// The function where we're completing, if inside a function.
|
||||
@ -867,6 +868,7 @@ impl<'a> CompletionContext<'a> {
|
||||
CompleteSemicolon::DoNotComplete
|
||||
};
|
||||
|
||||
let display_target = krate.to_display_target(db);
|
||||
let ctx = CompletionContext {
|
||||
sema,
|
||||
scope,
|
||||
@ -888,6 +890,7 @@ impl<'a> CompletionContext<'a> {
|
||||
exclude_flyimport,
|
||||
exclude_traits,
|
||||
complete_semicolon,
|
||||
display_target,
|
||||
};
|
||||
Some((ctx, analysis))
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ fn check_expected_type_and_name(#[rust_analyzer::rust_fixture] ra_fixture: &str,
|
||||
|
||||
let ty = completion_context
|
||||
.expected_type
|
||||
.map(|t| t.display_test(&db).to_string())
|
||||
.map(|t| t.display_test(&db, completion_context.krate.to_display_target(&db)).to_string())
|
||||
.unwrap_or("?".to_owned());
|
||||
|
||||
let name =
|
||||
|
@ -252,14 +252,16 @@ impl CompletionRelevance {
|
||||
/// Provides a relevance score. Higher values are more relevant.
|
||||
///
|
||||
/// The absolute value of the relevance score is not meaningful, for
|
||||
/// example a value of 0 doesn't mean "not relevant", rather
|
||||
/// example a value of BASE_SCORE doesn't mean "not relevant", rather
|
||||
/// it means "least relevant". The score value should only be used
|
||||
/// for relative ordering.
|
||||
///
|
||||
/// See is_relevant if you need to make some judgement about score
|
||||
/// in an absolute sense.
|
||||
const BASE_SCORE: u32 = u32::MAX / 2;
|
||||
|
||||
pub fn score(self) -> u32 {
|
||||
let mut score = !0 / 2;
|
||||
let mut score = Self::BASE_SCORE;
|
||||
let CompletionRelevance {
|
||||
exact_name_match,
|
||||
type_match,
|
||||
@ -350,7 +352,7 @@ impl CompletionRelevance {
|
||||
/// some threshold such that we think it is especially likely
|
||||
/// to be relevant.
|
||||
pub fn is_relevant(&self) -> bool {
|
||||
self.score() > 0
|
||||
self.score() > Self::BASE_SCORE
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -144,7 +144,7 @@ pub(crate) fn render_field(
|
||||
is_skipping_completion: receiver.is_some(),
|
||||
..CompletionRelevance::default()
|
||||
});
|
||||
item.detail(ty.display(db, ctx.completion.edition).to_string())
|
||||
item.detail(ty.display(db, ctx.completion.display_target).to_string())
|
||||
.set_documentation(field.docs(db))
|
||||
.set_deprecated(is_deprecated)
|
||||
.lookup_by(name);
|
||||
@ -212,7 +212,7 @@ pub(crate) fn render_tuple_field(
|
||||
field_with_receiver(receiver.as_deref(), &field.to_string()),
|
||||
ctx.completion.edition,
|
||||
);
|
||||
item.detail(ty.display(ctx.db(), ctx.completion.edition).to_string())
|
||||
item.detail(ty.display(ctx.db(), ctx.completion.display_target).to_string())
|
||||
.lookup_by(field.to_string());
|
||||
item.set_relevance(CompletionRelevance {
|
||||
is_skipping_completion: receiver.is_some(),
|
||||
@ -303,7 +303,8 @@ pub(crate) fn render_expr(
|
||||
|
||||
let cfg = ctx.config.import_path_config(ctx.is_nightly);
|
||||
|
||||
let label = expr.gen_source_code(&ctx.scope, &mut label_formatter, cfg, ctx.edition).ok()?;
|
||||
let label =
|
||||
expr.gen_source_code(&ctx.scope, &mut label_formatter, cfg, ctx.display_target).ok()?;
|
||||
|
||||
let source_range = match ctx.original_token.parent() {
|
||||
Some(node) => match node.ancestors().find_map(ast::Path::cast) {
|
||||
@ -318,7 +319,7 @@ pub(crate) fn render_expr(
|
||||
|
||||
let snippet = format!(
|
||||
"{}$0",
|
||||
expr.gen_source_code(&ctx.scope, &mut snippet_formatter, cfg, ctx.edition).ok()?
|
||||
expr.gen_source_code(&ctx.scope, &mut snippet_formatter, cfg, ctx.display_target).ok()?
|
||||
);
|
||||
let edit = TextEdit::replace(source_range, snippet);
|
||||
item.snippet_edit(ctx.config.snippet_cap?, edit);
|
||||
@ -398,6 +399,8 @@ fn render_resolution_path(
|
||||
let _p = tracing::info_span!("render_resolution_path").entered();
|
||||
use hir::ModuleDef::*;
|
||||
|
||||
let krate = ctx.completion.display_target;
|
||||
|
||||
match resolution {
|
||||
ScopeDef::ModuleDef(Macro(mac)) => {
|
||||
let ctx = ctx.import_to_add(import_to_add);
|
||||
@ -459,7 +462,7 @@ fn render_resolution_path(
|
||||
|
||||
let mut set_item_relevance = |ty: Type| {
|
||||
if !ty.is_unknown() {
|
||||
item.detail(ty.display(db, completion.edition).to_string());
|
||||
item.detail(ty.display(db, krate).to_string());
|
||||
}
|
||||
|
||||
item.set_relevance(CompletionRelevance {
|
||||
@ -1151,6 +1154,24 @@ fn main() { Foo::Fo$0 }
|
||||
),
|
||||
lookup: "Foo{}",
|
||||
detail: "Foo { x: i32, y: i32 }",
|
||||
relevance: CompletionRelevance {
|
||||
exact_name_match: false,
|
||||
type_match: None,
|
||||
is_local: false,
|
||||
trait_: None,
|
||||
is_name_already_imported: false,
|
||||
requires_import: false,
|
||||
is_private_editable: false,
|
||||
postfix_match: None,
|
||||
function: Some(
|
||||
CompletionRelevanceFn {
|
||||
has_params: true,
|
||||
has_self_param: false,
|
||||
return_type: DirectConstructor,
|
||||
},
|
||||
),
|
||||
is_skipping_completion: false,
|
||||
},
|
||||
trigger_call_info: true,
|
||||
},
|
||||
]
|
||||
@ -1183,6 +1204,24 @@ fn main() { Foo::Fo$0 }
|
||||
),
|
||||
lookup: "Foo()",
|
||||
detail: "Foo(i32, i32)",
|
||||
relevance: CompletionRelevance {
|
||||
exact_name_match: false,
|
||||
type_match: None,
|
||||
is_local: false,
|
||||
trait_: None,
|
||||
is_name_already_imported: false,
|
||||
requires_import: false,
|
||||
is_private_editable: false,
|
||||
postfix_match: None,
|
||||
function: Some(
|
||||
CompletionRelevanceFn {
|
||||
has_params: true,
|
||||
has_self_param: false,
|
||||
return_type: DirectConstructor,
|
||||
},
|
||||
),
|
||||
is_skipping_completion: false,
|
||||
},
|
||||
trigger_call_info: true,
|
||||
},
|
||||
]
|
||||
@ -1261,6 +1300,24 @@ fn main() { Foo::Fo$0 }
|
||||
Variant,
|
||||
),
|
||||
detail: "Foo",
|
||||
relevance: CompletionRelevance {
|
||||
exact_name_match: false,
|
||||
type_match: None,
|
||||
is_local: false,
|
||||
trait_: None,
|
||||
is_name_already_imported: false,
|
||||
requires_import: false,
|
||||
is_private_editable: false,
|
||||
postfix_match: None,
|
||||
function: Some(
|
||||
CompletionRelevanceFn {
|
||||
has_params: false,
|
||||
has_self_param: false,
|
||||
return_type: DirectConstructor,
|
||||
},
|
||||
),
|
||||
is_skipping_completion: false,
|
||||
},
|
||||
trigger_call_info: true,
|
||||
},
|
||||
]
|
||||
@ -1335,7 +1392,13 @@ fn main() { let _: m::Spam = S$0 }
|
||||
requires_import: false,
|
||||
is_private_editable: false,
|
||||
postfix_match: None,
|
||||
function: None,
|
||||
function: Some(
|
||||
CompletionRelevanceFn {
|
||||
has_params: true,
|
||||
has_self_param: false,
|
||||
return_type: DirectConstructor,
|
||||
},
|
||||
),
|
||||
is_skipping_completion: false,
|
||||
},
|
||||
trigger_call_info: true,
|
||||
@ -1365,7 +1428,13 @@ fn main() { let _: m::Spam = S$0 }
|
||||
requires_import: false,
|
||||
is_private_editable: false,
|
||||
postfix_match: None,
|
||||
function: None,
|
||||
function: Some(
|
||||
CompletionRelevanceFn {
|
||||
has_params: false,
|
||||
has_self_param: false,
|
||||
return_type: DirectConstructor,
|
||||
},
|
||||
),
|
||||
is_skipping_completion: false,
|
||||
},
|
||||
trigger_call_info: true,
|
||||
@ -1590,6 +1659,24 @@ use self::E::*;
|
||||
documentation: Documentation(
|
||||
"variant docs",
|
||||
),
|
||||
relevance: CompletionRelevance {
|
||||
exact_name_match: false,
|
||||
type_match: None,
|
||||
is_local: false,
|
||||
trait_: None,
|
||||
is_name_already_imported: false,
|
||||
requires_import: false,
|
||||
is_private_editable: false,
|
||||
postfix_match: None,
|
||||
function: Some(
|
||||
CompletionRelevanceFn {
|
||||
has_params: false,
|
||||
has_self_param: false,
|
||||
return_type: DirectConstructor,
|
||||
},
|
||||
),
|
||||
is_skipping_completion: false,
|
||||
},
|
||||
trigger_call_info: true,
|
||||
},
|
||||
CompletionItem {
|
||||
@ -2081,8 +2168,8 @@ fn main() {
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
lc ssss S [type+local]
|
||||
st S S [type]
|
||||
lc ssss S [type+local]
|
||||
st S S [type]
|
||||
ex ssss [type]
|
||||
ex S [type]
|
||||
@ -2153,14 +2240,14 @@ fn main() {
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
st S S []
|
||||
st &S [type]
|
||||
ex core::ops::Deref::deref(&t) [type_could_unify]
|
||||
lc m i32 [local]
|
||||
lc t T [local]
|
||||
lc &t [type+local]
|
||||
st S S []
|
||||
st &S [type]
|
||||
st S S []
|
||||
st &S [type]
|
||||
st T T []
|
||||
st &T [type]
|
||||
fn foo(…) fn(&S) []
|
||||
@ -2202,14 +2289,14 @@ fn main() {
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
st S S []
|
||||
st &mut S [type]
|
||||
ex core::ops::DerefMut::deref_mut(&mut t) [type_could_unify]
|
||||
lc m i32 [local]
|
||||
lc t T [local]
|
||||
lc &mut t [type+local]
|
||||
st S S []
|
||||
st &mut S [type]
|
||||
st S S []
|
||||
st &mut S [type]
|
||||
st T T []
|
||||
st &mut T [type]
|
||||
fn foo(…) fn(&mut S) []
|
||||
@ -2306,9 +2393,9 @@ fn main() {
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
ex core::ops::Deref::deref(&bar()) [type_could_unify]
|
||||
st S S []
|
||||
st &S [type]
|
||||
ex core::ops::Deref::deref(&bar()) [type_could_unify]
|
||||
st S S []
|
||||
st &S [type]
|
||||
st T T []
|
||||
@ -2686,10 +2773,12 @@ fn foo(f: Foo) { let _: &u32 = f.b$0 }
|
||||
Indel {
|
||||
insert: "(",
|
||||
delete: 107..107,
|
||||
annotation: None,
|
||||
},
|
||||
Indel {
|
||||
insert: "qux)()",
|
||||
delete: 109..110,
|
||||
annotation: None,
|
||||
},
|
||||
],
|
||||
},
|
||||
@ -2827,11 +2916,11 @@ fn foo() {
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
ev Foo::B Foo::B [type_could_unify]
|
||||
ev Foo::A(…) Foo::A(T) [type_could_unify]
|
||||
lc foo Foo<u32> [type+local]
|
||||
ex foo [type]
|
||||
ex Foo::B [type]
|
||||
ev Foo::A(…) Foo::A(T) [type_could_unify]
|
||||
ev Foo::B Foo::B [type_could_unify]
|
||||
en Foo Foo<{unknown}> [type_could_unify]
|
||||
fn foo() fn() []
|
||||
fn bar() fn() -> Foo<u8> []
|
||||
|
@ -16,7 +16,7 @@ fn render(ctx: RenderContext<'_>, const_: hir::Const) -> Option<CompletionItem>
|
||||
let name = const_.name(db)?;
|
||||
let (name, escaped_name) =
|
||||
(name.as_str().to_smolstr(), name.display(db, ctx.completion.edition).to_smolstr());
|
||||
let detail = const_.display(db, ctx.completion.edition).to_string();
|
||||
let detail = const_.display(db, ctx.completion.display_target).to_string();
|
||||
|
||||
let mut item =
|
||||
CompletionItem::new(SymbolKind::Const, ctx.source_range(), name, ctx.completion.edition);
|
||||
|
@ -4,7 +4,7 @@ use hir::{db::HirDatabase, AsAssocItem, HirDisplay};
|
||||
use ide_db::{SnippetCap, SymbolKind};
|
||||
use itertools::Itertools;
|
||||
use stdx::{format_to, to_lower_snake_case};
|
||||
use syntax::{format_smolstr, AstNode, Edition, SmolStr, ToSmolStr};
|
||||
use syntax::{format_smolstr, AstNode, SmolStr, ToSmolStr};
|
||||
|
||||
use crate::{
|
||||
context::{
|
||||
@ -142,9 +142,9 @@ fn render(
|
||||
}
|
||||
|
||||
let detail = if ctx.completion.config.full_function_signatures {
|
||||
detail_full(db, func, ctx.completion.edition)
|
||||
detail_full(ctx.completion, func)
|
||||
} else {
|
||||
detail(ctx.completion, func, ctx.completion.edition)
|
||||
detail(ctx.completion, func)
|
||||
};
|
||||
item.set_documentation(ctx.docs(func))
|
||||
.set_deprecated(ctx.is_deprecated(func) || ctx.is_deprecated_assoc_item(func))
|
||||
@ -251,7 +251,7 @@ pub(super) fn add_call_parens<'b>(
|
||||
format!(
|
||||
"{}(${{1:{}}}{}{})$0",
|
||||
escaped_name,
|
||||
self_param.display(ctx.db, ctx.edition),
|
||||
self_param.display(ctx.db, ctx.display_target),
|
||||
if params.is_empty() { "" } else { ", " },
|
||||
function_params_snippet
|
||||
)
|
||||
@ -307,7 +307,7 @@ fn ref_of_param(ctx: &CompletionContext<'_>, arg: &str, ty: &hir::Type) -> &'sta
|
||||
""
|
||||
}
|
||||
|
||||
fn detail(ctx: &CompletionContext<'_>, func: hir::Function, edition: Edition) -> String {
|
||||
fn detail(ctx: &CompletionContext<'_>, func: hir::Function) -> String {
|
||||
let mut ret_ty = func.ret_type(ctx.db);
|
||||
let mut detail = String::new();
|
||||
|
||||
@ -324,15 +324,15 @@ fn detail(ctx: &CompletionContext<'_>, func: hir::Function, edition: Edition) ->
|
||||
format_to!(detail, "unsafe ");
|
||||
}
|
||||
|
||||
format_to!(detail, "fn({})", params_display(ctx.db, func, edition));
|
||||
format_to!(detail, "fn({})", params_display(ctx, func));
|
||||
if !ret_ty.is_unit() {
|
||||
format_to!(detail, " -> {}", ret_ty.display(ctx.db, edition));
|
||||
format_to!(detail, " -> {}", ret_ty.display(ctx.db, ctx.display_target));
|
||||
}
|
||||
detail
|
||||
}
|
||||
|
||||
fn detail_full(db: &dyn HirDatabase, func: hir::Function, edition: Edition) -> String {
|
||||
let signature = format!("{}", func.display(db, edition));
|
||||
fn detail_full(ctx: &CompletionContext<'_>, func: hir::Function) -> String {
|
||||
let signature = format!("{}", func.display(ctx.db, ctx.display_target));
|
||||
let mut detail = String::with_capacity(signature.len());
|
||||
|
||||
for segment in signature.split_whitespace() {
|
||||
@ -346,24 +346,24 @@ fn detail_full(db: &dyn HirDatabase, func: hir::Function, edition: Edition) -> S
|
||||
detail
|
||||
}
|
||||
|
||||
fn params_display(db: &dyn HirDatabase, func: hir::Function, edition: Edition) -> String {
|
||||
if let Some(self_param) = func.self_param(db) {
|
||||
let assoc_fn_params = func.assoc_fn_params(db);
|
||||
fn params_display(ctx: &CompletionContext<'_>, func: hir::Function) -> String {
|
||||
if let Some(self_param) = func.self_param(ctx.db) {
|
||||
let assoc_fn_params = func.assoc_fn_params(ctx.db);
|
||||
let params = assoc_fn_params
|
||||
.iter()
|
||||
.skip(1) // skip the self param because we are manually handling that
|
||||
.map(|p| p.ty().display(db, edition));
|
||||
.map(|p| p.ty().display(ctx.db, ctx.display_target));
|
||||
format!(
|
||||
"{}{}",
|
||||
self_param.display(db, edition),
|
||||
self_param.display(ctx.db, ctx.display_target),
|
||||
params.format_with("", |display, f| {
|
||||
f(&", ")?;
|
||||
f(&display)
|
||||
})
|
||||
)
|
||||
} else {
|
||||
let assoc_fn_params = func.assoc_fn_params(db);
|
||||
assoc_fn_params.iter().map(|p| p.ty().display(db, edition)).join(", ")
|
||||
let assoc_fn_params = func.assoc_fn_params(ctx.db);
|
||||
assoc_fn_params.iter().map(|p| p.ty().display(ctx.db, ctx.display_target)).join(", ")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,7 @@ use ide_db::{
|
||||
|
||||
use crate::{
|
||||
context::{CompletionContext, PathCompletionCtx, PathKind},
|
||||
item::{Builder, CompletionItem},
|
||||
item::{Builder, CompletionItem, CompletionRelevanceFn},
|
||||
render::{
|
||||
compute_type_match,
|
||||
variant::{
|
||||
@ -17,7 +17,7 @@ use crate::{
|
||||
},
|
||||
RenderContext,
|
||||
},
|
||||
CompletionItemKind, CompletionRelevance,
|
||||
CompletionItemKind, CompletionRelevance, CompletionRelevanceReturnType,
|
||||
};
|
||||
|
||||
pub(crate) fn render_variant_lit(
|
||||
@ -82,10 +82,10 @@ fn render(
|
||||
|
||||
let mut rendered = match kind {
|
||||
StructKind::Tuple if should_add_parens => {
|
||||
render_tuple_lit(db, snippet_cap, &fields, &escaped_qualified_name, completion.edition)
|
||||
render_tuple_lit(completion, snippet_cap, &fields, &escaped_qualified_name)
|
||||
}
|
||||
StructKind::Record if should_add_parens => {
|
||||
render_record_lit(db, snippet_cap, &fields, &escaped_qualified_name, completion.edition)
|
||||
render_record_lit(completion, snippet_cap, &fields, &escaped_qualified_name)
|
||||
}
|
||||
_ => RenderedLiteral {
|
||||
literal: escaped_qualified_name.clone(),
|
||||
@ -131,6 +131,12 @@ fn render(
|
||||
let ty = thing.ty(db);
|
||||
item.set_relevance(CompletionRelevance {
|
||||
type_match: compute_type_match(ctx.completion, &ty),
|
||||
// function is a misnomer here, this is more about constructor information
|
||||
function: Some(CompletionRelevanceFn {
|
||||
has_params: !fields.is_empty(),
|
||||
has_self_param: false,
|
||||
return_type: CompletionRelevanceReturnType::DirectConstructor,
|
||||
}),
|
||||
..ctx.completion_relevance()
|
||||
});
|
||||
|
||||
|
@ -62,7 +62,7 @@ fn render(
|
||||
completion.edition,
|
||||
);
|
||||
item.set_deprecated(ctx.is_deprecated(macro_))
|
||||
.detail(macro_.display(completion.db, completion.edition).to_string())
|
||||
.detail(macro_.display(completion.db, completion.display_target).to_string())
|
||||
.set_documentation(docs)
|
||||
.set_relevance(ctx.completion_relevance());
|
||||
|
||||
|
@ -38,7 +38,7 @@ fn render(
|
||||
} else {
|
||||
(name.as_str().to_smolstr(), name.display_no_db(ctx.completion.edition).to_smolstr())
|
||||
};
|
||||
let detail = type_alias.display(db, ctx.completion.edition).to_string();
|
||||
let detail = type_alias.display(db, ctx.completion.display_target).to_string();
|
||||
|
||||
let mut item = CompletionItem::new(
|
||||
SymbolKind::TypeAlias,
|
||||
|
@ -88,7 +88,7 @@ pub(crate) fn render_union_literal(
|
||||
f(&format_args!(
|
||||
"{}: {}",
|
||||
field.name(ctx.db()).display(ctx.db(), ctx.completion.edition),
|
||||
field.ty(ctx.db()).display(ctx.db(), ctx.completion.edition)
|
||||
field.ty(ctx.db()).display(ctx.db(), ctx.completion.display_target)
|
||||
))
|
||||
}),
|
||||
if fields_omitted { ", .." } else { "" }
|
||||
|
@ -1,10 +1,10 @@
|
||||
//! Code common to structs, unions, and enum variants.
|
||||
|
||||
use crate::context::CompletionContext;
|
||||
use hir::{db::HirDatabase, sym, HasAttrs, HasCrate, HasVisibility, HirDisplay, StructKind};
|
||||
use hir::{sym, HasAttrs, HasCrate, HasVisibility, HirDisplay, StructKind};
|
||||
use ide_db::SnippetCap;
|
||||
use itertools::Itertools;
|
||||
use syntax::{Edition, SmolStr};
|
||||
use syntax::SmolStr;
|
||||
|
||||
/// A rendered struct, union, or enum variant, split into fields for actual
|
||||
/// auto-completion (`literal`, using `field: ()`) and display in the
|
||||
@ -17,11 +17,10 @@ pub(crate) struct RenderedLiteral {
|
||||
/// Render a record type (or sub-type) to a `RenderedCompound`. Use `None` for
|
||||
/// the `name` argument for an anonymous type.
|
||||
pub(crate) fn render_record_lit(
|
||||
db: &dyn HirDatabase,
|
||||
ctx: &CompletionContext<'_>,
|
||||
snippet_cap: Option<SnippetCap>,
|
||||
fields: &[hir::Field],
|
||||
path: &str,
|
||||
edition: Edition,
|
||||
) -> RenderedLiteral {
|
||||
if snippet_cap.is_none() {
|
||||
return RenderedLiteral { literal: path.to_owned(), detail: path.to_owned() };
|
||||
@ -30,19 +29,19 @@ pub(crate) fn render_record_lit(
|
||||
if snippet_cap.is_some() {
|
||||
f(&format_args!(
|
||||
"{}: ${{{}:()}}",
|
||||
field.name(db).display(db.upcast(), edition),
|
||||
field.name(ctx.db).display(ctx.db, ctx.edition),
|
||||
idx + 1
|
||||
))
|
||||
} else {
|
||||
f(&format_args!("{}: ()", field.name(db).display(db.upcast(), edition)))
|
||||
f(&format_args!("{}: ()", field.name(ctx.db).display(ctx.db, ctx.edition)))
|
||||
}
|
||||
});
|
||||
|
||||
let types = fields.iter().format_with(", ", |field, f| {
|
||||
f(&format_args!(
|
||||
"{}: {}",
|
||||
field.name(db).display(db.upcast(), edition),
|
||||
field.ty(db).display(db, edition)
|
||||
field.name(ctx.db).display(ctx.db, ctx.edition),
|
||||
field.ty(ctx.db).display(ctx.db, ctx.display_target)
|
||||
))
|
||||
});
|
||||
|
||||
@ -55,11 +54,10 @@ pub(crate) fn render_record_lit(
|
||||
/// Render a tuple type (or sub-type) to a `RenderedCompound`. Use `None` for
|
||||
/// the `name` argument for an anonymous type.
|
||||
pub(crate) fn render_tuple_lit(
|
||||
db: &dyn HirDatabase,
|
||||
ctx: &CompletionContext<'_>,
|
||||
snippet_cap: Option<SnippetCap>,
|
||||
fields: &[hir::Field],
|
||||
path: &str,
|
||||
edition: Edition,
|
||||
) -> RenderedLiteral {
|
||||
if snippet_cap.is_none() {
|
||||
return RenderedLiteral { literal: path.to_owned(), detail: path.to_owned() };
|
||||
@ -72,7 +70,9 @@ pub(crate) fn render_tuple_lit(
|
||||
}
|
||||
});
|
||||
|
||||
let types = fields.iter().format_with(", ", |field, f| f(&field.ty(db).display(db, edition)));
|
||||
let types = fields
|
||||
.iter()
|
||||
.format_with(", ", |field, f| f(&field.ty(ctx.db).display(ctx.db, ctx.display_target)));
|
||||
|
||||
RenderedLiteral {
|
||||
literal: format!("{path}({completions})"),
|
||||
|
@ -170,6 +170,7 @@ impl Unit {
|
||||
kw if let
|
||||
kw impl
|
||||
kw let
|
||||
kw letm
|
||||
kw loop
|
||||
kw match
|
||||
kw mod
|
||||
@ -247,6 +248,7 @@ fn complete_in_block() {
|
||||
kw if let
|
||||
kw impl
|
||||
kw let
|
||||
kw letm
|
||||
kw loop
|
||||
kw match
|
||||
kw mod
|
||||
@ -297,6 +299,7 @@ fn complete_after_if_expr() {
|
||||
kw if let
|
||||
kw impl
|
||||
kw let
|
||||
kw letm
|
||||
kw loop
|
||||
kw match
|
||||
kw mod
|
||||
@ -370,6 +373,7 @@ fn completes_in_loop_ctx() {
|
||||
kw if let
|
||||
kw impl
|
||||
kw let
|
||||
kw letm
|
||||
kw loop
|
||||
kw match
|
||||
kw mod
|
||||
@ -942,6 +946,7 @@ fn foo() { if foo {} $0 }
|
||||
kw if let
|
||||
kw impl
|
||||
kw let
|
||||
kw letm
|
||||
kw loop
|
||||
kw match
|
||||
kw mod
|
||||
@ -983,6 +988,7 @@ fn foo() { if foo {} el$0 }
|
||||
kw if let
|
||||
kw impl
|
||||
kw let
|
||||
kw letm
|
||||
kw loop
|
||||
kw match
|
||||
kw mod
|
||||
@ -1072,6 +1078,7 @@ fn foo() { if foo {} $0 let x = 92; }
|
||||
kw if let
|
||||
kw impl
|
||||
kw let
|
||||
kw letm
|
||||
kw loop
|
||||
kw match
|
||||
kw mod
|
||||
@ -1113,6 +1120,7 @@ fn foo() { if foo {} el$0 let x = 92; }
|
||||
kw if let
|
||||
kw impl
|
||||
kw let
|
||||
kw letm
|
||||
kw loop
|
||||
kw match
|
||||
kw mod
|
||||
@ -1154,6 +1162,7 @@ fn foo() { if foo {} el$0 { let x = 92; } }
|
||||
kw if let
|
||||
kw impl
|
||||
kw let
|
||||
kw letm
|
||||
kw loop
|
||||
kw match
|
||||
kw mod
|
||||
@ -1205,6 +1214,7 @@ pub struct UnstableThisShouldNotBeListed;
|
||||
kw if let
|
||||
kw impl
|
||||
kw let
|
||||
kw letm
|
||||
kw loop
|
||||
kw match
|
||||
kw mod
|
||||
@ -1258,6 +1268,7 @@ pub struct UnstableButWeAreOnNightlyAnyway;
|
||||
kw if let
|
||||
kw impl
|
||||
kw let
|
||||
kw letm
|
||||
kw loop
|
||||
kw match
|
||||
kw mod
|
||||
@ -1495,6 +1506,7 @@ fn main() {
|
||||
kw if let
|
||||
kw impl
|
||||
kw let
|
||||
kw letm
|
||||
kw loop
|
||||
kw match
|
||||
kw mod
|
||||
@ -1920,6 +1932,7 @@ fn bar() {
|
||||
md rust_2015 (use core::prelude::rust_2015)
|
||||
md rust_2018 (use core::prelude::rust_2018)
|
||||
md rust_2021 (use core::prelude::rust_2021)
|
||||
md rust_2024 (use core::prelude::rust_2024)
|
||||
tt Clone
|
||||
tt Copy
|
||||
tt IntoIterator
|
||||
@ -1944,6 +1957,7 @@ fn bar() {
|
||||
kw if let
|
||||
kw impl
|
||||
kw let
|
||||
kw letm
|
||||
kw loop
|
||||
kw match
|
||||
kw mod
|
||||
@ -2015,6 +2029,7 @@ fn foo() {
|
||||
kw if let
|
||||
kw impl
|
||||
kw let
|
||||
kw letm
|
||||
kw loop
|
||||
kw match
|
||||
kw mod
|
||||
|
@ -285,6 +285,7 @@ fn bar() {
|
||||
kw if let
|
||||
kw impl
|
||||
kw let
|
||||
kw letm
|
||||
kw loop
|
||||
kw match
|
||||
kw mod
|
||||
|
@ -1009,6 +1009,7 @@ fn here_we_go() {
|
||||
kw if let
|
||||
kw impl
|
||||
kw let
|
||||
kw letm
|
||||
kw loop
|
||||
kw match
|
||||
kw mod
|
||||
@ -1059,6 +1060,7 @@ fn here_we_go() {
|
||||
kw if let
|
||||
kw impl
|
||||
kw let
|
||||
kw letm
|
||||
kw loop
|
||||
kw match
|
||||
kw mod
|
||||
@ -1182,6 +1184,7 @@ fn bar() { qu$0 }
|
||||
kw if let
|
||||
kw impl
|
||||
kw let
|
||||
kw letm
|
||||
kw loop
|
||||
kw match
|
||||
kw mod
|
||||
@ -1437,6 +1440,7 @@ fn foo() {
|
||||
kw if let
|
||||
kw impl
|
||||
kw let
|
||||
kw letm
|
||||
kw loop
|
||||
kw match
|
||||
kw mod
|
||||
|
@ -12,11 +12,11 @@ use arrayvec::ArrayVec;
|
||||
use either::Either;
|
||||
use hir::{
|
||||
Adt, AsAssocItem, AsExternAssocItem, AssocItem, AttributeTemplate, BuiltinAttr, BuiltinType,
|
||||
Const, Crate, DefWithBody, DeriveHelper, DocLinkDef, ExternAssocItem, ExternCrateDecl, Field,
|
||||
Function, GenericDef, GenericParam, GenericSubstitution, HasContainer, HasVisibility,
|
||||
HirDisplay, Impl, InlineAsmOperand, ItemContainer, Label, Local, Macro, Module, ModuleDef,
|
||||
Name, PathResolution, Semantics, Static, StaticLifetime, Struct, ToolModule, Trait, TraitAlias,
|
||||
TupleField, TypeAlias, Variant, VariantDef, Visibility,
|
||||
Const, Crate, DefWithBody, DeriveHelper, DisplayTarget, DocLinkDef, ExternAssocItem,
|
||||
ExternCrateDecl, Field, Function, GenericDef, GenericParam, GenericSubstitution, HasContainer,
|
||||
HasVisibility, HirDisplay, Impl, InlineAsmOperand, ItemContainer, Label, Local, Macro, Module,
|
||||
ModuleDef, Name, PathResolution, Semantics, Static, StaticLifetime, Struct, ToolModule, Trait,
|
||||
TraitAlias, TupleField, TypeAlias, Variant, VariantDef, Visibility,
|
||||
};
|
||||
use span::Edition;
|
||||
use stdx::{format_to, impl_from};
|
||||
@ -207,7 +207,7 @@ impl Definition {
|
||||
&self,
|
||||
db: &RootDatabase,
|
||||
famous_defs: Option<&FamousDefs<'_, '_>>,
|
||||
edition: Edition,
|
||||
display_target: DisplayTarget,
|
||||
) -> Option<Documentation> {
|
||||
let docs = match self {
|
||||
Definition::Macro(it) => it.docs(db),
|
||||
@ -228,7 +228,7 @@ impl Definition {
|
||||
let docs = adt.docs(db)?;
|
||||
let docs = format!(
|
||||
"*This is the documentation for* `{}`\n\n{}",
|
||||
adt.display(db, edition),
|
||||
adt.display(db, display_target),
|
||||
docs.as_str()
|
||||
);
|
||||
Some(Documentation::new(docs))
|
||||
@ -237,8 +237,9 @@ impl Definition {
|
||||
Definition::BuiltinType(it) => {
|
||||
famous_defs.and_then(|fd| {
|
||||
// std exposes prim_{} modules with docstrings on the root to document the builtins
|
||||
let primitive_mod = format!("prim_{}", it.name().display(fd.0.db, edition));
|
||||
let doc_owner = find_std_module(fd, &primitive_mod, edition)?;
|
||||
let primitive_mod =
|
||||
format!("prim_{}", it.name().display(fd.0.db, display_target.edition));
|
||||
let doc_owner = find_std_module(fd, &primitive_mod, display_target.edition)?;
|
||||
doc_owner.docs(fd.0.db)
|
||||
})
|
||||
}
|
||||
@ -256,16 +257,21 @@ impl Definition {
|
||||
let AttributeTemplate { word, list, name_value_str } = it.template(db)?;
|
||||
let mut docs = "Valid forms are:".to_owned();
|
||||
if word {
|
||||
format_to!(docs, "\n - #\\[{}]", name.display(db, edition));
|
||||
format_to!(docs, "\n - #\\[{}]", name.display(db, display_target.edition));
|
||||
}
|
||||
if let Some(list) = list {
|
||||
format_to!(docs, "\n - #\\[{}({})]", name.display(db, edition), list);
|
||||
format_to!(
|
||||
docs,
|
||||
"\n - #\\[{}({})]",
|
||||
name.display(db, display_target.edition),
|
||||
list
|
||||
);
|
||||
}
|
||||
if let Some(name_value_str) = name_value_str {
|
||||
format_to!(
|
||||
docs,
|
||||
"\n - #\\[{} = {}]",
|
||||
name.display(db, edition),
|
||||
name.display(db, display_target.edition),
|
||||
name_value_str
|
||||
);
|
||||
}
|
||||
@ -288,49 +294,60 @@ impl Definition {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn label(&self, db: &RootDatabase, edition: Edition) -> String {
|
||||
pub fn label(&self, db: &RootDatabase, display_target: DisplayTarget) -> String {
|
||||
match *self {
|
||||
Definition::Macro(it) => it.display(db, edition).to_string(),
|
||||
Definition::Field(it) => it.display(db, edition).to_string(),
|
||||
Definition::TupleField(it) => it.display(db, edition).to_string(),
|
||||
Definition::Module(it) => it.display(db, edition).to_string(),
|
||||
Definition::Crate(it) => it.display(db, edition).to_string(),
|
||||
Definition::Function(it) => it.display(db, edition).to_string(),
|
||||
Definition::Adt(it) => it.display(db, edition).to_string(),
|
||||
Definition::Variant(it) => it.display(db, edition).to_string(),
|
||||
Definition::Const(it) => it.display(db, edition).to_string(),
|
||||
Definition::Static(it) => it.display(db, edition).to_string(),
|
||||
Definition::Trait(it) => it.display(db, edition).to_string(),
|
||||
Definition::TraitAlias(it) => it.display(db, edition).to_string(),
|
||||
Definition::TypeAlias(it) => it.display(db, edition).to_string(),
|
||||
Definition::BuiltinType(it) => it.name().display(db, edition).to_string(),
|
||||
Definition::BuiltinLifetime(it) => it.name().display(db, edition).to_string(),
|
||||
Definition::Macro(it) => it.display(db, display_target).to_string(),
|
||||
Definition::Field(it) => it.display(db, display_target).to_string(),
|
||||
Definition::TupleField(it) => it.display(db, display_target).to_string(),
|
||||
Definition::Module(it) => it.display(db, display_target).to_string(),
|
||||
Definition::Crate(it) => it.display(db, display_target).to_string(),
|
||||
Definition::Function(it) => it.display(db, display_target).to_string(),
|
||||
Definition::Adt(it) => it.display(db, display_target).to_string(),
|
||||
Definition::Variant(it) => it.display(db, display_target).to_string(),
|
||||
Definition::Const(it) => it.display(db, display_target).to_string(),
|
||||
Definition::Static(it) => it.display(db, display_target).to_string(),
|
||||
Definition::Trait(it) => it.display(db, display_target).to_string(),
|
||||
Definition::TraitAlias(it) => it.display(db, display_target).to_string(),
|
||||
Definition::TypeAlias(it) => it.display(db, display_target).to_string(),
|
||||
Definition::BuiltinType(it) => {
|
||||
it.name().display(db, display_target.edition).to_string()
|
||||
}
|
||||
Definition::BuiltinLifetime(it) => {
|
||||
it.name().display(db, display_target.edition).to_string()
|
||||
}
|
||||
Definition::Local(it) => {
|
||||
let ty = it.ty(db);
|
||||
let ty_display = ty.display_truncated(db, None, edition);
|
||||
let ty_display = ty.display_truncated(db, None, display_target);
|
||||
let is_mut = if it.is_mut(db) { "mut " } else { "" };
|
||||
if it.is_self(db) {
|
||||
format!("{is_mut}self: {ty_display}")
|
||||
} else {
|
||||
let name = it.name(db);
|
||||
let let_kw = if it.is_param(db) { "" } else { "let " };
|
||||
format!("{let_kw}{is_mut}{}: {ty_display}", name.display(db, edition))
|
||||
format!(
|
||||
"{let_kw}{is_mut}{}: {ty_display}",
|
||||
name.display(db, display_target.edition)
|
||||
)
|
||||
}
|
||||
}
|
||||
Definition::SelfType(impl_def) => {
|
||||
let self_ty = &impl_def.self_ty(db);
|
||||
match self_ty.as_adt() {
|
||||
Some(it) => it.display(db, edition).to_string(),
|
||||
None => self_ty.display(db, edition).to_string(),
|
||||
Some(it) => it.display(db, display_target).to_string(),
|
||||
None => self_ty.display(db, display_target).to_string(),
|
||||
}
|
||||
}
|
||||
Definition::GenericParam(it) => it.display(db, edition).to_string(),
|
||||
Definition::Label(it) => it.name(db).display(db, edition).to_string(),
|
||||
Definition::ExternCrateDecl(it) => it.display(db, edition).to_string(),
|
||||
Definition::BuiltinAttr(it) => format!("#[{}]", it.name(db).display(db, edition)),
|
||||
Definition::ToolModule(it) => it.name(db).display(db, edition).to_string(),
|
||||
Definition::GenericParam(it) => it.display(db, display_target).to_string(),
|
||||
Definition::Label(it) => it.name(db).display(db, display_target.edition).to_string(),
|
||||
Definition::ExternCrateDecl(it) => it.display(db, display_target).to_string(),
|
||||
Definition::BuiltinAttr(it) => {
|
||||
format!("#[{}]", it.name(db).display(db, display_target.edition))
|
||||
}
|
||||
Definition::ToolModule(it) => {
|
||||
it.name(db).display(db, display_target.edition).to_string()
|
||||
}
|
||||
Definition::DeriveHelper(it) => {
|
||||
format!("derive_helper {}", it.name(db).display(db, edition))
|
||||
format!("derive_helper {}", it.name(db).display(db, display_target.edition))
|
||||
}
|
||||
// FIXME
|
||||
Definition::InlineAsmRegOrRegClass(_) => "inline_asm_reg_or_reg_class".to_owned(),
|
||||
|
@ -15052,7 +15052,7 @@ cannot be represented as the underlying type without loss."##,
|
||||
},
|
||||
Lint {
|
||||
label: "clippy::manual_bits",
|
||||
description: r##"Checks for usage of `std::mem::size_of::<T>() * 8` when
|
||||
description: r##"Checks for usage of `size_of::<T>() * 8` when
|
||||
`T::BITS` is available."##,
|
||||
default_severity: Severity::Allow,
|
||||
warn_since: None,
|
||||
@ -17394,7 +17394,7 @@ count of elements of type `T`"##,
|
||||
},
|
||||
Lint {
|
||||
label: "clippy::size_of_ref",
|
||||
description: r##"Checks for calls to `std::mem::size_of_val()` where the argument is
|
||||
description: r##"Checks for calls to `size_of_val()` where the argument is
|
||||
a reference to a reference."##,
|
||||
default_severity: Severity::Allow,
|
||||
warn_since: None,
|
||||
|
@ -192,7 +192,9 @@ impl<'a> PathTransform<'a> {
|
||||
}
|
||||
}
|
||||
(Either::Left(k), None) => {
|
||||
if let Some(default) = k.default(db, target_edition) {
|
||||
if let Some(default) =
|
||||
k.default(db, target_module.krate().to_display_target(db))
|
||||
{
|
||||
if let Some(default) = default.expr() {
|
||||
const_substs.insert(k, default.syntax().clone_for_update());
|
||||
defaulted_params.push(Either::Right(k));
|
||||
|
@ -22,7 +22,10 @@
|
||||
//! Our current behavior is ¯\_(ツ)_/¯.
|
||||
use std::fmt;
|
||||
|
||||
use crate::text_edit::{TextEdit, TextEditBuilder};
|
||||
use crate::{
|
||||
source_change::ChangeAnnotation,
|
||||
text_edit::{TextEdit, TextEditBuilder},
|
||||
};
|
||||
use base_db::AnchoredPathBuf;
|
||||
use either::Either;
|
||||
use hir::{FieldSource, FileRange, HirFileIdExt, InFile, ModuleSource, Semantics};
|
||||
@ -365,10 +368,12 @@ fn rename_reference(
|
||||
}));
|
||||
|
||||
let mut insert_def_edit = |def| {
|
||||
let (file_id, edit) = source_edit_from_def(sema, def, new_name)?;
|
||||
let (file_id, edit) = source_edit_from_def(sema, def, new_name, &mut source_change)?;
|
||||
source_change.insert_source_edit(file_id, edit);
|
||||
Ok(())
|
||||
};
|
||||
// This needs to come after the references edits, because we change the annotation of existing edits
|
||||
// if a conflict is detected.
|
||||
insert_def_edit(def)?;
|
||||
Ok(source_change)
|
||||
}
|
||||
@ -537,6 +542,7 @@ fn source_edit_from_def(
|
||||
sema: &Semantics<'_, RootDatabase>,
|
||||
def: Definition,
|
||||
new_name: &str,
|
||||
source_change: &mut SourceChange,
|
||||
) -> Result<(FileId, TextEdit)> {
|
||||
let new_name_edition_aware = |new_name: &str, file_id: EditionedFileId| {
|
||||
if is_raw_identifier(new_name, file_id.edition()) {
|
||||
@ -548,6 +554,23 @@ fn source_edit_from_def(
|
||||
let mut edit = TextEdit::builder();
|
||||
if let Definition::Local(local) = def {
|
||||
let mut file_id = None;
|
||||
|
||||
let conflict_annotation = if !sema.rename_conflicts(&local, new_name).is_empty() {
|
||||
Some(
|
||||
source_change.insert_annotation(ChangeAnnotation {
|
||||
label: "This rename will change the program's meaning".to_owned(),
|
||||
needs_confirmation: true,
|
||||
description: Some(
|
||||
"Some variable(s) will shadow the renamed variable \
|
||||
or be shadowed by it if the rename is performed"
|
||||
.to_owned(),
|
||||
),
|
||||
}),
|
||||
)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
for source in local.sources(sema.db) {
|
||||
let source = match source.source.clone().original_ast_node_rooted(sema.db) {
|
||||
Some(source) => source,
|
||||
@ -611,8 +634,15 @@ fn source_edit_from_def(
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut edit = edit.finish();
|
||||
|
||||
for (edit, _) in source_change.source_file_edits.values_mut() {
|
||||
edit.set_annotation(conflict_annotation);
|
||||
}
|
||||
edit.set_annotation(conflict_annotation);
|
||||
|
||||
let Some(file_id) = file_id else { bail!("No file available to rename") };
|
||||
return Ok((EditionedFileId::file_id(file_id), edit.finish()));
|
||||
return Ok((EditionedFileId::file_id(file_id), edit));
|
||||
}
|
||||
let FileRange { file_id, range } = def
|
||||
.range_for_rename(sema)
|
||||
|
@ -3,7 +3,7 @@
|
||||
//!
|
||||
//! It can be viewed as a dual for `Change`.
|
||||
|
||||
use std::{collections::hash_map::Entry, iter, mem};
|
||||
use std::{collections::hash_map::Entry, fmt, iter, mem};
|
||||
|
||||
use crate::text_edit::{TextEdit, TextEditBuilder};
|
||||
use crate::{assists::Command, syntax_helpers::tree_diff::diff, SnippetCap};
|
||||
@ -18,23 +18,33 @@ use syntax::{
|
||||
AstNode, SyntaxElement, SyntaxNode, SyntaxNodePtr, SyntaxToken, TextRange, TextSize,
|
||||
};
|
||||
|
||||
/// An annotation ID associated with an indel, to describe changes.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct ChangeAnnotationId(u32);
|
||||
|
||||
impl fmt::Display for ChangeAnnotationId {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Display::fmt(&self.0, f)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ChangeAnnotation {
|
||||
pub label: String,
|
||||
pub needs_confirmation: bool,
|
||||
pub description: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone)]
|
||||
pub struct SourceChange {
|
||||
pub source_file_edits: IntMap<FileId, (TextEdit, Option<SnippetEdit>)>,
|
||||
pub file_system_edits: Vec<FileSystemEdit>,
|
||||
pub is_snippet: bool,
|
||||
pub annotations: FxHashMap<ChangeAnnotationId, ChangeAnnotation>,
|
||||
next_annotation_id: u32,
|
||||
}
|
||||
|
||||
impl SourceChange {
|
||||
/// Creates a new SourceChange with the given label
|
||||
/// from the edits.
|
||||
pub fn from_edits(
|
||||
source_file_edits: IntMap<FileId, (TextEdit, Option<SnippetEdit>)>,
|
||||
file_system_edits: Vec<FileSystemEdit>,
|
||||
) -> Self {
|
||||
SourceChange { source_file_edits, file_system_edits, is_snippet: false }
|
||||
}
|
||||
|
||||
pub fn from_text_edit(file_id: impl Into<FileId>, edit: TextEdit) -> Self {
|
||||
SourceChange {
|
||||
source_file_edits: iter::once((file_id.into(), (edit, None))).collect(),
|
||||
@ -42,6 +52,13 @@ impl SourceChange {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert_annotation(&mut self, annotation: ChangeAnnotation) -> ChangeAnnotationId {
|
||||
let id = ChangeAnnotationId(self.next_annotation_id);
|
||||
self.next_annotation_id += 1;
|
||||
self.annotations.insert(id, annotation);
|
||||
id
|
||||
}
|
||||
|
||||
/// Inserts a [`TextEdit`] for the given [`FileId`]. This properly handles merging existing
|
||||
/// edits for a file if some already exist.
|
||||
pub fn insert_source_edit(&mut self, file_id: impl Into<FileId>, edit: TextEdit) {
|
||||
@ -120,7 +137,12 @@ impl From<IntMap<FileId, TextEdit>> for SourceChange {
|
||||
fn from(source_file_edits: IntMap<FileId, TextEdit>) -> SourceChange {
|
||||
let source_file_edits =
|
||||
source_file_edits.into_iter().map(|(file_id, edit)| (file_id, (edit, None))).collect();
|
||||
SourceChange { source_file_edits, file_system_edits: Vec::new(), is_snippet: false }
|
||||
SourceChange {
|
||||
source_file_edits,
|
||||
file_system_edits: Vec::new(),
|
||||
is_snippet: false,
|
||||
..SourceChange::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -482,6 +504,7 @@ impl From<FileSystemEdit> for SourceChange {
|
||||
source_file_edits: Default::default(),
|
||||
file_system_edits: vec![edit],
|
||||
is_snippet: false,
|
||||
..SourceChange::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -24,7 +24,6 @@ use std::{
|
||||
cmp::Ordering,
|
||||
fmt,
|
||||
hash::{Hash, Hasher},
|
||||
mem,
|
||||
ops::ControlFlow,
|
||||
};
|
||||
|
||||
@ -299,7 +298,7 @@ impl SymbolIndex {
|
||||
}
|
||||
|
||||
pub fn memory_size(&self) -> usize {
|
||||
self.map.as_fst().size() + self.symbols.len() * mem::size_of::<FileSymbol>()
|
||||
self.map.as_fst().size() + self.symbols.len() * size_of::<FileSymbol>()
|
||||
}
|
||||
|
||||
fn range_to_map_value(start: usize, end: usize) -> u64 {
|
||||
|
@ -1,4 +1,6 @@
|
||||
//! Various helper functions to work with SyntaxNodes.
|
||||
use std::ops::ControlFlow;
|
||||
|
||||
use itertools::Itertools;
|
||||
use parser::T;
|
||||
use span::Edition;
|
||||
@ -119,7 +121,10 @@ pub fn walk_patterns_in_expr(start: &ast::Expr, cb: &mut dyn FnMut(ast::Pat)) {
|
||||
match ast::Stmt::cast(node.clone()) {
|
||||
Some(ast::Stmt::LetStmt(l)) => {
|
||||
if let Some(pat) = l.pat() {
|
||||
walk_pat(&pat, cb);
|
||||
walk_pat(&pat, &mut |pat| {
|
||||
cb(pat);
|
||||
ControlFlow::<(), ()>::Continue(())
|
||||
});
|
||||
}
|
||||
if let Some(expr) = l.initializer() {
|
||||
walk_patterns_in_expr(&expr, cb);
|
||||
@ -154,7 +159,10 @@ pub fn walk_patterns_in_expr(start: &ast::Expr, cb: &mut dyn FnMut(ast::Pat)) {
|
||||
}
|
||||
} else if let Some(pat) = ast::Pat::cast(node) {
|
||||
preorder.skip_subtree();
|
||||
walk_pat(&pat, cb);
|
||||
walk_pat(&pat, &mut |pat| {
|
||||
cb(pat);
|
||||
ControlFlow::<(), ()>::Continue(())
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -162,7 +170,10 @@ pub fn walk_patterns_in_expr(start: &ast::Expr, cb: &mut dyn FnMut(ast::Pat)) {
|
||||
}
|
||||
|
||||
/// Preorder walk all the pattern's sub patterns.
|
||||
pub fn walk_pat(pat: &ast::Pat, cb: &mut dyn FnMut(ast::Pat)) {
|
||||
pub fn walk_pat<T>(
|
||||
pat: &ast::Pat,
|
||||
cb: &mut dyn FnMut(ast::Pat) -> ControlFlow<T>,
|
||||
) -> ControlFlow<T> {
|
||||
let mut preorder = pat.syntax().preorder();
|
||||
while let Some(event) = preorder.next() {
|
||||
let node = match event {
|
||||
@ -173,10 +184,10 @@ pub fn walk_pat(pat: &ast::Pat, cb: &mut dyn FnMut(ast::Pat)) {
|
||||
match ast::Pat::cast(node) {
|
||||
Some(pat @ ast::Pat::ConstBlockPat(_)) => {
|
||||
preorder.skip_subtree();
|
||||
cb(pat);
|
||||
cb(pat)?;
|
||||
}
|
||||
Some(pat) => {
|
||||
cb(pat);
|
||||
cb(pat)?;
|
||||
}
|
||||
// skip const args
|
||||
None if ast::GenericArg::can_cast(kind) => {
|
||||
@ -185,6 +196,7 @@ pub fn walk_pat(pat: &ast::Pat, cb: &mut dyn FnMut(ast::Pat)) {
|
||||
None => (),
|
||||
}
|
||||
}
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
|
||||
/// Preorder walk all the type's sub types.
|
||||
|
@ -8,6 +8,8 @@ use itertools::Itertools;
|
||||
pub use span::{TextRange, TextSize};
|
||||
use std::cmp::max;
|
||||
|
||||
use crate::source_change::ChangeAnnotationId;
|
||||
|
||||
/// `InsertDelete` -- a single "atomic" change to text
|
||||
///
|
||||
/// Must not overlap with other `InDel`s
|
||||
@ -16,6 +18,7 @@ pub struct Indel {
|
||||
pub insert: String,
|
||||
/// Refers to offsets in the original text
|
||||
pub delete: TextRange,
|
||||
pub annotation: Option<ChangeAnnotationId>,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone)]
|
||||
@ -37,7 +40,7 @@ impl Indel {
|
||||
Indel::replace(range, String::new())
|
||||
}
|
||||
pub fn replace(range: TextRange, replace_with: String) -> Indel {
|
||||
Indel { delete: range, insert: replace_with }
|
||||
Indel { delete: range, insert: replace_with, annotation: None }
|
||||
}
|
||||
|
||||
pub fn apply(&self, text: &mut String) {
|
||||
@ -138,6 +141,14 @@ impl TextEdit {
|
||||
}
|
||||
Some(res)
|
||||
}
|
||||
|
||||
pub fn set_annotation(&mut self, annotation: Option<ChangeAnnotationId>) {
|
||||
if annotation.is_some() {
|
||||
for indel in &mut self.indels {
|
||||
indel.annotation = annotation;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoIterator for TextEdit {
|
||||
@ -180,7 +191,7 @@ impl TextEditBuilder {
|
||||
pub fn invalidates_offset(&self, offset: TextSize) -> bool {
|
||||
self.indels.iter().any(|indel| indel.delete.contains_inclusive(offset))
|
||||
}
|
||||
fn indel(&mut self, indel: Indel) {
|
||||
pub fn indel(&mut self, indel: Indel) {
|
||||
self.indels.push(indel);
|
||||
if self.indels.len() <= 16 {
|
||||
assert_disjoint_or_equal(&mut self.indels);
|
||||
|
@ -12,7 +12,7 @@ pub(crate) fn expected_function(
|
||||
Diagnostic::new_with_syntax_node_ptr(
|
||||
ctx,
|
||||
DiagnosticCode::RustcHardError("E0618"),
|
||||
format!("expected function, found {}", d.found.display(ctx.sema.db, ctx.edition)),
|
||||
format!("expected function, found {}", d.found.display(ctx.sema.db, ctx.display_target)),
|
||||
d.call.map(|it| it.into()),
|
||||
)
|
||||
.experimental()
|
||||
|
@ -8,7 +8,7 @@ macro_rules! format_ty {
|
||||
$fmt,
|
||||
$(
|
||||
$arg
|
||||
.display($ctx.sema.db, $ctx.edition)
|
||||
.display($ctx.sema.db, $ctx.display_target)
|
||||
.with_closure_style(ClosureStyle::ClosureWithId)
|
||||
),*
|
||||
)
|
||||
|
@ -8,7 +8,7 @@ pub(crate) fn moved_out_of_ref(ctx: &DiagnosticsContext<'_>, d: &hir::MovedOutOf
|
||||
Diagnostic::new_with_syntax_node_ptr(
|
||||
ctx,
|
||||
DiagnosticCode::RustcHardError("E0507"),
|
||||
format!("cannot move `{}` out of reference", d.ty.display(ctx.sema.db, ctx.edition)),
|
||||
format!("cannot move `{}` out of reference", d.ty.display(ctx.sema.db, ctx.display_target)),
|
||||
d.span,
|
||||
)
|
||||
.experimental() // spans are broken, and I'm not sure how precise we can detect copy types
|
||||
|
@ -30,7 +30,7 @@ pub(crate) fn trait_impl_redundant_assoc_item(
|
||||
(
|
||||
format!("`fn {redundant_assoc_item_name}`"),
|
||||
function.source(db).map(|it| it.syntax().text_range()).unwrap_or(default_range),
|
||||
format!("\n {};", function.display(db, ctx.edition)),
|
||||
format!("\n {};", function.display(db, ctx.display_target)),
|
||||
)
|
||||
}
|
||||
hir::AssocItem::Const(id) => {
|
||||
@ -38,7 +38,7 @@ pub(crate) fn trait_impl_redundant_assoc_item(
|
||||
(
|
||||
format!("`const {redundant_assoc_item_name}`"),
|
||||
constant.source(db).map(|it| it.syntax().text_range()).unwrap_or(default_range),
|
||||
format!("\n {};", constant.display(db, ctx.edition)),
|
||||
format!("\n {};", constant.display(db, ctx.display_target)),
|
||||
)
|
||||
}
|
||||
hir::AssocItem::TypeAlias(id) => {
|
||||
|
@ -45,10 +45,10 @@ pub(crate) fn type_mismatch(ctx: &DiagnosticsContext<'_>, d: &hir::TypeMismatch)
|
||||
format!(
|
||||
"expected {}, found {}",
|
||||
d.expected
|
||||
.display(ctx.sema.db, ctx.edition)
|
||||
.display(ctx.sema.db, ctx.display_target)
|
||||
.with_closure_style(ClosureStyle::ClosureWithId),
|
||||
d.actual
|
||||
.display(ctx.sema.db, ctx.edition)
|
||||
.display(ctx.sema.db, ctx.display_target)
|
||||
.with_closure_style(ClosureStyle::ClosureWithId),
|
||||
),
|
||||
display_range,
|
||||
@ -306,8 +306,8 @@ fn str_ref_to_owned(
|
||||
expr_ptr: &InFile<AstPtr<ast::Expr>>,
|
||||
acc: &mut Vec<Assist>,
|
||||
) -> Option<()> {
|
||||
let expected = d.expected.display(ctx.sema.db, ctx.edition);
|
||||
let actual = d.actual.display(ctx.sema.db, ctx.edition);
|
||||
let expected = d.expected.display(ctx.sema.db, ctx.display_target);
|
||||
let actual = d.actual.display(ctx.sema.db, ctx.display_target);
|
||||
|
||||
// FIXME do this properly
|
||||
if expected.to_string() != "String" || actual.to_string() != "&str" {
|
||||
|
@ -27,7 +27,7 @@ pub(crate) fn typed_hole(ctx: &DiagnosticsContext<'_>, d: &hir::TypedHole) -> Di
|
||||
format!(
|
||||
"invalid `_` expression, expected type `{}`",
|
||||
d.expected
|
||||
.display(ctx.sema.db, ctx.edition)
|
||||
.display(ctx.sema.db, ctx.display_target)
|
||||
.with_closure_style(ClosureStyle::ClosureWithId),
|
||||
),
|
||||
fixes(ctx, d),
|
||||
@ -72,7 +72,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::TypedHole) -> Option<Vec<Assist>
|
||||
prefer_absolute: ctx.config.prefer_absolute,
|
||||
allow_unstable: ctx.is_nightly,
|
||||
},
|
||||
ctx.edition,
|
||||
ctx.display_target,
|
||||
)
|
||||
.ok()
|
||||
})
|
||||
|
@ -38,7 +38,7 @@ pub(crate) fn unresolved_field(
|
||||
format!(
|
||||
"no field `{}` on type `{}`{method_suffix}",
|
||||
d.name.display(ctx.sema.db, ctx.edition),
|
||||
d.receiver.display(ctx.sema.db, ctx.edition)
|
||||
d.receiver.display(ctx.sema.db, ctx.display_target)
|
||||
),
|
||||
adjusted_display_range(ctx, d.expr, &|expr| {
|
||||
Some(
|
||||
|
@ -31,7 +31,7 @@ pub(crate) fn unresolved_method(
|
||||
format!(
|
||||
"no method `{}` on type `{}`{suffix}",
|
||||
d.name.display(ctx.sema.db, ctx.edition),
|
||||
d.receiver.display(ctx.sema.db, ctx.edition)
|
||||
d.receiver.display(ctx.sema.db, ctx.display_target)
|
||||
),
|
||||
adjusted_display_range(ctx, d.expr, &|expr| {
|
||||
Some(
|
||||
@ -152,7 +152,7 @@ fn assoc_func_fix(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedMethodCall) -
|
||||
receiver_type.as_adt()?.name(db).display_no_db(ctx.edition).to_smolstr();
|
||||
|
||||
let generic_parameters: Vec<SmolStr> =
|
||||
receiver_type.generic_parameters(db, ctx.edition).collect();
|
||||
receiver_type.generic_parameters(db, ctx.display_target).collect();
|
||||
// if receiver should be pass as first arg in the assoc func,
|
||||
// we could omit generic parameters cause compiler can deduce it automatically
|
||||
if !need_to_take_receiver_as_first_arg && !generic_parameters.is_empty() {
|
||||
|
@ -81,7 +81,10 @@ mod tests;
|
||||
use std::{collections::hash_map, iter, sync::LazyLock};
|
||||
|
||||
use either::Either;
|
||||
use hir::{db::ExpandDatabase, diagnostics::AnyDiagnostic, Crate, HirFileId, InFile, Semantics};
|
||||
use hir::{
|
||||
db::ExpandDatabase, diagnostics::AnyDiagnostic, Crate, DisplayTarget, HirFileId, InFile,
|
||||
Semantics,
|
||||
};
|
||||
use ide_db::{
|
||||
assists::{Assist, AssistId, AssistKind, AssistResolveStrategy},
|
||||
base_db::{ReleaseChannel, SourceDatabase},
|
||||
@ -277,6 +280,7 @@ struct DiagnosticsContext<'a> {
|
||||
sema: Semantics<'a, RootDatabase>,
|
||||
resolve: &'a AssistResolveStrategy,
|
||||
edition: Edition,
|
||||
display_target: DisplayTarget,
|
||||
is_nightly: bool,
|
||||
}
|
||||
|
||||
@ -374,7 +378,18 @@ pub fn semantic_diagnostics(
|
||||
module.and_then(|m| db.toolchain_channel(m.krate().into())),
|
||||
Some(ReleaseChannel::Nightly) | None
|
||||
);
|
||||
let ctx = DiagnosticsContext { config, sema, resolve, edition: file_id.edition(), is_nightly };
|
||||
let krate = module.map(|module| module.krate()).unwrap_or_else(|| {
|
||||
(*db.crate_graph().crates_in_topological_order().last().unwrap()).into()
|
||||
});
|
||||
let display_target = krate.to_display_target(db);
|
||||
let ctx = DiagnosticsContext {
|
||||
config,
|
||||
sema,
|
||||
resolve,
|
||||
edition: file_id.edition(),
|
||||
is_nightly,
|
||||
display_target,
|
||||
};
|
||||
|
||||
let mut diags = Vec::new();
|
||||
match module {
|
||||
|
@ -7,8 +7,7 @@ use crate::{
|
||||
SsrMatches,
|
||||
};
|
||||
use hir::{FileRange, ImportPathConfig, Semantics};
|
||||
use ide_db::FxHashMap;
|
||||
use parser::Edition;
|
||||
use ide_db::{base_db::SourceDatabase, FxHashMap};
|
||||
use std::{cell::Cell, iter::Peekable};
|
||||
use syntax::{
|
||||
ast::{self, AstNode, AstToken, HasGenericArgs},
|
||||
@ -627,22 +626,23 @@ impl<'db, 'sema> Matcher<'db, 'sema> {
|
||||
match_error!("Failed to get receiver type for `{}`", expr.syntax().text())
|
||||
})?
|
||||
.original;
|
||||
let edition = self
|
||||
.sema
|
||||
.scope(expr.syntax())
|
||||
.map(|it| it.krate().edition(self.sema.db))
|
||||
.unwrap_or(Edition::CURRENT);
|
||||
// Temporary needed to make the borrow checker happy.
|
||||
let krate = self.sema.scope(expr.syntax()).map(|it| it.krate()).unwrap_or_else(|| {
|
||||
hir::Crate::from(
|
||||
*self.sema.db.crate_graph().crates_in_topological_order().last().unwrap(),
|
||||
)
|
||||
});
|
||||
let res = code_type
|
||||
.autoderef(self.sema.db)
|
||||
.enumerate()
|
||||
.find(|(_, deref_code_type)| pattern_type == deref_code_type)
|
||||
.map(|(count, _)| count)
|
||||
.ok_or_else(|| {
|
||||
let display_target = krate.to_display_target(self.sema.db);
|
||||
// Temporary needed to make the borrow checker happy.
|
||||
match_error!(
|
||||
"Pattern type `{}` didn't match code type `{}`",
|
||||
pattern_type.display(self.sema.db, edition),
|
||||
code_type.display(self.sema.db, edition)
|
||||
pattern_type.display(self.sema.db, display_target),
|
||||
code_type.display(self.sema.db, display_target)
|
||||
)
|
||||
});
|
||||
res
|
||||
|
@ -7,7 +7,8 @@ use std::{iter, ops::Not};
|
||||
|
||||
use either::Either;
|
||||
use hir::{
|
||||
db::DefDatabase, GenericDef, GenericSubstitution, HasCrate, HasSource, LangItem, Semantics,
|
||||
db::DefDatabase, DisplayTarget, GenericDef, GenericSubstitution, HasCrate, HasSource, LangItem,
|
||||
Semantics,
|
||||
};
|
||||
use ide_db::{
|
||||
defs::{Definition, IdentClass, NameRefClass, OperatorClass},
|
||||
@ -129,10 +130,18 @@ pub(crate) fn hover(
|
||||
let file = sema.parse_guess_edition(file_id).syntax().clone();
|
||||
let edition =
|
||||
sema.attach_first_edition(file_id).map(|it| it.edition()).unwrap_or(Edition::CURRENT);
|
||||
let display_target = sema.first_crate_or_default(file_id).to_display_target(db);
|
||||
let mut res = if range.is_empty() {
|
||||
hover_offset(sema, FilePosition { file_id, offset: range.start() }, file, config, edition)
|
||||
hover_offset(
|
||||
sema,
|
||||
FilePosition { file_id, offset: range.start() },
|
||||
file,
|
||||
config,
|
||||
edition,
|
||||
display_target,
|
||||
)
|
||||
} else {
|
||||
hover_ranged(sema, frange, file, config, edition)
|
||||
hover_ranged(sema, frange, file, config, edition, display_target)
|
||||
}?;
|
||||
|
||||
if let HoverDocFormat::PlainText = config.format {
|
||||
@ -148,6 +157,7 @@ fn hover_offset(
|
||||
file: SyntaxNode,
|
||||
config: &HoverConfig,
|
||||
edition: Edition,
|
||||
display_target: DisplayTarget,
|
||||
) -> Option<RangeInfo<HoverResult>> {
|
||||
let original_token = pick_best_token(file.token_at_offset(offset), |kind| match kind {
|
||||
IDENT
|
||||
@ -169,8 +179,18 @@ fn hover_offset(
|
||||
if let Some(doc_comment) = token_as_doc_comment(&original_token) {
|
||||
cov_mark::hit!(no_highlight_on_comment_hover);
|
||||
return doc_comment.get_definition_with_descend_at(sema, offset, |def, node, range| {
|
||||
let res =
|
||||
hover_for_definition(sema, file_id, def, None, &node, None, false, config, edition);
|
||||
let res = hover_for_definition(
|
||||
sema,
|
||||
file_id,
|
||||
def,
|
||||
None,
|
||||
&node,
|
||||
None,
|
||||
false,
|
||||
config,
|
||||
edition,
|
||||
display_target,
|
||||
);
|
||||
Some(RangeInfo::new(range, res))
|
||||
});
|
||||
}
|
||||
@ -188,6 +208,7 @@ fn hover_offset(
|
||||
false,
|
||||
config,
|
||||
edition,
|
||||
display_target,
|
||||
);
|
||||
return Some(RangeInfo::new(range, res));
|
||||
}
|
||||
@ -277,6 +298,7 @@ fn hover_offset(
|
||||
hovered_definition,
|
||||
config,
|
||||
edition,
|
||||
display_target,
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
@ -286,12 +308,12 @@ fn hover_offset(
|
||||
res.extend(definitions);
|
||||
continue;
|
||||
}
|
||||
let keywords = || render::keyword(sema, config, &token, edition);
|
||||
let keywords = || render::keyword(sema, config, &token, edition, display_target);
|
||||
let underscore = || {
|
||||
if !is_same_kind {
|
||||
return None;
|
||||
}
|
||||
render::underscore(sema, config, &token, edition)
|
||||
render::underscore(sema, config, &token, edition, display_target)
|
||||
};
|
||||
let rest_pat = || {
|
||||
if !is_same_kind || token.kind() != DOT2 {
|
||||
@ -305,7 +327,7 @@ fn hover_offset(
|
||||
let record_pat =
|
||||
record_pat_field_list.syntax().parent().and_then(ast::RecordPat::cast)?;
|
||||
|
||||
Some(render::struct_rest_pat(sema, config, &record_pat, edition))
|
||||
Some(render::struct_rest_pat(sema, config, &record_pat, edition, display_target))
|
||||
};
|
||||
let call = || {
|
||||
if !is_same_kind || token.kind() != T!['('] && token.kind() != T![')'] {
|
||||
@ -319,17 +341,17 @@ fn hover_offset(
|
||||
_ => return None,
|
||||
}
|
||||
};
|
||||
render::type_info_of(sema, config, &Either::Left(call_expr), edition)
|
||||
render::type_info_of(sema, config, &Either::Left(call_expr), edition, display_target)
|
||||
};
|
||||
let closure = || {
|
||||
if !is_same_kind || token.kind() != T![|] {
|
||||
return None;
|
||||
}
|
||||
let c = token.parent().and_then(|x| x.parent()).and_then(ast::ClosureExpr::cast)?;
|
||||
render::closure_expr(sema, config, c, edition)
|
||||
render::closure_expr(sema, config, c, edition, display_target)
|
||||
};
|
||||
let literal = || {
|
||||
render::literal(sema, original_token.clone(), edition)
|
||||
render::literal(sema, original_token.clone(), display_target)
|
||||
.map(|markup| HoverResult { markup, actions: vec![] })
|
||||
};
|
||||
if let Some(result) = keywords()
|
||||
@ -362,6 +384,7 @@ fn hover_ranged(
|
||||
file: SyntaxNode,
|
||||
config: &HoverConfig,
|
||||
edition: Edition,
|
||||
display_target: DisplayTarget,
|
||||
) -> Option<RangeInfo<HoverResult>> {
|
||||
// FIXME: make this work in attributes
|
||||
let expr_or_pat = file
|
||||
@ -371,16 +394,17 @@ fn hover_ranged(
|
||||
.find_map(Either::<ast::Expr, ast::Pat>::cast)?;
|
||||
let res = match &expr_or_pat {
|
||||
Either::Left(ast::Expr::TryExpr(try_expr)) => {
|
||||
render::try_expr(sema, config, try_expr, edition)
|
||||
render::try_expr(sema, config, try_expr, edition, display_target)
|
||||
}
|
||||
Either::Left(ast::Expr::PrefixExpr(prefix_expr))
|
||||
if prefix_expr.op_kind() == Some(ast::UnaryOp::Deref) =>
|
||||
{
|
||||
render::deref_expr(sema, config, prefix_expr, edition)
|
||||
render::deref_expr(sema, config, prefix_expr, edition, display_target)
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
let res = res.or_else(|| render::type_info_of(sema, config, &expr_or_pat, edition));
|
||||
let res =
|
||||
res.or_else(|| render::type_info_of(sema, config, &expr_or_pat, edition, display_target));
|
||||
res.map(|it| {
|
||||
let range = match expr_or_pat {
|
||||
Either::Left(it) => it.syntax().text_range(),
|
||||
@ -401,6 +425,7 @@ pub(crate) fn hover_for_definition(
|
||||
hovered_definition: bool,
|
||||
config: &HoverConfig,
|
||||
edition: Edition,
|
||||
display_target: DisplayTarget,
|
||||
) -> HoverResult {
|
||||
let famous_defs = match &def {
|
||||
Definition::BuiltinType(_) => sema.scope(scope_node).map(|it| FamousDefs(sema, it.krate())),
|
||||
@ -435,6 +460,7 @@ pub(crate) fn hover_for_definition(
|
||||
subst_types.as_ref(),
|
||||
config,
|
||||
edition,
|
||||
display_target,
|
||||
);
|
||||
HoverResult {
|
||||
markup: render::process_markup(sema.db, def, &markup, config),
|
||||
|
@ -3,7 +3,7 @@ use std::{env, mem, ops::Not};
|
||||
|
||||
use either::Either;
|
||||
use hir::{
|
||||
db::ExpandDatabase, Adt, AsAssocItem, AsExternAssocItem, CaptureKind, DropGlue,
|
||||
db::ExpandDatabase, Adt, AsAssocItem, AsExternAssocItem, CaptureKind, DisplayTarget, DropGlue,
|
||||
DynCompatibilityViolation, HasCrate, HasSource, HirDisplay, Layout, LayoutError,
|
||||
MethodViolationCode, Name, Semantics, Symbol, Trait, Type, TypeInfo, VariantDef,
|
||||
};
|
||||
@ -38,12 +38,13 @@ pub(super) fn type_info_of(
|
||||
_config: &HoverConfig,
|
||||
expr_or_pat: &Either<ast::Expr, ast::Pat>,
|
||||
edition: Edition,
|
||||
display_target: DisplayTarget,
|
||||
) -> Option<HoverResult> {
|
||||
let ty_info = match expr_or_pat {
|
||||
Either::Left(expr) => sema.type_of_expr(expr)?,
|
||||
Either::Right(pat) => sema.type_of_pat(pat)?,
|
||||
};
|
||||
type_info(sema, _config, ty_info, edition)
|
||||
type_info(sema, _config, ty_info, edition, display_target)
|
||||
}
|
||||
|
||||
pub(super) fn closure_expr(
|
||||
@ -51,9 +52,10 @@ pub(super) fn closure_expr(
|
||||
config: &HoverConfig,
|
||||
c: ast::ClosureExpr,
|
||||
edition: Edition,
|
||||
display_target: DisplayTarget,
|
||||
) -> Option<HoverResult> {
|
||||
let TypeInfo { original, .. } = sema.type_of_expr(&c.into())?;
|
||||
closure_ty(sema, config, &TypeInfo { original, adjusted: None }, edition)
|
||||
closure_ty(sema, config, &TypeInfo { original, adjusted: None }, edition, display_target)
|
||||
}
|
||||
|
||||
pub(super) fn try_expr(
|
||||
@ -61,6 +63,7 @@ pub(super) fn try_expr(
|
||||
_config: &HoverConfig,
|
||||
try_expr: &ast::TryExpr,
|
||||
edition: Edition,
|
||||
display_target: DisplayTarget,
|
||||
) -> Option<HoverResult> {
|
||||
let inner_ty = sema.type_of_expr(&try_expr.expr()?)?.original;
|
||||
let mut ancestors = try_expr.syntax().ancestors();
|
||||
@ -127,8 +130,8 @@ pub(super) fn try_expr(
|
||||
res.actions.push(actions);
|
||||
}
|
||||
|
||||
let inner_ty = inner_ty.display(sema.db, edition).to_string();
|
||||
let body_ty = body_ty.display(sema.db, edition).to_string();
|
||||
let inner_ty = inner_ty.display(sema.db, display_target).to_string();
|
||||
let body_ty = body_ty.display(sema.db, display_target).to_string();
|
||||
let ty_len_max = inner_ty.len().max(body_ty.len());
|
||||
|
||||
let l = "Propagated as: ".len() - " Type: ".len();
|
||||
@ -153,6 +156,7 @@ pub(super) fn deref_expr(
|
||||
_config: &HoverConfig,
|
||||
deref_expr: &ast::PrefixExpr,
|
||||
edition: Edition,
|
||||
display_target: DisplayTarget,
|
||||
) -> Option<HoverResult> {
|
||||
let inner_ty = sema.type_of_expr(&deref_expr.expr()?)?.original;
|
||||
let TypeInfo { original, adjusted } =
|
||||
@ -170,9 +174,9 @@ pub(super) fn deref_expr(
|
||||
|
||||
res.markup = if let Some(adjusted_ty) = adjusted {
|
||||
walk_and_push_ty(sema.db, &adjusted_ty, &mut push_new_def);
|
||||
let original = original.display(sema.db, edition).to_string();
|
||||
let adjusted = adjusted_ty.display(sema.db, edition).to_string();
|
||||
let inner = inner_ty.display(sema.db, edition).to_string();
|
||||
let original = original.display(sema.db, display_target).to_string();
|
||||
let adjusted = adjusted_ty.display(sema.db, display_target).to_string();
|
||||
let inner = inner_ty.display(sema.db, display_target).to_string();
|
||||
let type_len = "To type: ".len();
|
||||
let coerced_len = "Coerced to: ".len();
|
||||
let deref_len = "Dereferenced from: ".len();
|
||||
@ -190,8 +194,8 @@ pub(super) fn deref_expr(
|
||||
)
|
||||
.into()
|
||||
} else {
|
||||
let original = original.display(sema.db, edition).to_string();
|
||||
let inner = inner_ty.display(sema.db, edition).to_string();
|
||||
let original = original.display(sema.db, display_target).to_string();
|
||||
let inner = inner_ty.display(sema.db, display_target).to_string();
|
||||
let type_len = "To type: ".len();
|
||||
let deref_len = "Dereferenced from: ".len();
|
||||
let max_len = (original.len() + type_len).max(inner.len() + deref_len);
|
||||
@ -216,6 +220,7 @@ pub(super) fn underscore(
|
||||
config: &HoverConfig,
|
||||
token: &SyntaxToken,
|
||||
edition: Edition,
|
||||
display_target: DisplayTarget,
|
||||
) -> Option<HoverResult> {
|
||||
if token.kind() != T![_] {
|
||||
return None;
|
||||
@ -224,8 +229,8 @@ pub(super) fn underscore(
|
||||
let _it = match_ast! {
|
||||
match parent {
|
||||
ast::InferType(it) => it,
|
||||
ast::UnderscoreExpr(it) => return type_info_of(sema, config, &Either::Left(ast::Expr::UnderscoreExpr(it)),edition),
|
||||
ast::WildcardPat(it) => return type_info_of(sema, config, &Either::Right(ast::Pat::WildcardPat(it)),edition),
|
||||
ast::UnderscoreExpr(it) => return type_info_of(sema, config, &Either::Left(ast::Expr::UnderscoreExpr(it)),edition, display_target),
|
||||
ast::WildcardPat(it) => return type_info_of(sema, config, &Either::Right(ast::Pat::WildcardPat(it)),edition, display_target),
|
||||
_ => return None,
|
||||
}
|
||||
};
|
||||
@ -259,6 +264,7 @@ pub(super) fn keyword(
|
||||
config: &HoverConfig,
|
||||
token: &SyntaxToken,
|
||||
edition: Edition,
|
||||
display_target: DisplayTarget,
|
||||
) -> Option<HoverResult> {
|
||||
if !token.kind().is_keyword(edition) || !config.documentation || !config.keywords {
|
||||
return None;
|
||||
@ -267,7 +273,7 @@ pub(super) fn keyword(
|
||||
let famous_defs = FamousDefs(sema, sema.scope(&parent)?.krate());
|
||||
|
||||
let KeywordHint { description, keyword_mod, actions } =
|
||||
keyword_hints(sema, token, parent, edition);
|
||||
keyword_hints(sema, token, parent, edition, display_target);
|
||||
|
||||
let doc_owner = find_std_module(&famous_defs, &keyword_mod, edition)?;
|
||||
let docs = doc_owner.docs(sema.db)?;
|
||||
@ -288,6 +294,7 @@ pub(super) fn struct_rest_pat(
|
||||
_config: &HoverConfig,
|
||||
pattern: &ast::RecordPat,
|
||||
edition: Edition,
|
||||
display_target: DisplayTarget,
|
||||
) -> HoverResult {
|
||||
let missing_fields = sema.record_pattern_missing_fields(pattern);
|
||||
|
||||
@ -309,7 +316,7 @@ pub(super) fn struct_rest_pat(
|
||||
res.markup = {
|
||||
let mut s = String::from(".., ");
|
||||
for (f, _) in &missing_fields {
|
||||
s += f.display(sema.db, edition).to_string().as_ref();
|
||||
s += f.display(sema.db, display_target).to_string().as_ref();
|
||||
s += ", ";
|
||||
}
|
||||
// get rid of trailing comma
|
||||
@ -479,41 +486,44 @@ pub(super) fn definition(
|
||||
subst_types: Option<&Vec<(Symbol, Type)>>,
|
||||
config: &HoverConfig,
|
||||
edition: Edition,
|
||||
display_target: DisplayTarget,
|
||||
) -> Markup {
|
||||
let mod_path = definition_path(db, &def, edition);
|
||||
let label = match def {
|
||||
Definition::Trait(trait_) => {
|
||||
trait_.display_limited(db, config.max_trait_assoc_items_count, edition).to_string()
|
||||
}
|
||||
Definition::Trait(trait_) => trait_
|
||||
.display_limited(db, config.max_trait_assoc_items_count, display_target)
|
||||
.to_string(),
|
||||
Definition::Adt(adt @ (Adt::Struct(_) | Adt::Union(_))) => {
|
||||
adt.display_limited(db, config.max_fields_count, edition).to_string()
|
||||
adt.display_limited(db, config.max_fields_count, display_target).to_string()
|
||||
}
|
||||
Definition::Variant(variant) => {
|
||||
variant.display_limited(db, config.max_fields_count, edition).to_string()
|
||||
variant.display_limited(db, config.max_fields_count, display_target).to_string()
|
||||
}
|
||||
Definition::Adt(adt @ Adt::Enum(_)) => {
|
||||
adt.display_limited(db, config.max_enum_variants_count, edition).to_string()
|
||||
adt.display_limited(db, config.max_enum_variants_count, display_target).to_string()
|
||||
}
|
||||
Definition::SelfType(impl_def) => {
|
||||
let self_ty = &impl_def.self_ty(db);
|
||||
match self_ty.as_adt() {
|
||||
Some(adt) => adt.display_limited(db, config.max_fields_count, edition).to_string(),
|
||||
None => self_ty.display(db, edition).to_string(),
|
||||
Some(adt) => {
|
||||
adt.display_limited(db, config.max_fields_count, display_target).to_string()
|
||||
}
|
||||
None => self_ty.display(db, display_target).to_string(),
|
||||
}
|
||||
}
|
||||
Definition::Macro(it) => {
|
||||
let mut label = it.display(db, edition).to_string();
|
||||
let mut label = it.display(db, display_target).to_string();
|
||||
if let Some(macro_arm) = macro_arm {
|
||||
format_to!(label, " // matched arm #{}", macro_arm);
|
||||
}
|
||||
label
|
||||
}
|
||||
Definition::Function(fn_) => {
|
||||
fn_.display_with_container_bounds(db, true, edition).to_string()
|
||||
fn_.display_with_container_bounds(db, true, display_target).to_string()
|
||||
}
|
||||
_ => def.label(db, edition),
|
||||
_ => def.label(db, display_target),
|
||||
};
|
||||
let docs = def.docs(db, famous_defs, edition);
|
||||
let docs = def.docs(db, famous_defs, display_target);
|
||||
let value = || match def {
|
||||
Definition::Variant(it) => {
|
||||
if !it.parent_enum(db).is_data_carrying(db) {
|
||||
@ -525,7 +535,10 @@ pub(super) fn definition(
|
||||
let res = it.value(db).map(|it| format!("{it:?}"));
|
||||
if env::var_os("RA_DEV").is_some() {
|
||||
let res = res.as_deref().unwrap_or("");
|
||||
Some(format!("{res} ({})", render_const_eval_error(db, err, edition)))
|
||||
Some(format!(
|
||||
"{res} ({})",
|
||||
render_const_eval_error(db, err, display_target)
|
||||
))
|
||||
} else {
|
||||
res
|
||||
}
|
||||
@ -541,9 +554,12 @@ pub(super) fn definition(
|
||||
Ok(it) => match it.render_debug(db) {
|
||||
Ok(it) => it,
|
||||
Err(err) => {
|
||||
let it = it.render(db, edition);
|
||||
let it = it.render(db, display_target);
|
||||
if env::var_os("RA_DEV").is_some() {
|
||||
format!("{it}\n{}", render_const_eval_error(db, err.into(), edition))
|
||||
format!(
|
||||
"{it}\n{}",
|
||||
render_const_eval_error(db, err.into(), display_target)
|
||||
)
|
||||
} else {
|
||||
it
|
||||
}
|
||||
@ -557,7 +573,7 @@ pub(super) fn definition(
|
||||
body = prettify_macro_expansion(db, body, &span_map, it.krate(db).into());
|
||||
}
|
||||
if env::var_os("RA_DEV").is_some() {
|
||||
format!("{body}\n{}", render_const_eval_error(db, err, edition))
|
||||
format!("{body}\n{}", render_const_eval_error(db, err, display_target))
|
||||
} else {
|
||||
body.to_string()
|
||||
}
|
||||
@ -570,9 +586,12 @@ pub(super) fn definition(
|
||||
Ok(it) => match it.render_debug(db) {
|
||||
Ok(it) => it,
|
||||
Err(err) => {
|
||||
let it = it.render(db, edition);
|
||||
let it = it.render(db, display_target);
|
||||
if env::var_os("RA_DEV").is_some() {
|
||||
format!("{it}\n{}", render_const_eval_error(db, err.into(), edition))
|
||||
format!(
|
||||
"{it}\n{}",
|
||||
render_const_eval_error(db, err.into(), display_target)
|
||||
)
|
||||
} else {
|
||||
it
|
||||
}
|
||||
@ -586,7 +605,7 @@ pub(super) fn definition(
|
||||
body = prettify_macro_expansion(db, body, &span_map, it.krate(db).into());
|
||||
}
|
||||
if env::var_os("RA_DEV").is_some() {
|
||||
format!("{body}\n{}", render_const_eval_error(db, err, edition))
|
||||
format!("{body}\n{}", render_const_eval_error(db, err, display_target))
|
||||
} else {
|
||||
body.to_string()
|
||||
}
|
||||
@ -728,7 +747,9 @@ pub(super) fn definition(
|
||||
|
||||
let mut extra = String::new();
|
||||
if hovered_definition {
|
||||
if let Some(notable_traits) = render_notable_trait(db, notable_traits, edition) {
|
||||
if let Some(notable_traits) =
|
||||
render_notable_trait(db, notable_traits, edition, display_target)
|
||||
{
|
||||
extra.push_str("\n___\n");
|
||||
extra.push_str(¬able_traits);
|
||||
}
|
||||
@ -772,7 +793,7 @@ pub(super) fn definition(
|
||||
.format_with(", ", |(name, ty), fmt| {
|
||||
fmt(&format_args!(
|
||||
"`{name}` = `{}`",
|
||||
ty.display_truncated(db, limit, edition)
|
||||
ty.display_truncated(db, limit, display_target)
|
||||
))
|
||||
})
|
||||
.to_string()
|
||||
@ -799,7 +820,7 @@ struct DropInfo {
|
||||
pub(super) fn literal(
|
||||
sema: &Semantics<'_, RootDatabase>,
|
||||
token: SyntaxToken,
|
||||
edition: Edition,
|
||||
display_target: DisplayTarget,
|
||||
) -> Option<Markup> {
|
||||
let lit = token.parent().and_then(ast::Literal::cast)?;
|
||||
let ty = if let Some(p) = lit.syntax().parent().and_then(ast::Pat::cast) {
|
||||
@ -847,7 +868,7 @@ pub(super) fn literal(
|
||||
_ => return None
|
||||
}
|
||||
};
|
||||
let ty = ty.display(sema.db, edition);
|
||||
let ty = ty.display(sema.db, display_target);
|
||||
|
||||
let mut s = format!("```rust\n{ty}\n```\n___\n\n");
|
||||
match value {
|
||||
@ -879,6 +900,7 @@ fn render_notable_trait(
|
||||
db: &RootDatabase,
|
||||
notable_traits: &[(Trait, Vec<(Option<Type>, Name)>)],
|
||||
edition: Edition,
|
||||
display_target: DisplayTarget,
|
||||
) -> Option<String> {
|
||||
let mut desc = String::new();
|
||||
let mut needs_impl_header = true;
|
||||
@ -898,7 +920,7 @@ fn render_notable_trait(
|
||||
f(&name.display(db, edition))?;
|
||||
f(&" = ")?;
|
||||
match ty {
|
||||
Some(ty) => f(&ty.display(db, edition)),
|
||||
Some(ty) => f(&ty.display(db, display_target)),
|
||||
None => f(&"?"),
|
||||
}
|
||||
})
|
||||
@ -914,8 +936,9 @@ fn type_info(
|
||||
config: &HoverConfig,
|
||||
ty: TypeInfo,
|
||||
edition: Edition,
|
||||
display_target: DisplayTarget,
|
||||
) -> Option<HoverResult> {
|
||||
if let Some(res) = closure_ty(sema, config, &ty, edition) {
|
||||
if let Some(res) = closure_ty(sema, config, &ty, edition, display_target) {
|
||||
return Some(res);
|
||||
};
|
||||
let db = sema.db;
|
||||
@ -951,7 +974,7 @@ fn type_info(
|
||||
f(&name.display(db, edition))?;
|
||||
f(&" = ")?;
|
||||
match ty {
|
||||
Some(ty) => f(&ty.display(db, edition)),
|
||||
Some(ty) => f(&ty.display(db, display_target)),
|
||||
None => f(&"?"),
|
||||
}
|
||||
})
|
||||
@ -965,8 +988,8 @@ fn type_info(
|
||||
desc
|
||||
};
|
||||
|
||||
let original = original.display(db, edition).to_string();
|
||||
let adjusted = adjusted_ty.display(db, edition).to_string();
|
||||
let original = original.display(db, display_target).to_string();
|
||||
let adjusted = adjusted_ty.display(db, display_target).to_string();
|
||||
let static_text_diff_len = "Coerced to: ".len() - "Type: ".len();
|
||||
format!(
|
||||
"```text\nType: {:>apad$}\nCoerced to: {:>opad$}\n{notable}```\n",
|
||||
@ -977,8 +1000,10 @@ fn type_info(
|
||||
)
|
||||
.into()
|
||||
} else {
|
||||
let mut desc = format!("```rust\n{}\n```", original.display(db, edition));
|
||||
if let Some(extra) = render_notable_trait(db, ¬able_traits(db, &original), edition) {
|
||||
let mut desc = format!("```rust\n{}\n```", original.display(db, display_target));
|
||||
if let Some(extra) =
|
||||
render_notable_trait(db, ¬able_traits(db, &original), edition, display_target)
|
||||
{
|
||||
desc.push_str("\n___\n");
|
||||
desc.push_str(&extra);
|
||||
};
|
||||
@ -995,6 +1020,7 @@ fn closure_ty(
|
||||
config: &HoverConfig,
|
||||
TypeInfo { original, adjusted }: &TypeInfo,
|
||||
edition: Edition,
|
||||
display_target: DisplayTarget,
|
||||
) -> Option<HoverResult> {
|
||||
let c = original.as_closure()?;
|
||||
let mut captures_rendered = c.captured_items(sema.db)
|
||||
@ -1027,12 +1053,14 @@ fn closure_ty(
|
||||
walk_and_push_ty(sema.db, adjusted_ty, &mut push_new_def);
|
||||
format!(
|
||||
"\nCoerced to: {}",
|
||||
adjusted_ty.display(sema.db, edition).with_closure_style(hir::ClosureStyle::ImplFn)
|
||||
adjusted_ty
|
||||
.display(sema.db, display_target)
|
||||
.with_closure_style(hir::ClosureStyle::ImplFn)
|
||||
)
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
let mut markup = format!("```rust\n{}\n```", c.display_with_impl(sema.db, edition));
|
||||
let mut markup = format!("```rust\n{}\n```", c.display_with_impl(sema.db, display_target));
|
||||
|
||||
if let Some(trait_) = c.fn_trait(sema.db).get_id(sema.db, original.krate(sema.db).into()) {
|
||||
push_new_def(hir::Trait::from(trait_).into())
|
||||
@ -1213,6 +1241,7 @@ fn keyword_hints(
|
||||
token: &SyntaxToken,
|
||||
parent: syntax::SyntaxNode,
|
||||
edition: Edition,
|
||||
display_target: DisplayTarget,
|
||||
) -> KeywordHint {
|
||||
match token.kind() {
|
||||
T![await] | T![loop] | T![match] | T![unsafe] | T![as] | T![try] | T![if] | T![else] => {
|
||||
@ -1230,7 +1259,8 @@ fn keyword_hints(
|
||||
walk_and_push_ty(sema.db, &ty.original, &mut push_new_def);
|
||||
|
||||
let ty = ty.adjusted();
|
||||
let description = format!("{}: {}", token.text(), ty.display(sema.db, edition));
|
||||
let description =
|
||||
format!("{}: {}", token.text(), ty.display(sema.db, display_target));
|
||||
|
||||
KeywordHint {
|
||||
description,
|
||||
|
@ -10947,3 +10947,42 @@ pub struct ManuallyDrop$0<T: ?Sized> {
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn projection_const() {
|
||||
// This uses two crates, which have *no* relation between them, to test another thing:
|
||||
// `render_const_scalar()` used to just use the last crate for the trait env, which will
|
||||
// fail in this scenario.
|
||||
check(
|
||||
r#"
|
||||
//- /foo.rs crate:foo
|
||||
pub trait PublicFlags {
|
||||
type Internal;
|
||||
}
|
||||
|
||||
pub struct NoteDialects(<NoteDialects as PublicFlags>::Internal);
|
||||
|
||||
impl NoteDialects {
|
||||
pub const CLAP$0: Self = Self(InternalBitFlags);
|
||||
}
|
||||
|
||||
pub struct InternalBitFlags;
|
||||
|
||||
impl PublicFlags for NoteDialects {
|
||||
type Internal = InternalBitFlags;
|
||||
}
|
||||
//- /bar.rs crate:bar
|
||||
"#,
|
||||
expect![[r#"
|
||||
*CLAP*
|
||||
|
||||
```rust
|
||||
foo::NoteDialects
|
||||
```
|
||||
|
||||
```rust
|
||||
pub const CLAP: Self = NoteDialects(InternalBitFlags)
|
||||
```
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
@ -5,14 +5,14 @@ use std::{
|
||||
|
||||
use either::Either;
|
||||
use hir::{
|
||||
sym, ClosureStyle, HasVisibility, HirDisplay, HirDisplayError, HirWrite, ModuleDef,
|
||||
ModuleDefId, Semantics,
|
||||
sym, ClosureStyle, DisplayTarget, HasVisibility, HirDisplay, HirDisplayError, HirWrite,
|
||||
ModuleDef, ModuleDefId, Semantics,
|
||||
};
|
||||
use ide_db::{famous_defs::FamousDefs, FileRange, RootDatabase};
|
||||
use ide_db::{text_edit::TextEdit, FxHashSet};
|
||||
use itertools::Itertools;
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use span::{Edition, EditionedFileId};
|
||||
use span::EditionedFileId;
|
||||
use stdx::never;
|
||||
use syntax::{
|
||||
ast::{self, AstNode, HasGenericParams},
|
||||
@ -207,7 +207,8 @@ fn hints(
|
||||
file_id: EditionedFileId,
|
||||
node: SyntaxNode,
|
||||
) {
|
||||
closing_brace::hints(hints, sema, config, file_id, node.clone());
|
||||
let display_target = sema.first_crate_or_default(file_id.file_id()).to_display_target(sema.db);
|
||||
closing_brace::hints(hints, sema, config, file_id, display_target, node.clone());
|
||||
if let Some(any_has_generic_args) = ast::AnyHasGenericArgs::cast(node.clone()) {
|
||||
generic_param::hints(hints, famous_defs, config, any_has_generic_args);
|
||||
}
|
||||
@ -215,8 +216,8 @@ fn hints(
|
||||
match_ast! {
|
||||
match node {
|
||||
ast::Expr(expr) => {
|
||||
chaining::hints(hints, famous_defs, config, file_id, &expr);
|
||||
adjustment::hints(hints, famous_defs, config, file_id, &expr);
|
||||
chaining::hints(hints, famous_defs, config, display_target, &expr);
|
||||
adjustment::hints(hints, famous_defs, config, display_target, &expr);
|
||||
match expr {
|
||||
ast::Expr::CallExpr(it) => param_name::hints(hints, famous_defs, config, file_id, ast::Expr::from(it)),
|
||||
ast::Expr::MethodCallExpr(it) => {
|
||||
@ -224,7 +225,7 @@ fn hints(
|
||||
}
|
||||
ast::Expr::ClosureExpr(it) => {
|
||||
closure_captures::hints(hints, famous_defs, config, file_id, it.clone());
|
||||
closure_ret::hints(hints, famous_defs, config, file_id, it)
|
||||
closure_ret::hints(hints, famous_defs, config, display_target, it)
|
||||
},
|
||||
ast::Expr::RangeExpr(it) => range_exclusive::hints(hints, famous_defs, config, file_id, it),
|
||||
_ => Some(()),
|
||||
@ -234,7 +235,7 @@ fn hints(
|
||||
binding_mode::hints(hints, famous_defs, config, file_id, &it);
|
||||
match it {
|
||||
ast::Pat::IdentPat(it) => {
|
||||
bind_pat::hints(hints, famous_defs, config, file_id, &it);
|
||||
bind_pat::hints(hints, famous_defs, config, display_target, &it);
|
||||
}
|
||||
ast::Pat::RangePat(it) => {
|
||||
range_exclusive::hints(hints, famous_defs, config, file_id, it);
|
||||
@ -704,7 +705,7 @@ fn label_of_ty(
|
||||
famous_defs @ FamousDefs(sema, _): &FamousDefs<'_, '_>,
|
||||
config: &InlayHintsConfig,
|
||||
ty: &hir::Type,
|
||||
edition: Edition,
|
||||
display_target: DisplayTarget,
|
||||
) -> Option<InlayHintLabel> {
|
||||
fn rec(
|
||||
sema: &Semantics<'_, RootDatabase>,
|
||||
@ -713,7 +714,7 @@ fn label_of_ty(
|
||||
ty: &hir::Type,
|
||||
label_builder: &mut InlayHintLabelBuilder<'_>,
|
||||
config: &InlayHintsConfig,
|
||||
edition: Edition,
|
||||
display_target: DisplayTarget,
|
||||
) -> Result<(), HirDisplayError> {
|
||||
let iter_item_type = hint_iterator(sema, famous_defs, ty);
|
||||
match iter_item_type {
|
||||
@ -744,12 +745,12 @@ fn label_of_ty(
|
||||
label_builder.write_str(LABEL_ITEM)?;
|
||||
label_builder.end_location_link();
|
||||
label_builder.write_str(LABEL_MIDDLE2)?;
|
||||
rec(sema, famous_defs, max_length, &ty, label_builder, config, edition)?;
|
||||
rec(sema, famous_defs, max_length, &ty, label_builder, config, display_target)?;
|
||||
label_builder.write_str(LABEL_END)?;
|
||||
Ok(())
|
||||
}
|
||||
None => ty
|
||||
.display_truncated(sema.db, max_length, edition)
|
||||
.display_truncated(sema.db, max_length, display_target)
|
||||
.with_closure_style(config.closure_style)
|
||||
.write_to(label_builder),
|
||||
}
|
||||
@ -762,7 +763,8 @@ fn label_of_ty(
|
||||
result: InlayHintLabel::default(),
|
||||
resolve: config.fields_to_resolve.resolve_label_location,
|
||||
};
|
||||
let _ = rec(sema, famous_defs, config.max_length, ty, &mut label_builder, config, edition);
|
||||
let _ =
|
||||
rec(sema, famous_defs, config.max_length, ty, &mut label_builder, config, display_target);
|
||||
let r = label_builder.finish();
|
||||
Some(r)
|
||||
}
|
||||
|
@ -7,12 +7,12 @@ use std::ops::Not;
|
||||
|
||||
use either::Either;
|
||||
use hir::{
|
||||
Adjust, Adjustment, AutoBorrow, HirDisplay, Mutability, OverloadedDeref, PointerCast, Safety,
|
||||
Adjust, Adjustment, AutoBorrow, DisplayTarget, HirDisplay, Mutability, OverloadedDeref,
|
||||
PointerCast, Safety,
|
||||
};
|
||||
use ide_db::famous_defs::FamousDefs;
|
||||
|
||||
use ide_db::text_edit::TextEditBuilder;
|
||||
use span::EditionedFileId;
|
||||
use syntax::ast::{self, prec::ExprPrecedence, AstNode};
|
||||
|
||||
use crate::{
|
||||
@ -24,7 +24,7 @@ pub(super) fn hints(
|
||||
acc: &mut Vec<InlayHint>,
|
||||
FamousDefs(sema, _): &FamousDefs<'_, '_>,
|
||||
config: &InlayHintsConfig,
|
||||
file_id: EditionedFileId,
|
||||
display_target: DisplayTarget,
|
||||
expr: &ast::Expr,
|
||||
) -> Option<()> {
|
||||
if config.adjustment_hints_hide_outside_unsafe && !sema.is_inside_unsafe(expr) {
|
||||
@ -163,8 +163,8 @@ pub(super) fn hints(
|
||||
tooltip: Some(config.lazy_tooltip(|| {
|
||||
InlayTooltip::Markdown(format!(
|
||||
"`{}` → `{}` ({coercion} coercion)",
|
||||
source.display(sema.db, file_id.edition()),
|
||||
target.display(sema.db, file_id.edition()),
|
||||
source.display(sema.db, display_target),
|
||||
target.display(sema.db, display_target),
|
||||
))
|
||||
})),
|
||||
};
|
||||
|
@ -3,11 +3,10 @@
|
||||
//! fn f(a: i32, b: i32) -> i32 { a + b }
|
||||
//! let _x /* i32 */= f(4, 4);
|
||||
//! ```
|
||||
use hir::Semantics;
|
||||
use hir::{DisplayTarget, Semantics};
|
||||
use ide_db::{famous_defs::FamousDefs, RootDatabase};
|
||||
|
||||
use itertools::Itertools;
|
||||
use span::EditionedFileId;
|
||||
use syntax::{
|
||||
ast::{self, AstNode, HasGenericArgs, HasName},
|
||||
match_ast,
|
||||
@ -22,7 +21,7 @@ pub(super) fn hints(
|
||||
acc: &mut Vec<InlayHint>,
|
||||
famous_defs @ FamousDefs(sema, _): &FamousDefs<'_, '_>,
|
||||
config: &InlayHintsConfig,
|
||||
file_id: EditionedFileId,
|
||||
display_target: DisplayTarget,
|
||||
pat: &ast::IdentPat,
|
||||
) -> Option<()> {
|
||||
if !config.type_hints {
|
||||
@ -70,7 +69,7 @@ pub(super) fn hints(
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut label = label_of_ty(famous_defs, config, &ty, file_id.edition())?;
|
||||
let mut label = label_of_ty(famous_defs, config, &ty, display_target)?;
|
||||
|
||||
if config.hide_named_constructor_hints
|
||||
&& is_named_constructor(sema, pat, &label.to_string()).is_some()
|
||||
|
@ -1,6 +1,6 @@
|
||||
//! Implementation of "chaining" inlay hints.
|
||||
use hir::DisplayTarget;
|
||||
use ide_db::famous_defs::FamousDefs;
|
||||
use span::EditionedFileId;
|
||||
use syntax::{
|
||||
ast::{self, AstNode},
|
||||
Direction, NodeOrToken, SyntaxKind, T,
|
||||
@ -14,7 +14,7 @@ pub(super) fn hints(
|
||||
acc: &mut Vec<InlayHint>,
|
||||
famous_defs @ FamousDefs(sema, _): &FamousDefs<'_, '_>,
|
||||
config: &InlayHintsConfig,
|
||||
file_id: EditionedFileId,
|
||||
display_target: DisplayTarget,
|
||||
expr: &ast::Expr,
|
||||
) -> Option<()> {
|
||||
if !config.chaining_hints {
|
||||
@ -58,7 +58,7 @@ pub(super) fn hints(
|
||||
}
|
||||
}
|
||||
}
|
||||
let label = label_of_ty(famous_defs, config, &ty, file_id.edition())?;
|
||||
let label = label_of_ty(famous_defs, config, &ty, display_target)?;
|
||||
acc.push(InlayHint {
|
||||
range: expr.syntax().text_range(),
|
||||
kind: InlayKind::Chaining,
|
||||
|
@ -3,7 +3,7 @@
|
||||
//! fn g() {
|
||||
//! } /* fn g */
|
||||
//! ```
|
||||
use hir::{HirDisplay, Semantics};
|
||||
use hir::{DisplayTarget, HirDisplay, Semantics};
|
||||
use ide_db::{FileRange, RootDatabase};
|
||||
use span::EditionedFileId;
|
||||
use syntax::{
|
||||
@ -21,6 +21,7 @@ pub(super) fn hints(
|
||||
sema: &Semantics<'_, RootDatabase>,
|
||||
config: &InlayHintsConfig,
|
||||
file_id: EditionedFileId,
|
||||
display_target: DisplayTarget,
|
||||
original_node: SyntaxNode,
|
||||
) -> Option<()> {
|
||||
let min_lines = config.closing_brace_hints_min_lines?;
|
||||
@ -43,9 +44,9 @@ pub(super) fn hints(
|
||||
Some(tr) => format!(
|
||||
"impl {} for {}",
|
||||
tr.name(sema.db).display(sema.db, file_id.edition()),
|
||||
ty.display_truncated(sema.db, config.max_length, file_id.edition(),
|
||||
ty.display_truncated(sema.db, config.max_length, display_target,
|
||||
)),
|
||||
None => format!("impl {}", ty.display_truncated(sema.db, config.max_length, file_id.edition())),
|
||||
None => format!("impl {}", ty.display_truncated(sema.db, config.max_length, display_target)),
|
||||
};
|
||||
(hint_text, None)
|
||||
},
|
||||
|
@ -1,8 +1,8 @@
|
||||
//! Implementation of "closure return type" inlay hints.
|
||||
//!
|
||||
//! Tests live in [`bind_pat`][super::bind_pat] module.
|
||||
use hir::DisplayTarget;
|
||||
use ide_db::famous_defs::FamousDefs;
|
||||
use span::EditionedFileId;
|
||||
use syntax::ast::{self, AstNode};
|
||||
|
||||
use crate::{
|
||||
@ -14,7 +14,7 @@ pub(super) fn hints(
|
||||
acc: &mut Vec<InlayHint>,
|
||||
famous_defs @ FamousDefs(sema, _): &FamousDefs<'_, '_>,
|
||||
config: &InlayHintsConfig,
|
||||
file_id: EditionedFileId,
|
||||
display_target: DisplayTarget,
|
||||
closure: ast::ClosureExpr,
|
||||
) -> Option<()> {
|
||||
if config.closure_return_type_hints == ClosureReturnTypeHints::Never {
|
||||
@ -43,7 +43,7 @@ pub(super) fn hints(
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut label = label_of_ty(famous_defs, config, &ty, file_id.edition())?;
|
||||
let mut label = label_of_ty(famous_defs, config, &ty, display_target)?;
|
||||
|
||||
if arrow.is_none() {
|
||||
label.prepend_str(" -> ");
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user