Auto merge of #138206 - amy-kwan:amy-kwan/reprc-struct-power-align-ignore-packed-align, r=workingjubilee [AIX] Ignore linting on repr(C) structs with repr(packed) or repr(align(n)) This PR updates the lint added in 9b40bd7 to ignore repr(C) structs that also have repr(packed) or repr(align(n)). As these representations can be modifiers on repr(C), it is assumed that users that add these should know what they are doing, and thus the the lint should not warn on the respective structs. For example, for the time being, using repr(packed) and manually padding a repr(C) struct can be done to correctly align struct members on AIX.
diff --git a/.editorconfig b/.editorconfig index 6bb743a..2d28ebd 100644 --- a/.editorconfig +++ b/.editorconfig
@@ -15,3 +15,6 @@ [*.{yml,yaml}] indent_size = 2 + +[COMMIT_EDITMSG] +max_line_length = unset
diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index bc770db..7a6b43a 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml
@@ -35,19 +35,48 @@ 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 @@ - 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 @@ - 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 @@ 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 @@ 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 @@ 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 @@ # 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 @@ - 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 @@ 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 @@ 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) }}'
diff --git a/Cargo.lock b/Cargo.lock index c57953b..1e1d68f 100644 --- a/Cargo.lock +++ b/Cargo.lock
@@ -191,9 +191,9 @@ [[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 @@ [[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 @@ [[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 @@ [[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 @@ "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 @@ [[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 @@ [[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 @@ [[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 @@ [[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 @@ [[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 @@ [[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",
diff --git a/Cargo.toml b/Cargo.toml index 1132acb..ce2d660 100644 --- a/Cargo.toml +++ b/Cargo.toml
@@ -85,12 +85,12 @@ 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 @@ 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"
diff --git a/crates/cfg/src/cfg_expr.rs b/crates/cfg/src/cfg_expr.rs index 84b91a5..0ec082d 100644 --- a/crates/cfg/src/cfg_expr.rs +++ b/crates/cfg/src/cfg_expr.rs
@@ -18,6 +18,25 @@ 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 {
diff --git a/crates/cfg/src/dnf.rs b/crates/cfg/src/dnf.rs index f3ebca0..424672a 100644 --- a/crates/cfg/src/dnf.rs +++ b/crates/cfg/src/dnf.rs
@@ -66,9 +66,9 @@ } } - 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 @@ }; // 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 {
diff --git a/crates/cfg/src/lib.rs b/crates/cfg/src/lib.rs index 6a6213a..08545b6 100644 --- a/crates/cfg/src/lib.rs +++ b/crates/cfg/src/lib.rs
@@ -148,16 +148,20 @@ } 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.
diff --git a/crates/hir-def/src/hir/format_args.rs b/crates/hir-def/src/hir/format_args.rs index 28c824f..24badc5 100644 --- a/crates/hir-def/src/hir/format_args.rs +++ b/crates/hir-def/src/hir/format_args.rs
@@ -137,7 +137,7 @@ #[derive(Clone, Debug, PartialEq, Eq)] pub enum FormatCount { /// `{:5}` or `{:.5}` - Literal(usize), + Literal(u16), /// `{:.*}`, `{:.5$}`, or `{:a$}`, etc. Argument(FormatArgPosition), }
diff --git a/crates/hir-def/src/hir/type_ref.rs b/crates/hir-def/src/hir/type_ref.rs index 6d4d519..6de4026 100644 --- a/crates/hir-def/src/hir/type_ref.rs +++ b/crates/hir-def/src/hir/type_ref.rs
@@ -260,7 +260,7 @@ } #[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 {
diff --git a/crates/hir-def/src/macro_expansion_tests/mbe/tt_conversion.rs b/crates/hir-def/src/macro_expansion_tests/mbe/tt_conversion.rs index c365a60..138db1b 100644 --- a/crates/hir-def/src/macro_expansion_tests/mbe/tt_conversion.rs +++ b/crates/hir-def/src/macro_expansion_tests/mbe/tt_conversion.rs
@@ -93,15 +93,15 @@ 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 */ } "#]], ) }
diff --git a/crates/hir-def/src/path.rs b/crates/hir-def/src/path.rs index e6c2504..713e738 100644 --- a/crates/hir-def/src/path.rs +++ b/crates/hir-def/src/path.rs
@@ -57,7 +57,7 @@ /// 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 @@ 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, }
diff --git a/crates/hir-def/src/resolver.rs b/crates/hir-def/src/resolver.rs index e5774b4..a2e6e4c 100644 --- a/crates/hir-def/src/resolver.rs +++ b/crates/hir-def/src/resolver.rs
@@ -3,10 +3,11 @@ 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 @@ } 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 @@ } } 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 @@ }) } + /// 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 @@ } } +#[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 {
diff --git a/crates/hir-expand/src/fixup.rs b/crates/hir-expand/src/fixup.rs index eb43017..2889453 100644 --- a/crates/hir-expand/src/fixup.rs +++ b/crates/hir-expand/src/fixup.rs
@@ -148,7 +148,6 @@ } 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 @@ } 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 @@ 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 @@ 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 @@ 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 @@ } } }, - 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 @@ } } 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 @@ 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 @@ } "#, expect![[r#" -fn foo () {for _ in __ra_fixup { }} +fn foo () {for _ in __ra_fixup {}} "#]], ) } @@ -624,7 +608,7 @@ } "#, expect![[r#" -fn foo () {for bar in qux { }} +fn foo () {for bar in qux {}} "#]], ) } @@ -655,7 +639,7 @@ } "#, expect![[r#" -fn foo () {match __ra_fixup { }} +fn foo () {match __ra_fixup {}} "#]], ) } @@ -687,7 +671,7 @@ } "#, expect![[r#" -fn foo () {match __ra_fixup { }} +fn foo () {match __ra_fixup {}} "#]], ) } @@ -802,7 +786,7 @@ } "#, expect![[r#" -fn foo () {if a { }} +fn foo () {if a {}} "#]], ) } @@ -816,7 +800,7 @@ } "#, expect![[r#" -fn foo () {if __ra_fixup { }} +fn foo () {if __ra_fixup {}} "#]], ) } @@ -830,7 +814,7 @@ } "#, expect![[r#" -fn foo () {if __ra_fixup {} { }} +fn foo () {if __ra_fixup {} {}} "#]], ) } @@ -844,7 +828,7 @@ } "#, expect![[r#" -fn foo () {while __ra_fixup { }} +fn foo () {while __ra_fixup {}} "#]], ) } @@ -858,7 +842,7 @@ } "#, expect![[r#" -fn foo () {while foo { }} +fn foo () {while foo {}} "#]], ) } @@ -885,7 +869,7 @@ } "#, expect![[r#" -fn foo () {loop { }} +fn foo () {loop {}} "#]], ) } @@ -941,7 +925,7 @@ } "#, expect![[r#" -fn foo () { foo ( a ) } +fn foo () {foo (a)} "#]], ); check( @@ -951,7 +935,7 @@ } "#, expect![[r#" -fn foo () { bar . foo ( a ) } +fn foo () {bar . foo (a)} "#]], ); }
diff --git a/crates/hir-expand/src/lib.rs b/crates/hir-expand/src/lib.rs index 41603e3..c1d808c 100644 --- a/crates/hir-expand/src/lib.rs +++ b/crates/hir-expand/src/lib.rs
@@ -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;
diff --git a/crates/hir-expand/src/mod_path.rs b/crates/hir-expand/src/mod_path.rs index f0cf7eb..75b5861 100644 --- a/crates/hir-expand/src/mod_path.rs +++ b/crates/hir-expand/src/mod_path.rs
@@ -388,7 +388,7 @@ ($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 @@ 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()),)* ]) }); }
diff --git a/crates/hir-ty/Cargo.toml b/crates/hir-ty/Cargo.toml index 27849f3..1d12bee 100644 --- a/crates/hir-ty/Cargo.toml +++ b/crates/hir-ty/Cargo.toml
@@ -35,7 +35,6 @@ 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
diff --git a/crates/hir-ty/src/chalk_db.rs b/crates/hir-ty/src/chalk_db.rs index c8ff6cba..65fb342 100644 --- a/crates/hir-ty/src/chalk_db.rs +++ b/crates/hir-ty/src/chalk_db.rs
@@ -145,19 +145,19 @@ let mut result = vec![]; if fps.is_empty() { debug!("Unrestricted search for {:?} impls...", trait_); - self.for_trait_impls(trait_, self_ty_fp, |impls| { + let _ = self.for_trait_impls(trait_, self_ty_fp, |impls| { result.extend(impls.for_trait(trait_).map(id_to_chalk)); ControlFlow::Continue(()) - }) + }); } else { - self.for_trait_impls(trait_, self_ty_fp, |impls| { + let _ = self.for_trait_impls(trait_, self_ty_fp, |impls| { result.extend( fps.iter().flat_map(move |fp| { impls.for_trait_and_self_ty(trait_, *fp).map(id_to_chalk) }), ); ControlFlow::Continue(()) - }) + }); }; debug!("impls_for_trait returned {} impls", result.len()); @@ -708,6 +708,9 @@ 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 @@ LangItem::Tuple => WellKnownTrait::Tuple, LangItem::PointeeTrait => WellKnownTrait::Pointee, LangItem::FnPtrTrait => WellKnownTrait::FnPtr, + LangItem::Future => WellKnownTrait::Future, _ => return None, }) } @@ -730,6 +734,9 @@ 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 @@ WellKnownTrait::Unsize => LangItem::Unsize, WellKnownTrait::Pointee => LangItem::PointeeTrait, WellKnownTrait::FnPtr => LangItem::FnPtrTrait, + WellKnownTrait::Future => LangItem::Future, } }
diff --git a/crates/hir-ty/src/consteval.rs b/crates/hir-ty/src/consteval.rs index 7839589..fb60456 100644 --- a/crates/hir-ty/src/consteval.rs +++ b/crates/hir-ty/src/consteval.rs
@@ -15,9 +15,10 @@ 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 @@ 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) + } } } }
diff --git a/crates/hir-ty/src/consteval/tests.rs b/crates/hir-ty/src/consteval/tests.rs index 2d7d4ca..26a3b70 100644 --- a/crates/hir-ty/src/consteval/tests.rs +++ b/crates/hir-ty/src/consteval/tests.rs
@@ -10,8 +10,8 @@ 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 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 @@ 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() }; }
diff --git a/crates/hir-ty/src/diagnostics.rs b/crates/hir-ty/src/diagnostics.rs index 30c02a2..845d333 100644 --- a/crates/hir-ty/src/diagnostics.rs +++ b/crates/hir-ty/src/diagnostics.rs
@@ -9,5 +9,8 @@ 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, + }, };
diff --git a/crates/hir-ty/src/diagnostics/expr.rs b/crates/hir-ty/src/diagnostics/expr.rs index 59aaf85..cc6f4d9 100644 --- a/crates/hir-ty/src/diagnostics/expr.rs +++ b/crates/hir-ty/src/diagnostics/expr.rs
@@ -16,7 +16,6 @@ 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 @@ self, pat_analysis::{self, DeconstructedPat, MatchCheckCtx, WitnessPat}, }, - display::HirDisplay, + display::{DisplayTarget, HirDisplay}, Adjust, InferenceResult, Interner, Ty, TyExt, TyKind, }; @@ -633,24 +632,24 @@ 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)),
diff --git a/crates/hir-ty/src/diagnostics/unsafe_check.rs b/crates/hir-ty/src/diagnostics/unsafe_check.rs index ac849b0..d2b9088 100644 --- a/crates/hir-ty/src/diagnostics/unsafe_check.rs +++ b/crates/hir-ty/src/diagnostics/unsafe_check.rs
@@ -95,7 +95,26 @@ 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 @@ 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 @@ } } } + 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 {
diff --git a/crates/hir-ty/src/display.rs b/crates/hir-ty/src/display.rs index 49f0618..95ce363 100644 --- a/crates/hir-ty/src/display.rs +++ b/crates/hir-ty/src/display.rs
@@ -4,7 +4,7 @@ use std::{ fmt::{self, Debug}, - mem::{self, size_of}, + mem, }; use base_db::CrateId; @@ -45,6 +45,7 @@ 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 @@ 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 @@ 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 @@ 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 @@ limited_size, omit_verbose_types, display_target, + display_kind, closure_style, show_container_bounds, } @@ -191,7 +195,7 @@ fn display<'a>( &'a self, db: &'a dyn HirDatabase, - edition: Edition, + display_target: DisplayTarget, ) -> HirDisplayWrapper<'a, Self> where Self: Sized, @@ -203,7 +207,8 @@ 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 @@ &'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 @@ 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 @@ &'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 @@ 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 @@ 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 @@ } /// 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 @@ 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 @@ } } +#[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 @@ 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 @@ 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 @@ // 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 @@ 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 @@ 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 @@ } 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 @@ }; 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 @@ 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 @@ 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 @@ } } 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 @@ } 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 @@ }; } 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 @@ } 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 @@ 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 @@ 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"),
diff --git a/crates/hir-ty/src/dyn_compatibility.rs b/crates/hir-ty/src/dyn_compatibility.rs index 6a01579..e042c35 100644 --- a/crates/hir-ty/src/dyn_compatibility.rs +++ b/crates/hir-ty/src/dyn_compatibility.rs
@@ -116,7 +116,7 @@ trait_: TraitId, ) -> Option<DynCompatibilityViolation> { let mut res = None; - dyn_compatibility_of_trait_with_callback(db, trait_, &mut |osv| { + let _ = dyn_compatibility_of_trait_with_callback(db, trait_, &mut |osv| { res = Some(osv); ControlFlow::Break(()) }); @@ -597,7 +597,7 @@ let ret = sig.skip_binders().ret(); let mut visitor = OpaqueTypeCollector(FxHashSet::default()); - ret.visit_with(visitor.as_dyn(), DebruijnIndex::INNERMOST); + let _ = ret.visit_with(visitor.as_dyn(), DebruijnIndex::INNERMOST); // Since we haven't implemented RPITIT in proper way like rustc yet, // just check whether `ret` contains RPIT for now
diff --git a/crates/hir-ty/src/dyn_compatibility/tests.rs b/crates/hir-ty/src/dyn_compatibility/tests.rs index 3060b61..5085132 100644 --- a/crates/hir-ty/src/dyn_compatibility/tests.rs +++ b/crates/hir-ty/src/dyn_compatibility/tests.rs
@@ -53,7 +53,7 @@ continue; }; let mut osvs = FxHashSet::default(); - dyn_compatibility_with_callback(&db, trait_id, &mut |osv| { + let _ = dyn_compatibility_with_callback(&db, trait_id, &mut |osv| { osvs.insert(match osv { DynCompatibilityViolation::SizedSelf => SizedSelf, DynCompatibilityViolation::SelfReferential => SelfReferential,
diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs index 556091c..3e0ce7f 100644 --- a/crates/hir-ty/src/infer.rs +++ b/crates/hir-ty/src/infer.rs
@@ -1143,7 +1143,7 @@ non_assocs: FxHashMap::default(), }; for ty in tait_candidates { - ty.visit_with(collector.as_dyn(), DebruijnIndex::INNERMOST); + let _ = ty.visit_with(collector.as_dyn(), DebruijnIndex::INNERMOST); } // Non-assoc TAITs can be define-used everywhere as long as they are
diff --git a/crates/hir-ty/src/layout.rs b/crates/hir-ty/src/layout.rs index 7af31da..bbd419d 100644 --- a/crates/hir-ty/src/layout.rs +++ b/crates/hir-ty/src/layout.rs
@@ -6,16 +6,14 @@ use chalk_ir::{AdtId, FloatTy, IntTy, TyKind, UintTy}; use hir_def::{ layout::{ - BackendRepr, FieldsShape, Float, Integer, LayoutCalculator, LayoutCalculatorError, - LayoutData, Primitive, ReprOptions, Scalar, Size, StructKind, TargetDataLayout, - WrappingRange, + Float, Integer, LayoutCalculator, LayoutCalculatorError, LayoutData, Primitive, + ReprOptions, Scalar, StructKind, TargetDataLayout, WrappingRange, }, LocalFieldId, StructId, }; use la_arena::{Idx, RawIdx}; use rustc_abi::AddressSpace; -use rustc_hashes::Hash64; -use rustc_index::{IndexSlice, IndexVec}; +use rustc_index::IndexVec; use triomphe::Arc; @@ -23,7 +21,6 @@ consteval::try_const_usize, db::{HirDatabase, InternedClosure}, infer::normalize, - layout::adt::struct_variant_idx, utils::ClosureSubst, Interner, ProjectionTy, Substitution, TraitEnvironment, Ty, }; @@ -125,10 +122,10 @@ } } -// FIXME: move this to the `rustc_abi`. fn layout_of_simd_ty( db: &dyn HirDatabase, id: StructId, + repr_packed: bool, subst: &Substitution, env: Arc<TraitEnvironment>, dl: &TargetDataLayout, @@ -149,33 +146,10 @@ }; let e_len = try_const_usize(db, &e_len).ok_or(LayoutError::HasErrorConst)? as u64; - - // Compute the ABI of the element type: let e_ly = db.layout_of_ty(e_ty, env)?; - let BackendRepr::Scalar(e_abi) = e_ly.backend_repr else { - return Err(LayoutError::Unknown); - }; - // Compute the size and alignment of the vector: - let size = e_ly - .size - .checked_mul(e_len, dl) - .ok_or(LayoutError::BadCalc(LayoutCalculatorError::SizeOverflow))?; - let align = dl.llvmlike_vector_align(size); - let size = size.align_to(align.abi); - - Ok(Arc::new(Layout { - variants: Variants::Single { index: struct_variant_idx() }, - fields: FieldsShape::Arbitrary { offsets: [Size::ZERO].into(), memory_index: [0].into() }, - backend_repr: BackendRepr::SimdVector { element: e_abi, count: e_len }, - largest_niche: e_ly.largest_niche, - uninhabited: false, - size, - align, - max_repr_align: None, - unadjusted_abi_align: align.abi, - randomization_seed: Hash64::ZERO, - })) + let cx = LayoutCx::new(dl); + Ok(Arc::new(cx.calc.simd_type(e_ly, e_len, repr_packed)?)) } pub fn layout_of_ty_query( @@ -190,13 +164,14 @@ let dl = &*target; let cx = LayoutCx::new(dl); let ty = normalize(db, trait_env.clone(), ty); - let result = match ty.kind(Interner) { + let kind = ty.kind(Interner); + let result = match kind { TyKind::Adt(AdtId(def), subst) => { if let hir_def::AdtId::StructId(s) = def { let data = db.struct_data(*s); let repr = data.repr.unwrap_or_default(); if repr.simd() { - return layout_of_simd_ty(db, *s, subst, trait_env, &target); + return layout_of_simd_ty(db, *s, repr.packed(), subst, trait_env, &target); } }; return db.layout_of_adt(*def, subst.clone(), trait_env); @@ -216,42 +191,51 @@ valid_range: WrappingRange { start: 0, end: 0x10FFFF }, }, ), - chalk_ir::Scalar::Int(i) => scalar( + 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) => scalar( + 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) => scalar( + 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) => { @@ -268,56 +252,16 @@ TyKind::Array(element, count) => { let count = try_const_usize(db, count).ok_or(LayoutError::HasErrorConst)? as u64; let element = db.layout_of_ty(element.clone(), trait_env)?; - let size = element - .size - .checked_mul(count, dl) - .ok_or(LayoutError::BadCalc(LayoutCalculatorError::SizeOverflow))?; - - let backend_repr = BackendRepr::Memory { sized: true }; - - let largest_niche = if count != 0 { element.largest_niche } else { None }; - let uninhabited = if count != 0 { element.uninhabited } else { false }; - - Layout { - variants: Variants::Single { index: struct_variant_idx() }, - fields: FieldsShape::Array { stride: element.size, count }, - backend_repr, - largest_niche, - uninhabited, - align: element.align, - size, - max_repr_align: None, - unadjusted_abi_align: element.align.abi, - randomization_seed: Hash64::ZERO, - } + cx.calc.array_like::<_, _, ()>(&element, Some(count))? } TyKind::Slice(element) => { let element = db.layout_of_ty(element.clone(), trait_env)?; - Layout { - variants: Variants::Single { index: struct_variant_idx() }, - fields: FieldsShape::Array { stride: element.size, count: 0 }, - backend_repr: BackendRepr::Memory { sized: false }, - largest_niche: None, - uninhabited: false, - align: element.align, - size: Size::ZERO, - max_repr_align: None, - unadjusted_abi_align: element.align.abi, - randomization_seed: Hash64::ZERO, - } + cx.calc.array_like::<_, _, ()>(&element, None)? } - TyKind::Str => Layout { - variants: Variants::Single { index: struct_variant_idx() }, - fields: FieldsShape::Array { stride: Size::from_bytes(1), count: 0 }, - backend_repr: BackendRepr::Memory { sized: false }, - largest_niche: None, - uninhabited: false, - align: dl.i8_align, - size: Size::ZERO, - max_repr_align: None, - unadjusted_abi_align: dl.i8_align.abi, - randomization_seed: Hash64::ZERO, - }, + TyKind::Str => { + let element = scalar_unit(dl, Primitive::Int(Integer::I8, false)); + cx.calc.array_like::<_, _, ()>(&Layout::scalar(dl, element), None)? + } // Potentially-wide pointers. TyKind::Ref(_, _, pointee) | TyKind::Raw(_, pointee) => { let mut data_ptr = scalar_unit(dl, Primitive::Pointer(AddressSpace::DATA)); @@ -355,17 +299,12 @@ }; // Effectively a (ptr, meta) tuple. - cx.calc.scalar_pair(data_ptr, metadata) + LayoutData::scalar_pair(dl, data_ptr, metadata) } - TyKind::FnDef(_, _) => layout_of_unit(&cx)?, - TyKind::Never => cx.calc.layout_of_never_type(), - TyKind::Dyn(_) | TyKind::Foreign(_) => { - let mut unit = layout_of_unit(&cx)?; - match &mut unit.backend_repr { - BackendRepr::Memory { sized } => *sized = false, - _ => return Err(LayoutError::Unknown), - } - unit + TyKind::Never => LayoutData::never_type(dl), + TyKind::FnDef(..) | TyKind::Dyn(_) | TyKind::Foreign(_) => { + let sized = matches!(kind, TyKind::FnDef(..)); + LayoutData::unit(dl, sized) } TyKind::Function(_) => { let mut ptr = scalar_unit(dl, Primitive::Pointer(dl.instruction_address_space)); @@ -410,6 +349,9 @@ 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(), @@ -434,16 +376,6 @@ Err(LayoutError::RecursiveTypeWithoutIndirection) } -fn layout_of_unit(cx: &LayoutCx<'_>) -> Result<Layout, LayoutError> { - cx.calc - .univariant::<RustcFieldIdx, RustcEnumVariantIdx, &&Layout>( - IndexSlice::empty(), - &ReprOptions::default(), - StructKind::AlwaysSized, - ) - .map_err(Into::into) -} - fn struct_tail_erasing_lifetimes(db: &dyn HirDatabase, pointee: Ty) -> Ty { match pointee.kind(Interner) { TyKind::Adt(AdtId(hir_def::AdtId::StructId(i)), subst) => { @@ -474,9 +406,5 @@ Scalar::Initialized { value, valid_range: WrappingRange::full(value.size(dl)) } } -fn scalar(dl: &TargetDataLayout, value: Primitive) -> Layout { - Layout::scalar(dl, scalar_unit(dl, value)) -} - #[cfg(test)] mod tests;
diff --git a/crates/hir-ty/src/layout/adt.rs b/crates/hir-ty/src/layout/adt.rs index 0ba765b..eb4729f 100644 --- a/crates/hir-ty/src/layout/adt.rs +++ b/crates/hir-ty/src/layout/adt.rs
@@ -16,16 +16,12 @@ use crate::{ db::HirDatabase, lang_items::is_unsafe_cell, - layout::{field_ty, Layout, LayoutError, RustcEnumVariantIdx}, + layout::{field_ty, Layout, LayoutError}, Substitution, TraitEnvironment, }; use super::LayoutCx; -pub(crate) fn struct_variant_idx() -> RustcEnumVariantIdx { - RustcEnumVariantIdx(0) -} - pub fn layout_of_adt_query( db: &dyn HirDatabase, def: AdtId,
diff --git a/crates/hir-ty/src/layout/tests.rs b/crates/hir-ty/src/layout/tests.rs index 4d38966..8b74b73 100644 --- a/crates/hir-ty/src/layout/tests.rs +++ b/crates/hir-ty/src/layout/tests.rs
@@ -342,7 +342,7 @@ check_size_and_align( r#" #[repr(simd)] - struct SimdType(i64, i64); + struct SimdType([i64; 2]); struct Goal(SimdType); "#, "",
diff --git a/crates/hir-ty/src/lib.rs b/crates/hir-ty/src/lib.rs index 707c437..cc02b71 100644 --- a/crates/hir-ty/src/lib.rs +++ b/crates/hir-ty/src/lib.rs
@@ -12,9 +12,6 @@ #[cfg(feature = "in-rust-tree")] extern crate rustc_abi; -#[cfg(feature = "in-rust-tree")] -extern crate rustc_hashes; - #[cfg(not(feature = "in-rust-tree"))] extern crate ra_ap_rustc_abi as rustc_abi; @@ -24,9 +21,6 @@ #[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; @@ -76,13 +70,15 @@ 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, }; @@ -1037,14 +1033,14 @@ T: ?Sized + TypeVisitable<Interner>, { let mut collector = PlaceholderCollector { db, placeholders: FxHashSet::default() }; - value.visit_with(&mut collector, DebruijnIndex::INNERMOST); + let _ = value.visit_with(&mut collector, DebruijnIndex::INNERMOST); collector.placeholders.into_iter().collect() } 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 { @@ -1055,7 +1051,7 @@ _ => (), } } - 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)]
diff --git a/crates/hir-ty/src/method_resolution.rs b/crates/hir-ty/src/method_resolution.rs index db94351..c722800 100644 --- a/crates/hir-ty/src/method_resolution.rs +++ b/crates/hir-ty/src/method_resolution.rs
@@ -596,7 +596,7 @@ mut callback: impl FnMut(ReceiverAdjustments, AssocItemId, bool) -> Option<T>, ) -> Option<T> { let mut slot = None; - iterate_method_candidates_dyn( + let _ = iterate_method_candidates_dyn( ty, db, env,
diff --git a/crates/hir-ty/src/mir.rs b/crates/hir-ty/src/mir.rs index 41304bb..56c431e 100644 --- a/crates/hir-ty/src/mir.rs +++ b/crates/hir-ty/src/mir.rs
@@ -5,7 +5,7 @@ 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 @@ _ => { 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) }
diff --git a/crates/hir-ty/src/mir/borrowck.rs b/crates/hir-ty/src/mir/borrowck.rs index 9c86d3b..fbcca38 100644 --- a/crates/hir-ty/src/mir/borrowck.rs +++ b/crates/hir-ty/src/mir/borrowck.rs
@@ -13,6 +13,7 @@ use crate::{ db::{HirDatabase, InternedClosure}, + display::DisplayTarget, mir::Operand, utils::ClosureSubst, ClosureId, Interner, Substitution, Ty, TyExt, TypeFlags, @@ -422,7 +423,10 @@ 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; };
diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs index 6b20522..74a34e2 100644 --- a/crates/hir-ty/src/mir/eval.rs +++ b/crates/hir-ty/src/mir/eval.rs
@@ -24,7 +24,7 @@ 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 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 @@ } } - 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ "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 @@ 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())?;
diff --git a/crates/hir-ty/src/mir/eval/shim.rs b/crates/hir-ty/src/mir/eval/shim.rs index 7d3376f..f61ecab 100644 --- a/crates/hir-ty/src/mir/eval/shim.rs +++ b/crates/hir-ty/src/mir/eval/shim.rs
@@ -14,6 +14,7 @@ use stdx::never; use crate::{ + display::DisplayTarget, error_lifetime, mir::eval::{ pad16, Address, AdtId, Arc, BuiltinType, Evaluator, FunctionId, HasModule, HirDisplay, @@ -835,8 +836,7 @@ // 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();
diff --git a/crates/hir-ty/src/mir/eval/tests.rs b/crates/hir-ty/src/mir/eval/tests.rs index 2b5486f..084c391 100644 --- a/crates/hir-ty/src/mir/eval/tests.rs +++ b/crates/hir-ty/src/mir/eval/tests.rs
@@ -3,6 +3,7 @@ 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 @@ 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)) => {
diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs index f886966..520717e 100644 --- a/crates/hir-ty/src/mir/lower.rs +++ b/crates/hir-ty/src/mir/lower.rs
@@ -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 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ MirLowerError::unresolved_path( self.db, p, - self.edition(), + DisplayTarget::from_crate(self.db, self.krate()), &self.body.types, ) })?; @@ -838,7 +841,7 @@ 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 @@ 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 @@ .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 @@ } 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(
diff --git a/crates/hir-ty/src/mir/lower/pattern_matching.rs b/crates/hir-ty/src/mir/lower/pattern_matching.rs index 289175f..783f92b 100644 --- a/crates/hir-ty/src/mir/lower/pattern_matching.rs +++ b/crates/hir-ty/src/mir/lower/pattern_matching.rs
@@ -350,7 +350,12 @@ )?, 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
diff --git a/crates/hir-ty/src/mir/pretty.rs b/crates/hir-ty/src/mir/pretty.rs index 2a26101..7d7d410 100644 --- a/crates/hir-ty/src/mir/pretty.rs +++ b/crates/hir-ty/src/mir/pretty.rs
@@ -9,11 +9,10 @@ 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 @@ } 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 @@ 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 @@ "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 @@ // 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 @@ result: String, indent: String, local_to_binding: ArenaMap<LocalId, BindingId>, + display_target: DisplayTarget, } impl Write for MirPrettyCtx<'_> { @@ -182,7 +186,12 @@ 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 @@ indent: String::new(), local_to_binding, hir_body, + display_target, } } @@ -208,7 +218,7 @@ 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 @@ 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 @@ 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 @@ 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 @@ } 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 @@ 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 @@ } 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) } }
diff --git a/crates/hir-ty/src/tests.rs b/crates/hir-ty/src/tests.rs index f5a4d4f..81e38be 100644 --- a/crates/hir-ty/src/tests.rs +++ b/crates/hir-ty/src/tests.rs
@@ -15,7 +15,7 @@ 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 crate::{ db::HirDatabase, - display::HirDisplay, + display::{DisplayTarget, HirDisplay}, infer::{Adjustment, TypeMismatch}, test_db::TestDB, InferenceResult, Ty, @@ -124,7 +124,7 @@ } 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 @@ }; 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ macro_prefix, range, ellipsize(text, 15), - ty.display_test(&db) + ty.display_test(&db, display_target) ); } if include_mismatches { @@ -377,8 +381,8 @@ "{}{:?}: 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 @@ 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 @@ } 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());
diff --git a/crates/hir-ty/src/tests/closure_captures.rs b/crates/hir-ty/src/tests/closure_captures.rs index 34d299e..6f7bfc4 100644 --- a/crates/hir-ty/src/tests/closure_captures.rs +++ b/crates/hir-ty/src/tests/closure_captures.rs
@@ -8,7 +8,7 @@ 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 @@ .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()
diff --git a/crates/hir-ty/src/tests/simple.rs b/crates/hir-ty/src/tests/simple.rs index 50a1ecd..4c5cca2 100644 --- a/crates/hir-ty/src/tests/simple.rs +++ b/crates/hir-ty/src/tests/simple.rs
@@ -3861,3 +3861,42 @@ "#]], ); } + +#[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 + "#]], + ); +}
diff --git a/crates/hir/src/attrs.rs b/crates/hir/src/attrs.rs index 4351a34..4e45b5a 100644 --- a/crates/hir/src/attrs.rs +++ b/crates/hir/src/attrs.rs
@@ -260,7 +260,7 @@ // attributes here. Use path resolution directly instead. // // FIXME: resolve type aliases (which are not yielded by iterate_path_candidates) - method_resolution::iterate_path_candidates( + let _ = method_resolution::iterate_path_candidates( &canonical, db, environment,
diff --git a/crates/hir/src/has_source.rs b/crates/hir/src/has_source.rs index a34b498..372c725 100644 --- a/crates/hir/src/has_source.rs +++ b/crates/hir/src/has_source.rs
@@ -15,7 +15,7 @@ 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 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>> {
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 55448d4..29f4584 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs
@@ -39,7 +39,7 @@ }; 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 @@ }, 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 @@ 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 @@ 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 @@ 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 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 @@ } } +// FIXME: Rename to `EnumVariant` #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct Variant { pub(crate) id: EnumVariantId, @@ -1863,7 +1892,7 @@ 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 @@ 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 @@ 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 @@ } 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> { @@ -2879,7 +2911,7 @@ db: &dyn HirDatabase, ) -> Option<Vec<DynCompatibilityViolation>> { let mut violations = vec![]; - hir_ty::dyn_compatibility::dyn_compatibility_with_callback(db, self.id, &mut |violation| { + let _ = hir_ty::dyn_compatibility::dyn_compatibility_with_callback(db, self.id, &mut |violation| { violations.push(violation); ControlFlow::Continue(()) }); @@ -4195,9 +4227,13 @@ 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 @@ 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 @@ 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 @@ .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 @@ 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 @@ }) .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>( @@ -5461,7 +5497,7 @@ .generic_def() .map_or_else(|| TraitEnvironment::empty(krate.id), |d| db.trait_environment(d)); - method_resolution::iterate_method_candidates_dyn( + let _ = method_resolution::iterate_method_candidates_dyn( &canonical, db, environment, @@ -5548,7 +5584,7 @@ .generic_def() .map_or_else(|| TraitEnvironment::empty(krate.id), |d| db.trait_environment(d)); - method_resolution::iterate_path_candidates( + let _ = method_resolution::iterate_path_candidates( &canonical, db, environment,
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index bb67fa6..5e2eebc 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs
@@ -12,7 +12,8 @@ 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 @@ 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 @@ 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 @@ 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 @@ ) } + /// 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 @@ .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 @@ 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 @@ 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 @@ &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)); + } +}
diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs index 9019863..d1245f5 100644 --- a/crates/hir/src/source_analyzer.rs +++ b/crates/hir/src/source_analyzer.rs
@@ -35,7 +35,7 @@ }; 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 @@ 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 }) };
diff --git a/crates/hir/src/symbols.rs b/crates/hir/src/symbols.rs index 2ebd88e..81eb6a7 100644 --- a/crates/hir/src/symbols.rs +++ b/crates/hir/src/symbols.rs
@@ -13,11 +13,10 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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| {
diff --git a/crates/hir/src/term_search/expr.rs b/crates/hir/src/term_search/expr.rs index d2070f0..0d672dc 100644 --- a/crates/hir/src/term_search/expr.rs +++ b/crates/hir/src/term_search/expr.rs
@@ -4,7 +4,7 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ // 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(), } } };
diff --git a/crates/ide-assists/src/handlers/explicit_enum_discriminant.rs b/crates/ide-assists/src/handlers/add_explicit_enum_discriminant.rs similarity index 87% rename from crates/ide-assists/src/handlers/explicit_enum_discriminant.rs rename to crates/ide-assists/src/handlers/add_explicit_enum_discriminant.rs index fafc344..1a5de9c 100644 --- a/crates/ide-assists/src/handlers/explicit_enum_discriminant.rs +++ b/crates/ide-assists/src/handlers/add_explicit_enum_discriminant.rs
@@ -8,7 +8,7 @@ use crate::{AssistContext, Assists}; -// Assist: explicit_enum_discriminant +// Assist: add_explicit_enum_discriminant // // Adds explicit discriminant to all enum variants. // @@ -29,7 +29,10 @@ // 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 @@ } 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 @@ 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 @@ #[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 @@ #[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 @@ #[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 @@ #[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 @@ #[test] fn empty_enum_not_applicable() { check_assist_not_applicable( - explicit_enum_discriminant, + add_explicit_enum_discriminant, r#" enum TheEnum$0 {} "#,
diff --git a/crates/ide-assists/src/handlers/apply_demorgan.rs b/crates/ide-assists/src/handlers/apply_demorgan.rs index 77562c5..67bf8ee 100644 --- a/crates/ide-assists/src/handlers/apply_demorgan.rs +++ b/crates/ide-assists/src/handlers/apply_demorgan.rs
@@ -128,7 +128,9 @@ 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 @@ #[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); }", ) }
diff --git a/crates/ide-assists/src/handlers/bool_to_enum.rs b/crates/ide-assists/src/handlers/convert_bool_to_enum.rs similarity index 95% rename from crates/ide-assists/src/handlers/bool_to_enum.rs rename to crates/ide-assists/src/handlers/convert_bool_to_enum.rs index cbd3979..7716e99 100644 --- a/crates/ide-assists/src/handlers/bool_to_enum.rs +++ b/crates/ide-assists/src/handlers/convert_bool_to_enum.rs
@@ -26,7 +26,7 @@ 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 @@ // } // } // ``` -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 @@ #[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 @@ #[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 @@ #[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 @@ #[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 @@ #[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 @@ #[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 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 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 @@ #[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 @@ #[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 @@ #[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 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 @@ #[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 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 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 @@ #[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 @@ #[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 @@ #[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 @@ #[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 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 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 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 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 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 @@ #[test] fn field_enum_keeps_wildcard_patterns() { check_assist( - bool_to_enum, + convert_bool_to_enum, r#" enum Foo { Foo, @@ -1276,7 +1276,7 @@ #[test] fn field_union_basic() { check_assist( - bool_to_enum, + convert_bool_to_enum, r#" union Foo { $0foo: bool, @@ -1314,7 +1314,7 @@ #[test] fn field_negated() { check_assist( - bool_to_enum, + convert_bool_to_enum, r#" struct Foo { $0bar: bool, @@ -1350,7 +1350,7 @@ #[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 @@ #[test] fn field_multiple_initializations() { check_assist( - bool_to_enum, + convert_bool_to_enum, r#" struct Foo { $0bar: bool, @@ -1427,7 +1427,7 @@ 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 @@ #[test] fn field_initialized_with_other() { check_assist( - bool_to_enum, + convert_bool_to_enum, r#" struct Foo { $0foo: bool, @@ -1507,7 +1507,7 @@ #[test] fn field_method_chain_usage() { check_assist( - bool_to_enum, + convert_bool_to_enum, r#" struct Foo { $0bool: bool, @@ -1539,7 +1539,7 @@ #[test] fn field_in_macro() { check_assist( - bool_to_enum, + convert_bool_to_enum, r#" struct Struct { $0boolean: bool, @@ -1580,7 +1580,7 @@ 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 @@ #[test] fn const_basic() { check_assist( - bool_to_enum, + convert_bool_to_enum, r#" const $0FOO: bool = false; @@ -1624,7 +1624,7 @@ #[test] fn const_in_module() { check_assist( - bool_to_enum, + convert_bool_to_enum, r#" fn main() { if foo::FOO { @@ -1658,7 +1658,7 @@ #[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 @@ #[test] fn const_cross_file() { check_assist( - bool_to_enum, + convert_bool_to_enum, r#" //- /main.rs mod foo; @@ -1734,7 +1734,7 @@ #[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 @@ #[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 @@ #[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 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 @@ #[test] fn static_basic() { check_assist( - bool_to_enum, + convert_bool_to_enum, r#" static mut $0BOOL: bool = true; @@ -1910,7 +1910,7 @@ 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 @@ #[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() {}") } }
diff --git a/crates/ide-assists/src/handlers/expand_rest_pattern.rs b/crates/ide-assists/src/handlers/expand_rest_pattern.rs new file mode 100644 index 0000000..c79a982 --- /dev/null +++ b/crates/ide-assists/src/handlers/expand_rest_pattern.rs
@@ -0,0 +1,508 @@ +use hir::{PathResolution, StructKind}; +use ide_db::syntax_helpers::suggest_name::NameGenerator; +use syntax::{ + ast::{self, make}, + match_ast, AstNode, ToSmolStr, +}; + +use crate::{AssistContext, AssistId, Assists}; + +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. +// +// ``` +// struct Bar { y: Y, z: Z } +// +// fn foo(bar: Bar) { +// let Bar { ..$0 } = bar; +// } +// ``` +// -> +// ``` +// struct Bar { y: Y, z: Z } +// +// fn foo(bar: Bar) { +// let Bar { y, z } = bar; +// } +// ``` +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() { + cov_mark::hit!(no_missing_fields); + return None; + } + + 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() { + let edition = ctx.sema.scope(record_pat.syntax())?.krate().edition(ctx.db()); + let field = make::record_pat_field_shorthand(make::name_ref( + &f.name(ctx.sema.db).display_no_db(edition).to_smolstr(), + )); + new_field_list.add_field(field.clone_for_update()); + } + + 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("expand_tuple_struct_rest_pattern", crate::AssistKind::RefactorRewrite), + "Fill tuple struct fields", + target_range, + move |builder| builder.replace_ast(pat, new_pat), + ) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::tests::{check_assist, check_assist_not_applicable}; + + #[test] + fn fill_fields_enum_with_only_ellipsis() { + check_assist( + expand_rest_pattern, + r#" +enum Foo { + A(X), + B{y: Y, z: Z} +} + +fn bar(foo: Foo) { + match foo { + Foo::A(_) => false, + Foo::B{ ..$0 } => true, + }; +} +"#, + r#" +enum Foo { + A(X), + B{y: Y, z: Z} +} + +fn bar(foo: Foo) { + match foo { + Foo::A(_) => false, + Foo::B{ y, z } => true, + }; +} +"#, + ) + } + + #[test] + fn fill_fields_enum_with_fields() { + check_assist( + expand_rest_pattern, + r#" +enum Foo { + A(X), + B{y: Y, z: Z} +} + +fn bar(foo: Foo) { + match foo { + Foo::A(_) => false, + Foo::B{ y, ..$0 } => true, + }; +} +"#, + r#" +enum Foo { + A(X), + B{y: Y, z: Z} +} + +fn bar(foo: Foo) { + match foo { + Foo::A(_) => false, + Foo::B{ y, z } => true, + }; +} +"#, + ) + } + + #[test] + fn fill_fields_struct_with_only_ellipsis() { + check_assist( + expand_rest_pattern, + 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; +} +"#, + ); + 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; +} +"#, + ) + } + + #[test] + fn fill_fields_struct_with_fields() { + check_assist( + expand_rest_pattern, + r#" +struct Bar { + y: Y, + z: Z, +} + +fn foo(bar: Bar) { + let Bar { y, ..$0 } = bar; +} +"#, + r#" +struct Bar { + y: Y, + z: Z, +} + +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; +} +"#, + ) + } + + #[test] + fn fill_fields_struct_generated_by_macro() { + check_assist( + expand_rest_pattern, + r#" +macro_rules! position { + ($t: ty) => { + struct Pos {x: $t, y: $t} + }; +} + +position!(usize); + +fn macro_call(pos: Pos) { + let Pos { ..$0 } = pos; +} +"#, + r#" +macro_rules! position { + ($t: ty) => { + struct Pos {x: $t, y: $t} + }; +} + +position!(usize); + +fn macro_call(pos: Pos) { + let Pos { x, y } = pos; +} +"#, + ); + } + + #[test] + fn fill_fields_enum_generated_by_macro() { + check_assist( + expand_rest_pattern, + r#" +macro_rules! enum_gen { + ($t: ty) => { + enum Foo { + A($t), + B{x: $t, y: $t}, + } + }; +} + +enum_gen!(usize); + +fn macro_call(foo: Foo) { + match foo { + Foo::A(_) => false, + Foo::B{ ..$0 } => true, + } +} +"#, + r#" +macro_rules! enum_gen { + ($t: ty) => { + enum Foo { + A($t), + B{x: $t, y: $t}, + } + }; +} + +enum_gen!(usize); + +fn macro_call(foo: Foo) { + match foo { + Foo::A(_) => false, + Foo::B{ x, y } => true, + } +} +"#, + ); + } + + #[test] + fn not_applicable_when_not_in_ellipsis() { + check_assist_not_applicable( + expand_rest_pattern, + r#" +enum Foo { + A(X), + B{y: Y, z: Z} +} + +fn bar(foo: Foo) { + match foo { + Foo::A(_) => false, + Foo::B{..}$0 => true, + }; +} +"#, + ); + check_assist_not_applicable( + expand_rest_pattern, + r#" +enum Foo { + A(X), + B{y: Y, z: Z} +} + +fn bar(foo: Foo) { + match foo { + Foo::A(_) => false, + Foo::B$0{..} => true, + }; +} +"#, + ); + check_assist_not_applicable( + expand_rest_pattern, + r#" +enum Foo { + A(X), + B{y: Y, z: Z} +} + +fn bar(foo: Foo) { + match foo { + Foo::A(_) => false, + Foo::$0B{..} => true, + }; +} +"#, + ); + } + + #[test] + 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( + expand_rest_pattern, + r#" +enum Foo { + A(X), + B{y: Y, z: Z} +} + +fn bar(foo: Foo) { + match foo { + Foo::A(_) => false, + Foo::B{y, z, ..$0} => true, + }; +} +"#, + ); + check_assist_not_applicable( + expand_rest_pattern, + r#" +struct Bar { + y: Y, + z: Z, +} + +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; +} +"#, + ); + } +}
diff --git a/crates/ide-assists/src/handlers/extract_function.rs b/crates/ide-assists/src/handlers/extract_function.rs index 751e4a5..6f4b886 100644 --- a/crates/ide-assists/src/handlers/extract_function.rs +++ b/crates/ide-assists/src/handlers/extract_function.rs
@@ -750,7 +750,10 @@ ast::Stmt::Item(_) => (), ast::Stmt::LetStmt(stmt) => { if let Some(pat) = stmt.pat() { - walk_pat(&pat, cb); + let _ = walk_pat(&pat, &mut |pat| { + cb(pat); + std::ops::ControlFlow::<(), ()>::Continue(()) + }); } if let Some(expr) = stmt.initializer() { walk_patterns_in_expr(&expr, cb);
diff --git a/crates/ide-assists/src/handlers/fill_record_pattern_fields.rs b/crates/ide-assists/src/handlers/fill_record_pattern_fields.rs deleted file mode 100644 index ee32186..0000000 --- a/crates/ide-assists/src/handlers/fill_record_pattern_fields.rs +++ /dev/null
@@ -1,357 +0,0 @@ -use syntax::{ - ast::{self, make}, - AstNode, ToSmolStr, -}; - -use crate::{AssistContext, AssistId, Assists}; - -// Assist: fill_record_pattern_fields -// -// Fills fields by replacing rest pattern in record patterns. -// -// ``` -// struct Bar { y: Y, z: Z } -// -// fn foo(bar: Bar) { -// let Bar { ..$0 } = bar; -// } -// ``` -// -> -// ``` -// struct Bar { y: Y, z: Z } -// -// fn foo(bar: Bar) { -// 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(); - - let missing_fields = ctx.sema.record_pattern_missing_fields(&record_pat); - - if missing_fields.is_empty() { - cov_mark::hit!(no_missing_fields); - return None; - } - - let old_field_list = record_pat.record_pat_field_list()?; - let new_field_list = - make::record_pat_field_list(old_field_list.fields(), None).clone_for_update(); - for (f, _) in missing_fields.iter() { - let edition = ctx.sema.scope(record_pat.syntax())?.krate().edition(ctx.db()); - let field = make::record_pat_field_shorthand(make::name_ref( - &f.name(ctx.sema.db).display_no_db(edition).to_smolstr(), - )); - new_field_list.add_field(field.clone_for_update()); - } - - let old_range = ctx.sema.original_range_opt(old_field_list.syntax())?; - if old_range.file_id != ctx.file_id() { - return None; - } - - acc.add( - AssistId("fill_record_pattern_fields", crate::AssistKind::RefactorRewrite), - "Fill structure fields", - target_range, - move |builder| builder.replace_ast(old_field_list, new_field_list), - ) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::tests::{check_assist, check_assist_not_applicable}; - - #[test] - fn fill_fields_enum_with_only_ellipsis() { - check_assist( - fill_record_pattern_fields, - r#" -enum Foo { - A(X), - B{y: Y, z: Z} -} - -fn bar(foo: Foo) { - match foo { - Foo::A(_) => false, - Foo::B{ ..$0 } => true, - }; -} -"#, - r#" -enum Foo { - A(X), - B{y: Y, z: Z} -} - -fn bar(foo: Foo) { - match foo { - Foo::A(_) => false, - Foo::B{ y, z } => true, - }; -} -"#, - ) - } - - #[test] - fn fill_fields_enum_with_fields() { - check_assist( - fill_record_pattern_fields, - r#" -enum Foo { - A(X), - B{y: Y, z: Z} -} - -fn bar(foo: Foo) { - match foo { - Foo::A(_) => false, - Foo::B{ y, ..$0 } => true, - }; -} -"#, - r#" -enum Foo { - A(X), - B{y: Y, z: Z} -} - -fn bar(foo: Foo) { - match foo { - Foo::A(_) => false, - Foo::B{ y, z } => true, - }; -} -"#, - ) - } - - #[test] - fn fill_fields_struct_with_only_ellipsis() { - check_assist( - 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 fill_fields_struct_with_fields() { - check_assist( - fill_record_pattern_fields, - r#" -struct Bar { - y: Y, - z: Z, -} - -fn foo(bar: Bar) { - let Bar { y, ..$0 } = bar; -} -"#, - r#" -struct Bar { - y: Y, - z: Z, -} - -fn foo(bar: Bar) { - let Bar { y, z } = bar; -} -"#, - ) - } - - #[test] - fn fill_fields_struct_generated_by_macro() { - check_assist( - fill_record_pattern_fields, - r#" -macro_rules! position { - ($t: ty) => { - struct Pos {x: $t, y: $t} - }; -} - -position!(usize); - -fn macro_call(pos: Pos) { - let Pos { ..$0 } = pos; -} -"#, - r#" -macro_rules! position { - ($t: ty) => { - struct Pos {x: $t, y: $t} - }; -} - -position!(usize); - -fn macro_call(pos: Pos) { - let Pos { x, y } = pos; -} -"#, - ); - } - - #[test] - fn fill_fields_enum_generated_by_macro() { - check_assist( - fill_record_pattern_fields, - r#" -macro_rules! enum_gen { - ($t: ty) => { - enum Foo { - A($t), - B{x: $t, y: $t}, - } - }; -} - -enum_gen!(usize); - -fn macro_call(foo: Foo) { - match foo { - Foo::A(_) => false, - Foo::B{ ..$0 } => true, - } -} -"#, - r#" -macro_rules! enum_gen { - ($t: ty) => { - enum Foo { - A($t), - B{x: $t, y: $t}, - } - }; -} - -enum_gen!(usize); - -fn macro_call(foo: Foo) { - match foo { - Foo::A(_) => false, - Foo::B{ x, y } => true, - } -} -"#, - ); - } - - #[test] - fn not_applicable_when_not_in_ellipsis() { - check_assist_not_applicable( - fill_record_pattern_fields, - r#" -enum Foo { - A(X), - B{y: Y, z: Z} -} - -fn bar(foo: Foo) { - match foo { - Foo::A(_) => false, - Foo::B{..}$0 => true, - }; -} -"#, - ); - check_assist_not_applicable( - fill_record_pattern_fields, - r#" -enum Foo { - A(X), - B{y: Y, z: Z} -} - -fn bar(foo: Foo) { - match foo { - Foo::A(_) => false, - Foo::B$0{..} => true, - }; -} -"#, - ); - check_assist_not_applicable( - fill_record_pattern_fields, - r#" -enum Foo { - A(X), - B{y: Y, z: Z} -} - -fn bar(foo: Foo) { - match foo { - Foo::A(_) => false, - Foo::$0B{..} => true, - }; -} -"#, - ); - } - - #[test] - fn not_applicable_when_no_missing_fields() { - // This is still possible even though it's meaningless - cov_mark::check!(no_missing_fields); - check_assist_not_applicable( - fill_record_pattern_fields, - r#" -enum Foo { - A(X), - B{y: Y, z: Z} -} - -fn bar(foo: Foo) { - match foo { - Foo::A(_) => false, - Foo::B{y, z, ..$0} => true, - }; -} -"#, - ); - check_assist_not_applicable( - fill_record_pattern_fields, - r#" -struct Bar { - y: Y, - z: Z, -} - -fn foo(bar: Bar) { - let Bar { y, z, ..$0 } = bar; -} -"#, - ); - } -}
diff --git a/crates/ide-assists/src/handlers/generate_function.rs b/crates/ide-assists/src/handlers/generate_function.rs index 91e248a..7af2a2e 100644 --- a/crates/ide-assists/src/handlers/generate_function.rs +++ b/crates/ide-assists/src/handlers/generate_function.rs
@@ -724,9 +724,9 @@ 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 @@ 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()
diff --git a/crates/ide-assists/src/handlers/generate_getter_or_setter.rs b/crates/ide-assists/src/handlers/generate_getter_or_setter.rs index ac58af6..1b16ba5 100644 --- a/crates/ide-assists/src/handlers/generate_getter_or_setter.rs +++ b/crates/ide-assists/src/handlers/generate_getter_or_setter.rs
@@ -233,7 +233,7 @@ .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()), ) })
diff --git a/crates/ide-assists/src/handlers/inline_const_as_literal.rs b/crates/ide-assists/src/handlers/inline_const_as_literal.rs index 139078e..ca5882d 100644 --- a/crates/ide-assists/src/handlers/inline_const_as_literal.rs +++ b/crates/ide-assists/src/handlers/inline_const_as_literal.rs
@@ -42,7 +42,7 @@ 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);
diff --git a/crates/ide-assists/src/handlers/inline_local_variable.rs b/crates/ide-assists/src/handlers/inline_local_variable.rs index cc7bea5..36eed29 100644 --- a/crates/ide-assists/src/handlers/inline_local_variable.rs +++ b/crates/ide-assists/src/handlers/inline_local_variable.rs
@@ -5,7 +5,7 @@ EditionedFileId, RootDatabase, }; use syntax::{ - ast::{self, AstNode, AstToken, HasName}, + ast::{self, syntax_factory::SyntaxFactory, AstNode, AstToken, HasName}, SyntaxElement, TextRange, }; @@ -43,22 +43,6 @@ }?; 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 @@ } 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); }, ) } @@ -942,4 +946,52 @@ "#, ); } + + #[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(); +} +"#, + ); + } }
diff --git a/crates/ide-assists/src/handlers/introduce_named_generic.rs b/crates/ide-assists/src/handlers/introduce_named_type_parameter.rs similarity index 84% rename from crates/ide-assists/src/handlers/introduce_named_generic.rs rename to crates/ide-assists/src/handlers/introduce_named_type_parameter.rs index 8c27641..994e4a0 100644 --- a/crates/ide-assists/src/handlers/introduce_named_generic.rs +++ b/crates/ide-assists/src/handlers/introduce_named_type_parameter.rs
@@ -4,7 +4,7 @@ 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 @@ // ``` // 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 @@ #[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 @@ #[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 @@ #[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 @@ #[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 @@ #[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 @@ #[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 @@ #[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 @@ #[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 @@ #[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 @@ #[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 @@ #[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>) {}"#, );
diff --git a/crates/ide-assists/src/handlers/term_search.rs b/crates/ide-assists/src/handlers/term_search.rs index 66671c9..e10897b 100644 --- a/crates/ide-assists/src/handlers/term_search.rs +++ b/crates/ide-assists/src/handlers/term_search.rs
@@ -52,8 +52,13 @@ 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();
diff --git a/crates/ide-assists/src/lib.rs b/crates/ide-assists/src/lib.rs index 448bcad..e8480b0 100644 --- a/crates/ide-assists/src/lib.rs +++ b/crates/ide-assists/src/lib.rs
@@ -105,6 +105,7 @@ 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 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 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 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 @@ &[ // 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 @@ 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 @@ 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 @@ 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 @@ 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,
diff --git a/crates/ide-assists/src/tests.rs b/crates/ide-assists/src/tests.rs index 11aeb21..7d7012c 100644 --- a/crates/ide-assists/src/tests.rs +++ b/crates/ide-assists/src/tests.rs
@@ -710,18 +710,22 @@ 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 @@ }, file_system_edits: [], is_snippet: true, + annotations: {}, + next_annotation_id: 0, }, ), command: Some( @@ -839,18 +845,22 @@ 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 @@ }, file_system_edits: [], is_snippet: true, + annotations: {}, + next_annotation_id: 0, }, ), command: Some( @@ -902,22 +914,27 @@ 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 @@ }, file_system_edits: [], is_snippet: true, + annotations: {}, + next_annotation_id: 0, }, ), command: Some( @@ -969,22 +988,27 @@ 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 @@ }, file_system_edits: [], is_snippet: true, + annotations: {}, + next_annotation_id: 0, }, ), command: Some( @@ -1036,10 +1062,12 @@ 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 @@ }, file_system_edits: [], is_snippet: true, + annotations: {}, + next_annotation_id: 0, }, ), command: None,
diff --git a/crates/ide-assists/src/tests/generated.rs b/crates/ide-assists/src/tests/generated.rs index 91c1a3e..4234124 100644 --- a/crates/ide-assists/src/tests/generated.rs +++ b/crates/ide-assists/src/tests/generated.rs
@@ -28,6 +28,29 @@ } #[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( "add_explicit_type", @@ -305,34 +328,6 @@ } #[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( "change_visibility", @@ -383,6 +378,34 @@ } #[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( "convert_closure_to_fn", @@ -933,23 +956,42 @@ } #[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; } "#####, ) @@ -1118,27 +1160,6 @@ } #[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( "fix_visibility", @@ -2194,19 +2215,6 @@ } #[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( "introduce_named_lifetime", @@ -2232,6 +2240,19 @@ } #[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( "invert_if",
diff --git a/crates/ide-assists/src/utils.rs b/crates/ide-assists/src/utils.rs index 39686f0..a6fa170 100644 --- a/crates/ide-assists/src/utils.rs +++ b/crates/ide-assists/src/utils.rs
@@ -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 @@ 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 @@ } 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}>") } };
diff --git a/crates/ide-completion/src/completions/expr.rs b/crates/ide-completion/src/completions/expr.rs index 365d2dd..b28b6e5 100644 --- a/crates/ide-completion/src/completions/expr.rs +++ b/crates/ide-completion/src/completions/expr.rs
@@ -365,7 +365,8 @@ 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 {
diff --git a/crates/ide-completion/src/completions/keyword.rs b/crates/ide-completion/src/completions/keyword.rs index 6541ee5..26c29e0 100644 --- a/crates/ide-completion/src/completions/keyword.rs +++ b/crates/ide-completion/src/completions/keyword.rs
@@ -330,4 +330,34 @@ ", ) } + + #[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 +} +"#, + ); + } }
diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs index 919b30f..e686a29 100644 --- a/crates/ide-completion/src/context.rs +++ b/crates/ide-completion/src/context.rs
@@ -7,8 +7,8 @@ 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) 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 @@ CompleteSemicolon::DoNotComplete }; + let display_target = krate.to_display_target(db); let ctx = CompletionContext { sema, scope, @@ -888,6 +890,7 @@ exclude_flyimport, exclude_traits, complete_semicolon, + display_target, }; Some((ctx, analysis)) }
diff --git a/crates/ide-completion/src/context/tests.rs b/crates/ide-completion/src/context/tests.rs index fc2bfc0..a03f632c 100644 --- a/crates/ide-completion/src/context/tests.rs +++ b/crates/ide-completion/src/context/tests.rs
@@ -13,7 +13,7 @@ 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 =
diff --git a/crates/ide-completion/src/item.rs b/crates/ide-completion/src/item.rs index b3dd8a8..8d6dc4c 100644 --- a/crates/ide-completion/src/item.rs +++ b/crates/ide-completion/src/item.rs
@@ -252,14 +252,16 @@ /// 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 @@ /// 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 } }
diff --git a/crates/ide-completion/src/render.rs b/crates/ide-completion/src/render.rs index 4f6c4cb..c82905e 100644 --- a/crates/ide-completion/src/render.rs +++ b/crates/ide-completion/src/render.rs
@@ -144,7 +144,7 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ ), 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 @@ ), 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ } "#, 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 @@ } "#, 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 @@ } "#, 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 @@ } "#, 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 @@ Indel { insert: "(", delete: 107..107, + annotation: None, }, Indel { insert: "qux)()", delete: 109..110, + annotation: None, }, ], }, @@ -2827,11 +2916,11 @@ } "#, 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> []
diff --git a/crates/ide-completion/src/render/const_.rs b/crates/ide-completion/src/render/const_.rs index e357ab2..f11b302 100644 --- a/crates/ide-completion/src/render/const_.rs +++ b/crates/ide-completion/src/render/const_.rs
@@ -16,7 +16,7 @@ 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);
diff --git a/crates/ide-completion/src/render/function.rs b/crates/ide-completion/src/render/function.rs index fd90613..4693bdc 100644 --- a/crates/ide-completion/src/render/function.rs +++ b/crates/ide-completion/src/render/function.rs
@@ -4,7 +4,7 @@ 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 @@ } 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 @@ 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 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 @@ 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 @@ 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(", ") } }
diff --git a/crates/ide-completion/src/render/literal.rs b/crates/ide-completion/src/render/literal.rs index aab54ca..ffda52f 100644 --- a/crates/ide-completion/src/render/literal.rs +++ b/crates/ide-completion/src/render/literal.rs
@@ -8,7 +8,7 @@ use crate::{ context::{CompletionContext, PathCompletionCtx, PathKind}, - item::{Builder, CompletionItem}, + item::{Builder, CompletionItem, CompletionRelevanceFn}, render::{ compute_type_match, variant::{ @@ -17,7 +17,7 @@ }, RenderContext, }, - CompletionItemKind, CompletionRelevance, + CompletionItemKind, CompletionRelevance, CompletionRelevanceReturnType, }; pub(crate) fn render_variant_lit( @@ -82,10 +82,10 @@ 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 @@ 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() });
diff --git a/crates/ide-completion/src/render/macro_.rs b/crates/ide-completion/src/render/macro_.rs index e265e92..8b2476d 100644 --- a/crates/ide-completion/src/render/macro_.rs +++ b/crates/ide-completion/src/render/macro_.rs
@@ -62,7 +62,7 @@ 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());
diff --git a/crates/ide-completion/src/render/type_alias.rs b/crates/ide-completion/src/render/type_alias.rs index 1b952f3..d57feee 100644 --- a/crates/ide-completion/src/render/type_alias.rs +++ b/crates/ide-completion/src/render/type_alias.rs
@@ -38,7 +38,7 @@ } 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,
diff --git a/crates/ide-completion/src/render/union_literal.rs b/crates/ide-completion/src/render/union_literal.rs index 7420362..09154e8 100644 --- a/crates/ide-completion/src/render/union_literal.rs +++ b/crates/ide-completion/src/render/union_literal.rs
@@ -88,7 +88,7 @@ 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 { "" }
diff --git a/crates/ide-completion/src/render/variant.rs b/crates/ide-completion/src/render/variant.rs index d8516ea..83718e5 100644 --- a/crates/ide-completion/src/render/variant.rs +++ b/crates/ide-completion/src/render/variant.rs
@@ -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 @@ /// 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 @@ 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 @@ /// 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 @@ } }); - 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})"),
diff --git a/crates/ide-completion/src/tests/expression.rs b/crates/ide-completion/src/tests/expression.rs index 3755751..9b3c676 100644 --- a/crates/ide-completion/src/tests/expression.rs +++ b/crates/ide-completion/src/tests/expression.rs
@@ -170,6 +170,7 @@ kw if let kw impl kw let + kw letm kw loop kw match kw mod @@ -247,6 +248,7 @@ kw if let kw impl kw let + kw letm kw loop kw match kw mod @@ -297,6 +299,7 @@ kw if let kw impl kw let + kw letm kw loop kw match kw mod @@ -370,6 +373,7 @@ kw if let kw impl kw let + kw letm kw loop kw match kw mod @@ -942,6 +946,7 @@ kw if let kw impl kw let + kw letm kw loop kw match kw mod @@ -983,6 +988,7 @@ kw if let kw impl kw let + kw letm kw loop kw match kw mod @@ -1072,6 +1078,7 @@ kw if let kw impl kw let + kw letm kw loop kw match kw mod @@ -1113,6 +1120,7 @@ kw if let kw impl kw let + kw letm kw loop kw match kw mod @@ -1154,6 +1162,7 @@ kw if let kw impl kw let + kw letm kw loop kw match kw mod @@ -1205,6 +1214,7 @@ kw if let kw impl kw let + kw letm kw loop kw match kw mod @@ -1258,6 +1268,7 @@ kw if let kw impl kw let + kw letm kw loop kw match kw mod @@ -1495,6 +1506,7 @@ kw if let kw impl kw let + kw letm kw loop kw match kw mod @@ -1920,6 +1932,7 @@ 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 @@ kw if let kw impl kw let + kw letm kw loop kw match kw mod @@ -2015,6 +2029,7 @@ kw if let kw impl kw let + kw letm kw loop kw match kw mod
diff --git a/crates/ide-completion/src/tests/item.rs b/crates/ide-completion/src/tests/item.rs index bea6d60..be2c37d 100644 --- a/crates/ide-completion/src/tests/item.rs +++ b/crates/ide-completion/src/tests/item.rs
@@ -285,6 +285,7 @@ kw if let kw impl kw let + kw letm kw loop kw match kw mod
diff --git a/crates/ide-completion/src/tests/special.rs b/crates/ide-completion/src/tests/special.rs index 2b05184..005263d 100644 --- a/crates/ide-completion/src/tests/special.rs +++ b/crates/ide-completion/src/tests/special.rs
@@ -1009,6 +1009,7 @@ kw if let kw impl kw let + kw letm kw loop kw match kw mod @@ -1059,6 +1060,7 @@ kw if let kw impl kw let + kw letm kw loop kw match kw mod @@ -1182,6 +1184,7 @@ kw if let kw impl kw let + kw letm kw loop kw match kw mod @@ -1437,6 +1440,7 @@ kw if let kw impl kw let + kw letm kw loop kw match kw mod
diff --git a/crates/ide-db/src/defs.rs b/crates/ide-db/src/defs.rs index 6f71c3d..502314e 100644 --- a/crates/ide-db/src/defs.rs +++ b/crates/ide-db/src/defs.rs
@@ -12,11 +12,11 @@ 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 @@ &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 @@ 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 @@ 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 @@ 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 @@ }) } - 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(),
diff --git a/crates/ide-db/src/generated/lints.rs b/crates/ide-db/src/generated/lints.rs index 9c983e7..0a7a7d1 100644 --- a/crates/ide-db/src/generated/lints.rs +++ b/crates/ide-db/src/generated/lints.rs
@@ -15052,7 +15052,7 @@ }, 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 @@ }, 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,
diff --git a/crates/ide-db/src/path_transform.rs b/crates/ide-db/src/path_transform.rs index 126b304..a348a4e 100644 --- a/crates/ide-db/src/path_transform.rs +++ b/crates/ide-db/src/path_transform.rs
@@ -192,7 +192,9 @@ } } (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));
diff --git a/crates/ide-db/src/rename.rs b/crates/ide-db/src/rename.rs index 59914be..1633065 100644 --- a/crates/ide-db/src/rename.rs +++ b/crates/ide-db/src/rename.rs
@@ -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 @@ })); 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 @@ 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 @@ 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 @@ } } } + 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)
diff --git a/crates/ide-db/src/source_change.rs b/crates/ide-db/src/source_change.rs index 34642d7..b4d0b0d 100644 --- a/crates/ide-db/src/source_change.rs +++ b/crates/ide-db/src/source_change.rs
@@ -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 @@ 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 @@ } } + 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 @@ 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 @@ source_file_edits: Default::default(), file_system_edits: vec![edit], is_snippet: false, + ..SourceChange::default() } } }
diff --git a/crates/ide-db/src/symbol_index.rs b/crates/ide-db/src/symbol_index.rs index bb4c289..2737436 100644 --- a/crates/ide-db/src/symbol_index.rs +++ b/crates/ide-db/src/symbol_index.rs
@@ -24,7 +24,6 @@ cmp::Ordering, fmt, hash::{Hash, Hasher}, - mem, ops::ControlFlow, }; @@ -299,7 +298,7 @@ } 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 {
diff --git a/crates/ide-db/src/syntax_helpers/node_ext.rs b/crates/ide-db/src/syntax_helpers/node_ext.rs index 74c0b8e..56a6607 100644 --- a/crates/ide-db/src/syntax_helpers/node_ext.rs +++ b/crates/ide-db/src/syntax_helpers/node_ext.rs
@@ -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 @@ match ast::Stmt::cast(node.clone()) { Some(ast::Stmt::LetStmt(l)) => { if let Some(pat) = l.pat() { - walk_pat(&pat, cb); + let _ = 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 @@ } } else if let Some(pat) = ast::Pat::cast(node) { preorder.skip_subtree(); - walk_pat(&pat, cb); + let _ = walk_pat(&pat, &mut |pat| { + cb(pat); + ControlFlow::<(), ()>::Continue(()) + }); } } } @@ -162,7 +170,10 @@ } /// 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 @@ 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 @@ None => (), } } + ControlFlow::Continue(()) } /// Preorder walk all the type's sub types.
diff --git a/crates/ide-db/src/text_edit.rs b/crates/ide-db/src/text_edit.rs index 0c675f0..b59010f 100644 --- a/crates/ide-db/src/text_edit.rs +++ b/crates/ide-db/src/text_edit.rs
@@ -8,6 +8,8 @@ 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 insert: String, /// Refers to offsets in the original text pub delete: TextRange, + pub annotation: Option<ChangeAnnotationId>, } #[derive(Default, Debug, Clone)] @@ -37,7 +40,7 @@ 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 @@ } 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 @@ 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);
diff --git a/crates/ide-diagnostics/src/handlers/expected_function.rs b/crates/ide-diagnostics/src/handlers/expected_function.rs index e3a1e12..af25c2b 100644 --- a/crates/ide-diagnostics/src/handlers/expected_function.rs +++ b/crates/ide-diagnostics/src/handlers/expected_function.rs
@@ -12,7 +12,7 @@ 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()
diff --git a/crates/ide-diagnostics/src/handlers/invalid_cast.rs b/crates/ide-diagnostics/src/handlers/invalid_cast.rs index 5730508..82cd1f2f 100644 --- a/crates/ide-diagnostics/src/handlers/invalid_cast.rs +++ b/crates/ide-diagnostics/src/handlers/invalid_cast.rs
@@ -8,7 +8,7 @@ $fmt, $( $arg - .display($ctx.sema.db, $ctx.edition) + .display($ctx.sema.db, $ctx.display_target) .with_closure_style(ClosureStyle::ClosureWithId) ),* )
diff --git a/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs b/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs index a9ff06f..7d0f109 100644 --- a/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs +++ b/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs
@@ -8,7 +8,7 @@ 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
diff --git a/crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs b/crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs index 1363a8f..3db2e01 100644 --- a/crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs +++ b/crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs
@@ -30,7 +30,7 @@ ( 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 @@ ( 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) => {
diff --git a/crates/ide-diagnostics/src/handlers/type_mismatch.rs b/crates/ide-diagnostics/src/handlers/type_mismatch.rs index 7cf8282..c726a3b 100644 --- a/crates/ide-diagnostics/src/handlers/type_mismatch.rs +++ b/crates/ide-diagnostics/src/handlers/type_mismatch.rs
@@ -45,10 +45,10 @@ 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 @@ 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" {
diff --git a/crates/ide-diagnostics/src/handlers/typed_hole.rs b/crates/ide-diagnostics/src/handlers/typed_hole.rs index b023a95..c25318e 100644 --- a/crates/ide-diagnostics/src/handlers/typed_hole.rs +++ b/crates/ide-diagnostics/src/handlers/typed_hole.rs
@@ -27,7 +27,7 @@ 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 @@ prefer_absolute: ctx.config.prefer_absolute, allow_unstable: ctx.is_nightly, }, - ctx.edition, + ctx.display_target, ) .ok() })
diff --git a/crates/ide-diagnostics/src/handlers/unresolved_field.rs b/crates/ide-diagnostics/src/handlers/unresolved_field.rs index dfb03ee..6ab713a 100644 --- a/crates/ide-diagnostics/src/handlers/unresolved_field.rs +++ b/crates/ide-diagnostics/src/handlers/unresolved_field.rs
@@ -38,7 +38,7 @@ 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(
diff --git a/crates/ide-diagnostics/src/handlers/unresolved_method.rs b/crates/ide-diagnostics/src/handlers/unresolved_method.rs index e4de107..35e7521 100644 --- a/crates/ide-diagnostics/src/handlers/unresolved_method.rs +++ b/crates/ide-diagnostics/src/handlers/unresolved_method.rs
@@ -31,7 +31,7 @@ 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 @@ 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() {
diff --git a/crates/ide-diagnostics/src/lib.rs b/crates/ide-diagnostics/src/lib.rs index 0a55b6e..e15d349 100644 --- a/crates/ide-diagnostics/src/lib.rs +++ b/crates/ide-diagnostics/src/lib.rs
@@ -81,7 +81,10 @@ 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 @@ sema: Semantics<'a, RootDatabase>, resolve: &'a AssistResolveStrategy, edition: Edition, + display_target: DisplayTarget, is_nightly: bool, } @@ -374,7 +378,18 @@ 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 {
diff --git a/crates/ide-ssr/src/matching.rs b/crates/ide-ssr/src/matching.rs index 4bead14..e219ba4 100644 --- a/crates/ide-ssr/src/matching.rs +++ b/crates/ide-ssr/src/matching.rs
@@ -7,8 +7,7 @@ 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 @@ 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
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs index 9a3e77f..b00de6b 100644 --- a/crates/ide/src/hover.rs +++ b/crates/ide/src/hover.rs
@@ -7,7 +7,8 @@ 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 @@ 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 @@ 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 @@ 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 @@ false, config, edition, + display_target, ); return Some(RangeInfo::new(range, res)); } @@ -277,6 +298,7 @@ hovered_definition, config, edition, + display_target, ) }) .collect::<Vec<_>>(), @@ -286,12 +308,12 @@ 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 @@ 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 @@ _ => 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 @@ 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 @@ .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 @@ 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 @@ subst_types.as_ref(), config, edition, + display_target, ); HoverResult { markup: render::process_markup(sema.db, def, &markup, config),
diff --git a/crates/ide/src/hover/render.rs b/crates/ide/src/hover/render.rs index c5a83e5..31ef89a 100644 --- a/crates/ide/src/hover/render.rs +++ b/crates/ide/src/hover/render.rs
@@ -3,7 +3,7 @@ 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 @@ _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 @@ 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 @@ _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 @@ 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 @@ _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 @@ 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 @@ ) .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 @@ config: &HoverConfig, token: &SyntaxToken, edition: Edition, + display_target: DisplayTarget, ) -> Option<HoverResult> { if token.kind() != T![_] { return None; @@ -224,8 +229,8 @@ 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 @@ 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 @@ 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 @@ _config: &HoverConfig, pattern: &ast::RecordPat, edition: Edition, + display_target: DisplayTarget, ) -> HoverResult { let missing_fields = sema.record_pattern_missing_fields(pattern); @@ -309,7 +316,7 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ .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 @@ 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 @@ _ => 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ ) .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 @@ 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 @@ 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 @@ 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 @@ 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,
diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs index 7c720d9..6b470d9 100644 --- a/crates/ide/src/hover/tests.rs +++ b/crates/ide/src/hover/tests.rs
@@ -10947,3 +10947,42 @@ "#]], ); } + +#[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) + ``` + "#]], + ); +}
diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs index 63039b1..6babdff 100644 --- a/crates/ide/src/inlay_hints.rs +++ b/crates/ide/src/inlay_hints.rs
@@ -5,14 +5,14 @@ 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 @@ 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 @@ 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 @@ } 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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) }
diff --git a/crates/ide/src/inlay_hints/adjustment.rs b/crates/ide/src/inlay_hints/adjustment.rs index 8522ef0..91b8187 100644 --- a/crates/ide/src/inlay_hints/adjustment.rs +++ b/crates/ide/src/inlay_hints/adjustment.rs
@@ -7,12 +7,12 @@ 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 @@ 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 @@ 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), )) })), };
diff --git a/crates/ide/src/inlay_hints/bind_pat.rs b/crates/ide/src/inlay_hints/bind_pat.rs index c2986a9..4379153a 100644 --- a/crates/ide/src/inlay_hints/bind_pat.rs +++ b/crates/ide/src/inlay_hints/bind_pat.rs
@@ -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 @@ 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 @@ 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()
diff --git a/crates/ide/src/inlay_hints/chaining.rs b/crates/ide/src/inlay_hints/chaining.rs index 8471547..604719b 100644 --- a/crates/ide/src/inlay_hints/chaining.rs +++ b/crates/ide/src/inlay_hints/chaining.rs
@@ -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 @@ 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 @@ } } } - 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,
diff --git a/crates/ide/src/inlay_hints/closing_brace.rs b/crates/ide/src/inlay_hints/closing_brace.rs index 3767d34..bec6d38 100644 --- a/crates/ide/src/inlay_hints/closing_brace.rs +++ b/crates/ide/src/inlay_hints/closing_brace.rs
@@ -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 @@ 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 @@ 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) },
diff --git a/crates/ide/src/inlay_hints/closure_ret.rs b/crates/ide/src/inlay_hints/closure_ret.rs index 7858b1d..61c9c25 100644 --- a/crates/ide/src/inlay_hints/closure_ret.rs +++ b/crates/ide/src/inlay_hints/closure_ret.rs
@@ -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 @@ 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 @@ 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(" -> ");
diff --git a/crates/ide/src/interpret.rs b/crates/ide/src/interpret.rs index ae11072..74dad48 100644 --- a/crates/ide/src/interpret.rs +++ b/crates/ide/src/interpret.rs
@@ -1,6 +1,5 @@ -use hir::{ConstEvalError, DefWithBody, Semantics}; +use hir::{ConstEvalError, DefWithBody, DisplayTarget, Semantics}; use ide_db::{base_db::SourceRootDatabase, FilePosition, LineIndexDatabase, RootDatabase}; -use span::Edition; use std::time::{Duration, Instant}; use stdx::format_to; use syntax::{algo::ancestors_at_offset, ast, AstNode, TextRange}; @@ -46,15 +45,15 @@ None => format!("file://{path} range {text_range:?}"), } }; - let edition = def.module(db).krate().edition(db); + let display_target = def.module(db).krate().to_display_target(db); let start_time = Instant::now(); let res = match def { DefWithBody::Function(it) => it.eval(db, span_formatter), - DefWithBody::Static(it) => it.eval(db).map(|it| it.render(db, edition)), - DefWithBody::Const(it) => it.eval(db).map(|it| it.render(db, edition)), + DefWithBody::Static(it) => it.eval(db).map(|it| it.render(db, display_target)), + DefWithBody::Const(it) => it.eval(db).map(|it| it.render(db, display_target)), _ => unreachable!(), }; - let res = res.unwrap_or_else(|e| render_const_eval_error(db, e, edition)); + let res = res.unwrap_or_else(|e| render_const_eval_error(db, e, display_target)); let duration = Instant::now() - start_time; Some((duration, res)) } @@ -62,7 +61,7 @@ pub(crate) fn render_const_eval_error( db: &RootDatabase, e: ConstEvalError, - edition: Edition, + display_target: DisplayTarget, ) -> String { let span_formatter = |file_id, text_range: TextRange| { let path = &db @@ -76,6 +75,6 @@ } }; let mut r = String::new(); - _ = e.pretty_print(&mut r, db, span_formatter, edition); + _ = e.pretty_print(&mut r, db, span_formatter, display_target); r }
diff --git a/crates/ide/src/moniker.rs b/crates/ide/src/moniker.rs index 25d12a4..5754b4f 100644 --- a/crates/ide/src/moniker.rs +++ b/crates/ide/src/moniker.rs
@@ -11,7 +11,6 @@ FilePosition, RootDatabase, }; use itertools::Itertools; -use span::Edition; use syntax::{AstNode, SyntaxKind::*, T}; use crate::{doc_links::token_as_doc_comment, parent_module::crates_for, RangeInfo}; @@ -305,13 +304,13 @@ if let Some(trait_ref) = impl_.trait_ref(db) { // Trait impls use the trait type for the 2nd parameter. reverse_description.push(MonikerDescriptor { - name: display(db, edition, module, trait_ref), + name: display(db, module, trait_ref), desc: MonikerDescriptorKind::TypeParameter, }); } // Both inherent and trait impls use the self type for the first parameter. reverse_description.push(MonikerDescriptor { - name: display(db, edition, module, impl_.self_ty(db)), + name: display(db, module, impl_.self_ty(db)), desc: MonikerDescriptorKind::TypeParameter, }); reverse_description.push(MonikerDescriptor { @@ -390,17 +389,12 @@ }) } -fn display<T: HirDisplay>( - db: &RootDatabase, - edition: Edition, - module: hir::Module, - it: T, -) -> String { +fn display<T: HirDisplay>(db: &RootDatabase, module: hir::Module, it: T) -> String { match it.display_source_code(db, module.into(), true) { Ok(result) => result, // Fallback on display variant that always succeeds Err(_) => { - let fallback_result = it.display(db, edition).to_string(); + let fallback_result = it.display(db, module.krate().to_display_target(db)).to_string(); tracing::error!( display = %fallback_result, "`display_source_code` failed; falling back to using display" );
diff --git a/crates/ide/src/navigation_target.rs b/crates/ide/src/navigation_target.rs index d9f80cb..d67aaac 100644 --- a/crates/ide/src/navigation_target.rs +++ b/crates/ide/src/navigation_target.rs
@@ -6,7 +6,7 @@ use either::Either; use hir::{ db::ExpandDatabase, symbols::FileSymbol, AssocItem, FieldSource, HasContainer, HasCrate, - HasSource, HirDisplay, HirFileId, HirFileIdExt, InFile, LocalSource, ModuleSource, + HasSource, HirDisplay, HirFileId, InFile, LocalSource, ModuleSource, }; use ide_db::{ defs::Definition, @@ -116,7 +116,9 @@ SymbolKind::Module, ); res.docs = module.docs(db); - res.description = Some(module.display(db, edition).to_string()); + res.description = Some( + module.display(db, module.krate().to_display_target(db)).to_string(), + ); res }, ) @@ -183,6 +185,7 @@ fn try_to_nav(&self, db: &RootDatabase) -> Option<UpmappingResult<NavigationTarget>> { let edition = self.def.module(db).map(|it| it.krate().edition(db)).unwrap_or(Edition::CURRENT); + let display_target = self.def.krate(db).to_display_target(db); Some( orig_range_with_focus_r( db, @@ -203,16 +206,34 @@ focus_range, container_name: self.container_name.clone(), description: match self.def { - hir::ModuleDef::Module(it) => Some(it.display(db, edition).to_string()), - hir::ModuleDef::Function(it) => Some(it.display(db, edition).to_string()), - hir::ModuleDef::Adt(it) => Some(it.display(db, edition).to_string()), - hir::ModuleDef::Variant(it) => Some(it.display(db, edition).to_string()), - hir::ModuleDef::Const(it) => Some(it.display(db, edition).to_string()), - hir::ModuleDef::Static(it) => Some(it.display(db, edition).to_string()), - hir::ModuleDef::Trait(it) => Some(it.display(db, edition).to_string()), - hir::ModuleDef::TraitAlias(it) => Some(it.display(db, edition).to_string()), - hir::ModuleDef::TypeAlias(it) => Some(it.display(db, edition).to_string()), - hir::ModuleDef::Macro(it) => Some(it.display(db, edition).to_string()), + hir::ModuleDef::Module(it) => { + Some(it.display(db, display_target).to_string()) + } + hir::ModuleDef::Function(it) => { + Some(it.display(db, display_target).to_string()) + } + hir::ModuleDef::Adt(it) => Some(it.display(db, display_target).to_string()), + hir::ModuleDef::Variant(it) => { + Some(it.display(db, display_target).to_string()) + } + hir::ModuleDef::Const(it) => { + Some(it.display(db, display_target).to_string()) + } + hir::ModuleDef::Static(it) => { + Some(it.display(db, display_target).to_string()) + } + hir::ModuleDef::Trait(it) => { + Some(it.display(db, display_target).to_string()) + } + hir::ModuleDef::TraitAlias(it) => { + Some(it.display(db, display_target).to_string()) + } + hir::ModuleDef::TypeAlias(it) => { + Some(it.display(db, display_target).to_string()) + } + hir::ModuleDef::Macro(it) => { + Some(it.display(db, display_target).to_string()) + } hir::ModuleDef::BuiltinType(_) => None, }, docs: None, @@ -353,12 +374,11 @@ impl<D> TryToNav for D where - D: HasSource + ToNavFromAst + Copy + HasDocs + HirDisplay, + D: HasSource + ToNavFromAst + Copy + HasDocs + HirDisplay + HasCrate, D::Ast: ast::HasName, { fn try_to_nav(&self, db: &RootDatabase) -> Option<UpmappingResult<NavigationTarget>> { let src = self.source(db)?; - let edition = src.file_id.original_file(db).edition(); Some( NavigationTarget::from_named( db, @@ -367,7 +387,8 @@ ) .map(|mut res| { res.docs = self.docs(db); - res.description = Some(self.display(db, edition).to_string()); + res.description = + Some(self.display(db, self.krate(db).to_display_target(db)).to_string()); res.container_name = self.container_name(db); res }), @@ -439,7 +460,8 @@ let focus = value .rename() .map_or_else(|| value.name_ref().map(Either::Left), |it| it.name().map(Either::Right)); - let edition = self.module(db).krate().edition(db); + let krate = self.module(db).krate(); + let edition = krate.edition(db); Some(orig_range_with_focus(db, file_id, value.syntax(), focus).map( |(FileRange { file_id, range: full_range }, focus_range)| { @@ -455,7 +477,7 @@ ); res.docs = self.docs(db); - res.description = Some(self.display(db, edition).to_string()); + res.description = Some(self.display(db, krate.to_display_target(db)).to_string()); res.container_name = container_name(db, *self, edition); res }, @@ -466,14 +488,15 @@ impl TryToNav for hir::Field { fn try_to_nav(&self, db: &RootDatabase) -> Option<UpmappingResult<NavigationTarget>> { let src = self.source(db)?; - let edition = self.parent_def(db).module(db).krate().edition(db); + let krate = self.parent_def(db).module(db).krate(); let field_source = match &src.value { FieldSource::Named(it) => { NavigationTarget::from_named(db, src.with_value(it), SymbolKind::Field).map( |mut res| { res.docs = self.docs(db); - res.description = Some(self.display(db, edition).to_string()); + res.description = + Some(self.display(db, krate.to_display_target(db)).to_string()); res }, )
diff --git a/crates/ide/src/rename.rs b/crates/ide/src/rename.rs index 3e8295e..d0e1c20 100644 --- a/crates/ide/src/rename.rs +++ b/crates/ide/src/rename.rs
@@ -446,6 +446,7 @@ use expect_test::{expect, Expect}; use ide_db::source_change::SourceChange; use ide_db::text_edit::TextEdit; + use itertools::Itertools; use stdx::trim_indent; use test_utils::assert_eq_text; @@ -496,6 +497,31 @@ }; } + #[track_caller] + fn check_conflicts(new_name: &str, #[rust_analyzer::rust_fixture] ra_fixture: &str) { + let (analysis, position, conflicts) = fixture::annotations(ra_fixture); + let source_change = analysis.rename(position, new_name).unwrap().unwrap(); + let expected_conflicts = conflicts + .into_iter() + .map(|(file_range, _)| (file_range.file_id, file_range.range)) + .sorted_unstable_by_key(|(file_id, range)| (*file_id, range.start())) + .collect_vec(); + let found_conflicts = source_change + .source_file_edits + .iter() + .flat_map(|(file_id, (edit, _))| { + edit.into_iter() + .filter(|edit| edit.annotation.is_some()) + .map(move |edit| (*file_id, edit.delete)) + }) + .sorted_unstable_by_key(|(file_id, range)| (*file_id, range.start())) + .collect_vec(); + assert_eq!( + expected_conflicts, found_conflicts, + "rename conflicts mismatch: {source_change:#?}" + ); + } + fn check_expect( new_name: &str, #[rust_analyzer::rust_fixture] ra_fixture: &str, @@ -548,6 +574,37 @@ } #[test] + fn rename_will_shadow() { + check_conflicts( + "new_name", + r#" +fn foo() { + let mut new_name = 123; + let old_name$0 = 456; + // ^^^^^^^^ + new_name = 789 + new_name; +} + "#, + ); + } + + #[test] + fn rename_will_be_shadowed() { + check_conflicts( + "new_name", + r#" +fn foo() { + let mut old_name$0 = 456; + // ^^^^^^^^ + let new_name = 123; + old_name = 789 + old_name; + // ^^^^^^^^ ^^^^^^^^ +} + "#, + ); + } + + #[test] fn test_prepare_rename_namelikes() { check_prepare(r"fn name$0<'lifetime>() {}", expect![[r#"3..7: name"#]]); check_prepare(r"fn name<'lifetime$0>() {}", expect![[r#"9..17: lifetime"#]]); @@ -1024,6 +1081,7 @@ Indel { insert: "foo2", delete: 4..7, + annotation: None, }, ], ), @@ -1071,6 +1129,7 @@ Indel { insert: "quux", delete: 8..11, + annotation: None, }, ], ), @@ -1082,6 +1141,7 @@ Indel { insert: "quux", delete: 11..14, + annotation: None, }, ], ), @@ -1123,6 +1183,7 @@ Indel { insert: "foo2", delete: 4..7, + annotation: None, }, ], ), @@ -1171,6 +1232,7 @@ Indel { insert: "bar", delete: 16..19, + annotation: None, }, ], ), @@ -1242,6 +1304,7 @@ Indel { insert: "foo2", delete: 27..30, + annotation: None, }, ], ), @@ -1253,6 +1316,7 @@ Indel { insert: "foo2", delete: 8..11, + annotation: None, }, ], ), @@ -1308,6 +1372,7 @@ Indel { insert: "foo2", delete: 4..7, + annotation: None, }, ], ), @@ -1441,10 +1506,12 @@ Indel { insert: "r#fn", delete: 4..7, + annotation: None, }, Indel { insert: "r#fn", delete: 22..25, + annotation: None, }, ], ), @@ -1509,10 +1576,12 @@ Indel { insert: "foo", delete: 4..8, + annotation: None, }, Indel { insert: "foo", delete: 23..27, + annotation: None, }, ], ), @@ -1574,6 +1643,7 @@ Indel { insert: "dyn", delete: 7..10, + annotation: None, }, ], ), @@ -1585,6 +1655,7 @@ Indel { insert: "r#dyn", delete: 18..21, + annotation: None, }, ], ), @@ -1614,6 +1685,7 @@ Indel { insert: "r#dyn", delete: 7..10, + annotation: None, }, ], ), @@ -1625,6 +1697,7 @@ Indel { insert: "dyn", delete: 18..21, + annotation: None, }, ], ), @@ -1654,6 +1727,7 @@ Indel { insert: "r#dyn", delete: 7..10, + annotation: None, }, ], ), @@ -1665,6 +1739,7 @@ Indel { insert: "dyn", delete: 18..21, + annotation: None, }, ], ), @@ -1701,10 +1776,12 @@ Indel { insert: "abc", delete: 7..10, + annotation: None, }, Indel { insert: "abc", delete: 32..35, + annotation: None, }, ], ), @@ -1716,6 +1793,7 @@ Indel { insert: "abc", delete: 18..23, + annotation: None, }, ], ), @@ -1749,10 +1827,12 @@ Indel { insert: "abc", delete: 7..12, + annotation: None, }, Indel { insert: "abc", delete: 34..39, + annotation: None, }, ], ), @@ -1764,6 +1844,7 @@ Indel { insert: "abc", delete: 18..21, + annotation: None, }, ], ),
diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs index 509ae32..b8deed0 100644 --- a/crates/ide/src/runnables.rs +++ b/crates/ide/src/runnables.rs
@@ -9,6 +9,7 @@ }; use ide_assists::utils::{has_test_related_attribute, test_related_attribute_syn}; use ide_db::{ + base_db::SourceDatabase, defs::Definition, documentation::docs_from_attrs, helpers::visit_file_defs, @@ -399,7 +400,8 @@ sema: &Semantics<'_, RootDatabase>, def: &hir::Impl, ) -> Option<Runnable> { - let edition = def.module(sema.db).krate().edition(sema.db); + let display_target = def.module(sema.db).krate().to_display_target(sema.db); + let edition = display_target.edition; let attrs = def.attrs(sema.db); if !has_runnable_doc_test(&attrs) { return None; @@ -408,7 +410,7 @@ let nav = def.try_to_nav(sema.db)?.call_site(); let ty = def.self_ty(sema.db); let adt_name = ty.as_adt()?.name(sema.db); - let mut ty_args = ty.generic_parameters(sema.db, edition).peekable(); + let mut ty_args = ty.generic_parameters(sema.db, display_target).peekable(); let params = if ty_args.peek().is_some() { format!("<{}>", ty_args.format_with(",", |ty, cb| cb(&ty))) } else { @@ -494,7 +496,11 @@ Definition::SelfType(it) => it.attrs(db), _ => return None, }; - let edition = def.krate(db).map(|it| it.edition(db)).unwrap_or(Edition::CURRENT); + let krate = def.krate(db); + let edition = krate.map(|it| it.edition(db)).unwrap_or(Edition::CURRENT); + let display_target = krate + .unwrap_or_else(|| (*db.crate_graph().crates_in_topological_order().last().unwrap()).into()) + .to_display_target(db); if !has_runnable_doc_test(&attrs) { return None; } @@ -509,7 +515,7 @@ if let Some(ty) = assoc_item.implementing_ty(db) { if let Some(adt) = ty.as_adt() { let name = adt.name(db); - let mut ty_args = ty.generic_parameters(db, edition).peekable(); + let mut ty_args = ty.generic_parameters(db, display_target).peekable(); format_to!(path, "{}", name.display(db, edition)); if ty_args.peek().is_some() { format_to!(path, "<{}>", ty_args.format_with(",", |ty, cb| cb(&ty)));
diff --git a/crates/ide/src/signature_help.rs b/crates/ide/src/signature_help.rs index f997211..b5468a5 100644 --- a/crates/ide/src/signature_help.rs +++ b/crates/ide/src/signature_help.rs
@@ -4,7 +4,9 @@ use std::collections::BTreeSet; use either::Either; -use hir::{AssocItem, GenericParam, HirDisplay, ModuleDef, PathResolution, Semantics, Trait}; +use hir::{ + AssocItem, DisplayTarget, GenericParam, HirDisplay, ModuleDef, PathResolution, Semantics, Trait, +}; use ide_db::{ active_parameter::{callable_for_node, generic_def_for_node}, documentation::{Documentation, HasDocs}, @@ -82,6 +84,7 @@ let token = sema.descend_into_macros_single_exact(token); 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); for node in token.parent_ancestors() { match_ast! { @@ -91,49 +94,49 @@ if cursor_outside { continue; } - return signature_help_for_call(&sema, arg_list, token, edition); + return signature_help_for_call(&sema, arg_list, token, edition, display_target); }, ast::GenericArgList(garg_list) => { let cursor_outside = garg_list.r_angle_token().as_ref() == Some(&token); if cursor_outside { continue; } - return signature_help_for_generics(&sema, garg_list, token, edition); + return signature_help_for_generics(&sema, garg_list, token, edition, display_target); }, ast::RecordExpr(record) => { let cursor_outside = record.record_expr_field_list().and_then(|list| list.r_curly_token()).as_ref() == Some(&token); if cursor_outside { continue; } - return signature_help_for_record_lit(&sema, record, token, edition); + return signature_help_for_record_lit(&sema, record, token, edition, display_target); }, ast::RecordPat(record) => { let cursor_outside = record.record_pat_field_list().and_then(|list| list.r_curly_token()).as_ref() == Some(&token); if cursor_outside { continue; } - return signature_help_for_record_pat(&sema, record, token, edition); + return signature_help_for_record_pat(&sema, record, token, edition, display_target); }, ast::TupleStructPat(tuple_pat) => { let cursor_outside = tuple_pat.r_paren_token().as_ref() == Some(&token); if cursor_outside { continue; } - return signature_help_for_tuple_struct_pat(&sema, tuple_pat, token, edition); + return signature_help_for_tuple_struct_pat(&sema, tuple_pat, token, edition, display_target); }, ast::TuplePat(tuple_pat) => { let cursor_outside = tuple_pat.r_paren_token().as_ref() == Some(&token); if cursor_outside { continue; } - return signature_help_for_tuple_pat(&sema, tuple_pat, token, edition); + return signature_help_for_tuple_pat(&sema, tuple_pat, token, display_target); }, ast::TupleExpr(tuple_expr) => { let cursor_outside = tuple_expr.r_paren_token().as_ref() == Some(&token); if cursor_outside { continue; } - return signature_help_for_tuple_expr(&sema, tuple_expr, token, edition); + return signature_help_for_tuple_expr(&sema, tuple_expr, token, display_target); }, _ => (), } @@ -158,6 +161,7 @@ arg_list: ast::ArgList, token: SyntaxToken, edition: Edition, + display_target: DisplayTarget, ) -> Option<SignatureHelp> { // Find the calling expression and its NameRef let mut nodes = arg_list.syntax().ancestors().skip(1); @@ -221,7 +225,7 @@ res.signature.push('('); { if let Some((self_param, _)) = callable.receiver_param(db) { - format_to!(res.signature, "{}", self_param.display(db, edition)) + format_to!(res.signature, "{}", self_param.display(db, display_target)) } let mut buf = String::new(); for (idx, p) in callable.params().into_iter().enumerate() { @@ -242,9 +246,9 @@ // (see FIXME in tests::impl_trait) and falling back on any unknowns. match (p.ty().contains_unknown(), fn_params.as_deref()) { (true, Some(fn_params)) => { - format_to!(buf, "{}", fn_params[idx].ty().display(db, edition)) + format_to!(buf, "{}", fn_params[idx].ty().display(db, display_target)) } - _ => format_to!(buf, "{}", p.ty().display(db, edition)), + _ => format_to!(buf, "{}", p.ty().display(db, display_target)), } res.push_call_param(&buf); } @@ -253,7 +257,7 @@ let mut render = |ret_type: hir::Type| { if !ret_type.is_unit() { - format_to!(res.signature, " -> {}", ret_type.display(db, edition)); + format_to!(res.signature, " -> {}", ret_type.display(db, display_target)); } }; match callable.kind() { @@ -274,6 +278,7 @@ arg_list: ast::GenericArgList, token: SyntaxToken, edition: Edition, + display_target: DisplayTarget, ) -> Option<SignatureHelp> { let (generics_def, mut active_parameter, first_arg_is_non_lifetime, variant) = generic_def_for_node(sema, &arg_list, &token)?; @@ -345,7 +350,7 @@ } buf.clear(); - format_to!(buf, "{}", param.display(db, edition)); + format_to!(buf, "{}", param.display(db, display_target)); res.push_generic_param(&buf); } if let hir::GenericDef::Trait(tr) = generics_def { @@ -400,6 +405,7 @@ record: ast::RecordExpr, token: SyntaxToken, edition: Edition, + display_target: DisplayTarget, ) -> Option<SignatureHelp> { signature_help_for_record_( sema, @@ -412,6 +418,7 @@ .map(|(field, _, ty)| (field, ty)), token, edition, + display_target, ) } @@ -420,6 +427,7 @@ record: ast::RecordPat, token: SyntaxToken, edition: Edition, + display_target: DisplayTarget, ) -> Option<SignatureHelp> { signature_help_for_record_( sema, @@ -431,6 +439,7 @@ .filter_map(|field| sema.resolve_record_pat_field(&field)), token, edition, + display_target, ) } @@ -439,6 +448,7 @@ pat: ast::TupleStructPat, token: SyntaxToken, edition: Edition, + display_target: DisplayTarget, ) -> Option<SignatureHelp> { let path = pat.path()?; let path_res = sema.resolve_path(&path)?; @@ -484,7 +494,7 @@ token, pat.fields(), fields.into_iter().map(|it| it.ty(db)), - edition, + display_target, )) } @@ -492,7 +502,7 @@ sema: &Semantics<'_, RootDatabase>, pat: ast::TuplePat, token: SyntaxToken, - edition: Edition, + display_target: DisplayTarget, ) -> Option<SignatureHelp> { let db = sema.db; let field_pats = pat.fields(); @@ -512,7 +522,7 @@ token, field_pats, fields.into_iter(), - edition, + display_target, )) } @@ -520,7 +530,7 @@ sema: &Semantics<'_, RootDatabase>, expr: ast::TupleExpr, token: SyntaxToken, - edition: Edition, + display_target: DisplayTarget, ) -> Option<SignatureHelp> { let active_parameter = Some( expr.syntax() @@ -542,7 +552,7 @@ let fields = expr.original.tuple_fields(db); let mut buf = String::new(); for ty in fields { - format_to!(buf, "{}", ty.display_truncated(db, Some(20), edition)); + format_to!(buf, "{}", ty.display_truncated(db, Some(20), display_target)); res.push_call_param(&buf); buf.clear(); } @@ -557,6 +567,7 @@ fields2: impl Iterator<Item = (hir::Field, hir::Type)>, token: SyntaxToken, edition: Edition, + display_target: DisplayTarget, ) -> Option<SignatureHelp> { let active_parameter = field_list_children .filter_map(NodeOrToken::into_token) @@ -617,7 +628,7 @@ buf, "{}: {}", name.display(db, edition), - ty.display_truncated(db, Some(20), edition) + ty.display_truncated(db, Some(20), display_target) ); res.push_record_field(&buf); buf.clear(); @@ -632,7 +643,7 @@ buf, "{}: {}", name.display(db, edition), - field.ty(db).display_truncated(db, Some(20), edition) + field.ty(db).display_truncated(db, Some(20), display_target) ); res.push_record_field(&buf); buf.clear(); @@ -648,7 +659,7 @@ token: SyntaxToken, mut field_pats: AstChildren<ast::Pat>, fields: impl ExactSizeIterator<Item = hir::Type>, - edition: Edition, + display_target: DisplayTarget, ) -> SignatureHelp { let rest_pat = field_pats.find(|it| matches!(it, ast::Pat::RestPat(_))); let is_left_of_rest_pat = @@ -675,7 +686,7 @@ let mut buf = String::new(); for ty in fields { - format_to!(buf, "{}", ty.display_truncated(db, Some(20), edition)); + format_to!(buf, "{}", ty.display_truncated(db, Some(20), display_target)); res.push_call_param(&buf); buf.clear(); }
diff --git a/crates/ide/src/ssr.rs b/crates/ide/src/ssr.rs index 77a011c..90e3509 100644 --- a/crates/ide/src/ssr.rs +++ b/crates/ide/src/ssr.rs
@@ -139,6 +139,7 @@ Indel { insert: "3", delete: 33..34, + annotation: None, }, ], }, @@ -147,6 +148,8 @@ }, file_system_edits: [], is_snippet: false, + annotations: {}, + next_annotation_id: 0, }, ), command: None, @@ -179,6 +182,7 @@ Indel { insert: "3", delete: 33..34, + annotation: None, }, ], }, @@ -192,6 +196,7 @@ Indel { insert: "3", delete: 11..12, + annotation: None, }, ], }, @@ -200,6 +205,8 @@ }, file_system_edits: [], is_snippet: false, + annotations: {}, + next_annotation_id: 0, }, ), command: None,
diff --git a/crates/ide/src/static_index.rs b/crates/ide/src/static_index.rs index 41957ba..332aecf 100644 --- a/crates/ide/src/static_index.rs +++ b/crates/ide/src/static_index.rs
@@ -3,7 +3,7 @@ use hir::{db::HirDatabase, Crate, HirFileIdExt, Module, Semantics}; use ide_db::{ - base_db::{SourceRootDatabase, VfsPath}, + base_db::{SourceDatabase, SourceRootDatabase, VfsPath}, defs::Definition, documentation::Documentation, famous_defs::FamousDefs, @@ -118,7 +118,11 @@ def.docs( sema.db, famous_defs.as_ref(), - def.krate(sema.db).map(|it| it.edition(sema.db)).unwrap_or(Edition::CURRENT), + def.krate(sema.db) + .unwrap_or_else(|| { + (*sema.db.crate_graph().crates_in_topological_order().last().unwrap()).into() + }) + .to_display_target(sema.db), ) } @@ -173,6 +177,7 @@ let root = 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(self.db); let tokens = root.descendants_with_tokens().filter_map(|it| match it { syntax::NodeOrToken::Node(_) => None, syntax::NodeOrToken::Token(it) => Some(it), @@ -213,6 +218,7 @@ false, &hover_config, edition, + display_target, )), definition: def.try_to_nav(self.db).map(UpmappingResult::call_site).map(|it| { FileRange { file_id: it.file_id, range: it.focus_or_full_range() } @@ -222,7 +228,7 @@ display_name: def .name(self.db) .map(|name| name.display(self.db, edition).to_string()), - signature: Some(def.label(self.db, edition)), + signature: Some(def.label(self.db, display_target)), kind: def_to_kind(self.db, def), }); self.def_map.insert(def, it);
diff --git a/crates/ide/src/status.rs b/crates/ide/src/status.rs index f8ecaa8..a44be67 100644 --- a/crates/ide/src/status.rs +++ b/crates/ide/src/status.rs
@@ -268,8 +268,7 @@ impl fmt::Display for AttrsStats { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - let size = - self.entries * std::mem::size_of::<Attrs>() + self.total * std::mem::size_of::<Attr>(); + let size = self.entries * size_of::<Attrs>() + self.total * size_of::<Attr>(); let size = Bytes::new(size as _); write!( fmt,
diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs index 519133e..8308249 100644 --- a/crates/ide/src/syntax_highlighting.rs +++ b/crates/ide/src/syntax_highlighting.rs
@@ -14,8 +14,11 @@ use std::ops::ControlFlow; -use hir::{HirFileIdExt, InFile, InRealFile, MacroFileIdExt, MacroKind, Name, Semantics}; -use ide_db::{FxHashMap, Ranker, RootDatabase, SymbolKind}; +use either::Either; +use hir::{ + DefWithBody, HirFileIdExt, InFile, InRealFile, MacroFileIdExt, MacroKind, Name, Semantics, +}; +use ide_db::{FxHashMap, FxHashSet, Ranker, RootDatabase, SymbolKind}; use span::EditionedFileId; use syntax::{ ast::{self, IsString}, @@ -232,7 +235,6 @@ range_to_highlight: TextRange, ) { let is_unlinked = sema.file_to_module_def(file_id).is_none(); - let mut bindings_shadow_count: FxHashMap<Name, u32> = FxHashMap::default(); enum AttrOrDerive { Attr(ast::Item), @@ -247,13 +249,22 @@ } } + let empty = FxHashSet::default(); + + // FIXME: accommodate range highlighting let mut tt_level = 0; + // FIXME: accommodate range highlighting let mut attr_or_derive_item = None; // FIXME: these are not perfectly accurate, we determine them by the real file's syntax tree // an attribute nested in a macro call will not emit `inside_attribute` let mut inside_attribute = false; + // FIXME: accommodate range highlighting + let mut body_stack: Vec<Option<DefWithBody>> = vec![]; + let mut per_body_cache: FxHashMap<DefWithBody, (FxHashSet<_>, FxHashMap<Name, u32>)> = + FxHashMap::default(); + // Walk all nodes, keeping track of whether we are inside a macro or not. // If in macro, expand it first and highlight the expanded code. let mut preorder = root.preorder_with_tokens(); @@ -282,48 +293,68 @@ Leave(NodeOrToken::Node(node)) if ast::Attr::can_cast(node.kind()) => { inside_attribute = false } - Enter(NodeOrToken::Node(node)) => { - if let Some(item) = ast::Item::cast(node.clone()) { + if let Some(item) = <Either<ast::Item, ast::Variant>>::cast(node.clone()) { match item { - ast::Item::Fn(_) | ast::Item::Const(_) | ast::Item::Static(_) => { - bindings_shadow_count.clear() - } - _ => (), - } - - if attr_or_derive_item.is_none() { - if sema.is_attr_macro_call(InFile::new(file_id.into(), &item)) { - attr_or_derive_item = Some(AttrOrDerive::Attr(item)); - } else { - let adt = match item { - ast::Item::Enum(it) => Some(ast::Adt::Enum(it)), - ast::Item::Struct(it) => Some(ast::Adt::Struct(it)), - ast::Item::Union(it) => Some(ast::Adt::Union(it)), - _ => None, - }; - match adt { - Some(adt) - if sema - .is_derive_annotated(InFile::new(file_id.into(), &adt)) => - { - attr_or_derive_item = - Some(AttrOrDerive::Derive(ast::Item::from(adt))); + Either::Left(item) => { + match &item { + ast::Item::Fn(it) => { + body_stack.push(sema.to_def(it).map(Into::into)) + } + ast::Item::Const(it) => { + body_stack.push(sema.to_def(it).map(Into::into)) + } + ast::Item::Static(it) => { + body_stack.push(sema.to_def(it).map(Into::into)) } _ => (), } + + if attr_or_derive_item.is_none() { + if sema.is_attr_macro_call(InFile::new(file_id.into(), &item)) { + attr_or_derive_item = Some(AttrOrDerive::Attr(item)); + } else { + let adt = match item { + ast::Item::Enum(it) => Some(ast::Adt::Enum(it)), + ast::Item::Struct(it) => Some(ast::Adt::Struct(it)), + ast::Item::Union(it) => Some(ast::Adt::Union(it)), + _ => None, + }; + match adt { + Some(adt) + if sema.is_derive_annotated(InFile::new( + file_id.into(), + &adt, + )) => + { + attr_or_derive_item = + Some(AttrOrDerive::Derive(ast::Item::from(adt))); + } + _ => (), + } + } + } } + Either::Right(it) => body_stack.push(sema.to_def(&it).map(Into::into)), } } } - Leave(NodeOrToken::Node(node)) if ast::Item::can_cast(node.kind()) => { + Leave(NodeOrToken::Node(node)) + if <Either<ast::Item, ast::Variant>>::can_cast(node.kind()) => + { match ast::Item::cast(node.clone()) { - Some(item) - if attr_or_derive_item.as_ref().is_some_and(|it| *it.item() == item) => - { - attr_or_derive_item = None; + Some(item) => { + if attr_or_derive_item.as_ref().is_some_and(|it| *it.item() == item) { + attr_or_derive_item = None; + } + if matches!( + item, + ast::Item::Fn(_) | ast::Item::Const(_) | ast::Item::Static(_) + ) { + body_stack.pop(); + } } - _ => (), + None => _ = body_stack.pop(), } } _ => (), @@ -361,16 +392,22 @@ None => false, }; - let descended_element = if in_macro { + let (descended_element, current_body) = match element { // Attempt to descend tokens into macro-calls. - match element { - NodeOrToken::Token(token) => descend_token(sema, InRealFile::new(file_id, token)), - n => InFile::new(file_id.into(), n), + NodeOrToken::Token(token) if in_macro => { + let descended = descend_token(sema, InRealFile::new(file_id, token)); + let body = match &descended.value { + NodeOrToken::Node(n) => { + sema.body_for(InFile::new(descended.file_id, n.syntax())) + } + NodeOrToken::Token(t) => { + t.parent().and_then(|it| sema.body_for(InFile::new(descended.file_id, &it))) + } + }; + (descended, body) } - } else { - InFile::new(file_id.into(), element) + n => (InFile::new(file_id.into(), n), body_stack.last().copied().flatten()), }; - // string highlight injections if let (Some(original_token), Some(descended_token)) = (original_token, descended_element.value.as_token()) @@ -390,12 +427,24 @@ } let edition = descended_element.file_id.edition(sema.db); + let (unsafe_ops, bindings_shadow_count) = match current_body { + Some(current_body) => { + let (ops, bindings) = per_body_cache + .entry(current_body) + .or_insert_with(|| (sema.get_unsafe_ops(current_body), Default::default())); + (&*ops, Some(bindings)) + } + None => (&empty, None), + }; + let is_unsafe_node = + |node| unsafe_ops.contains(&InFile::new(descended_element.file_id, node)); let element = match descended_element.value { NodeOrToken::Node(name_like) => { let hl = highlight::name_like( sema, krate, - &mut bindings_shadow_count, + bindings_shadow_count, + &is_unsafe_node, config.syntactic_name_ref_highlighting, name_like, edition, @@ -408,7 +457,8 @@ hl } NodeOrToken::Token(token) => { - highlight::token(sema, token, edition, tt_level > 0).zip(Some(None)) + highlight::token(sema, token, edition, &is_unsafe_node, tt_level > 0) + .zip(Some(None)) } }; if let Some((mut highlight, binding_hash)) = element { @@ -543,7 +593,7 @@ *tag = HlTag::Punctuation(HlPunct::Other); } } - HlTag::Punctuation(_) if !config.punctuation => return false, + HlTag::Punctuation(_) if !config.punctuation && highlight.mods.is_empty() => return false, tag @ HlTag::Punctuation(_) if !config.specialize_punctuation => { *tag = HlTag::Punctuation(HlPunct::Other); }
diff --git a/crates/ide/src/syntax_highlighting/format.rs b/crates/ide/src/syntax_highlighting/format.rs index c630436..cc02aff 100644 --- a/crates/ide/src/syntax_highlighting/format.rs +++ b/crates/ide/src/syntax_highlighting/format.rs
@@ -41,7 +41,7 @@ if let Some(res) = res { stack.add(HlRange { range, - highlight: highlight_def(sema, krate, Definition::from(res), edition), + highlight: highlight_def(sema, krate, Definition::from(res), edition, true), binding_hash: None, }) }
diff --git a/crates/ide/src/syntax_highlighting/highlight.rs b/crates/ide/src/syntax_highlighting/highlight.rs index 127861a..282fbb4 100644 --- a/crates/ide/src/syntax_highlighting/highlight.rs +++ b/crates/ide/src/syntax_highlighting/highlight.rs
@@ -1,17 +1,20 @@ //! Computes color for a single element. +use std::ops::ControlFlow; + use either::Either; use hir::{AsAssocItem, HasVisibility, MacroFileIdExt, Semantics}; use ide_db::{ defs::{Definition, IdentClass, NameClass, NameRefClass}, + syntax_helpers::node_ext::walk_pat, FxHashMap, RootDatabase, SymbolKind, }; use span::Edition; use stdx::hash_once; use syntax::{ - ast, match_ast, AstNode, AstToken, NodeOrToken, + ast, match_ast, AstNode, AstPtr, AstToken, NodeOrToken, SyntaxKind::{self, *}, - SyntaxNode, SyntaxToken, T, + SyntaxNode, SyntaxNodePtr, SyntaxToken, T, }; use crate::{ @@ -23,6 +26,7 @@ sema: &Semantics<'_, RootDatabase>, token: SyntaxToken, edition: Edition, + is_unsafe_node: &impl Fn(AstPtr<Either<ast::Expr, ast::Pat>>) -> bool, in_tt: bool, ) -> Option<Highlight> { if let Some(comment) = ast::Comment::cast(token.clone()) { @@ -33,11 +37,8 @@ }); } - let highlight: Highlight = match token.kind() { + let h = match token.kind() { STRING | BYTE_STRING | C_STRING => HlTag::StringLiteral.into(), - INT_NUMBER if token.parent_ancestors().nth(1).map(|it| it.kind()) == Some(FIELD_EXPR) => { - SymbolKind::Field.into() - } INT_NUMBER | FLOAT_NUMBER => HlTag::NumericLiteral.into(), BYTE => HlTag::ByteLiteral.into(), CHAR => HlTag::CharLiteral.into(), @@ -46,24 +47,25 @@ // that were not mapped down into macro invocations HlTag::None.into() } - p if p.is_punct() => punctuation(sema, token, p), + p if p.is_punct() => punctuation(sema, token, p, is_unsafe_node), k if k.is_keyword(edition) => { if in_tt && token.prev_token().is_some_and(|t| t.kind() == T![$]) { // we are likely within a macro definition where our keyword is a fragment name HlTag::None.into() } else { - keyword(sema, token, k)? + keyword(token, k) } } _ => return None, }; - Some(highlight) + Some(h) } pub(super) fn name_like( sema: &Semantics<'_, RootDatabase>, krate: hir::Crate, - bindings_shadow_count: &mut FxHashMap<hir::Name, u32>, + bindings_shadow_count: Option<&mut FxHashMap<hir::Name, u32>>, + is_unsafe_node: &impl Fn(AstPtr<Either<ast::Expr, ast::Pat>>) -> bool, syntactic_name_ref_highlighting: bool, name_like: ast::NameLike, edition: Edition, @@ -75,19 +77,26 @@ krate, bindings_shadow_count, &mut binding_hash, + is_unsafe_node, syntactic_name_ref_highlighting, name_ref, edition, ), - ast::NameLike::Name(name) => { - highlight_name(sema, bindings_shadow_count, &mut binding_hash, krate, name, edition) - } + ast::NameLike::Name(name) => highlight_name( + sema, + bindings_shadow_count, + &mut binding_hash, + is_unsafe_node, + krate, + name, + edition, + ), ast::NameLike::Lifetime(lifetime) => match IdentClass::classify_lifetime(sema, &lifetime) { Some(IdentClass::NameClass(NameClass::Definition(def))) => { - highlight_def(sema, krate, def, edition) | HlMod::Definition + highlight_def(sema, krate, def, edition, false) | HlMod::Definition } Some(IdentClass::NameRefClass(NameRefClass::Definition(def, _))) => { - highlight_def(sema, krate, def, edition) + highlight_def(sema, krate, def, edition, true) } // FIXME: Fallback for '_, as we do not resolve these yet _ => SymbolKind::LifetimeParam.into(), @@ -100,44 +109,49 @@ sema: &Semantics<'_, RootDatabase>, token: SyntaxToken, kind: SyntaxKind, + is_unsafe_node: &impl Fn(AstPtr<Either<ast::Expr, ast::Pat>>) -> bool, ) -> Highlight { - let parent = token.parent(); - let parent_kind = parent.as_ref().map_or(EOF, SyntaxNode::kind); - match (kind, parent_kind) { + let operator_parent = token.parent(); + let parent_kind = operator_parent.as_ref().map_or(EOF, SyntaxNode::kind); + let h = match (kind, parent_kind) { (T![?], TRY_EXPR) => HlTag::Operator(HlOperator::Other) | HlMod::ControlFlow, (T![&], BIN_EXPR) => HlOperator::Bitwise.into(), - (T![&], REF_EXPR) => { + (T![&], REF_EXPR | REF_PAT) => HlTag::Operator(HlOperator::Other).into(), + (T![..] | T![..=], _) => match token.parent().and_then(ast::Pat::cast) { + Some(pat) if is_unsafe_node(AstPtr::new(&pat).wrap_right()) => { + Highlight::from(HlOperator::Other) | HlMod::Unsafe + } + _ => HlOperator::Other.into(), + }, + (T![::] | T![->] | T![=>] | T![=] | T![@] | T![.], _) => HlOperator::Other.into(), + (T![!], MACRO_CALL) => { + if operator_parent + .and_then(ast::MacroCall::cast) + .is_some_and(|macro_call| sema.is_unsafe_macro_call(¯o_call)) + { + Highlight::from(HlPunct::MacroBang) | HlMod::Unsafe + } else { + HlPunct::MacroBang.into() + } + } + (T![!], MACRO_RULES) => HlPunct::MacroBang.into(), + (T![!], NEVER_TYPE) => HlTag::BuiltinType.into(), + (T![!], PREFIX_EXPR) => HlOperator::Logical.into(), + (T![*], PTR_TYPE) => HlTag::Keyword.into(), + (T![*], PREFIX_EXPR) => { let h = HlTag::Operator(HlOperator::Other).into(); - let is_unsafe = parent - .and_then(ast::RefExpr::cast) - .map(|ref_expr| sema.is_unsafe_ref_expr(&ref_expr)); - if let Some(true) = is_unsafe { + let ptr = operator_parent + .as_ref() + .and_then(|it| AstPtr::try_from_raw(SyntaxNodePtr::new(it))); + if ptr.is_some_and(is_unsafe_node) { h | HlMod::Unsafe } else { h } } - (T![::] | T![->] | T![=>] | T![..] | T![..=] | T![=] | T![@] | T![.], _) => { - HlOperator::Other.into() - } - (T![!], MACRO_CALL | MACRO_RULES) => HlPunct::MacroBang.into(), - (T![!], NEVER_TYPE) => HlTag::BuiltinType.into(), - (T![!], PREFIX_EXPR) => HlOperator::Logical.into(), - (T![*], PTR_TYPE) => HlTag::Keyword.into(), - (T![*], PREFIX_EXPR) => { - let is_raw_ptr = (|| { - let prefix_expr = parent.and_then(ast::PrefixExpr::cast)?; - let expr = prefix_expr.expr()?; - sema.type_of_expr(&expr)?.original.is_raw_ptr().then_some(()) - })(); - if let Some(()) = is_raw_ptr { - HlTag::Operator(HlOperator::Other) | HlMod::Unsafe - } else { - HlOperator::Other.into() - } - } (T![-], PREFIX_EXPR) => { - let prefix_expr = parent.and_then(ast::PrefixExpr::cast).and_then(|e| e.expr()); + let prefix_expr = + operator_parent.and_then(ast::PrefixExpr::cast).and_then(|e| e.expr()); match prefix_expr { Some(ast::Expr::Literal(_)) => HlTag::NumericLiteral, _ => HlTag::Operator(HlOperator::Other), @@ -157,36 +171,90 @@ HlOperator::Comparison.into() } (_, ATTR) => HlTag::AttributeBracket.into(), + (T![>], _) + if operator_parent + .as_ref() + .and_then(SyntaxNode::parent) + .is_some_and(|it| it.kind() == MACRO_RULES) => + { + HlOperator::Other.into() + } (kind, _) => match kind { - T!['['] | T![']'] => HlPunct::Bracket, - T!['{'] | T!['}'] => HlPunct::Brace, - T!['('] | T![')'] => HlPunct::Parenthesis, - T![>] - if parent + T!['['] | T![']'] => { + let is_unsafe_macro = operator_parent .as_ref() - .and_then(SyntaxNode::parent) - .is_some_and(|it| it.kind() == MACRO_RULES) => - { - return HlOperator::Other.into() + .and_then(|it| ast::TokenTree::cast(it.clone())?.syntax().parent()) + .and_then(ast::MacroCall::cast) + .is_some_and(|macro_call| sema.is_unsafe_macro_call(¯o_call)); + let is_unsafe = is_unsafe_macro + || operator_parent + .as_ref() + .and_then(|it| AstPtr::try_from_raw(SyntaxNodePtr::new(it))) + .is_some_and(is_unsafe_node); + if is_unsafe { + return Highlight::from(HlPunct::Bracket) | HlMod::Unsafe; + } else { + HlPunct::Bracket + } + } + T!['{'] | T!['}'] => { + let is_unsafe_macro = operator_parent + .as_ref() + .and_then(|it| ast::TokenTree::cast(it.clone())?.syntax().parent()) + .and_then(ast::MacroCall::cast) + .is_some_and(|macro_call| sema.is_unsafe_macro_call(¯o_call)); + let is_unsafe = is_unsafe_macro + || operator_parent + .as_ref() + .and_then(|it| AstPtr::try_from_raw(SyntaxNodePtr::new(it))) + .is_some_and(is_unsafe_node); + if is_unsafe { + return Highlight::from(HlPunct::Brace) | HlMod::Unsafe; + } else { + HlPunct::Brace + } + } + T!['('] | T![')'] => { + let is_unsafe_macro = operator_parent + .as_ref() + .and_then(|it| ast::TokenTree::cast(it.clone())?.syntax().parent()) + .and_then(ast::MacroCall::cast) + .is_some_and(|macro_call| sema.is_unsafe_macro_call(¯o_call)); + let is_unsafe = is_unsafe_macro + || operator_parent + .and_then(|it| { + if ast::ArgList::can_cast(it.kind()) { + it.parent() + } else { + Some(it) + } + }) + .and_then(|it| AstPtr::try_from_raw(SyntaxNodePtr::new(&it))) + .is_some_and(is_unsafe_node); + + if is_unsafe { + return Highlight::from(HlPunct::Parenthesis) | HlMod::Unsafe; + } else { + HlPunct::Parenthesis + } } T![<] | T![>] => HlPunct::Angle, - T![,] => HlPunct::Comma, + // Early return as otherwise we'd highlight these in + // asm expressions + T![,] => return HlPunct::Comma.into(), T![:] => HlPunct::Colon, T![;] => HlPunct::Semi, T![.] => HlPunct::Dot, _ => HlPunct::Other, } .into(), - } + }; + h } -fn keyword( - sema: &Semantics<'_, RootDatabase>, - token: SyntaxToken, - kind: SyntaxKind, -) -> Option<Highlight> { +fn keyword(token: SyntaxToken, kind: SyntaxKind) -> Highlight { let h = Highlight::new(HlTag::Keyword); - let h = match kind { + match kind { T![await] => h | HlMod::Async | HlMod::ControlFlow, T![async] => h | HlMod::Async, T![break] @@ -202,53 +270,33 @@ T![do] | T![yeet] if parent_matches::<ast::YeetExpr>(&token) => h | HlMod::ControlFlow, T![for] if parent_matches::<ast::ForExpr>(&token) => h | HlMod::ControlFlow, T![unsafe] => h | HlMod::Unsafe, - T![const] - if token.parent().is_some_and(|it| { - matches!( - it.kind(), - SyntaxKind::CONST - | SyntaxKind::FN - | SyntaxKind::IMPL - | SyntaxKind::BLOCK_EXPR - | SyntaxKind::CLOSURE_EXPR - | SyntaxKind::FN_PTR_TYPE - | SyntaxKind::TYPE_BOUND - | SyntaxKind::CONST_BLOCK_PAT - ) - }) => - { - h | HlMod::Const - } + T![const] => h | HlMod::Const, T![true] | T![false] => HlTag::BoolLiteral.into(), // crate is handled just as a token if it's in an `extern crate` T![crate] if parent_matches::<ast::ExternCrate>(&token) => h, - T![ref] => match token.parent().and_then(ast::IdentPat::cast) { - Some(ident) if sema.is_unsafe_ident_pat(&ident) => h | HlMod::Unsafe, - _ => h, - }, _ => h, - }; - Some(h) + } } fn highlight_name_ref( sema: &Semantics<'_, RootDatabase>, krate: hir::Crate, - bindings_shadow_count: &mut FxHashMap<hir::Name, u32>, + bindings_shadow_count: Option<&mut FxHashMap<hir::Name, u32>>, binding_hash: &mut Option<u64>, + is_unsafe_node: &impl Fn(AstPtr<Either<ast::Expr, ast::Pat>>) -> bool, syntactic_name_ref_highlighting: bool, name_ref: ast::NameRef, edition: Edition, ) -> Highlight { let db = sema.db; - if let Some(res) = highlight_method_call_by_name_ref(sema, krate, &name_ref, edition) { + if let Some(res) = highlight_method_call_by_name_ref(sema, krate, &name_ref, is_unsafe_node) { return res; } let name_class = match NameRefClass::classify(sema, &name_ref) { Some(name_kind) => name_kind, None if syntactic_name_ref_highlighting => { - return highlight_name_ref_by_syntax(name_ref, sema, krate, edition) + return highlight_name_ref_by_syntax(name_ref, sema, krate, is_unsafe_node) } // FIXME: This is required for helper attributes used by proc-macros, as those do not map down // to anything when used. @@ -267,17 +315,20 @@ let mut h = match name_class { NameRefClass::Definition(def, _) => { if let Definition::Local(local) = &def { - let name = local.name(db); - let shadow_count = bindings_shadow_count.entry(name.clone()).or_default(); - *binding_hash = Some(calc_binding_hash(&name, *shadow_count)) + if let Some(bindings_shadow_count) = bindings_shadow_count { + let name = local.name(sema.db); + let shadow_count = bindings_shadow_count.entry(name.clone()).or_default(); + *binding_hash = Some(calc_binding_hash(&name, *shadow_count)) + } }; - let mut h = highlight_def(sema, krate, def, edition); + let mut h = highlight_def(sema, krate, def, edition, true); match def { Definition::Local(local) if is_consumed_lvalue(name_ref.syntax(), &local, db) => { h |= HlMod::Consuming; } + // highlight unsafe traits as unsafe only in their implementations Definition::Trait(trait_) if trait_.is_unsafe(db) => { if ast::Impl::for_trait_name_ref(&name_ref) .is_some_and(|impl_| impl_.unsafe_token().is_some()) @@ -285,23 +336,66 @@ h |= HlMod::Unsafe; } } - Definition::Field(field) => { - if let Some(parent) = name_ref.syntax().parent() { - if matches!(parent.kind(), FIELD_EXPR | RECORD_PAT_FIELD) { - if let hir::VariantDef::Union(_) = field.parent_def(db) { - h |= HlMod::Unsafe; - } - } + Definition::Function(_) => { + let is_unsafe = name_ref + .syntax() + .parent() + .and_then(|it| ast::PathSegment::cast(it)?.parent_path().syntax().parent()) + .and_then(ast::PathExpr::cast) + .and_then(|it| it.syntax().parent()) + .and_then(ast::CallExpr::cast) + .is_some_and(|it| { + is_unsafe_node(AstPtr::new(&ast::Expr::CallExpr(it)).wrap_left()) + }); + if is_unsafe { + h |= HlMod::Unsafe; } } Definition::Macro(_) => { - if let Some(macro_call) = - ide_db::syntax_helpers::node_ext::full_path_of_name_ref(&name_ref) - .and_then(|it| it.syntax().parent().and_then(ast::MacroCall::cast)) - { - if sema.is_unsafe_macro_call(¯o_call) { - h |= HlMod::Unsafe; - } + let is_unsafe = name_ref + .syntax() + .parent() + .and_then(|it| ast::PathSegment::cast(it)?.parent_path().syntax().parent()) + .and_then(ast::MacroCall::cast) + .is_some_and(|macro_call| sema.is_unsafe_macro_call(¯o_call)); + if is_unsafe { + h |= HlMod::Unsafe; + } + } + Definition::Field(_) => { + let is_unsafe = name_ref + .syntax() + .parent() + .and_then(|it| { + match_ast! { match it { + ast::FieldExpr(expr) => Some(is_unsafe_node(AstPtr::new(&Either::Left(expr.into())))), + ast::RecordPatField(pat) => { + walk_pat(&pat.pat()?, &mut |pat| { + if is_unsafe_node(AstPtr::new(&Either::Right(pat))) { + ControlFlow::Break(true) + } + else {ControlFlow::Continue(())} + }).break_value() + }, + _ => None, + }} + }) + .unwrap_or(false); + if is_unsafe { + h |= HlMod::Unsafe; + } + } + Definition::Static(_) => { + let is_unsafe = name_ref + .syntax() + .parent() + .and_then(|it| ast::PathSegment::cast(it)?.parent_path().syntax().parent()) + .and_then(ast::PathExpr::cast) + .is_some_and(|it| { + is_unsafe_node(AstPtr::new(&ast::Expr::PathExpr(it)).wrap_left()) + }); + if is_unsafe { + h |= HlMod::Unsafe; } } _ => (), @@ -310,7 +404,7 @@ h } NameRefClass::FieldShorthand { field_ref, .. } => { - highlight_def(sema, krate, field_ref.into(), edition) + highlight_def(sema, krate, field_ref.into(), edition, true) } NameRefClass::ExternCrateShorthand { decl, krate: resolved_krate } => { let mut h = HlTag::Symbol(SymbolKind::Module).into(); @@ -342,22 +436,25 @@ fn highlight_name( sema: &Semantics<'_, RootDatabase>, - bindings_shadow_count: &mut FxHashMap<hir::Name, u32>, + bindings_shadow_count: Option<&mut FxHashMap<hir::Name, u32>>, binding_hash: &mut Option<u64>, + is_unsafe_node: &impl Fn(AstPtr<Either<ast::Expr, ast::Pat>>) -> bool, krate: hir::Crate, name: ast::Name, edition: Edition, ) -> Highlight { let name_kind = NameClass::classify(sema, &name); if let Some(NameClass::Definition(Definition::Local(local))) = &name_kind { - let name = local.name(sema.db); - let shadow_count = bindings_shadow_count.entry(name.clone()).or_default(); - *shadow_count += 1; - *binding_hash = Some(calc_binding_hash(&name, *shadow_count)) + if let Some(bindings_shadow_count) = bindings_shadow_count { + let name = local.name(sema.db); + let shadow_count = bindings_shadow_count.entry(name.clone()).or_default(); + *shadow_count += 1; + *binding_hash = Some(calc_binding_hash(&name, *shadow_count)) + } }; match name_kind { Some(NameClass::Definition(def)) => { - let mut h = highlight_def(sema, krate, def, edition) | HlMod::Definition; + let mut h = highlight_def(sema, krate, def, edition, false) | HlMod::Definition; if let Definition::Trait(trait_) = &def { if trait_.is_unsafe(sema.db) { h |= HlMod::Unsafe; @@ -365,10 +462,14 @@ } h } - Some(NameClass::ConstReference(def)) => highlight_def(sema, krate, def, edition), - Some(NameClass::PatFieldShorthand { field_ref, .. }) => { + Some(NameClass::ConstReference(def)) => highlight_def(sema, krate, def, edition, true), + Some(NameClass::PatFieldShorthand { .. }) => { let mut h = HlTag::Symbol(SymbolKind::Field).into(); - if let hir::VariantDef::Union(_) = field_ref.parent_def(sema.db) { + let is_unsafe = + name.syntax().parent().and_then(ast::IdentPat::cast).is_some_and(|it| { + is_unsafe_node(AstPtr::new(&ast::Pat::IdentPat(it)).wrap_right()) + }); + if is_unsafe { h |= HlMod::Unsafe; } h @@ -386,6 +487,7 @@ krate: hir::Crate, def: Definition, edition: Edition, + is_ref: bool, ) -> Highlight { let db = sema.db; let mut h = match def { @@ -439,7 +541,7 @@ // We probably should consider checking the current function, but I found no easy way to do // that (also I'm worried about perf). There's also an instance below. // FIXME: This should be the edition of the call. - if func.is_unsafe_to_call(db, None, edition) { + if !is_ref && func.is_unsafe_to_call(db, None, edition) { h |= HlMod::Unsafe; } if func.is_async(db) { @@ -509,7 +611,9 @@ if s.is_mut(db) { h |= HlMod::Mutable; - h |= HlMod::Unsafe; + if !is_ref { + h |= HlMod::Unsafe; + } } h @@ -587,23 +691,24 @@ sema: &Semantics<'_, RootDatabase>, krate: hir::Crate, name_ref: &ast::NameRef, - edition: Edition, + is_unsafe_node: &impl Fn(AstPtr<Either<ast::Expr, ast::Pat>>) -> bool, ) -> Option<Highlight> { let mc = name_ref.syntax().parent().and_then(ast::MethodCallExpr::cast)?; - highlight_method_call(sema, krate, &mc, edition) + highlight_method_call(sema, krate, &mc, is_unsafe_node) } fn highlight_method_call( sema: &Semantics<'_, RootDatabase>, krate: hir::Crate, method_call: &ast::MethodCallExpr, - edition: Edition, + is_unsafe_node: &impl Fn(AstPtr<Either<ast::Expr, ast::Pat>>) -> bool, ) -> Option<Highlight> { let func = sema.resolve_method_call(method_call)?; let mut h = SymbolKind::Method.into(); - if func.is_unsafe_to_call(sema.db, None, edition) || sema.is_unsafe_method_call(method_call) { + let is_unsafe = is_unsafe_node(AstPtr::new(method_call).upcast::<ast::Expr>().wrap_left()); + if is_unsafe { h |= HlMod::Unsafe; } if func.is_async(sema.db) { @@ -695,7 +800,7 @@ name: ast::NameRef, sema: &Semantics<'_, RootDatabase>, krate: hir::Crate, - edition: Edition, + is_unsafe_node: &impl Fn(AstPtr<Either<ast::Expr, ast::Pat>>) -> bool, ) -> Highlight { let default = HlTag::UnresolvedReference; @@ -707,19 +812,13 @@ match parent.kind() { EXTERN_CRATE => HlTag::Symbol(SymbolKind::Module) | HlMod::CrateRoot, METHOD_CALL_EXPR => ast::MethodCallExpr::cast(parent) - .and_then(|it| highlight_method_call(sema, krate, &it, edition)) + .and_then(|it| highlight_method_call(sema, krate, &it, is_unsafe_node)) .unwrap_or_else(|| SymbolKind::Method.into()), FIELD_EXPR => { let h = HlTag::Symbol(SymbolKind::Field); - let is_union = ast::FieldExpr::cast(parent) - .and_then(|field_expr| sema.resolve_field(&field_expr)) - .is_some_and(|field| match field { - Either::Left(field) => { - matches!(field.parent_def(sema.db), hir::VariantDef::Union(_)) - } - Either::Right(_) => false, - }); - if is_union { + let is_unsafe = ast::Expr::cast(parent) + .is_some_and(|it| is_unsafe_node(AstPtr::new(&it).wrap_left())); + if is_unsafe { h | HlMod::Unsafe } else { h.into()
diff --git a/crates/ide/src/syntax_highlighting/html.rs b/crates/ide/src/syntax_highlighting/html.rs index 47ad547..07d40ba 100644 --- a/crates/ide/src/syntax_highlighting/html.rs +++ b/crates/ide/src/syntax_highlighting/html.rs
@@ -88,12 +88,6 @@ .string_literal { color: #CC9393; } .field { color: #94BFF3; } .function { color: #93E0E3; } -.function.unsafe { color: #BC8383; } -.trait.unsafe { color: #BC8383; } -.operator.unsafe { color: #BC8383; } -.mutable.unsafe { color: #BC8383; text-decoration: underline; } -.keyword.unsafe { color: #BC8383; font-weight: bold; } -.macro.unsafe { color: #BC8383; } .parameter { color: #94BFF3; } .text { color: #DCDCCC; } .type { color: #7CB8BB; } @@ -115,6 +109,7 @@ .control { font-style: italic; } .reference { font-style: italic; font-weight: bold; } .const { font-weight: bolder; } +.unsafe { color: #BC8383; } .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_asm.html b/crates/ide/src/syntax_highlighting/test_data/highlight_asm.html index 2bc22f9..c8ffa9e 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_asm.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_asm.html
@@ -14,12 +14,6 @@ .string_literal { color: #CC9393; } .field { color: #94BFF3; } .function { color: #93E0E3; } -.function.unsafe { color: #BC8383; } -.trait.unsafe { color: #BC8383; } -.operator.unsafe { color: #BC8383; } -.mutable.unsafe { color: #BC8383; text-decoration: underline; } -.keyword.unsafe { color: #BC8383; font-weight: bold; } -.macro.unsafe { color: #BC8383; } .parameter { color: #94BFF3; } .text { color: #DCDCCC; } .type { color: #7CB8BB; } @@ -41,6 +35,7 @@ .control { font-style: italic; } .reference { font-style: italic; font-weight: bold; } .const { font-weight: bolder; } +.unsafe { color: #BC8383; } .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } @@ -49,26 +44,26 @@ <span class="keyword unsafe">unsafe</span> <span class="brace">{</span> <span class="keyword">let</span> <span class="variable declaration">foo</span> <span class="operator">=</span> <span class="numeric_literal">1</span><span class="semicolon">;</span> <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">o</span> <span class="operator">=</span> <span class="numeric_literal">0</span><span class="semicolon">;</span> - <span class="module crate_root default_library library">core</span><span class="operator">::</span><span class="module default_library library">arch</span><span class="operator">::</span><span class="macro default_library library unsafe">asm</span><span class="macro_bang">!</span><span class="parenthesis">(</span> + <span class="module crate_root default_library library">core</span><span class="operator">::</span><span class="module default_library library">arch</span><span class="operator">::</span><span class="macro default_library library unsafe">asm</span><span class="macro_bang unsafe">!</span><span class="parenthesis unsafe">(</span> <span class="string_literal macro">"%input = OpLoad _ {</span><span class="variable">0</span><span class="string_literal macro">}"</span><span class="comma macro">,</span> <span class="macro default_library library macro">concat</span><span class="macro_bang macro">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"%result = "</span><span class="comma macro">,</span> <span class="string_literal macro">"bar"</span><span class="comma macro">,</span> <span class="string_literal macro">" _ %input"</span><span class="parenthesis macro">)</span><span class="comma macro">,</span> <span class="string_literal macro">"OpStore {</span><span class="variable">1</span><span class="string_literal macro">} %result"</span><span class="comma macro">,</span> <span class="keyword control macro">in</span><span class="parenthesis macro">(</span><span class="reg library macro">reg</span><span class="parenthesis macro">)</span> <span class="operator macro">&</span><span class="variable macro">foo</span><span class="comma macro">,</span> <span class="keyword control macro">in</span><span class="parenthesis macro">(</span><span class="reg library macro">reg</span><span class="parenthesis macro">)</span> <span class="operator macro">&</span><span class="keyword macro">mut</span> <span class="variable macro mutable">o</span><span class="comma macro">,</span> - <span class="parenthesis">)</span><span class="semicolon">;</span> + <span class="parenthesis unsafe">)</span><span class="semicolon">;</span> <span class="keyword">let</span> <span class="variable declaration">thread_id</span><span class="colon">:</span> <span class="builtin_type">usize</span><span class="semicolon">;</span> - <span class="module crate_root default_library library">core</span><span class="operator">::</span><span class="module default_library library">arch</span><span class="operator">::</span><span class="macro default_library library unsafe">asm</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">" + <span class="module crate_root default_library library">core</span><span class="operator">::</span><span class="module default_library library">arch</span><span class="operator">::</span><span class="macro default_library library unsafe">asm</span><span class="macro_bang unsafe">!</span><span class="parenthesis unsafe">(</span><span class="string_literal macro">" mov {</span><span class="variable">0</span><span class="string_literal macro">}, gs:[0x30] mov {</span><span class="variable">0</span><span class="string_literal macro">}, [{</span><span class="variable">0</span><span class="string_literal macro">}+0x48] - "</span><span class="comma macro">,</span> <span class="keyword macro">out</span><span class="parenthesis macro">(</span><span class="reg library macro">reg</span><span class="parenthesis macro">)</span> <span class="variable macro">thread_id</span><span class="comma macro">,</span> <span class="keyword macro">options</span><span class="parenthesis macro">(</span><span class="keyword macro">pure</span><span class="comma macro">,</span> <span class="keyword macro">readonly</span><span class="comma macro">,</span> <span class="keyword macro">nostack</span><span class="parenthesis macro">)</span><span class="parenthesis">)</span><span class="semicolon">;</span> + "</span><span class="comma macro">,</span> <span class="keyword macro">out</span><span class="parenthesis macro">(</span><span class="reg library macro">reg</span><span class="parenthesis macro">)</span> <span class="variable macro">thread_id</span><span class="comma macro">,</span> <span class="keyword macro">options</span><span class="parenthesis macro">(</span><span class="keyword macro">pure</span><span class="comma macro">,</span> <span class="keyword macro">readonly</span><span class="comma macro">,</span> <span class="keyword macro">nostack</span><span class="parenthesis macro">)</span><span class="parenthesis unsafe">)</span><span class="semicolon">;</span> <span class="keyword">static</span> <span class="static declaration">UNMAP_BASE</span><span class="colon">:</span> <span class="builtin_type">usize</span><span class="semicolon">;</span> <span class="keyword const">const</span> <span class="constant const declaration">MEM_RELEASE</span><span class="colon">:</span> <span class="builtin_type">usize</span><span class="semicolon">;</span> <span class="keyword">static</span> <span class="static declaration">VirtualFree</span><span class="colon">:</span> <span class="builtin_type">usize</span><span class="semicolon">;</span> <span class="keyword const">const</span> <span class="constant const declaration">OffPtr</span><span class="colon">:</span> <span class="builtin_type">usize</span><span class="semicolon">;</span> <span class="keyword const">const</span> <span class="constant const declaration">OffFn</span><span class="colon">:</span> <span class="builtin_type">usize</span><span class="semicolon">;</span> - <span class="module crate_root default_library library">core</span><span class="operator">::</span><span class="module default_library library">arch</span><span class="operator">::</span><span class="macro default_library library unsafe">asm</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">" + <span class="module crate_root default_library library">core</span><span class="operator">::</span><span class="module default_library library">arch</span><span class="operator">::</span><span class="macro default_library library unsafe">asm</span><span class="macro_bang unsafe">!</span><span class="parenthesis unsafe">(</span><span class="string_literal macro">" push {</span><span class="variable">free_type</span><span class="string_literal macro">} push {</span><span class="variable">free_size</span><span class="string_literal macro">} push {</span><span class="variable">base</span><span class="string_literal macro">} @@ -82,26 +77,26 @@ jmp {</span><span class="variable">virtual_free</span><span class="string_literal macro">} "</span><span class="comma macro">,</span> - <span class="variable declaration macro">off_ptr</span> <span class="operator macro">=</span> <span class="keyword macro">const</span> <span class="constant const macro">OffPtr</span><span class="comma macro">,</span> - <span class="variable declaration macro">off_fn</span> <span class="operator macro">=</span> <span class="keyword macro">const</span> <span class="constant const macro">OffFn</span><span class="comma macro">,</span> + <span class="variable declaration macro">off_ptr</span> <span class="operator macro">=</span> <span class="keyword const macro">const</span> <span class="constant const macro">OffPtr</span><span class="comma macro">,</span> + <span class="variable declaration macro">off_fn</span> <span class="operator macro">=</span> <span class="keyword const macro">const</span> <span class="constant const macro">OffFn</span><span class="comma macro">,</span> - <span class="variable declaration macro">free_size</span> <span class="operator macro">=</span> <span class="keyword macro">const</span> <span class="numeric_literal macro">0</span><span class="comma macro">,</span> - <span class="variable declaration macro">free_type</span> <span class="operator macro">=</span> <span class="keyword macro">const</span> <span class="constant const macro">MEM_RELEASE</span><span class="comma macro">,</span> + <span class="variable declaration macro">free_size</span> <span class="operator macro">=</span> <span class="keyword const macro">const</span> <span class="numeric_literal macro">0</span><span class="comma macro">,</span> + <span class="variable declaration macro">free_type</span> <span class="operator macro">=</span> <span class="keyword const macro">const</span> <span class="constant const macro">MEM_RELEASE</span><span class="comma macro">,</span> <span class="variable declaration macro">virtual_free</span> <span class="operator macro">=</span> <span class="keyword macro">sym</span> <span class="static macro">VirtualFree</span><span class="comma macro">,</span> <span class="variable declaration macro">base</span> <span class="operator macro">=</span> <span class="keyword macro">sym</span> <span class="static macro">UNMAP_BASE</span><span class="comma macro">,</span> <span class="keyword macro">options</span><span class="parenthesis macro">(</span><span class="keyword macro">noreturn</span><span class="parenthesis macro">)</span><span class="comma macro">,</span> - <span class="parenthesis">)</span><span class="semicolon">;</span> + <span class="parenthesis unsafe">)</span><span class="semicolon">;</span> <span class="brace">}</span> <span class="brace">}</span> <span class="comment">// taken from https://github.com/rust-embedded/cortex-m/blob/47921b51f8b960344fcfa1255a50a0d19efcde6d/cortex-m/src/asm.rs#L254-L274</span> <span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute">inline</span><span class="attribute_bracket attribute">]</span> -<span class="keyword">pub</span> <span class="keyword unsafe">unsafe</span> <span class="keyword">fn</span> <span class="function declaration public unsafe">bootstrap</span><span class="parenthesis">(</span><span class="value_param declaration">msp</span><span class="colon">:</span> <span class="keyword">*</span><span class="keyword">const</span> <span class="builtin_type">u32</span><span class="comma">,</span> <span class="value_param declaration">rv</span><span class="colon">:</span> <span class="keyword">*</span><span class="keyword">const</span> <span class="builtin_type">u32</span><span class="parenthesis">)</span> <span class="operator">-></span> <span class="builtin_type">!</span> <span class="brace">{</span> +<span class="keyword">pub</span> <span class="keyword unsafe">unsafe</span> <span class="keyword">fn</span> <span class="function declaration public unsafe">bootstrap</span><span class="parenthesis">(</span><span class="value_param declaration">msp</span><span class="colon">:</span> <span class="keyword">*</span><span class="keyword const">const</span> <span class="builtin_type">u32</span><span class="comma">,</span> <span class="value_param declaration">rv</span><span class="colon">:</span> <span class="keyword">*</span><span class="keyword const">const</span> <span class="builtin_type">u32</span><span class="parenthesis">)</span> <span class="operator">-></span> <span class="builtin_type">!</span> <span class="brace">{</span> <span class="comment">// Ensure thumb mode is set.</span> <span class="keyword">let</span> <span class="variable declaration">rv</span> <span class="operator">=</span> <span class="parenthesis">(</span><span class="value_param">rv</span> <span class="keyword">as</span> <span class="builtin_type">u32</span><span class="parenthesis">)</span> <span class="bitwise">|</span> <span class="numeric_literal">1</span><span class="semicolon">;</span> <span class="keyword">let</span> <span class="variable declaration">msp</span> <span class="operator">=</span> <span class="value_param">msp</span> <span class="keyword">as</span> <span class="builtin_type">u32</span><span class="semicolon">;</span> - <span class="module crate_root default_library library">core</span><span class="operator">::</span><span class="module default_library library">arch</span><span class="operator">::</span><span class="macro default_library library unsafe">asm</span><span class="macro_bang">!</span><span class="parenthesis">(</span> + <span class="module crate_root default_library library">core</span><span class="operator">::</span><span class="module default_library library">arch</span><span class="operator">::</span><span class="macro default_library library unsafe">asm</span><span class="macro_bang unsafe">!</span><span class="parenthesis unsafe">(</span> <span class="string_literal macro">"mrs {</span><span class="variable">tmp</span><span class="string_literal macro">}, CONTROL"</span><span class="comma macro">,</span> <span class="string_literal macro">"bics {</span><span class="variable">tmp</span><span class="string_literal macro">}, {</span><span class="variable">spsel</span><span class="string_literal macro">}"</span><span class="comma macro">,</span> <span class="string_literal macro">"msr CONTROL, {</span><span class="variable">tmp</span><span class="string_literal macro">}"</span><span class="comma macro">,</span> @@ -115,5 +110,5 @@ <span class="variable declaration macro">msp</span> <span class="operator macro">=</span> <span class="keyword control macro">in</span><span class="parenthesis macro">(</span><span class="reg library macro">reg</span><span class="parenthesis macro">)</span> <span class="variable macro">msp</span><span class="comma macro">,</span> <span class="variable declaration macro">rv</span> <span class="operator macro">=</span> <span class="keyword control macro">in</span><span class="parenthesis macro">(</span><span class="reg library macro">reg</span><span class="parenthesis macro">)</span> <span class="variable macro">rv</span><span class="comma macro">,</span> <span class="keyword macro">options</span><span class="parenthesis macro">(</span><span class="keyword macro">noreturn</span><span class="comma macro">,</span> <span class="keyword macro">nomem</span><span class="comma macro">,</span> <span class="keyword macro">nostack</span><span class="parenthesis macro">)</span><span class="comma macro">,</span> - <span class="parenthesis">)</span><span class="semicolon">;</span> + <span class="parenthesis unsafe">)</span><span class="semicolon">;</span> <span class="brace">}</span></code></pre> \ No newline at end of file
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html b/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html index 9c7f03f..faace6e 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html
@@ -14,12 +14,6 @@ .string_literal { color: #CC9393; } .field { color: #94BFF3; } .function { color: #93E0E3; } -.function.unsafe { color: #BC8383; } -.trait.unsafe { color: #BC8383; } -.operator.unsafe { color: #BC8383; } -.mutable.unsafe { color: #BC8383; text-decoration: underline; } -.keyword.unsafe { color: #BC8383; font-weight: bold; } -.macro.unsafe { color: #BC8383; } .parameter { color: #94BFF3; } .text { color: #DCDCCC; } .type { color: #7CB8BB; } @@ -41,6 +35,7 @@ .control { font-style: italic; } .reference { font-style: italic; font-weight: bold; } .const { font-weight: bolder; } +.unsafe { color: #BC8383; } .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html b/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html index e1d51dc..d59f4ca 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html
@@ -14,12 +14,6 @@ .string_literal { color: #CC9393; } .field { color: #94BFF3; } .function { color: #93E0E3; } -.function.unsafe { color: #BC8383; } -.trait.unsafe { color: #BC8383; } -.operator.unsafe { color: #BC8383; } -.mutable.unsafe { color: #BC8383; text-decoration: underline; } -.keyword.unsafe { color: #BC8383; font-weight: bold; } -.macro.unsafe { color: #BC8383; } .parameter { color: #94BFF3; } .text { color: #DCDCCC; } .type { color: #7CB8BB; } @@ -41,6 +35,7 @@ .control { font-style: italic; } .reference { font-style: italic; font-weight: bold; } .const { font-weight: bolder; } +.unsafe { color: #BC8383; } .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_block_mod_items.html b/crates/ide/src/syntax_highlighting/test_data/highlight_block_mod_items.html index af29af3..3beda39 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_block_mod_items.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_block_mod_items.html
@@ -14,12 +14,6 @@ .string_literal { color: #CC9393; } .field { color: #94BFF3; } .function { color: #93E0E3; } -.function.unsafe { color: #BC8383; } -.trait.unsafe { color: #BC8383; } -.operator.unsafe { color: #BC8383; } -.mutable.unsafe { color: #BC8383; text-decoration: underline; } -.keyword.unsafe { color: #BC8383; font-weight: bold; } -.macro.unsafe { color: #BC8383; } .parameter { color: #94BFF3; } .text { color: #DCDCCC; } .type { color: #7CB8BB; } @@ -41,6 +35,7 @@ .control { font-style: italic; } .reference { font-style: italic; font-weight: bold; } .const { font-weight: bolder; } +.unsafe { color: #BC8383; } .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } @@ -58,7 +53,7 @@ <span class="macro public">foo</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="struct declaration macro public">Bar</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="keyword">fn</span> <span class="function declaration">func</span><span class="parenthesis">(</span><span class="punctuation">_</span><span class="colon">:</span> <span class="module">y</span><span class="operator">::</span><span class="struct public">Bar</span><span class="parenthesis">)</span> <span class="brace">{</span> <span class="keyword">mod</span> <span class="module declaration">inner</span> <span class="brace">{</span> - <span class="keyword">struct</span> <span class="struct declaration">Innerest</span><span class="angle"><</span><span class="keyword">const</span> <span class="const_param const declaration">C</span><span class="colon">:</span> <span class="unresolved_reference">usize</span><span class="angle">></span> <span class="brace">{</span> <span class="field declaration">field</span><span class="colon">:</span> <span class="bracket">[</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="brace">{</span><span class="const_param const">C</span><span class="brace">}</span><span class="bracket">]</span> <span class="brace">}</span> + <span class="keyword">struct</span> <span class="struct declaration">Innerest</span><span class="angle"><</span><span class="keyword const">const</span> <span class="const_param const declaration">C</span><span class="colon">:</span> <span class="unresolved_reference">usize</span><span class="angle">></span> <span class="brace">{</span> <span class="field declaration">field</span><span class="colon">:</span> <span class="bracket">[</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="brace">{</span><span class="const_param const">C</span><span class="brace">}</span><span class="bracket">]</span> <span class="brace">}</span> <span class="brace">}</span> <span class="brace">}</span> <span class="brace">}</span>
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_const.html b/crates/ide/src/syntax_highlighting/test_data/highlight_const.html index 6d8f6b3..9c7324e 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_const.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_const.html
@@ -14,12 +14,6 @@ .string_literal { color: #CC9393; } .field { color: #94BFF3; } .function { color: #93E0E3; } -.function.unsafe { color: #BC8383; } -.trait.unsafe { color: #BC8383; } -.operator.unsafe { color: #BC8383; } -.mutable.unsafe { color: #BC8383; text-decoration: underline; } -.keyword.unsafe { color: #BC8383; font-weight: bold; } -.macro.unsafe { color: #BC8383; } .parameter { color: #94BFF3; } .text { color: #DCDCCC; } .type { color: #7CB8BB; } @@ -41,6 +35,7 @@ .control { font-style: italic; } .reference { font-style: italic; font-weight: bold; } .const { font-weight: bolder; } +.unsafe { color: #BC8383; } .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } @@ -50,21 +45,21 @@ <span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span> <span class="brace">}</span><span class="semicolon">;</span> <span class="brace">}</span> -<span class="keyword const">const</span> <span class="constant const declaration">CONST_ITEM</span><span class="colon">:</span> <span class="keyword">*</span><span class="keyword">const</span> <span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">=</span> <span class="operator">&</span><span class="keyword">raw</span> <span class="keyword">const</span> <span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span> -<span class="keyword const">const</span> <span class="keyword">fn</span> <span class="function const declaration">const_fn</span><span class="angle"><</span><span class="keyword">const</span> <span class="const_param const declaration">CONST_PARAM</span><span class="colon">:</span> <span class="parenthesis">(</span><span class="parenthesis">)</span><span class="angle">></span><span class="parenthesis">(</span><span class="keyword const">const</span> <span class="brace">{</span><span class="brace">}</span><span class="colon">:</span> <span class="keyword">const</span> <span class="keyword">fn</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="parenthesis">)</span> <span class="keyword">where</span> <span class="parenthesis">(</span><span class="parenthesis">)</span><span class="colon">:</span> <span class="keyword const">const</span> <span class="trait">ConstTrait</span> <span class="brace">{</span> +<span class="keyword const">const</span> <span class="constant const declaration">CONST_ITEM</span><span class="colon">:</span> <span class="keyword">*</span><span class="keyword const">const</span> <span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">=</span> <span class="operator">&</span><span class="keyword">raw</span> <span class="keyword const">const</span> <span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span> +<span class="keyword const">const</span> <span class="keyword">fn</span> <span class="function const declaration">const_fn</span><span class="angle"><</span><span class="keyword const">const</span> <span class="const_param const declaration">CONST_PARAM</span><span class="colon">:</span> <span class="parenthesis">(</span><span class="parenthesis">)</span><span class="angle">></span><span class="parenthesis">(</span><span class="keyword const">const</span> <span class="brace">{</span><span class="brace">}</span><span class="colon">:</span> <span class="keyword const">const</span> <span class="keyword">fn</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="parenthesis">)</span> <span class="keyword">where</span> <span class="parenthesis">(</span><span class="parenthesis">)</span><span class="colon">:</span> <span class="keyword const">const</span> <span class="trait">ConstTrait</span> <span class="brace">{</span> <span class="constant const">CONST_ITEM</span><span class="semicolon">;</span> <span class="const_param const">CONST_PARAM</span><span class="semicolon">;</span> <span class="keyword const">const</span> <span class="brace">{</span> - <span class="keyword">const</span> <span class="punctuation">|</span><span class="punctuation">|</span> <span class="brace">{</span><span class="brace">}</span> + <span class="keyword const">const</span> <span class="punctuation">|</span><span class="punctuation">|</span> <span class="brace">{</span><span class="brace">}</span> <span class="brace">}</span> <span class="macro public">id</span><span class="macro_bang">!</span><span class="parenthesis">(</span> <span class="constant const macro">CONST_ITEM</span><span class="semicolon macro">;</span> <span class="const_param const macro">CONST_PARAM</span><span class="semicolon macro">;</span> <span class="keyword const macro">const</span> <span class="brace macro">{</span> - <span class="keyword macro">const</span> <span class="punctuation macro">|</span><span class="punctuation macro">|</span> <span class="brace macro">{</span><span class="brace macro">}</span> + <span class="keyword const macro">const</span> <span class="punctuation macro">|</span><span class="punctuation macro">|</span> <span class="brace macro">{</span><span class="brace macro">}</span> <span class="brace macro">}</span><span class="semicolon macro">;</span> - <span class="operator macro">&</span><span class="keyword macro">raw</span> <span class="keyword macro">const</span> <span class="parenthesis macro">(</span><span class="parenthesis macro">)</span><span class="semicolon macro">;</span> - <span class="keyword macro">const</span> + <span class="operator macro">&</span><span class="keyword macro">raw</span> <span class="keyword const macro">const</span> <span class="parenthesis macro">(</span><span class="parenthesis macro">)</span><span class="semicolon macro">;</span> + <span class="keyword const macro">const</span> <span class="parenthesis">)</span><span class="semicolon">;</span> <span class="parenthesis">(</span><span class="parenthesis">)</span><span class="operator">.</span><span class="method const consuming trait">assoc_const_method</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="brace">}</span> @@ -80,6 +75,6 @@ <span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration public">unsafe_deref</span> <span class="brace">{</span> <span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="operator">></span> <span class="brace">{</span> - <span class="punctuation">*</span><span class="parenthesis">(</span><span class="punctuation">&</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="keyword">as</span> <span class="punctuation">*</span><span class="keyword">const</span> <span class="parenthesis">(</span><span class="parenthesis">)</span><span class="parenthesis">)</span> + <span class="punctuation">*</span><span class="parenthesis">(</span><span class="punctuation">&</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="keyword">as</span> <span class="punctuation">*</span><span class="keyword const">const</span> <span class="parenthesis">(</span><span class="parenthesis">)</span><span class="parenthesis">)</span> <span class="brace">}</span><span class="semicolon">;</span> <span class="brace">}</span></code></pre> \ No newline at end of file
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_crate_root.html b/crates/ide/src/syntax_highlighting/test_data/highlight_crate_root.html index 63e1560..4613c65 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_crate_root.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_crate_root.html
@@ -14,12 +14,6 @@ .string_literal { color: #CC9393; } .field { color: #94BFF3; } .function { color: #93E0E3; } -.function.unsafe { color: #BC8383; } -.trait.unsafe { color: #BC8383; } -.operator.unsafe { color: #BC8383; } -.mutable.unsafe { color: #BC8383; text-decoration: underline; } -.keyword.unsafe { color: #BC8383; font-weight: bold; } -.macro.unsafe { color: #BC8383; } .parameter { color: #94BFF3; } .text { color: #DCDCCC; } .type { color: #7CB8BB; } @@ -41,6 +35,7 @@ .control { font-style: italic; } .reference { font-style: italic; font-weight: bold; } .const { font-weight: bolder; } +.unsafe { color: #BC8383; } .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_default_library.html b/crates/ide/src/syntax_highlighting/test_data/highlight_default_library.html index dfad3a6..b1b2c65 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_default_library.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_default_library.html
@@ -14,12 +14,6 @@ .string_literal { color: #CC9393; } .field { color: #94BFF3; } .function { color: #93E0E3; } -.function.unsafe { color: #BC8383; } -.trait.unsafe { color: #BC8383; } -.operator.unsafe { color: #BC8383; } -.mutable.unsafe { color: #BC8383; text-decoration: underline; } -.keyword.unsafe { color: #BC8383; font-weight: bold; } -.macro.unsafe { color: #BC8383; } .parameter { color: #94BFF3; } .text { color: #DCDCCC; } .type { color: #7CB8BB; } @@ -41,6 +35,7 @@ .control { font-style: italic; } .reference { font-style: italic; font-weight: bold; } .const { font-weight: bolder; } +.unsafe { color: #BC8383; } .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html index 263e454..c8c8c5d 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
@@ -14,12 +14,6 @@ .string_literal { color: #CC9393; } .field { color: #94BFF3; } .function { color: #93E0E3; } -.function.unsafe { color: #BC8383; } -.trait.unsafe { color: #BC8383; } -.operator.unsafe { color: #BC8383; } -.mutable.unsafe { color: #BC8383; text-decoration: underline; } -.keyword.unsafe { color: #BC8383; font-weight: bold; } -.macro.unsafe { color: #BC8383; } .parameter { color: #94BFF3; } .text { color: #DCDCCC; } .type { color: #7CB8BB; } @@ -41,6 +35,7 @@ .control { font-style: italic; } .reference { font-style: italic; font-weight: bold; } .const { font-weight: bolder; } +.unsafe { color: #BC8383; } .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } @@ -129,7 +124,7 @@ <span class="comment documentation">///</span> <span class="comment documentation">/// ```</span> <span class="comment documentation">///</span><span class="comment documentation"> </span><span class="comment injected">// functions</span> - <span class="comment documentation">///</span><span class="comment documentation"> </span><span class="keyword injected">fn</span><span class="none injected"> </span><span class="function declaration injected">foo</span><span class="angle injected"><</span><span class="type_param declaration injected">T</span><span class="comma injected">,</span><span class="none injected"> </span><span class="keyword injected">const</span><span class="none injected"> </span><span class="const_param const declaration injected">X</span><span class="colon injected">:</span><span class="none injected"> </span><span class="builtin_type injected">usize</span><span class="angle injected">></span><span class="parenthesis injected">(</span><span class="value_param declaration injected">arg</span><span class="colon injected">:</span><span class="none injected"> </span><span class="builtin_type injected">i32</span><span class="parenthesis injected">)</span><span class="none injected"> </span><span class="brace injected">{</span> + <span class="comment documentation">///</span><span class="comment documentation"> </span><span class="keyword injected">fn</span><span class="none injected"> </span><span class="function declaration injected">foo</span><span class="angle injected"><</span><span class="type_param declaration injected">T</span><span class="comma injected">,</span><span class="none injected"> </span><span class="keyword const injected">const</span><span class="none injected"> </span><span class="const_param const declaration injected">X</span><span class="colon injected">:</span><span class="none injected"> </span><span class="builtin_type injected">usize</span><span class="angle injected">></span><span class="parenthesis injected">(</span><span class="value_param declaration injected">arg</span><span class="colon injected">:</span><span class="none injected"> </span><span class="builtin_type injected">i32</span><span class="parenthesis injected">)</span><span class="none injected"> </span><span class="brace injected">{</span> <span class="comment documentation">///</span><span class="comment documentation"> </span><span class="none injected"> </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="variable declaration injected">x</span><span class="colon injected">:</span><span class="none injected"> </span><span class="type_param injected">T</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="const_param const injected">X</span><span class="semicolon injected">;</span> <span class="comment documentation">///</span><span class="comment documentation"> </span><span class="brace injected">}</span> <span class="comment documentation">/// ```</span>
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html b/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html index fe5f5ab..5399f83 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html
@@ -14,12 +14,6 @@ .string_literal { color: #CC9393; } .field { color: #94BFF3; } .function { color: #93E0E3; } -.function.unsafe { color: #BC8383; } -.trait.unsafe { color: #BC8383; } -.operator.unsafe { color: #BC8383; } -.mutable.unsafe { color: #BC8383; text-decoration: underline; } -.keyword.unsafe { color: #BC8383; font-weight: bold; } -.macro.unsafe { color: #BC8383; } .parameter { color: #94BFF3; } .text { color: #DCDCCC; } .type { color: #7CB8BB; } @@ -41,6 +35,7 @@ .control { font-style: italic; } .reference { font-style: italic; font-weight: bold; } .const { font-weight: bolder; } +.unsafe { color: #BC8383; } .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_general.html b/crates/ide/src/syntax_highlighting/test_data/highlight_general.html index eb532a5..00925bd 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_general.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_general.html
@@ -14,12 +14,6 @@ .string_literal { color: #CC9393; } .field { color: #94BFF3; } .function { color: #93E0E3; } -.function.unsafe { color: #BC8383; } -.trait.unsafe { color: #BC8383; } -.operator.unsafe { color: #BC8383; } -.mutable.unsafe { color: #BC8383; text-decoration: underline; } -.keyword.unsafe { color: #BC8383; font-weight: bold; } -.macro.unsafe { color: #BC8383; } .parameter { color: #94BFF3; } .text { color: #DCDCCC; } .type { color: #7CB8BB; } @@ -41,6 +35,7 @@ .control { font-style: italic; } .reference { font-style: italic; font-weight: bold; } .const { font-weight: bolder; } +.unsafe { color: #BC8383; } .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } @@ -109,7 +104,7 @@ <span class="keyword control">loop</span> <span class="brace">{</span><span class="brace">}</span> <span class="brace">}</span> -<span class="keyword">fn</span> <span class="function declaration">const_param</span><span class="angle"><</span><span class="keyword">const</span> <span class="const_param const declaration">FOO</span><span class="colon">:</span> <span class="builtin_type">usize</span><span class="angle">></span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">-></span> <span class="builtin_type">usize</span> <span class="brace">{</span> +<span class="keyword">fn</span> <span class="function declaration">const_param</span><span class="angle"><</span><span class="keyword const">const</span> <span class="const_param const declaration">FOO</span><span class="colon">:</span> <span class="builtin_type">usize</span><span class="angle">></span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">-></span> <span class="builtin_type">usize</span> <span class="brace">{</span> <span class="function">const_param</span><span class="operator">::</span><span class="angle"><</span><span class="brace">{</span> <span class="const_param const">FOO</span> <span class="brace">}</span><span class="angle">></span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="const_param const">FOO</span> <span class="brace">}</span>
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html b/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html index 1f94221..3b468ab 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html
@@ -14,12 +14,6 @@ .string_literal { color: #CC9393; } .field { color: #94BFF3; } .function { color: #93E0E3; } -.function.unsafe { color: #BC8383; } -.trait.unsafe { color: #BC8383; } -.operator.unsafe { color: #BC8383; } -.mutable.unsafe { color: #BC8383; text-decoration: underline; } -.keyword.unsafe { color: #BC8383; font-weight: bold; } -.macro.unsafe { color: #BC8383; } .parameter { color: #94BFF3; } .text { color: #DCDCCC; } .type { color: #7CB8BB; } @@ -41,6 +35,7 @@ .control { font-style: italic; } .reference { font-style: italic; font-weight: bold; } .const { font-weight: bolder; } +.unsafe { color: #BC8383; } .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_issue_18089.html b/crates/ide/src/syntax_highlighting/test_data/highlight_issue_18089.html index a846add..5ef6446 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_issue_18089.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_issue_18089.html
@@ -14,12 +14,6 @@ .string_literal { color: #CC9393; } .field { color: #94BFF3; } .function { color: #93E0E3; } -.function.unsafe { color: #BC8383; } -.trait.unsafe { color: #BC8383; } -.operator.unsafe { color: #BC8383; } -.mutable.unsafe { color: #BC8383; text-decoration: underline; } -.keyword.unsafe { color: #BC8383; font-weight: bold; } -.macro.unsafe { color: #BC8383; } .parameter { color: #94BFF3; } .text { color: #DCDCCC; } .type { color: #7CB8BB; } @@ -41,6 +35,7 @@ .control { font-style: italic; } .reference { font-style: italic; font-weight: bold; } .const { font-weight: bolder; } +.unsafe { color: #BC8383; } .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2015.html b/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2015.html index c337761..0407e68 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2015.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2015.html
@@ -14,12 +14,6 @@ .string_literal { color: #CC9393; } .field { color: #94BFF3; } .function { color: #93E0E3; } -.function.unsafe { color: #BC8383; } -.trait.unsafe { color: #BC8383; } -.operator.unsafe { color: #BC8383; } -.mutable.unsafe { color: #BC8383; text-decoration: underline; } -.keyword.unsafe { color: #BC8383; font-weight: bold; } -.macro.unsafe { color: #BC8383; } .parameter { color: #94BFF3; } .text { color: #DCDCCC; } .type { color: #7CB8BB; } @@ -41,6 +35,7 @@ .control { font-style: italic; } .reference { font-style: italic; font-weight: bold; } .const { font-weight: bolder; } +.unsafe { color: #BC8383; } .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2018.html b/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2018.html index 9b22500..f39d033 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2018.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2018.html
@@ -14,12 +14,6 @@ .string_literal { color: #CC9393; } .field { color: #94BFF3; } .function { color: #93E0E3; } -.function.unsafe { color: #BC8383; } -.trait.unsafe { color: #BC8383; } -.operator.unsafe { color: #BC8383; } -.mutable.unsafe { color: #BC8383; text-decoration: underline; } -.keyword.unsafe { color: #BC8383; font-weight: bold; } -.macro.unsafe { color: #BC8383; } .parameter { color: #94BFF3; } .text { color: #DCDCCC; } .type { color: #7CB8BB; } @@ -41,6 +35,7 @@ .control { font-style: italic; } .reference { font-style: italic; font-weight: bold; } .const { font-weight: bolder; } +.unsafe { color: #BC8383; } .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2021.html b/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2021.html index 9b22500..f39d033 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2021.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2021.html
@@ -14,12 +14,6 @@ .string_literal { color: #CC9393; } .field { color: #94BFF3; } .function { color: #93E0E3; } -.function.unsafe { color: #BC8383; } -.trait.unsafe { color: #BC8383; } -.operator.unsafe { color: #BC8383; } -.mutable.unsafe { color: #BC8383; text-decoration: underline; } -.keyword.unsafe { color: #BC8383; font-weight: bold; } -.macro.unsafe { color: #BC8383; } .parameter { color: #94BFF3; } .text { color: #DCDCCC; } .type { color: #7CB8BB; } @@ -41,6 +35,7 @@ .control { font-style: italic; } .reference { font-style: italic; font-weight: bold; } .const { font-weight: bolder; } +.unsafe { color: #BC8383; } .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2024.html b/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2024.html index ac83531..721185a 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2024.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2024.html
@@ -14,12 +14,6 @@ .string_literal { color: #CC9393; } .field { color: #94BFF3; } .function { color: #93E0E3; } -.function.unsafe { color: #BC8383; } -.trait.unsafe { color: #BC8383; } -.operator.unsafe { color: #BC8383; } -.mutable.unsafe { color: #BC8383; text-decoration: underline; } -.keyword.unsafe { color: #BC8383; font-weight: bold; } -.macro.unsafe { color: #BC8383; } .parameter { color: #94BFF3; } .text { color: #DCDCCC; } .type { color: #7CB8BB; } @@ -41,6 +35,7 @@ .control { font-style: italic; } .reference { font-style: italic; font-weight: bold; } .const { font-weight: bolder; } +.unsafe { color: #BC8383; } .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_macros.html b/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_macros.html index 694e54d..b2c8205 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_macros.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_macros.html
@@ -14,12 +14,6 @@ .string_literal { color: #CC9393; } .field { color: #94BFF3; } .function { color: #93E0E3; } -.function.unsafe { color: #BC8383; } -.trait.unsafe { color: #BC8383; } -.operator.unsafe { color: #BC8383; } -.mutable.unsafe { color: #BC8383; text-decoration: underline; } -.keyword.unsafe { color: #BC8383; font-weight: bold; } -.macro.unsafe { color: #BC8383; } .parameter { color: #94BFF3; } .text { color: #DCDCCC; } .type { color: #7CB8BB; } @@ -41,6 +35,7 @@ .control { font-style: italic; } .reference { font-style: italic; font-weight: bold; } .const { font-weight: bolder; } +.unsafe { color: #BC8383; } .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_lifetimes.html b/crates/ide/src/syntax_highlighting/test_data/highlight_lifetimes.html index 8428b81..618ea21 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_lifetimes.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_lifetimes.html
@@ -14,12 +14,6 @@ .string_literal { color: #CC9393; } .field { color: #94BFF3; } .function { color: #93E0E3; } -.function.unsafe { color: #BC8383; } -.trait.unsafe { color: #BC8383; } -.operator.unsafe { color: #BC8383; } -.mutable.unsafe { color: #BC8383; text-decoration: underline; } -.keyword.unsafe { color: #BC8383; font-weight: bold; } -.macro.unsafe { color: #BC8383; } .parameter { color: #94BFF3; } .text { color: #DCDCCC; } .type { color: #7CB8BB; } @@ -41,6 +35,7 @@ .control { font-style: italic; } .reference { font-style: italic; font-weight: bold; } .const { font-weight: bolder; } +.unsafe { color: #BC8383; } .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html b/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html index f224435..c314594 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html
@@ -14,12 +14,6 @@ .string_literal { color: #CC9393; } .field { color: #94BFF3; } .function { color: #93E0E3; } -.function.unsafe { color: #BC8383; } -.trait.unsafe { color: #BC8383; } -.operator.unsafe { color: #BC8383; } -.mutable.unsafe { color: #BC8383; text-decoration: underline; } -.keyword.unsafe { color: #BC8383; font-weight: bold; } -.macro.unsafe { color: #BC8383; } .parameter { color: #94BFF3; } .text { color: #DCDCCC; } .type { color: #7CB8BB; } @@ -41,6 +35,7 @@ .control { font-style: italic; } .reference { font-style: italic; font-weight: bold; } .const { font-weight: bolder; } +.unsafe { color: #BC8383; } .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_inline.html b/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_inline.html index 947d1bf..9996a87 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_inline.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_inline.html
@@ -14,12 +14,6 @@ .string_literal { color: #CC9393; } .field { color: #94BFF3; } .function { color: #93E0E3; } -.function.unsafe { color: #BC8383; } -.trait.unsafe { color: #BC8383; } -.operator.unsafe { color: #BC8383; } -.mutable.unsafe { color: #BC8383; text-decoration: underline; } -.keyword.unsafe { color: #BC8383; font-weight: bold; } -.macro.unsafe { color: #BC8383; } .parameter { color: #94BFF3; } .text { color: #DCDCCC; } .type { color: #7CB8BB; } @@ -41,6 +35,7 @@ .control { font-style: italic; } .reference { font-style: italic; font-weight: bold; } .const { font-weight: bolder; } +.unsafe { color: #BC8383; } .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_outline.html b/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_outline.html index 0fe2b6f..dc9e1de 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_outline.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_outline.html
@@ -14,12 +14,6 @@ .string_literal { color: #CC9393; } .field { color: #94BFF3; } .function { color: #93E0E3; } -.function.unsafe { color: #BC8383; } -.trait.unsafe { color: #BC8383; } -.operator.unsafe { color: #BC8383; } -.mutable.unsafe { color: #BC8383; text-decoration: underline; } -.keyword.unsafe { color: #BC8383; font-weight: bold; } -.macro.unsafe { color: #BC8383; } .parameter { color: #94BFF3; } .text { color: #DCDCCC; } .type { color: #7CB8BB; } @@ -41,6 +35,7 @@ .control { font-style: italic; } .reference { font-style: italic; font-weight: bold; } .const { font-weight: bolder; } +.unsafe { color: #BC8383; } .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_operators.html b/crates/ide/src/syntax_highlighting/test_data/highlight_operators.html index c60b6ab..9c42401 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_operators.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_operators.html
@@ -14,12 +14,6 @@ .string_literal { color: #CC9393; } .field { color: #94BFF3; } .function { color: #93E0E3; } -.function.unsafe { color: #BC8383; } -.trait.unsafe { color: #BC8383; } -.operator.unsafe { color: #BC8383; } -.mutable.unsafe { color: #BC8383; text-decoration: underline; } -.keyword.unsafe { color: #BC8383; font-weight: bold; } -.macro.unsafe { color: #BC8383; } .parameter { color: #94BFF3; } .text { color: #DCDCCC; } .type { color: #7CB8BB; } @@ -41,6 +35,7 @@ .control { font-style: italic; } .reference { font-style: italic; font-weight: bold; } .const { font-weight: bolder; } +.unsafe { color: #BC8383; } .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_rainbow.html b/crates/ide/src/syntax_highlighting/test_data/highlight_rainbow.html index a4449b5..7f6b4c2 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_rainbow.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_rainbow.html
@@ -14,12 +14,6 @@ .string_literal { color: #CC9393; } .field { color: #94BFF3; } .function { color: #93E0E3; } -.function.unsafe { color: #BC8383; } -.trait.unsafe { color: #BC8383; } -.operator.unsafe { color: #BC8383; } -.mutable.unsafe { color: #BC8383; text-decoration: underline; } -.keyword.unsafe { color: #BC8383; font-weight: bold; } -.macro.unsafe { color: #BC8383; } .parameter { color: #94BFF3; } .text { color: #DCDCCC; } .type { color: #7CB8BB; } @@ -41,6 +35,7 @@ .control { font-style: italic; } .reference { font-style: italic; font-weight: bold; } .const { font-weight: bolder; } +.unsafe { color: #BC8383; } .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html b/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html index 539c74f..f7d7982 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
@@ -14,12 +14,6 @@ .string_literal { color: #CC9393; } .field { color: #94BFF3; } .function { color: #93E0E3; } -.function.unsafe { color: #BC8383; } -.trait.unsafe { color: #BC8383; } -.operator.unsafe { color: #BC8383; } -.mutable.unsafe { color: #BC8383; text-decoration: underline; } -.keyword.unsafe { color: #BC8383; font-weight: bold; } -.macro.unsafe { color: #BC8383; } .parameter { color: #94BFF3; } .text { color: #DCDCCC; } .type { color: #7CB8BB; } @@ -41,6 +35,7 @@ .control { font-style: italic; } .reference { font-style: italic; font-weight: bold; } .const { font-weight: bolder; } +.unsafe { color: #BC8383; } .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } @@ -169,12 +164,12 @@ <span class="macro public">toho</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"{}fmt"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="keyword">let</span> <span class="variable declaration">i</span><span class="colon">:</span> <span class="builtin_type">u64</span> <span class="operator">=</span> <span class="numeric_literal">3</span><span class="semicolon">;</span> <span class="keyword">let</span> <span class="variable declaration">o</span><span class="colon">:</span> <span class="builtin_type">u64</span><span class="semicolon">;</span> - <span class="module crate_root default_library library">core</span><span class="operator">::</span><span class="module default_library library">arch</span><span class="operator">::</span><span class="macro default_library library unsafe">asm</span><span class="macro_bang">!</span><span class="parenthesis">(</span> + <span class="module crate_root default_library library">core</span><span class="operator">::</span><span class="module default_library library">arch</span><span class="operator">::</span><span class="macro default_library library unsafe">asm</span><span class="macro_bang unsafe">!</span><span class="parenthesis unsafe">(</span> <span class="string_literal macro">"mov {</span><span class="variable">0</span><span class="string_literal macro">}, {</span><span class="variable">1</span><span class="string_literal macro">}"</span><span class="comma macro">,</span> <span class="string_literal macro">"add {</span><span class="variable">0</span><span class="string_literal macro">}, 5"</span><span class="comma macro">,</span> <span class="keyword macro">out</span><span class="parenthesis macro">(</span><span class="reg library macro">reg</span><span class="parenthesis macro">)</span> <span class="variable macro">o</span><span class="comma macro">,</span> <span class="keyword control macro">in</span><span class="parenthesis macro">(</span><span class="reg library macro">reg</span><span class="parenthesis macro">)</span> <span class="variable macro">i</span><span class="comma macro">,</span> - <span class="parenthesis">)</span><span class="semicolon">;</span> + <span class="parenthesis unsafe">)</span><span class="semicolon">;</span> <span class="keyword const">const</span> <span class="constant const declaration">CONSTANT</span><span class="colon">:</span> <span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">=</span> <span class="parenthesis">(</span><span class="parenthesis">)</span><span class="colon">:</span> <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">m</span> <span class="operator">=</span> <span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html b/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html index 9a46d9f..828b8f7 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
@@ -14,12 +14,6 @@ .string_literal { color: #CC9393; } .field { color: #94BFF3; } .function { color: #93E0E3; } -.function.unsafe { color: #BC8383; } -.trait.unsafe { color: #BC8383; } -.operator.unsafe { color: #BC8383; } -.mutable.unsafe { color: #BC8383; text-decoration: underline; } -.keyword.unsafe { color: #BC8383; font-weight: bold; } -.macro.unsafe { color: #BC8383; } .parameter { color: #94BFF3; } .text { color: #DCDCCC; } .type { color: #7CB8BB; } @@ -41,6 +35,7 @@ .control { font-style: italic; } .reference { font-style: italic; font-weight: bold; } .const { font-weight: bolder; } +.unsafe { color: #BC8383; } .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } @@ -52,78 +47,81 @@ <span class="brace">}</span> <span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration public">unsafe_deref</span> <span class="brace">{</span> <span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="operator">></span> <span class="brace">{</span> - <span class="punctuation">*</span><span class="parenthesis">(</span><span class="punctuation">&</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="keyword">as</span> <span class="punctuation">*</span><span class="keyword">const</span> <span class="parenthesis">(</span><span class="parenthesis">)</span><span class="parenthesis">)</span> + <span class="punctuation">*</span><span class="parenthesis">(</span><span class="punctuation">&</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="keyword">as</span> <span class="punctuation">*</span><span class="keyword const">const</span> <span class="parenthesis">(</span><span class="parenthesis">)</span><span class="parenthesis">)</span> <span class="brace">}</span><span class="semicolon">;</span> <span class="brace">}</span> -<span class="keyword">static</span> <span class="keyword">mut</span> <span class="static declaration mutable unsafe">MUT_GLOBAL</span><span class="colon">:</span> <span class="struct">Struct</span> <span class="operator">=</span> <span class="struct">Struct</span> <span class="brace">{</span> <span class="field">field</span><span class="colon">:</span> <span class="numeric_literal">0</span> <span class="brace">}</span><span class="semicolon">;</span> -<span class="keyword">static</span> <span class="static declaration">GLOBAL</span><span class="colon">:</span> <span class="struct">Struct</span> <span class="operator">=</span> <span class="struct">Struct</span> <span class="brace">{</span> <span class="field">field</span><span class="colon">:</span> <span class="numeric_literal">0</span> <span class="brace">}</span><span class="semicolon">;</span> -<span class="keyword unsafe">unsafe</span> <span class="keyword">fn</span> <span class="function declaration unsafe">unsafe_fn</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span> <span class="keyword">union</span> <span class="union declaration">Union</span> <span class="brace">{</span> - <span class="field declaration">a</span><span class="colon">:</span> <span class="builtin_type">u32</span><span class="comma">,</span> - <span class="field declaration">b</span><span class="colon">:</span> <span class="builtin_type">f32</span><span class="comma">,</span> + <span class="field declaration">field</span><span class="colon">:</span> <span class="builtin_type">u32</span><span class="comma">,</span> <span class="brace">}</span> <span class="keyword">struct</span> <span class="struct declaration">Struct</span> <span class="brace">{</span> <span class="field declaration">field</span><span class="colon">:</span> <span class="builtin_type">i32</span> <span class="brace">}</span> + +<span class="keyword">static</span> <span class="keyword">mut</span> <span class="static declaration mutable unsafe">MUT_GLOBAL</span><span class="colon">:</span> <span class="struct">Struct</span> <span class="operator">=</span> <span class="struct">Struct</span> <span class="brace">{</span> <span class="field">field</span><span class="colon">:</span> <span class="numeric_literal">0</span> <span class="brace">}</span><span class="semicolon">;</span> +<span class="keyword unsafe">unsafe</span> <span class="keyword">fn</span> <span class="function declaration unsafe">unsafe_fn</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span> + <span class="keyword">impl</span> <span class="struct">Struct</span> <span class="brace">{</span> <span class="keyword unsafe">unsafe</span> <span class="keyword">fn</span> <span class="method associated declaration reference unsafe">unsafe_method</span><span class="parenthesis">(</span><span class="punctuation">&</span><span class="self_keyword declaration reference">self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span> <span class="brace">}</span> -<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute">repr</span><span class="parenthesis attribute">(</span><span class="none attribute">packed</span><span class="parenthesis attribute">)</span><span class="attribute_bracket attribute">]</span> -<span class="keyword">struct</span> <span class="struct declaration">Packed</span> <span class="brace">{</span> - <span class="field declaration">a</span><span class="colon">:</span> <span class="builtin_type">u16</span><span class="comma">,</span> -<span class="brace">}</span> - <span class="keyword unsafe">unsafe</span> <span class="keyword">trait</span> <span class="trait declaration unsafe">UnsafeTrait</span> <span class="brace">{</span><span class="brace">}</span> -<span class="keyword unsafe">unsafe</span> <span class="keyword">impl</span> <span class="trait unsafe">UnsafeTrait</span> <span class="keyword">for</span> <span class="struct">Packed</span> <span class="brace">{</span><span class="brace">}</span> +<span class="keyword unsafe">unsafe</span> <span class="keyword">impl</span> <span class="trait unsafe">UnsafeTrait</span> <span class="keyword">for</span> <span class="union">Union</span> <span class="brace">{</span><span class="brace">}</span> <span class="keyword">impl</span> <span class="punctuation">!</span><span class="trait">UnsafeTrait</span> <span class="keyword">for</span> <span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span> <span class="keyword">fn</span> <span class="function declaration">unsafe_trait_bound</span><span class="angle"><</span><span class="type_param declaration">T</span><span class="colon">:</span> <span class="trait">UnsafeTrait</span><span class="angle">></span><span class="parenthesis">(</span><span class="punctuation">_</span><span class="colon">:</span> <span class="type_param">T</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span> -<span class="keyword">trait</span> <span class="trait declaration">DoTheAutoref</span> <span class="brace">{</span> - <span class="keyword">fn</span> <span class="method associated declaration reference trait">calls_autoref</span><span class="parenthesis">(</span><span class="punctuation">&</span><span class="self_keyword declaration reference">self</span><span class="parenthesis">)</span><span class="semicolon">;</span> -<span class="brace">}</span> - -<span class="keyword">impl</span> <span class="trait">DoTheAutoref</span> <span class="keyword">for</span> <span class="builtin_type">u16</span> <span class="brace">{</span> - <span class="keyword">fn</span> <span class="method associated declaration reference trait">calls_autoref</span><span class="parenthesis">(</span><span class="punctuation">&</span><span class="self_keyword declaration reference">self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span> +<span class="keyword">extern</span> <span class="brace">{</span> + <span class="keyword">static</span> <span class="static declaration">EXTERN_STATIC</span><span class="colon">:</span> <span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="brace">}</span> <span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span> - <span class="keyword">let</span> <span class="variable declaration">x</span> <span class="operator">=</span> <span class="operator">&</span><span class="numeric_literal">5</span> <span class="keyword">as</span> <span class="keyword">*</span><span class="keyword">const</span> <span class="punctuation">_</span> <span class="keyword">as</span> <span class="keyword">*</span><span class="keyword">const</span> <span class="builtin_type">usize</span><span class="semicolon">;</span> - <span class="keyword">let</span> <span class="variable declaration">u</span> <span class="operator">=</span> <span class="union">Union</span> <span class="brace">{</span> <span class="field">b</span><span class="colon">:</span> <span class="numeric_literal">0</span> <span class="brace">}</span><span class="semicolon">;</span> + <span class="keyword">let</span> <span class="variable declaration">x</span><span class="colon">:</span> <span class="keyword">*</span><span class="keyword const">const</span> <span class="builtin_type">usize</span><span class="semicolon">;</span> + <span class="keyword">let</span> <span class="variable declaration">u</span><span class="colon">:</span> <span class="union">Union</span><span class="semicolon">;</span> + <span class="comment">// id should be safe here, but unsafe_deref should not</span> <span class="macro public">id</span><span class="macro_bang">!</span> <span class="brace">{</span> - <span class="keyword macro unsafe">unsafe</span> <span class="brace macro">{</span> <span class="macro macro public unsafe">unsafe_deref</span><span class="macro_bang macro">!</span><span class="parenthesis macro">(</span><span class="parenthesis macro">)</span> <span class="brace macro">}</span> + <span class="keyword macro unsafe">unsafe</span> <span class="brace macro">{</span> <span class="macro macro public unsafe">unsafe_deref</span><span class="macro_bang macro unsafe">!</span><span class="parenthesis macro unsafe">(</span><span class="parenthesis macro unsafe">)</span> <span class="brace macro">}</span> <span class="brace">}</span><span class="semicolon">;</span> <span class="keyword unsafe">unsafe</span> <span class="brace">{</span> - <span class="macro public unsafe">unsafe_deref</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span> - <span class="macro public unsafe">id</span><span class="macro_bang">!</span> <span class="brace">{</span> <span class="macro macro public unsafe">unsafe_deref</span><span class="macro_bang macro">!</span><span class="parenthesis macro">(</span><span class="parenthesis macro">)</span> <span class="brace">}</span><span class="semicolon">;</span> + <span class="comment">// unsafe macro calls</span> + <span class="macro public unsafe">unsafe_deref</span><span class="macro_bang unsafe">!</span><span class="parenthesis unsafe">(</span><span class="parenthesis unsafe">)</span><span class="semicolon">;</span> + <span class="macro public unsafe">id</span><span class="macro_bang unsafe">!</span> <span class="brace unsafe">{</span> <span class="macro macro public unsafe">unsafe_deref</span><span class="macro_bang macro unsafe">!</span><span class="parenthesis macro unsafe">(</span><span class="parenthesis macro unsafe">)</span> <span class="brace unsafe">}</span><span class="semicolon">;</span> <span class="comment">// unsafe fn and method calls</span> - <span class="function unsafe">unsafe_fn</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span> - <span class="keyword">let</span> <span class="variable declaration">b</span> <span class="operator">=</span> <span class="variable">u</span><span class="operator">.</span><span class="field unsafe">b</span><span class="semicolon">;</span> - <span class="keyword control">match</span> <span class="variable">u</span> <span class="brace">{</span> - <span class="union">Union</span> <span class="brace">{</span> <span class="field unsafe">b</span><span class="colon">:</span> <span class="numeric_literal">0</span> <span class="brace">}</span> <span class="operator">=></span> <span class="parenthesis">(</span><span class="parenthesis">)</span><span class="comma">,</span> - <span class="union">Union</span> <span class="brace">{</span> <span class="field unsafe">a</span> <span class="brace">}</span> <span class="operator">=></span> <span class="parenthesis">(</span><span class="parenthesis">)</span><span class="comma">,</span> - <span class="brace">}</span> - <span class="struct">Struct</span> <span class="brace">{</span> <span class="field">field</span><span class="colon">:</span> <span class="numeric_literal">0</span> <span class="brace">}</span><span class="operator">.</span><span class="method reference unsafe">unsafe_method</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span> + <span class="function unsafe">unsafe_fn</span><span class="parenthesis unsafe">(</span><span class="parenthesis unsafe">)</span><span class="semicolon">;</span> + <span class="self_keyword crate_root public">self</span><span class="operator">::</span><span class="function unsafe">unsafe_fn</span><span class="parenthesis unsafe">(</span><span class="parenthesis unsafe">)</span><span class="semicolon">;</span> + <span class="parenthesis">(</span><span class="function">unsafe_fn</span> <span class="keyword">as</span> <span class="keyword unsafe">unsafe</span> <span class="keyword">fn</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="parenthesis">)</span><span class="parenthesis unsafe">(</span><span class="parenthesis unsafe">)</span><span class="semicolon">;</span> + <span class="struct">Struct</span> <span class="brace">{</span> <span class="field">field</span><span class="colon">:</span> <span class="numeric_literal">0</span> <span class="brace">}</span><span class="operator">.</span><span class="method reference unsafe">unsafe_method</span><span class="parenthesis unsafe">(</span><span class="parenthesis unsafe">)</span><span class="semicolon">;</span> + + <span class="variable">u</span><span class="operator">.</span><span class="field unsafe">field</span><span class="semicolon">;</span> + <span class="operator">&</span><span class="variable">u</span><span class="operator">.</span><span class="field unsafe">field</span><span class="semicolon">;</span> + <span class="operator">&</span><span class="keyword">raw</span> <span class="keyword const">const</span> <span class="variable">u</span><span class="operator">.</span><span class="field unsafe">field</span><span class="semicolon">;</span> + <span class="comment">// this should be safe!</span> + <span class="keyword">let</span> <span class="union">Union</span> <span class="brace">{</span> <span class="field">field</span><span class="colon">:</span> <span class="punctuation">_</span> <span class="brace">}</span><span class="semicolon">;</span> + <span class="comment">// but not these</span> + <span class="keyword">let</span> <span class="union">Union</span> <span class="brace">{</span> <span class="field unsafe">field</span> <span class="brace">}</span><span class="semicolon">;</span> + <span class="keyword">let</span> <span class="union">Union</span> <span class="brace">{</span> <span class="field unsafe">field</span><span class="colon">:</span> <span class="variable declaration">field</span> <span class="brace">}</span><span class="semicolon">;</span> + <span class="keyword">let</span> <span class="union">Union</span> <span class="brace">{</span> <span class="field unsafe">field</span><span class="colon">:</span> <span class="keyword">ref</span> <span class="variable declaration reference">field</span> <span class="brace">}</span><span class="semicolon">;</span> + <span class="keyword">let</span> <span class="union">Union</span> <span class="brace">{</span> <span class="field unsafe">field</span><span class="colon">:</span> <span class="parenthesis">(</span><span class="punctuation">_</span> <span class="punctuation">|</span> <span class="keyword">ref</span> <span class="variable declaration reference">field</span><span class="parenthesis">)</span> <span class="brace">}</span><span class="semicolon">;</span> <span class="comment">// unsafe deref</span> - <span class="operator unsafe">*</span><span class="variable">x</span><span class="semicolon">;</span> + <span class="operator unsafe">*</span><span class="operator">&</span><span class="keyword">raw</span> <span class="keyword const">const</span><span class="operator">*</span><span class="operator">&</span><span class="operator unsafe">*</span><span class="variable">x</span><span class="semicolon">;</span> <span class="comment">// unsafe access to a static mut</span> <span class="static mutable unsafe">MUT_GLOBAL</span><span class="operator">.</span><span class="field">field</span><span class="semicolon">;</span> - <span class="static">GLOBAL</span><span class="operator">.</span><span class="field">field</span><span class="semicolon">;</span> + <span class="operator">&</span><span class="static mutable unsafe">MUT_GLOBAL</span><span class="operator">.</span><span class="field">field</span><span class="semicolon">;</span> + <span class="operator">&</span><span class="keyword">raw</span> <span class="keyword const">const</span> <span class="static mutable unsafe">MUT_GLOBAL</span><span class="operator">.</span><span class="field">field</span><span class="semicolon">;</span> + <span class="static mutable unsafe">MUT_GLOBAL</span><span class="semicolon">;</span> + <span class="operator">&</span><span class="static mutable unsafe">MUT_GLOBAL</span><span class="semicolon">;</span> + <span class="operator">&</span><span class="keyword">raw</span> <span class="keyword const">const</span> <span class="static mutable">MUT_GLOBAL</span><span class="semicolon">;</span> + <span class="static unsafe">EXTERN_STATIC</span><span class="semicolon">;</span> + <span class="operator">&</span><span class="static unsafe">EXTERN_STATIC</span><span class="semicolon">;</span> + <span class="operator">&</span><span class="keyword">raw</span> <span class="keyword const">const</span> <span class="static">EXTERN_STATIC</span><span class="semicolon">;</span> - <span class="comment">// unsafe ref of packed fields</span> - <span class="keyword">let</span> <span class="variable declaration">packed</span> <span class="operator">=</span> <span class="struct">Packed</span> <span class="brace">{</span> <span class="field">a</span><span class="colon">:</span> <span class="numeric_literal">0</span> <span class="brace">}</span><span class="semicolon">;</span> - <span class="keyword">let</span> <span class="variable declaration reference">a</span> <span class="operator">=</span> <span class="operator unsafe">&</span><span class="variable">packed</span><span class="operator">.</span><span class="field">a</span><span class="semicolon">;</span> - <span class="keyword">let</span> <span class="keyword unsafe">ref</span> <span class="variable declaration reference">a</span> <span class="operator">=</span> <span class="variable">packed</span><span class="operator">.</span><span class="field">a</span><span class="semicolon">;</span> - <span class="keyword">let</span> <span class="struct">Packed</span> <span class="brace">{</span> <span class="keyword unsafe">ref</span> <span class="field">a</span> <span class="brace">}</span> <span class="operator">=</span> <span class="variable">packed</span><span class="semicolon">;</span> - <span class="keyword">let</span> <span class="struct">Packed</span> <span class="brace">{</span> <span class="field">a</span><span class="colon">:</span> <span class="keyword unsafe">ref</span> <span class="variable declaration reference">_a</span> <span class="brace">}</span> <span class="operator">=</span> <span class="variable">packed</span><span class="semicolon">;</span> - - <span class="comment">// unsafe auto ref of packed field</span> - <span class="variable">packed</span><span class="operator">.</span><span class="field">a</span><span class="operator">.</span><span class="method reference trait unsafe">calls_autoref</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span> + <span class="module crate_root default_library library">core</span><span class="operator">::</span><span class="module default_library library">arch</span><span class="operator">::</span><span class="macro default_library library unsafe">asm</span><span class="macro_bang unsafe">!</span><span class="parenthesis unsafe">(</span> + <span class="string_literal macro">"push {</span><span class="variable">base</span><span class="string_literal macro">}"</span><span class="comma macro">,</span> + <span class="variable declaration macro">base</span> <span class="operator macro">=</span> <span class="keyword const macro">const</span> <span class="numeric_literal macro">0</span> + <span class="parenthesis unsafe">)</span><span class="semicolon">;</span> <span class="brace">}</span> <span class="brace">}</span></code></pre> \ No newline at end of file
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs index e48ca86..8f69bb8 100644 --- a/crates/ide/src/syntax_highlighting/tests.rs +++ b/crates/ide/src/syntax_highlighting/tests.rs
@@ -589,7 +589,7 @@ fn test_unsafe_highlighting() { check_highlighting( r#" -//- minicore: sized +//- minicore: sized, asm macro_rules! id { ($($tt:tt)*) => { $($tt)* @@ -600,76 +600,79 @@ *(&() as *const ()) }; } -static mut MUT_GLOBAL: Struct = Struct { field: 0 }; -static GLOBAL: Struct = Struct { field: 0 }; -unsafe fn unsafe_fn() {} union Union { - a: u32, - b: f32, + field: u32, } struct Struct { field: i32 } + +static mut MUT_GLOBAL: Struct = Struct { field: 0 }; +unsafe fn unsafe_fn() {} + impl Struct { unsafe fn unsafe_method(&self) {} } -#[repr(packed)] -struct Packed { - a: u16, -} - unsafe trait UnsafeTrait {} -unsafe impl UnsafeTrait for Packed {} +unsafe impl UnsafeTrait for Union {} impl !UnsafeTrait for () {} fn unsafe_trait_bound<T: UnsafeTrait>(_: T) {} -trait DoTheAutoref { - fn calls_autoref(&self); -} - -impl DoTheAutoref for u16 { - fn calls_autoref(&self) {} +extern { + static EXTERN_STATIC: (); } fn main() { - let x = &5 as *const _ as *const usize; - let u = Union { b: 0 }; + let x: *const usize; + let u: Union; + // id should be safe here, but unsafe_deref should not id! { unsafe { unsafe_deref!() } }; unsafe { + // unsafe macro calls unsafe_deref!(); id! { unsafe_deref!() }; // unsafe fn and method calls unsafe_fn(); - let b = u.b; - match u { - Union { b: 0 } => (), - Union { a } => (), - } + self::unsafe_fn(); + (unsafe_fn as unsafe fn())(); Struct { field: 0 }.unsafe_method(); + u.field; + &u.field; + &raw const u.field; + // this should be safe! + let Union { field: _ }; + // but not these + let Union { field }; + let Union { field: field }; + let Union { field: ref field }; + let Union { field: (_ | ref field) }; + // unsafe deref - *x; + *&raw const*&*x; // unsafe access to a static mut MUT_GLOBAL.field; - GLOBAL.field; + &MUT_GLOBAL.field; + &raw const MUT_GLOBAL.field; + MUT_GLOBAL; + &MUT_GLOBAL; + &raw const MUT_GLOBAL; + EXTERN_STATIC; + &EXTERN_STATIC; + &raw const EXTERN_STATIC; - // unsafe ref of packed fields - let packed = Packed { a: 0 }; - let a = &packed.a; - let ref a = packed.a; - let Packed { ref a } = packed; - let Packed { a: ref _a } = packed; - - // unsafe auto ref of packed field - packed.a.calls_autoref(); + core::arch::asm!( + "push {base}", + base = const 0 + ); } } "#,
diff --git a/crates/ide/src/view_memory_layout.rs b/crates/ide/src/view_memory_layout.rs index edb83bc..34bca7b 100644 --- a/crates/ide/src/view_memory_layout.rs +++ b/crates/ide/src/view_memory_layout.rs
@@ -1,12 +1,11 @@ use std::fmt; -use hir::{Field, HirDisplay, Layout, Semantics, Type}; +use hir::{DisplayTarget, Field, HirDisplay, Layout, Semantics, Type}; use ide_db::{ defs::Definition, helpers::{get_definition, pick_best_token}, RootDatabase, }; -use span::Edition; use syntax::{AstNode, SyntaxKind}; use crate::FilePosition; @@ -84,10 +83,7 @@ ) -> Option<RecursiveMemoryLayout> { let sema = Semantics::new(db); let file = sema.parse_guess_edition(position.file_id); - let edition = sema - .attach_first_edition(position.file_id) - .map(|it| it.edition()) - .unwrap_or(Edition::CURRENT); + let display_target = sema.first_crate_or_default(position.file_id).to_display_target(db); let token = pick_best_token(file.syntax().token_at_offset(position.offset), |kind| match kind { SyntaxKind::IDENT => 3, @@ -114,7 +110,7 @@ ty: &Type, layout: &Layout, parent_idx: usize, - edition: Edition, + display_target: DisplayTarget, ) { let mut fields = ty .fields(db) @@ -145,7 +141,7 @@ if let Ok(child_layout) = child_ty.layout(db) { nodes.push(MemoryLayoutNode { item_name: field.name(db), - typename: child_ty.display(db, edition).to_string(), + typename: child_ty.display(db, display_target).to_string(), size: child_layout.size(), alignment: child_layout.align(), offset: match *field { @@ -161,7 +157,7 @@ item_name: field.name(db) + format!("(no layout data: {:?})", child_ty.layout(db).unwrap_err()) .as_ref(), - typename: child_ty.display(db, edition).to_string(), + typename: child_ty.display(db, display_target).to_string(), size: 0, offset: 0, alignment: 0, @@ -174,7 +170,7 @@ for (i, (_, child_ty)) in fields.iter().enumerate() { if let Ok(child_layout) = child_ty.layout(db) { - read_layout(nodes, db, child_ty, &child_layout, children_start + i, edition); + read_layout(nodes, db, child_ty, &child_layout, children_start + i, display_target); } } } @@ -192,7 +188,7 @@ def => def.name(db).map(|n| n.as_str().to_owned()).unwrap_or("[ROOT]".to_owned()), }; - let typename = ty.display(db, edition).to_string(); + let typename = ty.display(db, display_target).to_string(); let mut nodes = vec![MemoryLayoutNode { item_name, @@ -204,7 +200,7 @@ children_start: -1, children_len: 0, }]; - read_layout(&mut nodes, db, &ty, &layout, 0, edition); + read_layout(&mut nodes, db, &ty, &layout, 0, display_target); RecursiveMemoryLayout { nodes } })
diff --git a/crates/intern/src/symbol.rs b/crates/intern/src/symbol.rs index b3bf285..0fa6701 100644 --- a/crates/intern/src/symbol.rs +++ b/crates/intern/src/symbol.rs
@@ -18,20 +18,17 @@ pub mod symbols; // some asserts for layout compatibility -const _: () = assert!(std::mem::size_of::<Box<str>>() == std::mem::size_of::<&str>()); -const _: () = assert!(std::mem::align_of::<Box<str>>() == std::mem::align_of::<&str>()); +const _: () = assert!(size_of::<Box<str>>() == size_of::<&str>()); +const _: () = assert!(align_of::<Box<str>>() == align_of::<&str>()); -const _: () = assert!(std::mem::size_of::<Arc<Box<str>>>() == std::mem::size_of::<&&str>()); -const _: () = assert!(std::mem::align_of::<Arc<Box<str>>>() == std::mem::align_of::<&&str>()); +const _: () = assert!(size_of::<Arc<Box<str>>>() == size_of::<&&str>()); +const _: () = assert!(align_of::<Arc<Box<str>>>() == align_of::<&&str>()); -const _: () = - assert!(std::mem::size_of::<*const *const str>() == std::mem::size_of::<TaggedArcPtr>()); -const _: () = - assert!(std::mem::align_of::<*const *const str>() == std::mem::align_of::<TaggedArcPtr>()); +const _: () = assert!(size_of::<*const *const str>() == size_of::<TaggedArcPtr>()); +const _: () = assert!(align_of::<*const *const str>() == align_of::<TaggedArcPtr>()); -const _: () = assert!(std::mem::size_of::<Arc<Box<str>>>() == std::mem::size_of::<TaggedArcPtr>()); -const _: () = - assert!(std::mem::align_of::<Arc<Box<str>>>() == std::mem::align_of::<TaggedArcPtr>()); +const _: () = assert!(size_of::<Arc<Box<str>>>() == size_of::<TaggedArcPtr>()); +const _: () = assert!(align_of::<Arc<Box<str>>>() == align_of::<TaggedArcPtr>()); /// A pointer that points to a pointer to a `str`, it may be backed as a `&'static &'static str` or /// `Arc<Box<str>>` but its size is that of a thin pointer. The active variant is encoded as a tag @@ -49,9 +46,7 @@ const BOOL_BITS: usize = true as usize; const fn non_arc(r: &'static &'static str) -> Self { - assert!( - mem::align_of::<&'static &'static str>().trailing_zeros() as usize > Self::BOOL_BITS - ); + assert!(align_of::<&'static &'static str>().trailing_zeros() as usize > Self::BOOL_BITS); // SAFETY: The pointer is non-null as it is derived from a reference // Ideally we would call out to `pack_arc` but for a `false` tag, unfortunately the // packing stuff requires reading out the pointer to an integer which is not supported @@ -64,9 +59,7 @@ } fn arc(arc: Arc<Box<str>>) -> Self { - assert!( - mem::align_of::<&'static &'static str>().trailing_zeros() as usize > Self::BOOL_BITS - ); + assert!(align_of::<&'static &'static str>().trailing_zeros() as usize > Self::BOOL_BITS); Self { packed: Self::pack_arc( // Safety: `Arc::into_raw` always returns a non null pointer @@ -131,8 +124,8 @@ } } -const _: () = assert!(std::mem::size_of::<Symbol>() == std::mem::size_of::<NonNull<()>>()); -const _: () = assert!(std::mem::align_of::<Symbol>() == std::mem::align_of::<NonNull<()>>()); +const _: () = assert!(size_of::<Symbol>() == size_of::<NonNull<()>>()); +const _: () = assert!(align_of::<Symbol>() == align_of::<NonNull<()>>()); static MAP: OnceLock<DashMap<SymbolProxy, (), BuildHasherDefault<FxHasher>>> = OnceLock::new();
diff --git a/crates/intern/src/symbol/symbols.rs b/crates/intern/src/symbol/symbols.rs index be0de6c..6b77c72 100644 --- a/crates/intern/src/symbol/symbols.rs +++ b/crates/intern/src/symbol/symbols.rs
@@ -185,6 +185,7 @@ Clone, coerce_unsized, column, + completion, compile_error, concat_bytes, concat_idents,
diff --git a/crates/load-cargo/src/lib.rs b/crates/load-cargo/src/lib.rs index 76f1a7f..72ca85c 100644 --- a/crates/load-cargo/src/lib.rs +++ b/crates/load-cargo/src/lib.rs
@@ -45,10 +45,18 @@ ) -> anyhow::Result<(RootDatabase, vfs::Vfs, Option<ProcMacroClient>)> { let root = AbsPathBuf::assert_utf8(std::env::current_dir()?.join(root)); let root = ProjectManifest::discover_single(&root)?; + let manifest_path = root.manifest_path().clone(); let mut workspace = ProjectWorkspace::load(root, cargo_config, progress)?; if load_config.load_out_dirs_from_check { let build_scripts = workspace.run_build_scripts(cargo_config, progress)?; + if let Some(error) = build_scripts.error() { + tracing::debug!( + "Errors occurred while running build scripts for {}: {}", + manifest_path, + error + ); + } workspace.set_build_scripts(build_scripts) }
diff --git a/crates/mbe/src/tests.rs b/crates/mbe/src/tests.rs index fb68d35..4a73b6f 100644 --- a/crates/mbe/src/tests.rs +++ b/crates/mbe/src/tests.rs
@@ -100,6 +100,23 @@ } #[test] +fn unbalanced_brace() { + check( + Edition::CURRENT, + Edition::CURRENT, + r#" +() => { { } +"#, + r#""#, + expect![[r#" + SUBTREE $$ 1:0@0..0#2 1:0@0..0#2 + SUBTREE {} 0:0@9..10#2 0:0@11..12#2 + + {}"#]], + ); +} + +#[test] fn token_mapping_smoke_test() { check( Edition::CURRENT,
diff --git a/crates/parser/src/syntax_kind/generated.rs b/crates/parser/src/syntax_kind/generated.rs index e56e09e..1ff0bbe 100644 --- a/crates/parser/src/syntax_kind/generated.rs +++ b/crates/parser/src/syntax_kind/generated.rs
@@ -315,6 +315,7 @@ USE_TREE, USE_TREE_LIST, VARIANT, + VARIANT_DEF, VARIANT_LIST, VISIBILITY, WHERE_CLAUSE, @@ -501,6 +502,7 @@ | USE_TREE | USE_TREE_LIST | VARIANT + | VARIANT_DEF | VARIANT_LIST | VISIBILITY | WHERE_CLAUSE
diff --git a/crates/profile/src/memory_usage.rs b/crates/profile/src/memory_usage.rs index 006748d..f5b8eca 100644 --- a/crates/profile/src/memory_usage.rs +++ b/crates/profile/src/memory_usage.rs
@@ -38,7 +38,7 @@ // approximate that by using the Commit Charge value. use windows_sys::Win32::System::{Threading::*, ProcessStatus::*}; - use std::mem::{MaybeUninit, size_of}; + use std::mem::MaybeUninit; let proc = unsafe { GetCurrentProcess() }; let mut mem_counters = MaybeUninit::uninit();
diff --git a/crates/project-model/src/cargo_workspace.rs b/crates/project-model/src/cargo_workspace.rs index 40ab8c5..014028a 100644 --- a/crates/project-model/src/cargo_workspace.rs +++ b/crates/project-model/src/cargo_workspace.rs
@@ -288,7 +288,23 @@ locked: bool, progress: &dyn Fn(String), ) -> anyhow::Result<(cargo_metadata::Metadata, Option<anyhow::Error>)> { - Self::fetch_metadata_(cargo_toml, current_dir, config, sysroot, locked, false, progress) + let res = Self::fetch_metadata_( + cargo_toml, + current_dir, + config, + sysroot, + locked, + false, + progress, + ); + if let Ok((_, Some(ref e))) = res { + tracing::warn!( + %cargo_toml, + ?e, + "`cargo metadata` failed, but retry with `--no-deps` succeeded" + ); + } + res } fn fetch_metadata_(
diff --git a/crates/project-model/src/project_json.rs b/crates/project-model/src/project_json.rs index 0282a71..b2df8e4 100644 --- a/crates/project-model/src/project_json.rs +++ b/crates/project-model/src/project_json.rs
@@ -86,7 +86,7 @@ /// * `manifest` - The path to the `rust-project.json`. /// * `base` - The path to the workspace root (i.e. the folder containing `rust-project.json`) /// * `data` - The parsed contents of `rust-project.json`, or project json that's passed via - /// configuration. + /// configuration. pub fn new( manifest: Option<ManifestPath>, base: &AbsPath,
diff --git a/crates/project-model/src/sysroot.rs b/crates/project-model/src/sysroot.rs index 1e3c5a9..13812e9 100644 --- a/crates/project-model/src/sysroot.rs +++ b/crates/project-model/src/sysroot.rs
@@ -30,6 +30,7 @@ pub enum RustLibSrcWorkspace { Workspace(CargoWorkspace), Json(ProjectJson), + Stitched(stitched::Stitched), Empty, } @@ -60,6 +61,7 @@ match &self.workspace { RustLibSrcWorkspace::Workspace(ws) => ws.packages().next().is_none(), RustLibSrcWorkspace::Json(project_json) => project_json.n_crates() == 0, + RustLibSrcWorkspace::Stitched(stitched) => stitched.crates.is_empty(), RustLibSrcWorkspace::Empty => true, } } @@ -72,6 +74,7 @@ match &self.workspace { RustLibSrcWorkspace::Workspace(ws) => ws.packages().count(), RustLibSrcWorkspace::Json(project_json) => project_json.n_crates(), + RustLibSrcWorkspace::Stitched(stitched) => stitched.crates.len(), RustLibSrcWorkspace::Empty => 0, } } @@ -197,6 +200,51 @@ return Some(loaded); } } + tracing::debug!("Stitching sysroot library: {src_root}"); + + let mut stitched = stitched::Stitched { crates: Default::default() }; + + for path in stitched::SYSROOT_CRATES.trim().lines() { + let name = path.split('/').next_back().unwrap(); + let root = [format!("{path}/src/lib.rs"), format!("lib{path}/lib.rs")] + .into_iter() + .map(|it| src_root.join(it)) + .filter_map(|it| ManifestPath::try_from(it).ok()) + .find(|it| fs::metadata(it).is_ok()); + + if let Some(root) = root { + stitched.crates.alloc(stitched::RustLibSrcCrateData { + name: name.into(), + root, + deps: Vec::new(), + }); + } + } + + if let Some(std) = stitched.by_name("std") { + for dep in stitched::STD_DEPS.trim().lines() { + if let Some(dep) = stitched.by_name(dep) { + stitched.crates[std].deps.push(dep) + } + } + } + + if let Some(alloc) = stitched.by_name("alloc") { + for dep in stitched::ALLOC_DEPS.trim().lines() { + if let Some(dep) = stitched.by_name(dep) { + stitched.crates[alloc].deps.push(dep) + } + } + } + + if let Some(proc_macro) = stitched.by_name("proc_macro") { + for dep in stitched::PROC_MACRO_DEPS.trim().lines() { + if let Some(dep) = stitched.by_name(dep) { + stitched.crates[proc_macro].deps.push(dep) + } + } + } + return Some(RustLibSrcWorkspace::Stitched(stitched)); } else if let RustSourceWorkspaceConfig::Json(project_json) = sysroot_source_config { return Some(RustLibSrcWorkspace::Json(project_json.clone())); } @@ -216,6 +264,7 @@ .crates() .filter_map(|(_, krate)| krate.display_name.clone()) .any(|name| name.canonical_name().as_str() == "core"), + RustLibSrcWorkspace::Stitched(stitched) => stitched.by_name("core").is_some(), RustLibSrcWorkspace::Empty => true, }; if !has_core { @@ -391,3 +440,91 @@ None } } + +// FIXME: Remove this, that will bump our project MSRV to 1.82 +pub(crate) mod stitched { + use std::ops; + + use base_db::CrateName; + use la_arena::{Arena, Idx}; + + use crate::ManifestPath; + + #[derive(Debug, Clone, Eq, PartialEq)] + pub struct Stitched { + pub(super) crates: Arena<RustLibSrcCrateData>, + } + + impl ops::Index<RustLibSrcCrate> for Stitched { + type Output = RustLibSrcCrateData; + fn index(&self, index: RustLibSrcCrate) -> &RustLibSrcCrateData { + &self.crates[index] + } + } + + impl Stitched { + pub(crate) fn public_deps( + &self, + ) -> impl Iterator<Item = (CrateName, RustLibSrcCrate, bool)> + '_ { + // core is added as a dependency before std in order to + // mimic rustcs dependency order + [("core", true), ("alloc", false), ("std", true), ("test", false)] + .into_iter() + .filter_map(move |(name, prelude)| { + Some((CrateName::new(name).unwrap(), self.by_name(name)?, prelude)) + }) + } + + pub(crate) fn proc_macro(&self) -> Option<RustLibSrcCrate> { + self.by_name("proc_macro") + } + + pub(crate) fn crates(&self) -> impl ExactSizeIterator<Item = RustLibSrcCrate> + '_ { + self.crates.iter().map(|(id, _data)| id) + } + + pub(super) fn by_name(&self, name: &str) -> Option<RustLibSrcCrate> { + let (id, _data) = self.crates.iter().find(|(_id, data)| data.name == name)?; + Some(id) + } + } + + pub(crate) type RustLibSrcCrate = Idx<RustLibSrcCrateData>; + + #[derive(Debug, Clone, Eq, PartialEq)] + pub(crate) struct RustLibSrcCrateData { + pub(crate) name: String, + pub(crate) root: ManifestPath, + pub(crate) deps: Vec<RustLibSrcCrate>, + } + + pub(super) const SYSROOT_CRATES: &str = " +alloc +backtrace +core +panic_abort +panic_unwind +proc_macro +profiler_builtins +std +stdarch/crates/std_detect +test +unwind"; + + pub(super) const ALLOC_DEPS: &str = "core"; + + pub(super) const STD_DEPS: &str = " +alloc +panic_unwind +panic_abort +core +profiler_builtins +unwind +std_detect +test"; + + // core is required for our builtin derives to work in the proc_macro lib currently + pub(super) const PROC_MACRO_DEPS: &str = " +std +core"; +}
diff --git a/crates/project-model/src/tests.rs b/crates/project-model/src/tests.rs index cfc6669..8374062 100644 --- a/crates/project-model/src/tests.rs +++ b/crates/project-model/src/tests.rs
@@ -166,7 +166,7 @@ #[test] fn cargo_hello_world_project_model_with_wildcard_overrides() { let cfg_overrides = CfgOverrides { - global: CfgDiff::new(Vec::new(), vec![CfgAtom::Flag(sym::test.clone())]).unwrap(), + global: CfgDiff::new(Vec::new(), vec![CfgAtom::Flag(sym::test.clone())]), selective: Default::default(), }; let (crate_graph, _proc_macros) = @@ -185,7 +185,7 @@ global: Default::default(), selective: std::iter::once(( "libc".to_owned(), - CfgDiff::new(Vec::new(), vec![CfgAtom::Flag(sym::test.clone())]).unwrap(), + CfgDiff::new(Vec::new(), vec![CfgAtom::Flag(sym::test.clone())]), )) .collect(), };
diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs index 2c9f41e..62c13c7 100644 --- a/crates/project-model/src/workspace.rs +++ b/crates/project-model/src/workspace.rs
@@ -692,6 +692,7 @@ exclude: krate.exclude.clone(), }) .collect(), + RustLibSrcWorkspace::Stitched(_) => vec![], RustLibSrcWorkspace::Empty => vec![], }; @@ -1521,7 +1522,7 @@ ) -> (SysrootPublicDeps, Option<CrateId>) { let mut pub_deps = vec![]; let mut libproc_macro = None; - let diff = CfgDiff::new(vec![], vec![CfgAtom::Flag(sym::test.clone())]).unwrap(); + let diff = CfgDiff::new(vec![], vec![CfgAtom::Flag(sym::test.clone())]); for (cid, c) in sysroot_crate_graph.iter_mut() { // uninject `test` flag so `core` keeps working. Arc::make_mut(&mut c.cfg_options).apply_diff(diff.clone()); @@ -1599,8 +1600,7 @@ CfgAtom::Flag(sym::miri.clone()), ], vec![], - ) - .unwrap(), + ), ..Default::default() }, &WorkspaceBuildScripts::default(), @@ -1623,8 +1623,7 @@ CfgAtom::Flag(sym::miri.clone()), ], vec![], - ) - .unwrap(), + ), ..Default::default() }, false, @@ -1632,7 +1631,62 @@ extend_crate_graph_with_sysroot(crate_graph, cg, pm) } + RustLibSrcWorkspace::Stitched(stitched) => { + let cfg_options = Arc::new({ + let mut cfg_options = CfgOptions::default(); + cfg_options.extend(rustc_cfg); + cfg_options.insert_atom(sym::debug_assertions.clone()); + cfg_options.insert_atom(sym::miri.clone()); + cfg_options + }); + let sysroot_crates: FxHashMap<crate::sysroot::stitched::RustLibSrcCrate, CrateId> = + stitched + .crates() + .filter_map(|krate| { + let file_id = load(&stitched[krate].root)?; + let display_name = + CrateDisplayName::from_canonical_name(&stitched[krate].name); + let crate_id = crate_graph.add_crate_root( + file_id, + Edition::CURRENT_FIXME, + Some(display_name), + None, + cfg_options.clone(), + None, + Env::default(), + CrateOrigin::Lang(LangCrateOrigin::from(&*stitched[krate].name)), + false, + None, + ); + Some((krate, crate_id)) + }) + .collect(); + + for from in stitched.crates() { + for &to in stitched[from].deps.iter() { + let name = CrateName::new(&stitched[to].name).unwrap(); + if let (Some(&from), Some(&to)) = + (sysroot_crates.get(&from), sysroot_crates.get(&to)) + { + add_dep(crate_graph, from, name, to); + } + } + } + + let public_deps = SysrootPublicDeps { + deps: stitched + .public_deps() + .filter_map(|(name, idx, prelude)| { + Some((name, *sysroot_crates.get(&idx)?, prelude)) + }) + .collect::<Vec<_>>(), + }; + + let libproc_macro = + stitched.proc_macro().and_then(|it| sysroot_crates.get(&it).copied()); + (public_deps, libproc_macro) + } RustLibSrcWorkspace::Empty => (SysrootPublicDeps { deps: vec![] }, None), } }
diff --git a/crates/project-model/test_data/output/rust_project_cfg_groups.txt b/crates/project-model/test_data/output/rust_project_cfg_groups.txt index 28ca4eb..9b4be19 100644 --- a/crates/project-model/test_data/output/rust_project_cfg_groups.txt +++ b/crates/project-model/test_data/output/rust_project_cfg_groups.txt
@@ -3,6 +3,417 @@ root_file_id: FileId( 1, ), + edition: Edition2021, + version: None, + display_name: Some( + CrateDisplayName { + crate_name: CrateName( + "alloc", + ), + canonical_name: "alloc", + }, + ), + cfg_options: CfgOptions( + [ + "debug_assertions", + "miri", + "true", + ], + ), + potential_cfg_options: None, + env: Env { + entries: {}, + }, + dependencies: [ + Dependency { + crate_id: Idx::<CrateData>(1), + name: CrateName( + "core", + ), + prelude: true, + sysroot: false, + }, + ], + origin: Lang( + Alloc, + ), + is_proc_macro: false, + proc_macro_cwd: None, + }, + 1: CrateData { + root_file_id: FileId( + 2, + ), + edition: Edition2021, + version: None, + display_name: Some( + CrateDisplayName { + crate_name: CrateName( + "core", + ), + canonical_name: "core", + }, + ), + cfg_options: CfgOptions( + [ + "debug_assertions", + "miri", + "true", + ], + ), + potential_cfg_options: None, + env: Env { + entries: {}, + }, + dependencies: [], + origin: Lang( + Core, + ), + is_proc_macro: false, + proc_macro_cwd: None, + }, + 2: CrateData { + root_file_id: FileId( + 3, + ), + edition: Edition2021, + version: None, + display_name: Some( + CrateDisplayName { + crate_name: CrateName( + "panic_abort", + ), + canonical_name: "panic_abort", + }, + ), + cfg_options: CfgOptions( + [ + "debug_assertions", + "miri", + "true", + ], + ), + potential_cfg_options: None, + env: Env { + entries: {}, + }, + dependencies: [], + origin: Lang( + Other, + ), + is_proc_macro: false, + proc_macro_cwd: None, + }, + 3: CrateData { + root_file_id: FileId( + 4, + ), + edition: Edition2021, + version: None, + display_name: Some( + CrateDisplayName { + crate_name: CrateName( + "panic_unwind", + ), + canonical_name: "panic_unwind", + }, + ), + cfg_options: CfgOptions( + [ + "debug_assertions", + "miri", + "true", + ], + ), + potential_cfg_options: None, + env: Env { + entries: {}, + }, + dependencies: [], + origin: Lang( + Other, + ), + is_proc_macro: false, + proc_macro_cwd: None, + }, + 4: CrateData { + root_file_id: FileId( + 5, + ), + edition: Edition2021, + version: None, + display_name: Some( + CrateDisplayName { + crate_name: CrateName( + "proc_macro", + ), + canonical_name: "proc_macro", + }, + ), + cfg_options: CfgOptions( + [ + "debug_assertions", + "miri", + "true", + ], + ), + potential_cfg_options: None, + env: Env { + entries: {}, + }, + dependencies: [ + Dependency { + crate_id: Idx::<CrateData>(6), + name: CrateName( + "std", + ), + prelude: true, + sysroot: false, + }, + Dependency { + crate_id: Idx::<CrateData>(1), + name: CrateName( + "core", + ), + prelude: true, + sysroot: false, + }, + ], + origin: Lang( + ProcMacro, + ), + is_proc_macro: false, + proc_macro_cwd: None, + }, + 5: CrateData { + root_file_id: FileId( + 6, + ), + edition: Edition2021, + version: None, + display_name: Some( + CrateDisplayName { + crate_name: CrateName( + "profiler_builtins", + ), + canonical_name: "profiler_builtins", + }, + ), + cfg_options: CfgOptions( + [ + "debug_assertions", + "miri", + "true", + ], + ), + potential_cfg_options: None, + env: Env { + entries: {}, + }, + dependencies: [], + origin: Lang( + Other, + ), + is_proc_macro: false, + proc_macro_cwd: None, + }, + 6: CrateData { + root_file_id: FileId( + 7, + ), + edition: Edition2021, + version: None, + display_name: Some( + CrateDisplayName { + crate_name: CrateName( + "std", + ), + canonical_name: "std", + }, + ), + cfg_options: CfgOptions( + [ + "debug_assertions", + "miri", + "true", + ], + ), + potential_cfg_options: None, + env: Env { + entries: {}, + }, + dependencies: [ + Dependency { + crate_id: Idx::<CrateData>(0), + name: CrateName( + "alloc", + ), + prelude: true, + sysroot: false, + }, + Dependency { + crate_id: Idx::<CrateData>(3), + name: CrateName( + "panic_unwind", + ), + prelude: true, + sysroot: false, + }, + Dependency { + crate_id: Idx::<CrateData>(2), + name: CrateName( + "panic_abort", + ), + prelude: true, + sysroot: false, + }, + Dependency { + crate_id: Idx::<CrateData>(1), + name: CrateName( + "core", + ), + prelude: true, + sysroot: false, + }, + Dependency { + crate_id: Idx::<CrateData>(5), + name: CrateName( + "profiler_builtins", + ), + prelude: true, + sysroot: false, + }, + Dependency { + crate_id: Idx::<CrateData>(9), + name: CrateName( + "unwind", + ), + prelude: true, + sysroot: false, + }, + Dependency { + crate_id: Idx::<CrateData>(7), + name: CrateName( + "std_detect", + ), + prelude: true, + sysroot: false, + }, + Dependency { + crate_id: Idx::<CrateData>(8), + name: CrateName( + "test", + ), + prelude: true, + sysroot: false, + }, + ], + origin: Lang( + Std, + ), + is_proc_macro: false, + proc_macro_cwd: None, + }, + 7: CrateData { + root_file_id: FileId( + 8, + ), + edition: Edition2021, + version: None, + display_name: Some( + CrateDisplayName { + crate_name: CrateName( + "std_detect", + ), + canonical_name: "std_detect", + }, + ), + cfg_options: CfgOptions( + [ + "debug_assertions", + "miri", + "true", + ], + ), + potential_cfg_options: None, + env: Env { + entries: {}, + }, + dependencies: [], + origin: Lang( + Other, + ), + is_proc_macro: false, + proc_macro_cwd: None, + }, + 8: CrateData { + root_file_id: FileId( + 9, + ), + edition: Edition2021, + version: None, + display_name: Some( + CrateDisplayName { + crate_name: CrateName( + "test", + ), + canonical_name: "test", + }, + ), + cfg_options: CfgOptions( + [ + "debug_assertions", + "miri", + "true", + ], + ), + potential_cfg_options: None, + env: Env { + entries: {}, + }, + dependencies: [], + origin: Lang( + Test, + ), + is_proc_macro: false, + proc_macro_cwd: None, + }, + 9: CrateData { + root_file_id: FileId( + 10, + ), + edition: Edition2021, + version: None, + display_name: Some( + CrateDisplayName { + crate_name: CrateName( + "unwind", + ), + canonical_name: "unwind", + }, + ), + cfg_options: CfgOptions( + [ + "debug_assertions", + "miri", + "true", + ], + ), + potential_cfg_options: None, + env: Env { + entries: {}, + }, + dependencies: [], + origin: Lang( + Other, + ), + is_proc_macro: false, + proc_macro_cwd: None, + }, + 10: CrateData { + root_file_id: FileId( + 11, + ), edition: Edition2018, version: None, display_name: Some( @@ -27,7 +438,48 @@ env: Env { entries: {}, }, - dependencies: [], + dependencies: [ + Dependency { + crate_id: Idx::<CrateData>(1), + name: CrateName( + "core", + ), + prelude: true, + sysroot: true, + }, + Dependency { + crate_id: Idx::<CrateData>(0), + name: CrateName( + "alloc", + ), + prelude: false, + sysroot: true, + }, + Dependency { + crate_id: Idx::<CrateData>(6), + name: CrateName( + "std", + ), + prelude: true, + sysroot: true, + }, + Dependency { + crate_id: Idx::<CrateData>(8), + name: CrateName( + "test", + ), + prelude: false, + sysroot: true, + }, + Dependency { + crate_id: Idx::<CrateData>(4), + name: CrateName( + "proc_macro", + ), + prelude: false, + sysroot: true, + }, + ], origin: Local { repo: None, name: Some( @@ -37,9 +489,9 @@ is_proc_macro: false, proc_macro_cwd: None, }, - 1: CrateData { + 11: CrateData { root_file_id: FileId( - 1, + 11, ), edition: Edition2018, version: None, @@ -65,7 +517,48 @@ env: Env { entries: {}, }, - dependencies: [], + dependencies: [ + Dependency { + crate_id: Idx::<CrateData>(1), + name: CrateName( + "core", + ), + prelude: true, + sysroot: true, + }, + Dependency { + crate_id: Idx::<CrateData>(0), + name: CrateName( + "alloc", + ), + prelude: false, + sysroot: true, + }, + Dependency { + crate_id: Idx::<CrateData>(6), + name: CrateName( + "std", + ), + prelude: true, + sysroot: true, + }, + Dependency { + crate_id: Idx::<CrateData>(8), + name: CrateName( + "test", + ), + prelude: false, + sysroot: true, + }, + Dependency { + crate_id: Idx::<CrateData>(4), + name: CrateName( + "proc_macro", + ), + prelude: false, + sysroot: true, + }, + ], origin: Local { repo: None, name: Some(
diff --git a/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt b/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt index dde8d30..4c8e66e 100644 --- a/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt +++ b/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt
@@ -3,6 +3,417 @@ root_file_id: FileId( 1, ), + edition: Edition2021, + version: None, + display_name: Some( + CrateDisplayName { + crate_name: CrateName( + "alloc", + ), + canonical_name: "alloc", + }, + ), + cfg_options: CfgOptions( + [ + "debug_assertions", + "miri", + "true", + ], + ), + potential_cfg_options: None, + env: Env { + entries: {}, + }, + dependencies: [ + Dependency { + crate_id: Idx::<CrateData>(1), + name: CrateName( + "core", + ), + prelude: true, + sysroot: false, + }, + ], + origin: Lang( + Alloc, + ), + is_proc_macro: false, + proc_macro_cwd: None, + }, + 1: CrateData { + root_file_id: FileId( + 2, + ), + edition: Edition2021, + version: None, + display_name: Some( + CrateDisplayName { + crate_name: CrateName( + "core", + ), + canonical_name: "core", + }, + ), + cfg_options: CfgOptions( + [ + "debug_assertions", + "miri", + "true", + ], + ), + potential_cfg_options: None, + env: Env { + entries: {}, + }, + dependencies: [], + origin: Lang( + Core, + ), + is_proc_macro: false, + proc_macro_cwd: None, + }, + 2: CrateData { + root_file_id: FileId( + 3, + ), + edition: Edition2021, + version: None, + display_name: Some( + CrateDisplayName { + crate_name: CrateName( + "panic_abort", + ), + canonical_name: "panic_abort", + }, + ), + cfg_options: CfgOptions( + [ + "debug_assertions", + "miri", + "true", + ], + ), + potential_cfg_options: None, + env: Env { + entries: {}, + }, + dependencies: [], + origin: Lang( + Other, + ), + is_proc_macro: false, + proc_macro_cwd: None, + }, + 3: CrateData { + root_file_id: FileId( + 4, + ), + edition: Edition2021, + version: None, + display_name: Some( + CrateDisplayName { + crate_name: CrateName( + "panic_unwind", + ), + canonical_name: "panic_unwind", + }, + ), + cfg_options: CfgOptions( + [ + "debug_assertions", + "miri", + "true", + ], + ), + potential_cfg_options: None, + env: Env { + entries: {}, + }, + dependencies: [], + origin: Lang( + Other, + ), + is_proc_macro: false, + proc_macro_cwd: None, + }, + 4: CrateData { + root_file_id: FileId( + 5, + ), + edition: Edition2021, + version: None, + display_name: Some( + CrateDisplayName { + crate_name: CrateName( + "proc_macro", + ), + canonical_name: "proc_macro", + }, + ), + cfg_options: CfgOptions( + [ + "debug_assertions", + "miri", + "true", + ], + ), + potential_cfg_options: None, + env: Env { + entries: {}, + }, + dependencies: [ + Dependency { + crate_id: Idx::<CrateData>(6), + name: CrateName( + "std", + ), + prelude: true, + sysroot: false, + }, + Dependency { + crate_id: Idx::<CrateData>(1), + name: CrateName( + "core", + ), + prelude: true, + sysroot: false, + }, + ], + origin: Lang( + ProcMacro, + ), + is_proc_macro: false, + proc_macro_cwd: None, + }, + 5: CrateData { + root_file_id: FileId( + 6, + ), + edition: Edition2021, + version: None, + display_name: Some( + CrateDisplayName { + crate_name: CrateName( + "profiler_builtins", + ), + canonical_name: "profiler_builtins", + }, + ), + cfg_options: CfgOptions( + [ + "debug_assertions", + "miri", + "true", + ], + ), + potential_cfg_options: None, + env: Env { + entries: {}, + }, + dependencies: [], + origin: Lang( + Other, + ), + is_proc_macro: false, + proc_macro_cwd: None, + }, + 6: CrateData { + root_file_id: FileId( + 7, + ), + edition: Edition2021, + version: None, + display_name: Some( + CrateDisplayName { + crate_name: CrateName( + "std", + ), + canonical_name: "std", + }, + ), + cfg_options: CfgOptions( + [ + "debug_assertions", + "miri", + "true", + ], + ), + potential_cfg_options: None, + env: Env { + entries: {}, + }, + dependencies: [ + Dependency { + crate_id: Idx::<CrateData>(0), + name: CrateName( + "alloc", + ), + prelude: true, + sysroot: false, + }, + Dependency { + crate_id: Idx::<CrateData>(3), + name: CrateName( + "panic_unwind", + ), + prelude: true, + sysroot: false, + }, + Dependency { + crate_id: Idx::<CrateData>(2), + name: CrateName( + "panic_abort", + ), + prelude: true, + sysroot: false, + }, + Dependency { + crate_id: Idx::<CrateData>(1), + name: CrateName( + "core", + ), + prelude: true, + sysroot: false, + }, + Dependency { + crate_id: Idx::<CrateData>(5), + name: CrateName( + "profiler_builtins", + ), + prelude: true, + sysroot: false, + }, + Dependency { + crate_id: Idx::<CrateData>(9), + name: CrateName( + "unwind", + ), + prelude: true, + sysroot: false, + }, + Dependency { + crate_id: Idx::<CrateData>(7), + name: CrateName( + "std_detect", + ), + prelude: true, + sysroot: false, + }, + Dependency { + crate_id: Idx::<CrateData>(8), + name: CrateName( + "test", + ), + prelude: true, + sysroot: false, + }, + ], + origin: Lang( + Std, + ), + is_proc_macro: false, + proc_macro_cwd: None, + }, + 7: CrateData { + root_file_id: FileId( + 8, + ), + edition: Edition2021, + version: None, + display_name: Some( + CrateDisplayName { + crate_name: CrateName( + "std_detect", + ), + canonical_name: "std_detect", + }, + ), + cfg_options: CfgOptions( + [ + "debug_assertions", + "miri", + "true", + ], + ), + potential_cfg_options: None, + env: Env { + entries: {}, + }, + dependencies: [], + origin: Lang( + Other, + ), + is_proc_macro: false, + proc_macro_cwd: None, + }, + 8: CrateData { + root_file_id: FileId( + 9, + ), + edition: Edition2021, + version: None, + display_name: Some( + CrateDisplayName { + crate_name: CrateName( + "test", + ), + canonical_name: "test", + }, + ), + cfg_options: CfgOptions( + [ + "debug_assertions", + "miri", + "true", + ], + ), + potential_cfg_options: None, + env: Env { + entries: {}, + }, + dependencies: [], + origin: Lang( + Test, + ), + is_proc_macro: false, + proc_macro_cwd: None, + }, + 9: CrateData { + root_file_id: FileId( + 10, + ), + edition: Edition2021, + version: None, + display_name: Some( + CrateDisplayName { + crate_name: CrateName( + "unwind", + ), + canonical_name: "unwind", + }, + ), + cfg_options: CfgOptions( + [ + "debug_assertions", + "miri", + "true", + ], + ), + potential_cfg_options: None, + env: Env { + entries: {}, + }, + dependencies: [], + origin: Lang( + Other, + ), + is_proc_macro: false, + proc_macro_cwd: None, + }, + 10: CrateData { + root_file_id: FileId( + 11, + ), edition: Edition2018, version: None, display_name: Some( @@ -24,7 +435,48 @@ env: Env { entries: {}, }, - dependencies: [], + dependencies: [ + Dependency { + crate_id: Idx::<CrateData>(1), + name: CrateName( + "core", + ), + prelude: true, + sysroot: true, + }, + Dependency { + crate_id: Idx::<CrateData>(0), + name: CrateName( + "alloc", + ), + prelude: false, + sysroot: true, + }, + Dependency { + crate_id: Idx::<CrateData>(6), + name: CrateName( + "std", + ), + prelude: true, + sysroot: true, + }, + Dependency { + crate_id: Idx::<CrateData>(8), + name: CrateName( + "test", + ), + prelude: false, + sysroot: true, + }, + Dependency { + crate_id: Idx::<CrateData>(4), + name: CrateName( + "proc_macro", + ), + prelude: false, + sysroot: true, + }, + ], origin: Local { repo: None, name: Some(
diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs index 4fc6180..dee76ee 100644 --- a/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/crates/rust-analyzer/src/cli/analysis_stats.rs
@@ -69,7 +69,7 @@ all_targets: true, set_test: !self.no_test, cfg_overrides: CfgOverrides { - global: CfgDiff::new(vec![CfgAtom::Flag(hir::sym::miri.clone())], vec![]).unwrap(), + global: CfgDiff::new(vec![CfgAtom::Flag(hir::sym::miri.clone())], vec![]), selective: Default::default(), }, ..Default::default() @@ -390,6 +390,8 @@ for &file_id in &file_ids { let sema = hir::Semantics::new(db); + let display_target = + sema.first_crate_or_default(file_id.file_id()).to_display_target(db); let parse = sema.parse_guess_edition(file_id.into()); let file_txt = db.file_text(file_id.into()); @@ -467,7 +469,7 @@ prefer_absolute: false, allow_unstable: true, }, - Edition::LATEST, + display_target, ) .unwrap(); syntax_hit_found |= trim(&original_text) == trim(&generated); @@ -641,6 +643,7 @@ for &body_id in bodies { let name = body_id.name(db).unwrap_or_else(Name::missing); let module = body_id.module(db); + let display_target = module.krate().to_display_target(db); let full_name = move || { module .krate() @@ -739,12 +742,12 @@ start.col, end.line + 1, end.col, - ty.display(db, Edition::LATEST) + ty.display(db, display_target) )); } else { bar.println(format!( "unknown location: {}", - ty.display(db, Edition::LATEST) + ty.display(db, display_target) )); } } @@ -752,7 +755,7 @@ println!( r#"{},type,"{}""#, location_csv_expr(db, vfs, &sm(), expr_id), - ty.display(db, Edition::LATEST) + ty.display(db, display_target) ); } if let Some(mismatch) = inference_result.type_mismatch_for_expr(expr_id) { @@ -767,15 +770,15 @@ start.col, end.line + 1, end.col, - mismatch.expected.display(db, Edition::LATEST), - mismatch.actual.display(db, Edition::LATEST) + mismatch.expected.display(db, display_target), + mismatch.actual.display(db, display_target) )); } else { bar.println(format!( "{}: Expected {}, got {}", name.display(db, Edition::LATEST), - mismatch.expected.display(db, Edition::LATEST), - mismatch.actual.display(db, Edition::LATEST) + mismatch.expected.display(db, display_target), + mismatch.actual.display(db, display_target) )); } } @@ -783,8 +786,8 @@ println!( r#"{},mismatch,"{}","{}""#, location_csv_expr(db, vfs, &sm(), expr_id), - mismatch.expected.display(db, Edition::LATEST), - mismatch.actual.display(db, Edition::LATEST) + mismatch.expected.display(db, display_target), + mismatch.actual.display(db, display_target) ); } } @@ -843,12 +846,12 @@ start.col, end.line + 1, end.col, - ty.display(db, Edition::LATEST) + ty.display(db, display_target) )); } else { bar.println(format!( "unknown location: {}", - ty.display(db, Edition::LATEST) + ty.display(db, display_target) )); } } @@ -856,7 +859,7 @@ println!( r#"{},type,"{}""#, location_csv_pat(db, vfs, &sm(), pat_id), - ty.display(db, Edition::LATEST) + ty.display(db, display_target) ); } if let Some(mismatch) = inference_result.type_mismatch_for_pat(pat_id) { @@ -870,15 +873,15 @@ start.col, end.line + 1, end.col, - mismatch.expected.display(db, Edition::LATEST), - mismatch.actual.display(db, Edition::LATEST) + mismatch.expected.display(db, display_target), + mismatch.actual.display(db, display_target) )); } else { bar.println(format!( "{}: Expected {}, got {}", name.display(db, Edition::LATEST), - mismatch.expected.display(db, Edition::LATEST), - mismatch.actual.display(db, Edition::LATEST) + mismatch.expected.display(db, display_target), + mismatch.actual.display(db, display_target) )); } } @@ -886,8 +889,8 @@ println!( r#"{},mismatch,"{}","{}""#, location_csv_pat(db, vfs, &sm(), pat_id), - mismatch.expected.display(db, Edition::LATEST), - mismatch.actual.display(db, Edition::LATEST) + mismatch.expected.display(db, display_target), + mismatch.actual.display(db, display_target) ); } }
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 713e28c..45ac683 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs
@@ -18,7 +18,7 @@ imports::insert_use::{ImportGranularity, InsertUseConfig, PrefixKind}, SnippetCap, }; -use itertools::Itertools; +use itertools::{Either, Itertools}; use paths::{Utf8Path, Utf8PathBuf}; use project_model::{ CargoConfig, CargoFeatures, ProjectJson, ProjectJsonData, ProjectJsonFromCommand, @@ -589,6 +589,10 @@ /// avoid checking unnecessary things. cargo_buildScripts_useRustcWrapper: bool = true, /// List of cfg options to enable with the given values. + /// + /// To enable a name without a value, use `"key"`. + /// To enable a name with a value, use `"key=value"`. + /// To disable, prefix the entry with a `!`. cargo_cfgs: Vec<String> = { vec!["debug_assertions".into(), "miri".into()] }, @@ -1980,27 +1984,35 @@ rustc_source, extra_includes, cfg_overrides: project_model::CfgOverrides { - global: CfgDiff::new( - self.cargo_cfgs(source_root) - .iter() - // parse any cfg setting formatted as key=value or just key (without value) - .filter_map(|s| { - let mut sp = s.splitn(2, "="); - let key = sp.next(); - let val = sp.next(); - key.map(|key| (key, val)) - }) - .map(|(key, val)| match val { - Some(val) => CfgAtom::KeyValue { - key: Symbol::intern(key), - value: Symbol::intern(val), - }, - None => CfgAtom::Flag(Symbol::intern(key)), - }) - .collect(), - vec![], - ) - .unwrap(), + global: { + let (enabled, disabled): (Vec<_>, Vec<_>) = + self.cargo_cfgs(source_root).iter().partition_map(|s| { + s.strip_prefix("!").map_or(Either::Left(s), Either::Right) + }); + CfgDiff::new( + enabled + .into_iter() + // parse any cfg setting formatted as key=value or just key (without value) + .map(|s| match s.split_once("=") { + Some((key, val)) => CfgAtom::KeyValue { + key: Symbol::intern(key), + value: Symbol::intern(val), + }, + None => CfgAtom::Flag(Symbol::intern(s)), + }) + .collect(), + disabled + .into_iter() + .map(|s| match s.split_once("=") { + Some((key, val)) => CfgAtom::KeyValue { + key: Symbol::intern(key), + value: Symbol::intern(val), + }, + None => CfgAtom::Flag(Symbol::intern(s)), + }) + .collect(), + ) + }, selective: Default::default(), }, wrap_rustc_in_build_scripts: *self.cargo_buildScripts_useRustcWrapper(source_root),
diff --git a/crates/rust-analyzer/src/flycheck.rs b/crates/rust-analyzer/src/flycheck.rs index 2309f94..7529e7c 100644 --- a/crates/rust-analyzer/src/flycheck.rs +++ b/crates/rust-analyzer/src/flycheck.rs
@@ -244,8 +244,14 @@ /// The receiver side of the channel mentioned above. command_receiver: Option<Receiver<CargoCheckMessage>>, diagnostics_cleared_for: FxHashSet<Arc<PackageId>>, - diagnostics_cleared_for_all: bool, - diagnostics_received: bool, + diagnostics_received: DiagnosticsReceived, +} + +#[derive(PartialEq)] +enum DiagnosticsReceived { + Yes, + No, + YesAndClearedForAll, } #[allow(clippy::large_enum_variant)] @@ -276,8 +282,7 @@ command_handle: None, command_receiver: None, diagnostics_cleared_for: Default::default(), - diagnostics_cleared_for_all: false, - diagnostics_received: false, + diagnostics_received: DiagnosticsReceived::No, } } @@ -354,7 +359,7 @@ error ); } - if !self.diagnostics_received { + if self.diagnostics_received == DiagnosticsReceived::No { tracing::trace!(flycheck_id = self.id, "clearing diagnostics"); // We finished without receiving any diagnostics. // Clear everything for good measure @@ -396,7 +401,7 @@ package_id = package_id.as_ref().map(|it| &it.repr), "diagnostic received" ); - self.diagnostics_received = true; + self.diagnostics_received = DiagnosticsReceived::Yes; if let Some(package_id) = &package_id { if self.diagnostics_cleared_for.insert(package_id.clone()) { tracing::trace!( @@ -409,8 +414,10 @@ package_id: Some(package_id.clone()), }); } - } else if !self.diagnostics_cleared_for_all { - self.diagnostics_cleared_for_all = true; + } else if self.diagnostics_received + != DiagnosticsReceived::YesAndClearedForAll + { + self.diagnostics_received = DiagnosticsReceived::YesAndClearedForAll; self.send(FlycheckMessage::ClearDiagnostics { id: self.id, package_id: None, @@ -445,8 +452,7 @@ fn clear_diagnostics_state(&mut self) { self.diagnostics_cleared_for.clear(); - self.diagnostics_cleared_for_all = false; - self.diagnostics_received = false; + self.diagnostics_received = DiagnosticsReceived::No; } /// Construct a `Command` object for checking the user's code. If the user
diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs index 70105cd..54670b6 100644 --- a/crates/rust-analyzer/src/global_state.rs +++ b/crates/rust-analyzer/src/global_state.rs
@@ -637,6 +637,25 @@ } }); } + + pub(crate) fn check_workspaces_msrv(&self) -> impl Iterator<Item = String> + '_ { + self.workspaces.iter().filter_map(|ws| { + if let Some(toolchain) = &ws.toolchain { + if *toolchain < crate::MINIMUM_SUPPORTED_TOOLCHAIN_VERSION { + return Some(format!( + "Workspace `{}` is using an outdated toolchain version `{}` but \ + rust-analyzer only supports `{}` and higher.\n\ + Consider using the rust-analyzer rustup component for your toolchain or + upgrade your toolchain to a supported version.\n\n", + ws.manifest_or_root(), + toolchain, + crate::MINIMUM_SUPPORTED_TOOLCHAIN_VERSION, + )); + } + } + None + }) + } } impl Drop for GlobalState {
diff --git a/crates/rust-analyzer/src/handlers/request.rs b/crates/rust-analyzer/src/handlers/request.rs index b91a5db..68b2d6b 100644 --- a/crates/rust-analyzer/src/handlers/request.rs +++ b/crates/rust-analyzer/src/handlers/request.rs
@@ -32,13 +32,13 @@ use vfs::{AbsPath, AbsPathBuf, FileId, VfsPath}; use crate::{ - completion_item_hash, config::{Config, RustfmtConfig, WorkspaceSymbolConfig}, diagnostics::convert_diagnostic, global_state::{FetchWorkspaceRequest, GlobalState, GlobalStateSnapshot}, hack_recover_crate_name, line_index::LineEndings, lsp::{ + completion_item_hash, ext::{ InternalTestingFetchConfigOption, InternalTestingFetchConfigParams, InternalTestingFetchConfigResponse, @@ -427,7 +427,12 @@ Some(it) => it, }; let line_index = snap.file_line_index(position.file_id)?; - let edit = to_proto::snippet_text_edit_vec(&line_index, true, edit); + let edit = to_proto::snippet_text_edit_vec( + &line_index, + true, + edit, + snap.config.change_annotation_support(), + ); Ok(Some(edit)) } @@ -464,7 +469,12 @@ let (_, (text_edit, snippet_edit)) = edit.source_file_edits.into_iter().next().unwrap(); stdx::always!(snippet_edit.is_none(), "on type formatting shouldn't use structured snippets"); - let change = to_proto::snippet_text_edit_vec(&line_index, edit.is_snippet, text_edit); + let change = to_proto::snippet_text_edit_vec( + &line_index, + edit.is_snippet, + text_edit, + snap.config.change_annotation_support(), + ); Ok(Some(change)) } @@ -2025,7 +2035,12 @@ match snap.analysis.move_item(range, direction)? { Some(text_edit) => { let line_index = snap.file_line_index(file_id)?; - Ok(to_proto::snippet_text_edit_vec(&line_index, true, text_edit)) + Ok(to_proto::snippet_text_edit_vec( + &line_index, + true, + text_edit, + snap.config.change_annotation_support(), + )) } None => Ok(vec![]), }
diff --git a/crates/rust-analyzer/src/lib.rs b/crates/rust-analyzer/src/lib.rs index 27d6225..a0d6a0d 100644 --- a/crates/rust-analyzer/src/lib.rs +++ b/crates/rust-analyzer/src/lib.rs
@@ -9,6 +9,15 @@ //! The `cli` submodule implements some batch-processing analysis, primarily as //! a debugging aid. +/// Any toolchain less than this version will likely not work with rust-analyzer built from this revision. +pub const MINIMUM_SUPPORTED_TOOLCHAIN_VERSION: semver::Version = semver::Version { + major: 1, + minor: 78, + patch: 0, + pre: semver::Prerelease::EMPTY, + build: semver::BuildMetadata::EMPTY, +}; + pub mod cli; mod command; @@ -47,10 +56,7 @@ #[cfg(test)] mod integrated_benchmarks; -use hir::Mutability; -use ide::{CompletionItem, CompletionItemRefMode, CompletionRelevance}; use serde::de::DeserializeOwned; -use tenthash::TentHash; pub use crate::{ lsp::capabilities::server_capabilities, main_loop::main_loop, reload::ws_to_crate_graph, @@ -65,115 +71,6 @@ .map_err(|e| anyhow::format_err!("Failed to deserialize {what}: {e}; {json}")) } -fn completion_item_hash(item: &CompletionItem, is_ref_completion: bool) -> [u8; 20] { - fn hash_completion_relevance(hasher: &mut TentHash, relevance: &CompletionRelevance) { - use ide_completion::{ - CompletionRelevancePostfixMatch, CompletionRelevanceReturnType, - CompletionRelevanceTypeMatch, - }; - - hasher.update([ - u8::from(relevance.exact_name_match), - u8::from(relevance.is_local), - u8::from(relevance.is_name_already_imported), - u8::from(relevance.requires_import), - u8::from(relevance.is_private_editable), - ]); - - match relevance.type_match { - None => hasher.update([0u8]), - Some(CompletionRelevanceTypeMatch::CouldUnify) => hasher.update([1u8]), - Some(CompletionRelevanceTypeMatch::Exact) => hasher.update([2u8]), - } - - hasher.update([u8::from(relevance.trait_.is_some())]); - if let Some(trait_) = &relevance.trait_ { - hasher.update([u8::from(trait_.is_op_method), u8::from(trait_.notable_trait)]); - } - - match relevance.postfix_match { - None => hasher.update([0u8]), - Some(CompletionRelevancePostfixMatch::NonExact) => hasher.update([1u8]), - Some(CompletionRelevancePostfixMatch::Exact) => hasher.update([2u8]), - } - - hasher.update([u8::from(relevance.function.is_some())]); - if let Some(function) = &relevance.function { - hasher.update([u8::from(function.has_params), u8::from(function.has_self_param)]); - let discriminant: u8 = match function.return_type { - CompletionRelevanceReturnType::Other => 0, - CompletionRelevanceReturnType::DirectConstructor => 1, - CompletionRelevanceReturnType::Constructor => 2, - CompletionRelevanceReturnType::Builder => 3, - }; - hasher.update([discriminant]); - } - } - - let mut hasher = TentHash::new(); - hasher.update([ - u8::from(is_ref_completion), - u8::from(item.is_snippet), - u8::from(item.deprecated), - u8::from(item.trigger_call_info), - ]); - - hasher.update(item.label.primary.len().to_ne_bytes()); - hasher.update(&item.label.primary); - - hasher.update([u8::from(item.label.detail_left.is_some())]); - if let Some(label_detail) = &item.label.detail_left { - hasher.update(label_detail.len().to_ne_bytes()); - hasher.update(label_detail); - } - - hasher.update([u8::from(item.label.detail_right.is_some())]); - if let Some(label_detail) = &item.label.detail_right { - hasher.update(label_detail.len().to_ne_bytes()); - hasher.update(label_detail); - } - - // NB: do not hash edits or source range, as those may change between the time the client sends the resolve request - // and the time it receives it: some editors do allow changing the buffer between that, leading to ranges being different. - // - // Documentation hashing is skipped too, as it's a large blob to process, - // while not really making completion properties more unique as they are already. - - let kind_tag = item.kind.tag(); - hasher.update(kind_tag.len().to_ne_bytes()); - hasher.update(kind_tag); - - hasher.update(item.lookup.len().to_ne_bytes()); - hasher.update(&item.lookup); - - hasher.update([u8::from(item.detail.is_some())]); - if let Some(detail) = &item.detail { - hasher.update(detail.len().to_ne_bytes()); - hasher.update(detail); - } - - hash_completion_relevance(&mut hasher, &item.relevance); - - hasher.update([u8::from(item.ref_match.is_some())]); - if let Some((ref_mode, text_size)) = &item.ref_match { - let discriminant = match ref_mode { - CompletionItemRefMode::Reference(Mutability::Shared) => 0u8, - CompletionItemRefMode::Reference(Mutability::Mut) => 1u8, - CompletionItemRefMode::Dereference => 2u8, - }; - hasher.update([discriminant]); - hasher.update(u32::from(*text_size).to_ne_bytes()); - } - - hasher.update(item.import_to_add.len().to_ne_bytes()); - for import_path in &item.import_to_add { - hasher.update(import_path.len().to_ne_bytes()); - hasher.update(import_path); - } - - hasher.finalize() -} - #[doc(hidden)] macro_rules! try_default_ { ($it:expr $(,)?) => {
diff --git a/crates/rust-analyzer/src/lsp.rs b/crates/rust-analyzer/src/lsp.rs index 122ad20..c7a5a95 100644 --- a/crates/rust-analyzer/src/lsp.rs +++ b/crates/rust-analyzer/src/lsp.rs
@@ -2,6 +2,10 @@ use core::fmt; +use hir::Mutability; +use ide::{CompletionItem, CompletionItemRefMode, CompletionRelevance}; +use tenthash::TentHash; + pub mod ext; pub(crate) mod capabilities; @@ -29,3 +33,112 @@ } impl std::error::Error for LspError {} + +pub(crate) fn completion_item_hash(item: &CompletionItem, is_ref_completion: bool) -> [u8; 20] { + fn hash_completion_relevance(hasher: &mut TentHash, relevance: &CompletionRelevance) { + use ide_completion::{ + CompletionRelevancePostfixMatch, CompletionRelevanceReturnType, + CompletionRelevanceTypeMatch, + }; + + hasher.update([ + u8::from(relevance.exact_name_match), + u8::from(relevance.is_local), + u8::from(relevance.is_name_already_imported), + u8::from(relevance.requires_import), + u8::from(relevance.is_private_editable), + ]); + + match relevance.type_match { + None => hasher.update([0u8]), + Some(CompletionRelevanceTypeMatch::CouldUnify) => hasher.update([1u8]), + Some(CompletionRelevanceTypeMatch::Exact) => hasher.update([2u8]), + } + + hasher.update([u8::from(relevance.trait_.is_some())]); + if let Some(trait_) = &relevance.trait_ { + hasher.update([u8::from(trait_.is_op_method), u8::from(trait_.notable_trait)]); + } + + match relevance.postfix_match { + None => hasher.update([0u8]), + Some(CompletionRelevancePostfixMatch::NonExact) => hasher.update([1u8]), + Some(CompletionRelevancePostfixMatch::Exact) => hasher.update([2u8]), + } + + hasher.update([u8::from(relevance.function.is_some())]); + if let Some(function) = &relevance.function { + hasher.update([u8::from(function.has_params), u8::from(function.has_self_param)]); + let discriminant: u8 = match function.return_type { + CompletionRelevanceReturnType::Other => 0, + CompletionRelevanceReturnType::DirectConstructor => 1, + CompletionRelevanceReturnType::Constructor => 2, + CompletionRelevanceReturnType::Builder => 3, + }; + hasher.update([discriminant]); + } + } + + let mut hasher = TentHash::new(); + hasher.update([ + u8::from(is_ref_completion), + u8::from(item.is_snippet), + u8::from(item.deprecated), + u8::from(item.trigger_call_info), + ]); + + hasher.update(item.label.primary.len().to_ne_bytes()); + hasher.update(&item.label.primary); + + hasher.update([u8::from(item.label.detail_left.is_some())]); + if let Some(label_detail) = &item.label.detail_left { + hasher.update(label_detail.len().to_ne_bytes()); + hasher.update(label_detail); + } + + hasher.update([u8::from(item.label.detail_right.is_some())]); + if let Some(label_detail) = &item.label.detail_right { + hasher.update(label_detail.len().to_ne_bytes()); + hasher.update(label_detail); + } + + // NB: do not hash edits or source range, as those may change between the time the client sends the resolve request + // and the time it receives it: some editors do allow changing the buffer between that, leading to ranges being different. + // + // Documentation hashing is skipped too, as it's a large blob to process, + // while not really making completion properties more unique as they are already. + + let kind_tag = item.kind.tag(); + hasher.update(kind_tag.len().to_ne_bytes()); + hasher.update(kind_tag); + + hasher.update(item.lookup.len().to_ne_bytes()); + hasher.update(&item.lookup); + + hasher.update([u8::from(item.detail.is_some())]); + if let Some(detail) = &item.detail { + hasher.update(detail.len().to_ne_bytes()); + hasher.update(detail); + } + + hash_completion_relevance(&mut hasher, &item.relevance); + + hasher.update([u8::from(item.ref_match.is_some())]); + if let Some((ref_mode, text_size)) = &item.ref_match { + let discriminant = match ref_mode { + CompletionItemRefMode::Reference(Mutability::Shared) => 0u8, + CompletionItemRefMode::Reference(Mutability::Mut) => 1u8, + CompletionItemRefMode::Dereference => 2u8, + }; + hasher.update([discriminant]); + hasher.update(u32::from(*text_size).to_ne_bytes()); + } + + hasher.update(item.import_to_add.len().to_ne_bytes()); + for import_path in &item.import_to_add { + hasher.update(import_path.len().to_ne_bytes()); + hasher.update(import_path); + } + + hasher.finalize() +}
diff --git a/crates/rust-analyzer/src/lsp/to_proto.rs b/crates/rust-analyzer/src/lsp/to_proto.rs index 446549c..6db7bcb 100644 --- a/crates/rust-analyzer/src/lsp/to_proto.rs +++ b/crates/rust-analyzer/src/lsp/to_proto.rs
@@ -24,11 +24,11 @@ use vfs::AbsPath; use crate::{ - completion_item_hash, config::{CallInfoConfig, Config}, global_state::GlobalStateSnapshot, line_index::{LineEndings, LineIndex, PositionEncoding}, lsp::{ + completion_item_hash, ext::ShellRunnableArgs, semantic_tokens::{self, standard_fallback_type}, utils::invalid_params_error, @@ -200,7 +200,10 @@ line_index: &LineIndex, is_snippet: bool, indel: Indel, + client_supports_annotations: bool, ) -> lsp_ext::SnippetTextEdit { + let annotation_id = + indel.annotation.filter(|_| client_supports_annotations).map(|it| it.to_string()); let text_edit = text_edit(line_index, indel); let insert_text_format = if is_snippet { Some(lsp_types::InsertTextFormat::SNIPPET) } else { None }; @@ -208,7 +211,7 @@ range: text_edit.range, new_text: text_edit.new_text, insert_text_format, - annotation_id: None, + annotation_id, } } @@ -223,10 +226,13 @@ line_index: &LineIndex, is_snippet: bool, text_edit: TextEdit, + clients_support_annotations: bool, ) -> Vec<lsp_ext::SnippetTextEdit> { text_edit .into_iter() - .map(|indel| self::snippet_text_edit(line_index, is_snippet, indel)) + .map(|indel| { + self::snippet_text_edit(line_index, is_snippet, indel, clients_support_annotations) + }) .collect() } @@ -1072,6 +1078,7 @@ line_index: &LineIndex, edit: TextEdit, snippet_edit: SnippetEdit, + client_supports_annotations: bool, ) -> Vec<SnippetTextEdit> { let mut edits: Vec<SnippetTextEdit> = vec![]; let mut snippets = snippet_edit.into_edit_ranges().into_iter().peekable(); @@ -1120,7 +1127,12 @@ edits.push(snippet_text_edit( line_index, true, - Indel { insert: format!("${snippet_index}"), delete: snippet_range }, + Indel { + insert: format!("${snippet_index}"), + delete: snippet_range, + annotation: None, + }, + client_supports_annotations, )) } @@ -1178,12 +1190,22 @@ edits.push(snippet_text_edit( line_index, true, - Indel { insert: new_text, delete: current_indel.delete }, + Indel { + insert: new_text, + delete: current_indel.delete, + annotation: current_indel.annotation, + }, + client_supports_annotations, )) } else { // snippet edit was beyond the current one // since it wasn't consumed, it's available for the next pass - edits.push(snippet_text_edit(line_index, false, current_indel)); + edits.push(snippet_text_edit( + line_index, + false, + current_indel, + client_supports_annotations, + )); } // update the final source -> initial source mapping offset @@ -1208,7 +1230,8 @@ snippet_text_edit( line_index, true, - Indel { insert: format!("${snippet_index}"), delete: snippet_range }, + Indel { insert: format!("${snippet_index}"), delete: snippet_range, annotation: None }, + client_supports_annotations, ) })); @@ -1224,10 +1247,13 @@ ) -> Cancellable<lsp_ext::SnippetTextDocumentEdit> { let text_document = optional_versioned_text_document_identifier(snap, file_id); let line_index = snap.file_line_index(file_id)?; + let client_supports_annotations = snap.config.change_annotation_support(); let mut edits = if let Some(snippet_edit) = snippet_edit { - merge_text_and_snippet_edits(&line_index, edit, snippet_edit) + merge_text_and_snippet_edits(&line_index, edit, snippet_edit, client_supports_annotations) } else { - edit.into_iter().map(|it| snippet_text_edit(&line_index, is_snippet, it)).collect() + edit.into_iter() + .map(|it| snippet_text_edit(&line_index, is_snippet, it, client_supports_annotations)) + .collect() }; if snap.analysis.is_library_file(file_id)? && snap.config.change_annotation_support() { @@ -1348,6 +1374,16 @@ )), }, )) + .chain(source_change.annotations.into_iter().map(|(id, annotation)| { + ( + id.to_string(), + lsp_types::ChangeAnnotation { + label: annotation.label, + description: annotation.description, + needs_confirmation: Some(annotation.needs_confirmation), + }, + ) + })) .collect(), ) } @@ -2023,7 +2059,7 @@ encoding: PositionEncoding::Utf8, }; - let res = merge_text_and_snippet_edits(&line_index, edit, snippets); + let res = merge_text_and_snippet_edits(&line_index, edit, snippets, true); // Ensure that none of the ranges overlap {
diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs index 56dcad0..dffaa88 100644 --- a/crates/rust-analyzer/src/reload.rs +++ b/crates/rust-analyzer/src/reload.rs
@@ -178,6 +178,11 @@ } if !self.workspaces.is_empty() { + self.check_workspaces_msrv().for_each(|e| { + status.health |= lsp_ext::Health::Warning; + format_to!(message, "{e}"); + }); + let proc_macro_clients = self.proc_macro_clients.iter().map(Some).chain(iter::repeat_with(|| None)); @@ -513,6 +518,11 @@ // we don't care about build-script results, they are stale. // FIXME: can we abort the build scripts here if they are already running? self.workspaces = Arc::new(workspaces); + self.check_workspaces_msrv().for_each(|message| { + self.send_notification::<lsp_types::notification::ShowMessage>( + lsp_types::ShowMessageParams { typ: lsp_types::MessageType::WARNING, message }, + ); + }); if self.config.run_build_scripts(None) { self.build_deps_changed = false;
diff --git a/crates/syntax-bridge/src/lib.rs b/crates/syntax-bridge/src/lib.rs index 19801c4..a59a327 100644 --- a/crates/syntax-bridge/src/lib.rs +++ b/crates/syntax-bridge/src/lib.rs
@@ -12,7 +12,7 @@ SyntaxKind::{self, *}, SyntaxNode, SyntaxToken, SyntaxTreeBuilder, TextRange, TextSize, WalkEvent, T, }; -use tt::{buffer::Cursor, token_to_literal}; +use tt::{buffer::Cursor, token_to_literal, Punct}; pub mod prettify_macro_expansion; mod to_parser_input; @@ -217,8 +217,39 @@ tt::TopSubtreeBuilder::new(tt::Delimiter::invisible_spanned(conv.call_site())); while let Some((token, abs_range)) = conv.bump() { - let delimiter = builder.expected_delimiter().map(|it| it.kind); let tt = match token.as_leaf() { + // These delimiters are not actually valid punctuation, but we produce them in syntax fixup. + // So we need to handle them specially here. + Some(&tt::Leaf::Punct(Punct { + char: char @ ('(' | ')' | '{' | '}' | '[' | ']'), + span, + spacing: _, + })) => { + let found_expected_delimiter = + builder.expected_delimiters().enumerate().find(|(_, delim)| match delim.kind { + tt::DelimiterKind::Parenthesis => char == ')', + tt::DelimiterKind::Brace => char == '}', + tt::DelimiterKind::Bracket => char == ']', + tt::DelimiterKind::Invisible => false, + }); + if let Some((idx, _)) = found_expected_delimiter { + for _ in 0..=idx { + builder.close(span); + } + continue; + } + + let delim = match char { + '(' => tt::DelimiterKind::Parenthesis, + '{' => tt::DelimiterKind::Brace, + '[' => tt::DelimiterKind::Bracket, + _ => panic!("unmatched closing delimiter from syntax fixup"), + }; + + // Start a new subtree + builder.open(delim, span); + continue; + } Some(leaf) => leaf.clone(), None => match token.kind(conv) { // Desugar doc comments into doc attributes @@ -228,17 +259,24 @@ continue; } kind if kind.is_punct() && kind != UNDERSCORE => { - let expected = match delimiter { - Some(tt::DelimiterKind::Parenthesis) => Some(T![')']), - Some(tt::DelimiterKind::Brace) => Some(T!['}']), - Some(tt::DelimiterKind::Bracket) => Some(T![']']), - Some(tt::DelimiterKind::Invisible) | None => None, - }; + let found_expected_delimiter = + builder.expected_delimiters().enumerate().find(|(_, delim)| { + match delim.kind { + tt::DelimiterKind::Parenthesis => kind == T![')'], + tt::DelimiterKind::Brace => kind == T!['}'], + tt::DelimiterKind::Bracket => kind == T![']'], + tt::DelimiterKind::Invisible => false, + } + }); // Current token is a closing delimiter that we expect, fix up the closing span - // and end the subtree here - if matches!(expected, Some(expected) if expected == kind) { - builder.close(conv.span_for(abs_range)); + // and end the subtree here. + // We also close any open inner subtrees that might be missing their delimiter. + if let Some((idx, _)) = found_expected_delimiter { + for _ in 0..=idx { + // FIXME: record an error somewhere if we're closing more than one tree here? + builder.close(conv.span_for(abs_range)); + } continue; } @@ -262,6 +300,7 @@ let Some(char) = token.to_char(conv) else { panic!("Token from lexer must be single char: token = {token:#?}") }; + // FIXME: this might still be an unmatched closing delimiter? Maybe we should assert here tt::Leaf::from(tt::Punct { char, spacing, span: conv.span_for(abs_range) }) } kind => { @@ -317,11 +356,10 @@ builder.push(tt); } - // If we get here, we've consumed all input tokens. - // We might have more than one subtree in the stack, if the delimiters are improperly balanced. - // Merge them so we're left with one. - builder.flatten_unclosed_subtrees(); - + while builder.expected_delimiters().next().is_some() { + // FIXME: record an error somewhere? + builder.close(conv.call_site()); + } builder.build_skip_top_subtree() }
diff --git a/crates/syntax/rust.ungram b/crates/syntax/rust.ungram index 88d7beb..70a91af 100644 --- a/crates/syntax/rust.ungram +++ b/crates/syntax/rust.ungram
@@ -279,6 +279,11 @@ | Struct | Union +VariantDef = + Struct +| Union +| Variant + Const = Attr* Visibility? 'default'?
diff --git a/crates/syntax/src/ast/generated/nodes.rs b/crates/syntax/src/ast/generated/nodes.rs index 58c76a4..ebee5e9 100644 --- a/crates/syntax/src/ast/generated/nodes.rs +++ b/crates/syntax/src/ast/generated/nodes.rs
@@ -2447,6 +2447,17 @@ } #[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum VariantDef { + Struct(Struct), + Union(Union), + Variant(Variant), +} +impl ast::HasAttrs for VariantDef {} +impl ast::HasDocComments for VariantDef {} +impl ast::HasName for VariantDef {} +impl ast::HasVisibility for VariantDef {} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct AnyHasArgList { pub(crate) syntax: SyntaxNode, } @@ -6738,6 +6749,40 @@ } } } +impl From<Struct> for VariantDef { + #[inline] + fn from(node: Struct) -> VariantDef { VariantDef::Struct(node) } +} +impl From<Union> for VariantDef { + #[inline] + fn from(node: Union) -> VariantDef { VariantDef::Union(node) } +} +impl From<Variant> for VariantDef { + #[inline] + fn from(node: Variant) -> VariantDef { VariantDef::Variant(node) } +} +impl AstNode for VariantDef { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { matches!(kind, STRUCT | UNION | VARIANT) } + #[inline] + fn cast(syntax: SyntaxNode) -> Option<Self> { + let res = match syntax.kind() { + STRUCT => VariantDef::Struct(Struct { syntax }), + UNION => VariantDef::Union(Union { syntax }), + VARIANT => VariantDef::Variant(Variant { syntax }), + _ => return None, + }; + Some(res) + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + match self { + VariantDef::Struct(it) => &it.syntax, + VariantDef::Union(it) => &it.syntax, + VariantDef::Variant(it) => &it.syntax, + } + } +} impl AnyHasArgList { #[inline] pub fn new<T: ast::HasArgList>(node: T) -> AnyHasArgList { @@ -7753,6 +7798,11 @@ std::fmt::Display::fmt(self.syntax(), f) } } +impl std::fmt::Display for VariantDef { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} impl std::fmt::Display for Abi { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f)
diff --git a/crates/syntax/src/ast/prec.rs b/crates/syntax/src/ast/prec.rs index 0c4da76..4f0e2ca 100644 --- a/crates/syntax/src/ast/prec.rs +++ b/crates/syntax/src/ast/prec.rs
@@ -1,5 +1,7 @@ //! Precedence representation. +use stdx::always; + use crate::{ ast::{self, BinaryOp, Expr, HasArgList, RangeItem}, match_ast, AstNode, SyntaxNode, @@ -140,6 +142,22 @@ } } +fn check_ancestry(ancestor: &SyntaxNode, descendent: &SyntaxNode) -> bool { + let bail = || always!(false, "{} is not an ancestor of {}", ancestor, descendent); + + if !ancestor.text_range().contains_range(descendent.text_range()) { + return bail(); + } + + for anc in descendent.ancestors() { + if anc == *ancestor { + return true; + } + } + + bail() +} + impl Expr { pub fn precedence(&self) -> ExprPrecedence { precedence(self) @@ -153,9 +171,19 @@ /// Returns `true` if `self` would need to be wrapped in parentheses given that its parent is `parent`. pub fn needs_parens_in(&self, parent: &SyntaxNode) -> bool { + self.needs_parens_in_place_of(parent, self.syntax()) + } + + /// Returns `true` if `self` would need to be wrapped in parentheses if it replaces `place_of` + /// given that `place_of`'s parent is `parent`. + pub fn needs_parens_in_place_of(&self, parent: &SyntaxNode, place_of: &SyntaxNode) -> bool { + if !check_ancestry(parent, place_of) { + return false; + } + match_ast! { match parent { - ast::Expr(e) => self.needs_parens_in_expr(&e), + ast::Expr(e) => self.needs_parens_in_expr(&e, place_of), ast::Stmt(e) => self.needs_parens_in_stmt(Some(&e)), ast::StmtList(_) => self.needs_parens_in_stmt(None), ast::ArgList(_) => false, @@ -165,7 +193,7 @@ } } - fn needs_parens_in_expr(&self, parent: &Expr) -> bool { + fn needs_parens_in_expr(&self, parent: &Expr, place_of: &SyntaxNode) -> bool { // Parentheses are necessary when calling a function-like pointer that is a member of a struct or union // (e.g. `(a.f)()`). let is_parent_call_expr = matches!(parent, ast::Expr::CallExpr(_)); @@ -199,13 +227,17 @@ if self.is_paren_like() || parent.is_paren_like() - || self.is_prefix() && (parent.is_prefix() || !self.is_ordered_before(parent)) - || self.is_postfix() && (parent.is_postfix() || self.is_ordered_before(parent)) + || self.is_prefix() + && (parent.is_prefix() + || !self.is_ordered_before_parent_in_place_of(parent, place_of)) + || self.is_postfix() + && (parent.is_postfix() + || self.is_ordered_before_parent_in_place_of(parent, place_of)) { return false; } - let (left, right, inv) = match self.is_ordered_before(parent) { + let (left, right, inv) = match self.is_ordered_before_parent_in_place_of(parent, place_of) { true => (self, parent, false), false => (parent, self, true), }; @@ -413,13 +445,28 @@ } } - fn is_ordered_before(&self, other: &Expr) -> bool { + fn is_ordered_before_parent_in_place_of(&self, parent: &Expr, place_of: &SyntaxNode) -> bool { + use rowan::TextSize; use Expr::*; - return order(self) < order(other); + let self_range = self.syntax().text_range(); + let place_of_range = place_of.text_range(); + + let self_order_adjusted = order(self) - self_range.start() + place_of_range.start(); + + let parent_order = order(parent); + let parent_order_adjusted = if parent_order <= place_of_range.start() { + parent_order + } else if parent_order >= place_of_range.end() { + parent_order - place_of_range.len() + self_range.len() + } else { + return false; + }; + + return self_order_adjusted < parent_order_adjusted; /// Returns text range that can be used to compare two expression for order (which goes first). - fn order(this: &Expr) -> rowan::TextSize { + fn order(this: &Expr) -> TextSize { // For non-paren-like operators: get the operator itself let token = match this { RangeExpr(e) => e.op_token(),
diff --git a/crates/syntax/src/ast/syntax_factory/constructors.rs b/crates/syntax/src/ast/syntax_factory/constructors.rs index 85393ca..44f1304 100644 --- a/crates/syntax/src/ast/syntax_factory/constructors.rs +++ b/crates/syntax/src/ast/syntax_factory/constructors.rs
@@ -783,6 +783,27 @@ ast } + pub fn record_expr_field( + &self, + name: ast::NameRef, + expr: Option<ast::Expr>, + ) -> ast::RecordExprField { + let ast = make::record_expr_field(name.clone(), expr.clone()).clone_for_update(); + + if let Some(mut mapping) = self.mappings() { + let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); + + builder.map_node(name.syntax().clone(), ast.name_ref().unwrap().syntax().clone()); + if let Some(expr) = expr { + builder.map_node(expr.syntax().clone(), ast.expr().unwrap().syntax().clone()); + } + + builder.finish(&mut mapping); + } + + ast + } + pub fn record_field_list( &self, fields: impl IntoIterator<Item = ast::RecordField>,
diff --git a/crates/syntax/src/validation.rs b/crates/syntax/src/validation.rs index 13d352d..85eefac 100644 --- a/crates/syntax/src/validation.rs +++ b/crates/syntax/src/validation.rs
@@ -37,6 +37,7 @@ ast::FnPtrType(it) => validate_trait_object_fn_ptr_ret_ty(it, errors), ast::MacroRules(it) => validate_macro_rules(it, errors), ast::LetExpr(it) => validate_let_expr(it, errors), + ast::ImplTraitType(it) => validate_impl_object_ty(it, errors), _ => (), } } @@ -340,17 +341,34 @@ fn validate_trait_object_ty(ty: ast::DynTraitType) -> Option<SyntaxError> { let tbl = ty.type_bound_list()?; + let bounds_count = tbl.bounds().count(); - if tbl.bounds().count() > 1 { - let dyn_token = ty.dyn_token()?; - let potential_parenthesis = - algo::skip_trivia_token(dyn_token.prev_token()?, Direction::Prev)?; - let kind = potential_parenthesis.kind(); - if !matches!(kind, T!['('] | T![<] | T![=]) { - return Some(SyntaxError::new("ambiguous `+` in a type", ty.syntax().text_range())); + match bounds_count { + 0 => Some(SyntaxError::new( + "At least one trait is required for an object type", + ty.syntax().text_range(), + )), + _ if bounds_count > 1 => { + let dyn_token = ty.dyn_token()?; + let preceding_token = + algo::skip_trivia_token(dyn_token.prev_token()?, Direction::Prev)?; + + if !matches!(preceding_token.kind(), T!['('] | T![<] | T![=]) { + return Some(SyntaxError::new("ambiguous `+` in a type", ty.syntax().text_range())); + } + None } + _ => None, } - None +} + +fn validate_impl_object_ty(ty: ast::ImplTraitType, errors: &mut Vec<SyntaxError>) { + if ty.type_bound_list().map_or(0, |tbl| tbl.bounds().count()) == 0 { + errors.push(SyntaxError::new( + "At least one trait must be specified", + ty.syntax().text_range(), + )); + } } fn validate_macro_rules(mac: ast::MacroRules, errors: &mut Vec<SyntaxError>) {
diff --git a/crates/syntax/test_data/parser/validation/0224_dangling_dyn.rast b/crates/syntax/test_data/parser/validation/0224_dangling_dyn.rast new file mode 100644 index 0000000..b31af5f --- /dev/null +++ b/crates/syntax/test_data/parser/validation/0224_dangling_dyn.rast
@@ -0,0 +1,25 @@ +SOURCE_FILE@0..16 + FN@0..16 + FN_KW@0..2 "fn" + WHITESPACE@2..3 " " + NAME@3..4 + IDENT@3..4 "f" + PARAM_LIST@4..13 + L_PAREN@4..5 "(" + PARAM@5..12 + WILDCARD_PAT@5..6 + UNDERSCORE@5..6 "_" + COLON@6..7 ":" + WHITESPACE@7..8 " " + REF_TYPE@8..12 + AMP@8..9 "&" + DYN_TRAIT_TYPE@9..12 + DYN_KW@9..12 "dyn" + TYPE_BOUND_LIST@12..12 + R_PAREN@12..13 ")" + WHITESPACE@13..14 " " + BLOCK_EXPR@14..16 + STMT_LIST@14..16 + L_CURLY@14..15 "{" + R_CURLY@15..16 "}" +error 9..12: At least one trait is required for an object type
diff --git a/crates/syntax/test_data/parser/validation/0224_dangling_dyn.rs b/crates/syntax/test_data/parser/validation/0224_dangling_dyn.rs new file mode 100644 index 0000000..2fdbb42 --- /dev/null +++ b/crates/syntax/test_data/parser/validation/0224_dangling_dyn.rs
@@ -0,0 +1 @@ +fn f(_: &dyn) {} \ No newline at end of file
diff --git a/crates/syntax/test_data/parser/validation/dangling_impl.rast b/crates/syntax/test_data/parser/validation/dangling_impl.rast new file mode 100644 index 0000000..2db07ae --- /dev/null +++ b/crates/syntax/test_data/parser/validation/dangling_impl.rast
@@ -0,0 +1,23 @@ +SOURCE_FILE@0..16 + FN@0..16 + FN_KW@0..2 "fn" + WHITESPACE@2..3 " " + NAME@3..4 + IDENT@3..4 "f" + PARAM_LIST@4..13 + L_PAREN@4..5 "(" + PARAM@5..12 + WILDCARD_PAT@5..6 + UNDERSCORE@5..6 "_" + COLON@6..7 ":" + WHITESPACE@7..8 " " + IMPL_TRAIT_TYPE@8..12 + IMPL_KW@8..12 "impl" + TYPE_BOUND_LIST@12..12 + R_PAREN@12..13 ")" + WHITESPACE@13..14 " " + BLOCK_EXPR@14..16 + STMT_LIST@14..16 + L_CURLY@14..15 "{" + R_CURLY@15..16 "}" +error 8..12: At least one trait must be specified
diff --git a/crates/syntax/test_data/parser/validation/dangling_impl.rs b/crates/syntax/test_data/parser/validation/dangling_impl.rs new file mode 100644 index 0000000..61706d9 --- /dev/null +++ b/crates/syntax/test_data/parser/validation/dangling_impl.rs
@@ -0,0 +1 @@ +fn f(_: impl) {} \ No newline at end of file
diff --git a/crates/syntax/test_data/parser/validation/dangling_impl_reference.rast b/crates/syntax/test_data/parser/validation/dangling_impl_reference.rast new file mode 100644 index 0000000..dbe6535 --- /dev/null +++ b/crates/syntax/test_data/parser/validation/dangling_impl_reference.rast
@@ -0,0 +1,25 @@ +SOURCE_FILE@0..17 + FN@0..17 + FN_KW@0..2 "fn" + WHITESPACE@2..3 " " + NAME@3..4 + IDENT@3..4 "f" + PARAM_LIST@4..14 + L_PAREN@4..5 "(" + PARAM@5..13 + WILDCARD_PAT@5..6 + UNDERSCORE@5..6 "_" + COLON@6..7 ":" + WHITESPACE@7..8 " " + REF_TYPE@8..13 + AMP@8..9 "&" + IMPL_TRAIT_TYPE@9..13 + IMPL_KW@9..13 "impl" + TYPE_BOUND_LIST@13..13 + R_PAREN@13..14 ")" + WHITESPACE@14..15 " " + BLOCK_EXPR@15..17 + STMT_LIST@15..17 + L_CURLY@15..16 "{" + R_CURLY@16..17 "}" +error 9..13: At least one trait must be specified
diff --git a/crates/syntax/test_data/parser/validation/dangling_impl_reference.rs b/crates/syntax/test_data/parser/validation/dangling_impl_reference.rs new file mode 100644 index 0000000..0b440b4 --- /dev/null +++ b/crates/syntax/test_data/parser/validation/dangling_impl_reference.rs
@@ -0,0 +1 @@ +fn f(_: &impl) {} \ No newline at end of file
diff --git a/crates/test-utils/src/minicore.rs b/crates/test-utils/src/minicore.rs index 202afeb..bf53e58 100644 --- a/crates/test-utils/src/minicore.rs +++ b/crates/test-utils/src/minicore.rs
@@ -1825,7 +1825,7 @@ ($($t:ty)*) => { $( impl $t { - pub const fn from_ne_bytes(bytes: [u8; mem::size_of::<Self>()]) -> Self { + pub const fn from_ne_bytes(bytes: [u8; size_of::<Self>()]) -> Self { unsafe { mem::transmute(bytes) } } } @@ -1874,6 +1874,7 @@ marker::Sized, // :sized marker::Sync, // :sync mem::drop, // :drop + mem::size_of, // :size_of ops::Drop, // :drop ops::{AsyncFn, AsyncFnMut, AsyncFnOnce}, // :async_fn ops::{Fn, FnMut, FnOnce}, // :fn @@ -1895,6 +1896,10 @@ pub mod rust_2021 { pub use super::v1::*; } + + pub mod rust_2024 { + pub use super::v1::*; + } } #[prelude_import]
diff --git a/crates/tt/src/lib.rs b/crates/tt/src/lib.rs index 7705ba8..1cfead5 100644 --- a/crates/tt/src/lib.rs +++ b/crates/tt/src/lib.rs
@@ -243,8 +243,8 @@ self.token_trees.extend(tt.0.iter().cloned()); } - pub fn expected_delimiter(&self) -> Option<&Delimiter<S>> { - self.unclosed_subtree_indices.last().map(|&subtree_idx| { + pub fn expected_delimiters(&self) -> impl Iterator<Item = &Delimiter<S>> { + self.unclosed_subtree_indices.iter().rev().map(|&subtree_idx| { let TokenTree::Subtree(subtree) = &self.token_trees[subtree_idx] else { unreachable!("unclosed token tree is always a subtree") }; @@ -252,28 +252,6 @@ }) } - /// Converts unclosed subtree to a punct of their open delimiter. - // FIXME: This is incorrect to do, delimiters can never be puncts. See #18244. - pub fn flatten_unclosed_subtrees(&mut self) { - for &subtree_idx in &self.unclosed_subtree_indices { - let TokenTree::Subtree(subtree) = &self.token_trees[subtree_idx] else { - unreachable!("unclosed token tree is always a subtree") - }; - let char = match subtree.delimiter.kind { - DelimiterKind::Parenthesis => '(', - DelimiterKind::Brace => '{', - DelimiterKind::Bracket => '[', - DelimiterKind::Invisible => '$', - }; - self.token_trees[subtree_idx] = TokenTree::Leaf(Leaf::Punct(Punct { - char, - spacing: Spacing::Alone, - span: subtree.delimiter.open, - })); - } - self.unclosed_subtree_indices.clear(); - } - /// Builds, and remove the top subtree if it has only one subtree child. pub fn build_skip_top_subtree(mut self) -> TopSubtree<S> { let top_tts = TokenTreesView::new(&self.token_trees[1..]); @@ -731,9 +709,9 @@ }; write!(f, "{align}SUBTREE {delim} ",)?; - fmt::Debug::fmt(&open, f)?; + write!(f, "{:#?}", open)?; write!(f, " ")?; - fmt::Debug::fmt(&close, f)?; + write!(f, "{:#?}", close)?; for child in iter { writeln!(f)?; print_debug_token(f, level + 1, child)?;
diff --git a/docs/book/src/assists_generated.md b/docs/book/src/assists_generated.md index c2063df..9a80185 100644 --- a/docs/book/src/assists_generated.md +++ b/docs/book/src/assists_generated.md
@@ -28,6 +28,32 @@ ``` +### `add_explicit_enum_discriminant` +**Source:** [add_explicit_enum_discriminant.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/add_explicit_enum_discriminant.rs#L11) + +Adds explicit discriminant to all enum variants. + +#### Before +```rust +enum TheEnum┃ { + Foo, + Bar, + Baz = 42, + Quux, +} +``` + +#### After +```rust +enum TheEnum { + Foo = 0, + Bar = 1, + Baz = 42, + Quux = 43, +} +``` + + ### `add_explicit_type` **Source:** [add_explicit_type.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/add_explicit_type.rs#L7) @@ -280,7 +306,7 @@ ### `apply_demorgan_iterator` -**Source:** [apply_demorgan.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/apply_demorgan.rs#L154) +**Source:** [apply_demorgan.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/apply_demorgan.rs#L156) Apply [De Morgan's law](https://en.wikipedia.org/wiki/De_Morgan%27s_laws) to `Iterator::all` and `Iterator::any`. @@ -350,40 +376,6 @@ ``` -### `bool_to_enum` -**Source:** [bool_to_enum.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/bool_to_enum.rs#L29) - -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 -all assignments with the variants and replacing all usages with `== Bool::True` or -`== Bool::False`. - -#### Before -```rust -fn main() { - let ┃bool = true; - - if bool { - println!("foo"); - } -} -``` - -#### After -```rust -#[derive(PartialEq, Eq)] -enum Bool { True, False } - -fn main() { - let bool = Bool::True; - - if bool == Bool::True { - println!("foo"); - } -} -``` - - ### `change_visibility` **Source:** [change_visibility.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/change_visibility.rs#L13) @@ -442,8 +434,42 @@ ``` +### `convert_bool_to_enum` +**Source:** [convert_bool_to_enum.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/convert_bool_to_enum.rs#L29) + +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 +all assignments with the variants and replacing all usages with `== Bool::True` or +`== Bool::False`. + +#### Before +```rust +fn main() { + let ┃bool = true; + + if bool { + println!("foo"); + } +} +``` + +#### After +```rust +#[derive(PartialEq, Eq)] +enum Bool { True, False } + +fn main() { + let bool = Bool::True; + + if bool == Bool::True { + println!("foo"); + } +} +``` + + ### `convert_closure_to_fn` -**Source:** [convert_closure_to_fn.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/convert_closure_to_fn.rs#L27) +**Source:** [convert_closure_to_fn.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/convert_closure_to_fn.rs#L25) This converts a closure to a freestanding function, changing all captures to parameters. @@ -1043,28 +1069,50 @@ ``` -### `explicit_enum_discriminant` -**Source:** [explicit_enum_discriminant.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/explicit_enum_discriminant.rs#L11) +### `expand_record_rest_pattern` +**Source:** [expand_rest_pattern.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/expand_rest_pattern.rs#L26) -Adds explicit discriminant to all enum variants. +Fills fields by replacing rest pattern in record patterns. #### Before ```rust -enum TheEnum┃ { - Foo, - Bar, - Baz = 42, - Quux, +struct Bar { y: Y, z: Z } + +fn foo(bar: Bar) { + let Bar { ..┃ } = bar; } ``` #### After ```rust -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; +} +``` + + +### `expand_tuple_struct_rest_pattern` +**Source:** [expand_rest_pattern.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/expand_rest_pattern.rs#L82) + +Fills fields by replacing rest pattern in tuple struct patterns. + +#### Before +```rust +struct Bar(Y, Z); + +fn foo(bar: Bar) { + let Bar(..┃) = bar; +} +``` + +#### After +```rust +struct Bar(Y, Z); + +fn foo(bar: Bar) { + let Bar(_0, _1) = bar; } ``` @@ -1255,30 +1303,6 @@ ``` -### `fill_record_pattern_fields` -**Source:** [fill_record_pattern_fields.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/fill_record_pattern_fields.rs#L8) - -Fills fields by replacing rest pattern in record patterns. - -#### Before -```rust -struct Bar { y: Y, z: Z } - -fn foo(bar: Bar) { - let Bar { ..┃ } = bar; -} -``` - -#### After -```rust -struct Bar { y: Y, z: Z } - -fn foo(bar: Bar) { - let Bar { y, z } = bar; -} -``` - - ### `fix_visibility` **Source:** [fix_visibility.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/fix_visibility.rs#L14) @@ -1348,7 +1372,7 @@ ### `flip_or_pattern` **Source:** [flip_or_pattern.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/flip_or_pattern.rs#L9) -Flips two trait bounds. +Flips two patterns in an or-pattern. #### Before ```rust @@ -2278,7 +2302,7 @@ ### `inline_local_variable` -**Source:** [inline_local_variable.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/inline_local_variable.rs#L21) +**Source:** [inline_local_variable.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/inline_local_variable.rs#L17) Inlines a local variable. @@ -2423,22 +2447,6 @@ ``` -### `introduce_named_generic` -**Source:** [introduce_named_generic.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/introduce_named_generic.rs#L7) - -Replaces `impl Trait` function argument with the named generic. - -#### Before -```rust -fn foo(bar: ┃impl Bar) {} -``` - -#### After -```rust -fn foo<┃B: Bar>(bar: B) {} -``` - - ### `introduce_named_lifetime` **Source:** [introduce_named_lifetime.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/introduce_named_lifetime.rs#L13) @@ -2467,6 +2475,22 @@ ``` +### `introduce_named_type_parameter` +**Source:** [introduce_named_type_parameter.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/introduce_named_type_parameter.rs#L7) + +Replaces `impl Trait` function argument with the named generic. + +#### Before +```rust +fn foo(bar: ┃impl Bar) {} +``` + +#### After +```rust +fn foo<┃B: Bar>(bar: B) {} +``` + + ### `invert_if` **Source:** [invert_if.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/invert_if.rs#L13)
diff --git a/docs/book/src/configuration_generated.md b/docs/book/src/configuration_generated.md index 1cbe518..0a612d2 100644 --- a/docs/book/src/configuration_generated.md +++ b/docs/book/src/configuration_generated.md
@@ -102,6 +102,10 @@ List of cfg options to enable with the given values. +To enable a name without a value, use `"key"`. +To enable a name with a value, use `"key=value"`. +To disable, prefix the entry with a `!`. + **rust-analyzer.cargo.extraArgs** (default: [])
diff --git a/editors/code/package.json b/editors/code/package.json index 587ae92..9df41c7 100644 --- a/editors/code/package.json +++ b/editors/code/package.json
@@ -825,7 +825,7 @@ "title": "cargo", "properties": { "rust-analyzer.cargo.cfgs": { - "markdownDescription": "List of cfg options to enable with the given values.", + "markdownDescription": "List of cfg options to enable with the given values.\n\nTo enable a name without a value, use `\"key\"`.\nTo enable a name with a value, use `\"key=value\"`.\nTo disable, prefix the entry with a `!`.", "default": [ "debug_assertions", "miri"
diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts index 2ae3291..896b3c1 100644 --- a/editors/code/src/config.ts +++ b/editors/code/src/config.ts
@@ -27,6 +27,7 @@ "server", "files", "cfg", + "showSyntaxTree", ].map((opt) => `${this.rootSection}.${opt}`); private readonly requiresWindowReloadOpts = ["testExplorer"].map(
diff --git a/editors/code/src/ctx.ts b/editors/code/src/ctx.ts index ac73f2a..37a2ee2 100644 --- a/editors/code/src/ctx.ts +++ b/editors/code/src/ctx.ts
@@ -5,6 +5,7 @@ import { Config, prepareVSCodeConfig } from "./config"; import { createClient } from "./client"; import { + isCargoTomlEditor, isDocumentInWorkspace, isRustDocument, isRustEditor, @@ -429,6 +430,11 @@ return editor && isRustEditor(editor) ? editor : undefined; } + get activeCargoTomlEditor(): RustEditor | undefined { + const editor = vscode.window.activeTextEditor; + return editor && isCargoTomlEditor(editor) ? editor : undefined; + } + get extensionPath(): string { return this.extCtx.extensionPath; }
diff --git a/editors/code/src/run.ts b/editors/code/src/run.ts index f71ab7f..40027cc 100644 --- a/editors/code/src/run.ts +++ b/editors/code/src/run.ts
@@ -7,7 +7,7 @@ import { makeDebugConfig } from "./debug"; import type { Config } from "./config"; import type { LanguageClient } from "vscode-languageclient/node"; -import { unwrapUndefinable, type RustEditor } from "./util"; +import { log, unwrapUndefinable, type RustEditor } from "./util"; const quickPickButtons = [ { iconPath: new vscode.ThemeIcon("save"), tooltip: "Save as a launch.json configuration." }, @@ -19,7 +19,7 @@ debuggeeOnly = false, showButtons: boolean = true, ): Promise<RunnableQuickPick | undefined> { - const editor = ctx.activeRustEditor; + const editor = ctx.activeRustEditor ?? ctx.activeCargoTomlEditor; if (!editor) return; // show a placeholder while we get the runnables from the server @@ -175,10 +175,17 @@ uri: editor.document.uri.toString(), }; - const runnables = await client.sendRequest(ra.runnables, { - textDocument, - position: client.code2ProtocolConverter.asPosition(editor.selection.active), - }); + const runnables = await client + .sendRequest(ra.runnables, { + textDocument, + position: client.code2ProtocolConverter.asPosition(editor.selection.active), + }) + .catch((err) => { + // If this command is run for a virtual manifest at the workspace root, then this request + // will fail as we do not watch this file. + log.error(`${err}`); + return []; + }); const items: RunnableQuickPick[] = []; if (prevRunnable) { items.push(prevRunnable);
diff --git a/editors/code/src/util.ts b/editors/code/src/util.ts index 2f7702b..93c7bf8 100644 --- a/editors/code/src/util.ts +++ b/editors/code/src/util.ts
@@ -90,6 +90,10 @@ return isRustDocument(editor.document); } +export function isCargoTomlEditor(editor: vscode.TextEditor): editor is RustEditor { + return isCargoTomlDocument(editor.document); +} + export function isDocumentInWorkspace(document: RustDocument): boolean { const workspaceFolders = vscode.workspace.workspaceFolders; if (!workspaceFolders) {
diff --git a/rust-version b/rust-version index e24e08d..0db4be8 100644 --- a/rust-version +++ b/rust-version
@@ -1 +1 @@ -daf59857d6d2b87af4b846316bf1561a6083ed51 +2c6a12ec44d0426c8939123c2f2cf27d2217de13