Unnamed repository; edit this file 'description' to name the repository.
-rw-r--r--Cargo.lock241
-rw-r--r--Cargo.toml7
-rw-r--r--crates/base-db/src/input.rs12
-rw-r--r--crates/base-db/src/lib.rs4
-rw-r--r--crates/hir-def/src/lang_item.rs2
-rw-r--r--crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs18
-rw-r--r--crates/hir-def/src/nameres.rs8
-rw-r--r--crates/hir-def/src/nameres/assoc.rs4
-rw-r--r--crates/hir-def/src/test_db.rs23
-rw-r--r--crates/hir-expand/src/builtin/fn_macro.rs25
-rw-r--r--crates/hir-ty/src/chalk_db.rs2
-rw-r--r--crates/hir-ty/src/diagnostics/expr.rs13
-rw-r--r--crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs24
-rw-r--r--crates/hir-ty/src/display.rs2
-rw-r--r--crates/hir-ty/src/infer/closure.rs56
-rw-r--r--crates/hir-ty/src/inhabitedness.rs23
-rw-r--r--crates/hir-ty/src/mir/eval/shim.rs2
-rw-r--r--crates/hir-ty/src/mir/lower.rs24
-rw-r--r--crates/hir-ty/src/test_db.rs22
-rw-r--r--crates/hir-ty/src/tests/traits.rs27
-rw-r--r--crates/hir-ty/src/traits.rs5
-rw-r--r--crates/hir/src/lib.rs53
-rw-r--r--crates/hir/src/semantics.rs150
-rw-r--r--crates/hir/src/semantics/child_by_source.rs19
-rw-r--r--crates/hir/src/semantics/source_to_def.rs24
-rw-r--r--crates/hir/src/source_analyzer.rs36
-rw-r--r--crates/ide-assists/src/handlers/desugar_try_expr.rs281
-rw-r--r--crates/ide-assists/src/handlers/generate_mut_trait_impl.rs94
-rw-r--r--crates/ide-assists/src/handlers/generate_new.rs137
-rw-r--r--crates/ide-assists/src/handlers/replace_try_expr_with_match.rs148
-rw-r--r--crates/ide-assists/src/lib.rs4
-rw-r--r--crates/ide-assists/src/tests/generated.rs62
-rw-r--r--crates/ide-completion/src/context.rs6
-rw-r--r--crates/ide-completion/src/context/analysis.rs33
-rw-r--r--crates/ide-completion/src/tests/expression.rs50
-rw-r--r--crates/ide-completion/src/tests/item.rs38
-rw-r--r--crates/ide-db/src/lib.rs4
-rw-r--r--crates/ide-db/src/search.rs34
-rw-r--r--crates/ide-diagnostics/src/handlers/missing_unsafe.rs43
-rw-r--r--crates/ide-diagnostics/src/handlers/non_exhaustive_let.rs25
-rw-r--r--crates/ide-diagnostics/src/handlers/type_mismatch.rs14
-rw-r--r--crates/ide-diagnostics/src/lib.rs181
-rw-r--r--crates/ide-ssr/src/lib.rs2
-rw-r--r--crates/ide-ssr/src/search.rs2
-rw-r--r--crates/ide/src/expand_macro.rs116
-rw-r--r--crates/ide/src/highlight_related.rs2
-rw-r--r--crates/ide/src/hover.rs1
-rw-r--r--crates/ide/src/hover/render.rs76
-rw-r--r--crates/ide/src/hover/tests.rs128
-rw-r--r--crates/intern/src/symbol/symbols.rs1
-rw-r--r--crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs4
-rw-r--r--crates/proc-macro-srv/src/server_impl/token_id.rs3
-rw-r--r--crates/proc-macro-srv/src/server_impl/token_stream.rs11
-rw-r--r--crates/proc-macro-srv/src/tests/mod.rs2
-rw-r--r--crates/query-group-macro/tests/logger_db.rs40
-rw-r--r--crates/rust-analyzer/src/cli/analysis_stats.rs2
-rw-r--r--crates/rust-analyzer/src/config.rs5
-rw-r--r--crates/rust-analyzer/src/integrated_benchmarks.rs20
-rw-r--r--crates/syntax/src/ast/syntax_factory/constructors.rs12
-rw-r--r--docs/book/src/configuration_generated.md9
-rw-r--r--docs/book/src/other_editors.md24
-rw-r--r--editors/code/package.json29
62 files changed, 1513 insertions, 956 deletions
diff --git a/Cargo.lock b/Cargo.lock
index bd8146defa..01de430925 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -18,15 +18,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
[[package]]
-name = "aho-corasick"
-version = "1.1.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
-dependencies = [
- "memchr",
-]
-
-[[package]]
name = "allocator-api2"
version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -124,12 +115,9 @@ dependencies = [
[[package]]
name = "boxcar"
-version = "0.2.11"
+version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6740c6e2fc6360fa57c35214c7493826aee95993926092606f27c983b40837be"
-dependencies = [
- "loom",
-]
+checksum = "66bb12751a83493ef4b8da1120451a262554e216a247f14b48cb5e8fe7ed8bdf"
[[package]]
name = "camino"
@@ -512,19 +500,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ab85b9b05e3978cc9a9cf8fea7f01b494e1a09ed3037e16ba39edc7a29eb61a"
[[package]]
-name = "generator"
-version = "0.8.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cc6bd114ceda131d3b1d665eba35788690ad37f5916457286b32ab6fd3c438dd"
-dependencies = [
- "cfg-if",
- "libc",
- "log",
- "rustversion",
- "windows 0.58.0",
-]
-
-[[package]]
name = "getrandom"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1214,19 +1189,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e"
[[package]]
-name = "loom"
-version = "0.7.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "419e0dc8046cb947daa77eb95ae174acfbddb7673b4151f56d1eed8e93fbfaca"
-dependencies = [
- "cfg-if",
- "generator",
- "scoped-tls",
- "tracing",
- "tracing-subscriber",
-]
-
-[[package]]
name = "lsp-server"
version = "0.7.8"
dependencies = [
@@ -1266,15 +1228,6 @@ dependencies = [
]
[[package]]
-name = "matchers"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558"
-dependencies = [
- "regex-automata 0.1.10",
-]
-
-[[package]]
name = "mbe"
version = "0.0.0"
dependencies = [
@@ -1402,16 +1355,6 @@ checksum = "5e0826a989adedc2a244799e823aece04662b66609d96af8dff7ac6df9a8925d"
[[package]]
name = "nu-ansi-term"
-version = "0.46.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
-dependencies = [
- "overload",
- "winapi",
-]
-
-[[package]]
-name = "nu-ansi-term"
version = "0.50.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4a28e057d01f97e61255210fcff094d74ed0466038633e95017f5beb68e4399"
@@ -1472,12 +1415,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
[[package]]
-name = "overload"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
-
-[[package]]
name = "parking_lot"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1648,7 +1585,7 @@ dependencies = [
"indexmap",
"nix",
"tracing",
- "windows 0.61.1",
+ "windows",
]
[[package]]
@@ -1865,50 +1802,6 @@ dependencies = [
]
[[package]]
-name = "regex"
-version = "1.11.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
-dependencies = [
- "aho-corasick",
- "memchr",
- "regex-automata 0.4.9",
- "regex-syntax 0.8.5",
-]
-
-[[package]]
-name = "regex-automata"
-version = "0.1.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
-dependencies = [
- "regex-syntax 0.6.29",
-]
-
-[[package]]
-name = "regex-automata"
-version = "0.4.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
-dependencies = [
- "aho-corasick",
- "memchr",
- "regex-syntax 0.8.5",
-]
-
-[[package]]
-name = "regex-syntax"
-version = "0.6.29"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
-
-[[package]]
-name = "regex-syntax"
-version = "0.8.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
-
-[[package]]
name = "rowan"
version = "0.15.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2027,12 +1920,6 @@ dependencies = [
]
[[package]]
-name = "rustversion"
-version = "1.0.20"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2"
-
-[[package]]
name = "ryu"
version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2040,9 +1927,9 @@ checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
[[package]]
name = "salsa"
-version = "0.21.1"
+version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6f80d5cf3c3fcab2cef898012f242a670477a1baa609267376af9cb4409026c5"
+checksum = "c8fff508e3d6ef42a32607f7538e17171a877a12015e32036f46e99d00c95781"
dependencies = [
"boxcar",
"crossbeam-queue",
@@ -2063,15 +1950,15 @@ dependencies = [
[[package]]
name = "salsa-macro-rules"
-version = "0.21.1"
+version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "05303d72606fbf2b9c9523cda2039bb8ecb00304027a3cd7e52b02a65c7d9185"
+checksum = "8ea72b3c06f2ce6350fe3a0eeb7aaaf842d1d8352b706973c19c4f02e298a87c"
[[package]]
name = "salsa-macros"
-version = "0.21.1"
+version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eb2f0e2a30c65cb3cd63440c491dde68d9af7e1be2b77832ac7057141107db50"
+checksum = "0ce92025bc160b27814a207cb78d680973af17f863c7f4fc56cf3a535e22f378"
dependencies = [
"heck",
"proc-macro2",
@@ -2556,15 +2443,9 @@ version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008"
dependencies = [
- "matchers",
- "nu-ansi-term 0.46.0",
- "once_cell",
- "regex",
"sharded-slab",
- "smallvec",
"thread_local",
"time",
- "tracing",
"tracing-core",
"tracing-log",
]
@@ -2575,7 +2456,7 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f459ca79f1b0d5f71c54ddfde6debfc59c8b6eeb46808ae492077f739dc7b49c"
dependencies = [
- "nu-ansi-term 0.50.1",
+ "nu-ansi-term",
"tracing-core",
"tracing-log",
"tracing-subscriber",
@@ -2710,22 +2591,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
-name = "winapi"
-version = "0.3.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
-dependencies = [
- "winapi-i686-pc-windows-gnu",
- "winapi-x86_64-pc-windows-gnu",
-]
-
-[[package]]
-name = "winapi-i686-pc-windows-gnu"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
-
-[[package]]
name = "winapi-util"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2735,29 +2600,13 @@ dependencies = [
]
[[package]]
-name = "winapi-x86_64-pc-windows-gnu"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
-
-[[package]]
-name = "windows"
-version = "0.58.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6"
-dependencies = [
- "windows-core 0.58.0",
- "windows-targets 0.52.6",
-]
-
-[[package]]
name = "windows"
version = "0.61.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c5ee8f3d025738cb02bad7868bbb5f8a6327501e870bf51f1b455b0a2454a419"
dependencies = [
"windows-collections",
- "windows-core 0.61.0",
+ "windows-core",
"windows-future",
"windows-link",
"windows-numerics",
@@ -2769,20 +2618,7 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8"
dependencies = [
- "windows-core 0.61.0",
-]
-
-[[package]]
-name = "windows-core"
-version = "0.58.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99"
-dependencies = [
- "windows-implement 0.58.0",
- "windows-interface 0.58.0",
- "windows-result 0.2.0",
- "windows-strings 0.1.0",
- "windows-targets 0.52.6",
+ "windows-core",
]
[[package]]
@@ -2791,11 +2627,11 @@ version = "0.61.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980"
dependencies = [
- "windows-implement 0.60.0",
- "windows-interface 0.59.1",
+ "windows-implement",
+ "windows-interface",
"windows-link",
- "windows-result 0.3.2",
- "windows-strings 0.4.0",
+ "windows-result",
+ "windows-strings",
]
[[package]]
@@ -2804,23 +2640,12 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a1d6bbefcb7b60acd19828e1bc965da6fcf18a7e39490c5f8be71e54a19ba32"
dependencies = [
- "windows-core 0.61.0",
+ "windows-core",
"windows-link",
]
[[package]]
name = "windows-implement"
-version = "0.58.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "windows-implement"
version = "0.60.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836"
@@ -2832,17 +2657,6 @@ dependencies = [
[[package]]
name = "windows-interface"
-version = "0.58.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "windows-interface"
version = "0.59.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8"
@@ -2864,21 +2678,12 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1"
dependencies = [
- "windows-core 0.61.0",
+ "windows-core",
"windows-link",
]
[[package]]
name = "windows-result"
-version = "0.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e"
-dependencies = [
- "windows-targets 0.52.6",
-]
-
-[[package]]
-name = "windows-result"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252"
@@ -2888,16 +2693,6 @@ dependencies = [
[[package]]
name = "windows-strings"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10"
-dependencies = [
- "windows-result 0.2.0",
- "windows-targets 0.52.6",
-]
-
-[[package]]
-name = "windows-strings"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97"
diff --git a/Cargo.toml b/Cargo.toml
index 07731bae3f..8c50718984 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -132,11 +132,8 @@ pulldown-cmark-to-cmark = "10.0.4"
pulldown-cmark = { version = "0.9.6", default-features = false }
rayon = "1.10.0"
rowan = "=0.15.15"
-salsa = { version = "0.21.1", default-features = false, features = [
- "rayon",
- "salsa_unstable",
-] }
-salsa-macros = "0.21.1"
+salsa = { version = "0.22.0", default-features = false, features = ["rayon","salsa_unstable"] }
+salsa-macros = "0.22.0"
semver = "1.0.26"
serde = { version = "1.0.219" }
serde_derive = { version = "1.0.219" }
diff --git a/crates/base-db/src/input.rs b/crates/base-db/src/input.rs
index d42d7e5707..745238167b 100644
--- a/crates/base-db/src/input.rs
+++ b/crates/base-db/src/input.rs
@@ -395,21 +395,21 @@ impl BuiltDependency {
pub type CratesIdMap = FxHashMap<CrateBuilderId, Crate>;
#[salsa_macros::input]
-#[derive(Debug)]
+#[derive(Debug, PartialOrd, Ord)]
pub struct Crate {
- #[return_ref]
+ #[returns(ref)]
pub data: BuiltCrateData,
/// Crate data that is not needed for analysis.
///
/// This is split into a separate field to increase incrementality.
- #[return_ref]
+ #[returns(ref)]
pub extra_data: ExtraCrateData,
// This is in `Arc` because it is shared for all crates in a workspace.
- #[return_ref]
+ #[returns(ref)]
pub workspace_data: Arc<CrateWorkspaceData>,
- #[return_ref]
+ #[returns(ref)]
pub cfg_options: CfgOptions,
- #[return_ref]
+ #[returns(ref)]
pub env: Env,
}
diff --git a/crates/base-db/src/lib.rs b/crates/base-db/src/lib.rs
index a67fbf75c0..4d4e6cae03 100644
--- a/crates/base-db/src/lib.rs
+++ b/crates/base-db/src/lib.rs
@@ -32,6 +32,7 @@ pub use vfs::{AnchoredPath, AnchoredPathBuf, FileId, VfsPath, file_set::FileSet}
macro_rules! impl_intern_key {
($id:ident, $loc:ident) => {
#[salsa_macros::interned(no_lifetime)]
+ #[derive(PartialOrd, Ord)]
pub struct $id {
pub loc: $loc,
}
@@ -165,6 +166,7 @@ impl Files {
}
#[salsa_macros::interned(no_lifetime, debug, constructor=from_span)]
+#[derive(PartialOrd, Ord)]
pub struct EditionedFileId {
pub editioned_file_id: span::EditionedFileId,
}
@@ -356,7 +358,7 @@ fn parse(db: &dyn RootQueryDb, file_id: EditionedFileId) -> Parse<ast::SourceFil
}
fn parse_errors(db: &dyn RootQueryDb, file_id: EditionedFileId) -> Option<&[SyntaxError]> {
- #[salsa_macros::tracked(return_ref)]
+ #[salsa_macros::tracked(returns(ref))]
fn parse_errors(db: &dyn RootQueryDb, file_id: EditionedFileId) -> Option<Box<[SyntaxError]>> {
let errors = db.parse(file_id).errors();
match &*errors {
diff --git a/crates/hir-def/src/lang_item.rs b/crates/hir-def/src/lang_item.rs
index 59344641f4..4ad44775ea 100644
--- a/crates/hir-def/src/lang_item.rs
+++ b/crates/hir-def/src/lang_item.rs
@@ -85,7 +85,7 @@ impl LangItemTarget {
}
/// Salsa query. This will look for lang items in a specific crate.
-#[salsa_macros::tracked(return_ref)]
+#[salsa_macros::tracked(returns(ref))]
pub fn crate_lang_items(db: &dyn DefDatabase, krate: Crate) -> Option<Box<LangItems>> {
let _p = tracing::info_span!("crate_lang_items_query").entered();
diff --git a/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs b/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs
index 3027aff316..293868df61 100644
--- a/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs
+++ b/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs
@@ -510,24 +510,6 @@ fn main() { "s"; }
}
#[test]
-fn test_concat_idents_expand() {
- check(
- r##"
-#[rustc_builtin_macro]
-macro_rules! concat_idents {}
-
-fn main() { concat_idents!(foo, bar); }
-"##,
- expect![[r##"
-#[rustc_builtin_macro]
-macro_rules! concat_idents {}
-
-fn main() { foobar; }
-"##]],
- );
-}
-
-#[test]
fn test_quote_string() {
check(
r##"
diff --git a/crates/hir-def/src/nameres.rs b/crates/hir-def/src/nameres.rs
index d4b30a1d3e..f337f83156 100644
--- a/crates/hir-def/src/nameres.rs
+++ b/crates/hir-def/src/nameres.rs
@@ -381,15 +381,15 @@ mod __ {
#[salsa_macros::tracked]
pub(crate) struct DefMapPair<'db> {
#[tracked]
- #[return_ref]
+ #[returns(ref)]
pub(crate) def_map: DefMap,
- #[return_ref]
+ #[returns(ref)]
pub(crate) local: LocalDefMap,
}
}
pub(crate) use __::DefMapPair;
-#[salsa_macros::tracked(return_ref)]
+#[salsa_macros::tracked(returns(ref))]
pub(crate) fn crate_local_def_map(db: &dyn DefDatabase, crate_id: Crate) -> DefMapPair<'_> {
let krate = crate_id.data(db);
let _p = tracing::info_span!(
@@ -420,7 +420,7 @@ pub(crate) fn crate_local_def_map(db: &dyn DefDatabase, crate_id: Crate) -> DefM
DefMapPair::new(db, def_map, local_def_map)
}
-#[salsa_macros::tracked(return_ref)]
+#[salsa_macros::tracked(returns(ref))]
pub fn block_def_map(db: &dyn DefDatabase, block_id: BlockId) -> DefMap {
let BlockLoc { ast_id, module } = block_id.lookup(db);
diff --git a/crates/hir-def/src/nameres/assoc.rs b/crates/hir-def/src/nameres/assoc.rs
index d45709b8b9..86225d33b4 100644
--- a/crates/hir-def/src/nameres/assoc.rs
+++ b/crates/hir-def/src/nameres/assoc.rs
@@ -75,7 +75,7 @@ impl TraitItems {
})
}
- pub fn attribute_calls(&self) -> impl Iterator<Item = (AstId<ast::Item>, MacroCallId)> + '_ {
+ pub fn macro_calls(&self) -> impl Iterator<Item = (AstId<ast::Item>, MacroCallId)> + '_ {
self.macro_calls.iter().flat_map(|it| it.iter()).copied()
}
}
@@ -109,7 +109,7 @@ impl ImplItems {
(Arc::new(ImplItems { items, macro_calls }), DefDiagnostics::new(diagnostics))
}
- pub fn attribute_calls(&self) -> impl Iterator<Item = (AstId<ast::Item>, MacroCallId)> + '_ {
+ pub fn macro_calls(&self) -> impl Iterator<Item = (AstId<ast::Item>, MacroCallId)> + '_ {
self.macro_calls.iter().flat_map(|it| it.iter()).copied()
}
}
diff --git a/crates/hir-def/src/test_db.rs b/crates/hir-def/src/test_db.rs
index 6c995ab6c2..e30a5b65a1 100644
--- a/crates/hir-def/src/test_db.rs
+++ b/crates/hir-def/src/test_db.rs
@@ -30,9 +30,18 @@ pub(crate) struct TestDB {
impl Default for TestDB {
fn default() -> Self {
+ let events = <Arc<Mutex<Option<Vec<salsa::Event>>>>>::default();
let mut this = Self {
- storage: Default::default(),
- events: Default::default(),
+ storage: salsa::Storage::new(Some(Box::new({
+ let events = events.clone();
+ move |event| {
+ let mut events = events.lock().unwrap();
+ if let Some(events) = &mut *events {
+ events.push(event);
+ }
+ }
+ }))),
+ events,
files: Default::default(),
crates_map: Default::default(),
};
@@ -45,15 +54,7 @@ impl Default for TestDB {
}
#[salsa_macros::db]
-impl salsa::Database for TestDB {
- fn salsa_event(&self, event: &dyn std::ops::Fn() -> salsa::Event) {
- let mut events = self.events.lock().unwrap();
- if let Some(events) = &mut *events {
- let event = event();
- events.push(event);
- }
- }
-}
+impl salsa::Database for TestDB {}
impl fmt::Debug for TestDB {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
diff --git a/crates/hir-expand/src/builtin/fn_macro.rs b/crates/hir-expand/src/builtin/fn_macro.rs
index 539c727728..3180b8dae1 100644
--- a/crates/hir-expand/src/builtin/fn_macro.rs
+++ b/crates/hir-expand/src/builtin/fn_macro.rs
@@ -140,7 +140,6 @@ register_builtin! {
EagerExpander:
(compile_error, CompileError) => compile_error_expand,
(concat, Concat) => concat_expand,
- (concat_idents, ConcatIdents) => concat_idents_expand,
(concat_bytes, ConcatBytes) => concat_bytes_expand,
(include, Include) => include_expand,
(include_bytes, IncludeBytes) => include_bytes_expand,
@@ -660,30 +659,6 @@ fn concat_bytes_expand_subtree(
Ok(())
}
-fn concat_idents_expand(
- _db: &dyn ExpandDatabase,
- _arg_id: MacroCallId,
- tt: &tt::TopSubtree,
- span: Span,
-) -> ExpandResult<tt::TopSubtree> {
- let mut err = None;
- let mut ident = String::new();
- for (i, t) in tt.iter().enumerate() {
- match t {
- TtElement::Leaf(tt::Leaf::Ident(id)) => {
- ident.push_str(id.sym.as_str());
- }
- TtElement::Leaf(tt::Leaf::Punct(punct)) if i % 2 == 1 && punct.char == ',' => (),
- _ => {
- err.get_or_insert(ExpandError::other(span, "unexpected token"));
- }
- }
- }
- // FIXME merge spans
- let ident = tt::Ident { sym: Symbol::intern(&ident), span, is_raw: tt::IdentIsRaw::No };
- ExpandResult { value: quote!(span =>#ident), err }
-}
-
fn relative_file(
db: &dyn ExpandDatabase,
call_id: MacroCallId,
diff --git a/crates/hir-ty/src/chalk_db.rs b/crates/hir-ty/src/chalk_db.rs
index cd799c03dd..22b96b55cb 100644
--- a/crates/hir-ty/src/chalk_db.rs
+++ b/crates/hir-ty/src/chalk_db.rs
@@ -259,7 +259,7 @@ impl chalk_solve::RustIrDatabase<Interner> for ChalkContext<'_> {
}
fn well_known_trait_id(
&self,
- well_known_trait: rust_ir::WellKnownTrait,
+ well_known_trait: WellKnownTrait,
) -> Option<chalk_ir::TraitId<Interner>> {
let lang_attr = lang_item_from_well_known_trait(well_known_trait);
let trait_ = lang_attr.resolve_trait(self.db, self.krate)?;
diff --git a/crates/hir-ty/src/diagnostics/expr.rs b/crates/hir-ty/src/diagnostics/expr.rs
index e4a23cbbac..9eb7ffe1c7 100644
--- a/crates/hir-ty/src/diagnostics/expr.rs
+++ b/crates/hir-ty/src/diagnostics/expr.rs
@@ -25,7 +25,7 @@ use triomphe::Arc;
use typed_arena::Arena;
use crate::{
- Adjust, InferenceResult, Interner, Ty, TyExt, TyKind,
+ Adjust, InferenceResult, Interner, TraitEnvironment, Ty, TyExt, TyKind,
db::HirDatabase,
diagnostics::match_check::{
self,
@@ -74,8 +74,9 @@ impl BodyValidationDiagnostic {
let _p = tracing::info_span!("BodyValidationDiagnostic::collect").entered();
let infer = db.infer(owner);
let body = db.body(owner);
+ let env = db.trait_environment_for_body(owner);
let mut validator =
- ExprValidator { owner, body, infer, diagnostics: Vec::new(), validate_lints };
+ ExprValidator { owner, body, infer, diagnostics: Vec::new(), validate_lints, env };
validator.validate_body(db);
validator.diagnostics
}
@@ -85,6 +86,7 @@ struct ExprValidator {
owner: DefWithBodyId,
body: Arc<Body>,
infer: Arc<InferenceResult>,
+ env: Arc<TraitEnvironment>,
diagnostics: Vec<BodyValidationDiagnostic>,
validate_lints: bool,
}
@@ -190,7 +192,7 @@ impl ExprValidator {
return;
}
- let cx = MatchCheckCtx::new(self.owner.module(db), self.owner, db);
+ let cx = MatchCheckCtx::new(self.owner.module(db), self.owner, db, self.env.clone());
let pattern_arena = Arena::new();
let mut m_arms = Vec::with_capacity(arms.len());
@@ -317,11 +319,14 @@ impl ExprValidator {
return;
};
let pattern_arena = Arena::new();
- let cx = MatchCheckCtx::new(self.owner.module(db), self.owner, db);
+ let cx = MatchCheckCtx::new(self.owner.module(db), self.owner, db, self.env.clone());
for stmt in &**statements {
let &Statement::Let { pat, initializer, else_branch: None, .. } = stmt else {
continue;
};
+ if self.infer.type_mismatch_for_pat(pat).is_some() {
+ continue;
+ }
let Some(initializer) = initializer else { continue };
let ty = &self.infer[initializer];
if ty.contains_unknown() {
diff --git a/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs b/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs
index 785277d70c..dd82a0f45c 100644
--- a/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs
+++ b/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs
@@ -12,9 +12,10 @@ use rustc_pattern_analysis::{
};
use smallvec::{SmallVec, smallvec};
use stdx::never;
+use triomphe::Arc;
use crate::{
- AdtId, Interner, Scalar, Ty, TyExt, TyKind,
+ AdtId, Interner, Scalar, TraitEnvironment, Ty, TyExt, TyKind,
db::HirDatabase,
infer::normalize,
inhabitedness::{is_enum_variant_uninhabited_from, is_ty_uninhabited_from},
@@ -69,13 +70,19 @@ pub(crate) struct MatchCheckCtx<'db> {
body: DefWithBodyId,
pub(crate) db: &'db dyn HirDatabase,
exhaustive_patterns: bool,
+ env: Arc<TraitEnvironment>,
}
impl<'db> MatchCheckCtx<'db> {
- pub(crate) fn new(module: ModuleId, body: DefWithBodyId, db: &'db dyn HirDatabase) -> Self {
+ pub(crate) fn new(
+ module: ModuleId,
+ body: DefWithBodyId,
+ db: &'db dyn HirDatabase,
+ env: Arc<TraitEnvironment>,
+ ) -> Self {
let def_map = module.crate_def_map(db);
let exhaustive_patterns = def_map.is_unstable_feature_enabled(&sym::exhaustive_patterns);
- Self { module, body, db, exhaustive_patterns }
+ Self { module, body, db, exhaustive_patterns, env }
}
pub(crate) fn compute_match_usefulness(
@@ -100,7 +107,7 @@ impl<'db> MatchCheckCtx<'db> {
}
fn is_uninhabited(&self, ty: &Ty) -> bool {
- is_ty_uninhabited_from(self.db, ty, self.module)
+ is_ty_uninhabited_from(self.db, ty, self.module, self.env.clone())
}
/// Returns whether the given ADT is from another crate declared `#[non_exhaustive]`.
@@ -459,8 +466,13 @@ impl PatCx for MatchCheckCtx<'_> {
} else {
let mut variants = IndexVec::with_capacity(enum_data.variants.len());
for &(variant, _) in enum_data.variants.iter() {
- let is_uninhabited =
- is_enum_variant_uninhabited_from(cx.db, variant, subst, cx.module);
+ let is_uninhabited = is_enum_variant_uninhabited_from(
+ cx.db,
+ variant,
+ subst,
+ cx.module,
+ self.env.clone(),
+ );
let visibility = if is_uninhabited {
VariantVisibility::Empty
} else {
diff --git a/crates/hir-ty/src/display.rs b/crates/hir-ty/src/display.rs
index f0989d9de9..f210dd8799 100644
--- a/crates/hir-ty/src/display.rs
+++ b/crates/hir-ty/src/display.rs
@@ -1463,6 +1463,8 @@ impl HirDisplay for Ty {
}
if f.closure_style == ClosureStyle::RANotation || !sig.ret().is_unit() {
write!(f, " -> ")?;
+ // FIXME: We display `AsyncFn` as `-> impl Future`, but this is hard to fix because
+ // we don't have a trait environment here, required to normalize `<Ret as Future>::Output`.
sig.ret().hir_fmt(f)?;
}
} else {
diff --git a/crates/hir-ty/src/infer/closure.rs b/crates/hir-ty/src/infer/closure.rs
index 800897c6fc..bd57ca8916 100644
--- a/crates/hir-ty/src/infer/closure.rs
+++ b/crates/hir-ty/src/infer/closure.rs
@@ -38,7 +38,7 @@ use crate::{
infer::{BreakableKind, CoerceMany, Diverges, coerce::CoerceNever},
make_binders,
mir::{BorrowKind, MirSpan, MutBorrowKind, ProjectionElem},
- to_chalk_trait_id,
+ to_assoc_type_id, to_chalk_trait_id,
traits::FnTrait,
utils::{self, elaborate_clause_supertraits},
};
@@ -245,7 +245,7 @@ impl InferenceContext<'_> {
}
fn deduce_closure_kind_from_predicate_clauses(
- &self,
+ &mut self,
expected_ty: &Ty,
clauses: impl DoubleEndedIterator<Item = WhereClause>,
closure_kind: ClosureKind,
@@ -378,7 +378,7 @@ impl InferenceContext<'_> {
}
fn deduce_sig_from_projection(
- &self,
+ &mut self,
closure_kind: ClosureKind,
projection_ty: &ProjectionTy,
projected_ty: &Ty,
@@ -392,13 +392,16 @@ impl InferenceContext<'_> {
// For now, we only do signature deduction based off of the `Fn` and `AsyncFn` traits,
// for closures and async closures, respectively.
- match closure_kind {
- ClosureKind::Closure | ClosureKind::Async
- if self.fn_trait_kind_from_trait_id(trait_).is_some() =>
- {
- self.extract_sig_from_projection(projection_ty, projected_ty)
- }
- _ => None,
+ let fn_trait_kind = self.fn_trait_kind_from_trait_id(trait_)?;
+ if !matches!(closure_kind, ClosureKind::Closure | ClosureKind::Async) {
+ return None;
+ }
+ if fn_trait_kind.is_async() {
+ // If the expected trait is `AsyncFn(...) -> X`, we don't know what the return type is,
+ // but we do know it must implement `Future<Output = X>`.
+ self.extract_async_fn_sig_from_projection(projection_ty, projected_ty)
+ } else {
+ self.extract_sig_from_projection(projection_ty, projected_ty)
}
}
@@ -424,6 +427,39 @@ impl InferenceContext<'_> {
)))
}
+ fn extract_async_fn_sig_from_projection(
+ &mut self,
+ projection_ty: &ProjectionTy,
+ projected_ty: &Ty,
+ ) -> Option<FnSubst<Interner>> {
+ let arg_param_ty = projection_ty.substitution.as_slice(Interner)[1].assert_ty_ref(Interner);
+
+ let TyKind::Tuple(_, input_tys) = arg_param_ty.kind(Interner) else {
+ return None;
+ };
+
+ let ret_param_future_output = projected_ty;
+ let ret_param_future = self.table.new_type_var();
+ let future_output =
+ LangItem::FutureOutput.resolve_type_alias(self.db, self.resolver.krate())?;
+ let future_projection = crate::AliasTy::Projection(crate::ProjectionTy {
+ associated_ty_id: to_assoc_type_id(future_output),
+ substitution: Substitution::from1(Interner, ret_param_future.clone()),
+ });
+ self.table.register_obligation(
+ crate::AliasEq { alias: future_projection, ty: ret_param_future_output.clone() }
+ .cast(Interner),
+ );
+
+ Some(FnSubst(Substitution::from_iter(
+ Interner,
+ input_tys.iter(Interner).map(|t| t.cast(Interner)).chain(Some(GenericArg::new(
+ Interner,
+ chalk_ir::GenericArgData::Ty(ret_param_future),
+ ))),
+ )))
+ }
+
fn fn_trait_kind_from_trait_id(&self, trait_id: hir_def::TraitId) -> Option<FnTrait> {
FnTrait::from_lang_item(self.db.lang_attr(trait_id.into())?)
}
diff --git a/crates/hir-ty/src/inhabitedness.rs b/crates/hir-ty/src/inhabitedness.rs
index e0c3279d3f..e81a5e3c31 100644
--- a/crates/hir-ty/src/inhabitedness.rs
+++ b/crates/hir-ty/src/inhabitedness.rs
@@ -7,17 +7,24 @@ use chalk_ir::{
};
use hir_def::{AdtId, EnumVariantId, ModuleId, VariantId, visibility::Visibility};
use rustc_hash::FxHashSet;
+use triomphe::Arc;
use crate::{
- Binders, Interner, Substitution, Ty, TyKind, consteval::try_const_usize, db::HirDatabase,
+ AliasTy, Binders, Interner, Substitution, TraitEnvironment, Ty, TyKind,
+ consteval::try_const_usize, db::HirDatabase,
};
// FIXME: Turn this into a query, it can be quite slow
/// Checks whether a type is visibly uninhabited from a particular module.
-pub(crate) fn is_ty_uninhabited_from(db: &dyn HirDatabase, ty: &Ty, target_mod: ModuleId) -> bool {
+pub(crate) fn is_ty_uninhabited_from(
+ db: &dyn HirDatabase,
+ ty: &Ty,
+ target_mod: ModuleId,
+ env: Arc<TraitEnvironment>,
+) -> bool {
let _p = tracing::info_span!("is_ty_uninhabited_from", ?ty).entered();
let mut uninhabited_from =
- UninhabitedFrom { target_mod, db, max_depth: 500, recursive_ty: FxHashSet::default() };
+ UninhabitedFrom { target_mod, db, max_depth: 500, recursive_ty: FxHashSet::default(), env };
let inhabitedness = ty.visit_with(&mut uninhabited_from, DebruijnIndex::INNERMOST);
inhabitedness == BREAK_VISIBLY_UNINHABITED
}
@@ -29,11 +36,12 @@ pub(crate) fn is_enum_variant_uninhabited_from(
variant: EnumVariantId,
subst: &Substitution,
target_mod: ModuleId,
+ env: Arc<TraitEnvironment>,
) -> bool {
let _p = tracing::info_span!("is_enum_variant_uninhabited_from").entered();
let mut uninhabited_from =
- UninhabitedFrom { target_mod, db, max_depth: 500, recursive_ty: FxHashSet::default() };
+ UninhabitedFrom { target_mod, db, max_depth: 500, recursive_ty: FxHashSet::default(), env };
let inhabitedness = uninhabited_from.visit_variant(variant.into(), subst);
inhabitedness == BREAK_VISIBLY_UNINHABITED
}
@@ -44,6 +52,7 @@ struct UninhabitedFrom<'a> {
// guard for preventing stack overflow in non trivial non terminating types
max_depth: usize,
db: &'a dyn HirDatabase,
+ env: Arc<TraitEnvironment>,
}
const CONTINUE_OPAQUELY_INHABITED: ControlFlow<VisiblyUninhabited> = Continue(());
@@ -78,6 +87,12 @@ impl TypeVisitor<Interner> for UninhabitedFrom<'_> {
Some(0) | None => CONTINUE_OPAQUELY_INHABITED,
Some(1..) => item_ty.super_visit_with(self, outer_binder),
},
+ TyKind::Alias(AliasTy::Projection(projection)) => {
+ // FIXME: I think this currently isn't used for monomorphized bodies, so there is no need to handle
+ // `TyKind::AssociatedType`, but perhaps in the future it will.
+ let normalized = self.db.normalize_projection(projection.clone(), self.env.clone());
+ self.visit_ty(&normalized, outer_binder)
+ }
_ => CONTINUE_OPAQUELY_INHABITED,
};
self.recursive_ty.remove(ty);
diff --git a/crates/hir-ty/src/mir/eval/shim.rs b/crates/hir-ty/src/mir/eval/shim.rs
index 7cf948b178..90c52ee96f 100644
--- a/crates/hir-ty/src/mir/eval/shim.rs
+++ b/crates/hir-ty/src/mir/eval/shim.rs
@@ -1121,7 +1121,7 @@ impl Evaluator<'_> {
// We don't call any drop glue yet, so there is nothing here
Ok(())
}
- "transmute" => {
+ "transmute" | "transmute_unchecked" => {
let [arg] = args else {
return Err(MirEvalError::InternalError(
"transmute arg is not provided".into(),
diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs
index 7fcc89e518..e6caf2d8d9 100644
--- a/crates/hir-ty/src/mir/lower.rs
+++ b/crates/hir-ty/src/mir/lower.rs
@@ -25,7 +25,7 @@ use syntax::TextRange;
use triomphe::Arc;
use crate::{
- Adjust, Adjustment, AutoBorrow, CallableDefId, TyBuilder, TyExt,
+ Adjust, Adjustment, AutoBorrow, CallableDefId, TraitEnvironment, TyBuilder, TyExt,
consteval::ConstEvalError,
db::{HirDatabase, InternedClosure, InternedClosureId},
display::{DisplayTarget, HirDisplay, hir_display_with_store},
@@ -79,6 +79,7 @@ struct MirLowerCtx<'db> {
infer: &'db InferenceResult,
resolver: Resolver<'db>,
drop_scopes: Vec<DropScope>,
+ env: Arc<TraitEnvironment>,
}
// FIXME: Make this smaller, its stored in database queries
@@ -288,6 +289,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
closures: vec![],
};
let resolver = owner.resolver(db);
+ let env = db.trait_environment_for_body(owner);
MirLowerCtx {
result: mir,
@@ -300,6 +302,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
labeled_loop_blocks: Default::default(),
discr_temp: None,
drop_scopes: vec![DropScope::default()],
+ env,
}
}
@@ -944,10 +947,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
let cast_kind = if source_ty.as_reference().is_some() {
CastKind::PointerCoercion(PointerCast::ArrayToPointer)
} else {
- let mut table = InferenceTable::new(
- self.db,
- self.db.trait_environment_for_body(self.owner),
- );
+ let mut table = InferenceTable::new(self.db, self.env.clone());
cast_kind(&mut table, &source_ty, &target_ty)?
};
@@ -1412,11 +1412,8 @@ impl<'ctx> MirLowerCtx<'ctx> {
}
fn lower_literal_to_operand(&mut self, ty: Ty, l: &Literal) -> Result<Operand> {
- let size = || {
- self.db
- .layout_of_ty(ty.clone(), self.db.trait_environment_for_body(self.owner))
- .map(|it| it.size.bytes_usize())
- };
+ let size =
+ || self.db.layout_of_ty(ty.clone(), self.env.clone()).map(|it| it.size.bytes_usize());
const USIZE_SIZE: usize = size_of::<usize>();
let bytes: Box<[_]> = match l {
hir_def::hir::Literal::String(b) => {
@@ -1723,7 +1720,12 @@ impl<'ctx> MirLowerCtx<'ctx> {
}
fn is_uninhabited(&self, expr_id: ExprId) -> bool {
- is_ty_uninhabited_from(self.db, &self.infer[expr_id], self.owner.module(self.db))
+ is_ty_uninhabited_from(
+ self.db,
+ &self.infer[expr_id],
+ self.owner.module(self.db),
+ self.env.clone(),
+ )
}
/// This function push `StorageLive` statement for the binding, and applies changes to add `StorageDead` and
diff --git a/crates/hir-ty/src/test_db.rs b/crates/hir-ty/src/test_db.rs
index 8f0d17c9dc..d049c678e2 100644
--- a/crates/hir-ty/src/test_db.rs
+++ b/crates/hir-ty/src/test_db.rs
@@ -27,9 +27,18 @@ pub(crate) struct TestDB {
impl Default for TestDB {
fn default() -> Self {
+ let events = <Arc<Mutex<Option<Vec<salsa::Event>>>>>::default();
let mut this = Self {
- storage: Default::default(),
- events: Default::default(),
+ storage: salsa::Storage::new(Some(Box::new({
+ let events = events.clone();
+ move |event| {
+ let mut events = events.lock().unwrap();
+ if let Some(events) = &mut *events {
+ events.push(event);
+ }
+ }
+ }))),
+ events,
files: Default::default(),
crates_map: Default::default(),
};
@@ -103,14 +112,7 @@ impl SourceDatabase for TestDB {
}
#[salsa_macros::db]
-impl salsa::Database for TestDB {
- fn salsa_event(&self, event: &dyn std::ops::Fn() -> salsa::Event) {
- let mut events = self.events.lock().unwrap();
- if let Some(events) = &mut *events {
- events.push(event());
- }
- }
-}
+impl salsa::Database for TestDB {}
impl panic::RefUnwindSafe for TestDB {}
diff --git a/crates/hir-ty/src/tests/traits.rs b/crates/hir-ty/src/tests/traits.rs
index 2b527a4ae1..e5d1fbe9de 100644
--- a/crates/hir-ty/src/tests/traits.rs
+++ b/crates/hir-ty/src/tests/traits.rs
@@ -4903,3 +4903,30 @@ fn main() {
"#]],
);
}
+
+#[test]
+fn async_fn_return_type() {
+ check_infer(
+ r#"
+//- minicore: async_fn
+fn foo<F: AsyncFn() -> R, R>(_: F) -> R {
+ loop {}
+}
+
+fn main() {
+ foo(async move || ());
+}
+ "#,
+ expect![[r#"
+ 29..30 '_': F
+ 40..55 '{ loop {} }': R
+ 46..53 'loop {}': !
+ 51..53 '{}': ()
+ 67..97 '{ ...()); }': ()
+ 73..76 'foo': fn foo<impl AsyncFn() -> impl Future<Output = ()>, ()>(impl AsyncFn() -> impl Future<Output = ()>)
+ 73..94 'foo(as...|| ())': ()
+ 77..93 'async ... || ()': impl AsyncFn() -> impl Future<Output = ()>
+ 91..93 '()': ()
+ "#]],
+ );
+}
diff --git a/crates/hir-ty/src/traits.rs b/crates/hir-ty/src/traits.rs
index f9f8776cff..7414b4fc60 100644
--- a/crates/hir-ty/src/traits.rs
+++ b/crates/hir-ty/src/traits.rs
@@ -291,4 +291,9 @@ impl FnTrait {
pub fn get_id(self, db: &dyn HirDatabase, krate: Crate) -> Option<TraitId> {
self.lang_item().resolve_trait(db, krate)
}
+
+ #[inline]
+ pub(crate) fn is_async(self) -> bool {
+ matches!(self, FnTrait::AsyncFn | FnTrait::AsyncFnMut | FnTrait::AsyncFnOnce)
+ }
}
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index 3a91050d15..e8218cf861 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -5972,6 +5972,59 @@ impl Layout {
}
}
+ pub fn tail_padding(&self, field_size: &mut impl FnMut(usize) -> Option<u64>) -> Option<u64> {
+ match self.0.fields {
+ layout::FieldsShape::Primitive => None,
+ layout::FieldsShape::Union(_) => None,
+ layout::FieldsShape::Array { stride, count } => count.checked_sub(1).and_then(|tail| {
+ let tail_field_size = field_size(tail as usize)?;
+ let offset = stride.bytes() * tail;
+ self.0.size.bytes().checked_sub(offset)?.checked_sub(tail_field_size)
+ }),
+ layout::FieldsShape::Arbitrary { ref offsets, ref memory_index } => {
+ let tail = memory_index.last_index()?;
+ let tail_field_size = field_size(tail.0.into_raw().into_u32() as usize)?;
+ let offset = offsets.get(tail)?.bytes();
+ self.0.size.bytes().checked_sub(offset)?.checked_sub(tail_field_size)
+ }
+ }
+ }
+
+ pub fn largest_padding(
+ &self,
+ field_size: &mut impl FnMut(usize) -> Option<u64>,
+ ) -> Option<u64> {
+ match self.0.fields {
+ layout::FieldsShape::Primitive => None,
+ layout::FieldsShape::Union(_) => None,
+ layout::FieldsShape::Array { stride: _, count: 0 } => None,
+ layout::FieldsShape::Array { stride, .. } => {
+ let size = field_size(0)?;
+ stride.bytes().checked_sub(size)
+ }
+ layout::FieldsShape::Arbitrary { ref offsets, ref memory_index } => {
+ let mut reverse_index = vec![None; memory_index.len()];
+ for (src, (mem, offset)) in memory_index.iter().zip(offsets.iter()).enumerate() {
+ reverse_index[*mem as usize] = Some((src, offset.bytes()));
+ }
+ if reverse_index.iter().any(|it| it.is_none()) {
+ stdx::never!();
+ return None;
+ }
+ reverse_index
+ .into_iter()
+ .flatten()
+ .chain(std::iter::once((0, self.0.size.bytes())))
+ .tuple_windows()
+ .filter_map(|((i, start), (_, end))| {
+ let size = field_size(i)?;
+ end.checked_sub(start)?.checked_sub(size)
+ })
+ .max()
+ }
+ }
+ }
+
pub fn enum_tag_size(&self) -> Option<usize> {
let tag_size =
if let layout::Variants::Multiple { tag, tag_encoding, .. } = &self.0.variants {
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index caa6700de9..aea22545ed 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -147,7 +147,7 @@ impl TypeInfo {
}
/// Primary API to get semantic information, like types, from syntax trees.
-pub struct Semantics<'db, DB> {
+pub struct Semantics<'db, DB: ?Sized> {
pub db: &'db DB,
imp: SemanticsImpl<'db>,
}
@@ -407,14 +407,10 @@ impl<'db> SemanticsImpl<'db> {
res
}
- pub fn expand_macro_call(&self, macro_call: &ast::MacroCall) -> Option<SyntaxNode> {
- let sa = self.analyze_no_infer(macro_call.syntax())?;
-
- let macro_call = InFile::new(sa.file_id, macro_call);
- let file_id = sa.expand(self.db, macro_call)?;
-
+ pub fn expand_macro_call(&self, macro_call: &ast::MacroCall) -> Option<InFile<SyntaxNode>> {
+ let file_id = self.to_def(macro_call)?;
let node = self.parse_or_expand(file_id.into());
- Some(node)
+ Some(InFile::new(file_id.into(), node))
}
pub fn check_cfg_attr(&self, attr: &ast::TokenTree) -> Option<bool> {
@@ -434,10 +430,7 @@ impl<'db> SemanticsImpl<'db> {
&self,
macro_call: &ast::MacroCall,
) -> Option<ExpandResult<SyntaxNode>> {
- let sa = self.analyze_no_infer(macro_call.syntax())?;
-
- let macro_call = InFile::new(sa.file_id, macro_call);
- let file_id = sa.expand(self.db, macro_call)?;
+ let file_id = self.to_def(macro_call)?;
let macro_call = self.db.lookup_intern_macro_call(file_id);
let skip = matches!(
@@ -468,10 +461,10 @@ impl<'db> SemanticsImpl<'db> {
}
/// If `item` has an attribute macro attached to it, expands it.
- pub fn expand_attr_macro(&self, item: &ast::Item) -> Option<ExpandResult<SyntaxNode>> {
+ pub fn expand_attr_macro(&self, item: &ast::Item) -> Option<ExpandResult<InFile<SyntaxNode>>> {
let src = self.wrap_node_infile(item.clone());
let macro_call_id = self.with_ctx(|ctx| ctx.item_to_macro_call(src.as_ref()))?;
- Some(self.expand(macro_call_id))
+ Some(self.expand(macro_call_id).map(|it| InFile::new(macro_call_id.into(), it)))
}
pub fn expand_derive_as_pseudo_attr_macro(&self, attr: &ast::Attr) -> Option<SyntaxNode> {
@@ -574,9 +567,7 @@ impl<'db> SemanticsImpl<'db> {
speculative_args: &ast::TokenTree,
token_to_map: SyntaxToken,
) -> Option<(SyntaxNode, Vec<(SyntaxToken, u8)>)> {
- let analyzer = self.analyze_no_infer(actual_macro_call.syntax())?;
- let macro_call = InFile::new(analyzer.file_id, actual_macro_call);
- let macro_file = analyzer.expansion(macro_call)?;
+ let macro_file = self.to_def(actual_macro_call)?;
hir_expand::db::expand_speculative(
self.db,
macro_file,
@@ -778,6 +769,31 @@ impl<'db> SemanticsImpl<'db> {
})
}
+ /// Descends the token into the include expansion, if its file is an included file.
+ pub fn descend_token_into_include_expansion(
+ &self,
+ tok: InRealFile<SyntaxToken>,
+ ) -> InFile<SyntaxToken> {
+ let Some(include) =
+ self.s2d_cache.borrow_mut().get_or_insert_include_for(self.db, tok.file_id)
+ else {
+ return tok.into();
+ };
+ let span = self.db.real_span_map(tok.file_id).span_for_range(tok.value.text_range());
+ let Some(InMacroFile { file_id, value: mut mapped_tokens }) = self.with_ctx(|ctx| {
+ Some(
+ ctx.cache
+ .get_or_insert_expansion(ctx.db, include)
+ .map_range_down(span)?
+ .map(SmallVec::<[_; 2]>::from_iter),
+ )
+ }) else {
+ return tok.into();
+ };
+ // We should only get one result at most
+ mapped_tokens.pop().map_or_else(|| tok.into(), |(tok, _)| InFile::new(file_id.into(), tok))
+ }
+
/// Maps a node down by mapping its first and last token down.
pub fn descend_node_into_attributes<N: AstNode>(&self, node: N) -> SmallVec<[N; 1]> {
// This might not be the correct way to do this, but it works for now
@@ -846,49 +862,35 @@ impl<'db> SemanticsImpl<'db> {
res
}
- // FIXME: This isn't quite right wrt to inner attributes
- /// Does a syntactic traversal to check whether this token might be inside a macro call
- pub fn might_be_inside_macro_call(&self, token: &SyntaxToken) -> bool {
- token.parent_ancestors().any(|ancestor| {
+ pub fn is_inside_macro_call(&self, token: InFile<&SyntaxToken>) -> bool {
+ // FIXME: Maybe `ancestors_with_macros()` is more suitable here? Currently
+ // this is only used on real (not macro) files so this is not a problem.
+ token.value.parent_ancestors().any(|ancestor| {
if ast::MacroCall::can_cast(ancestor.kind()) {
return true;
}
- // Check if it is an item (only items can have macro attributes) that has a non-builtin attribute.
- let Some(item) = ast::Item::cast(ancestor) else { return false };
- item.attrs().any(|attr| {
- let Some(meta) = attr.meta() else { return false };
- let Some(path) = meta.path() else { return false };
- if let Some(attr_name) = path.as_single_name_ref() {
- let attr_name = attr_name.text();
- let attr_name = Symbol::intern(attr_name.as_str());
- if attr_name == sym::derive {
- return true;
- }
- // We ignore `#[test]` and friends in the def map, so we cannot expand them.
- // FIXME: We match by text. This is both hacky and incorrect (people can, and do, create
- // other macros named `test`). We cannot fix that unfortunately because we use this method
- // for speculative expansion in completion, which we cannot analyze. Fortunately, most macros
- // named `test` are test-like, meaning their expansion is not terribly important for IDE.
- if attr_name == sym::test
- || attr_name == sym::bench
- || attr_name == sym::test_case
- || find_builtin_attr_idx(&attr_name).is_some()
- {
- return false;
- }
- }
- let mut segments = path.segments();
- let mut next_segment_text = || segments.next().and_then(|it| it.name_ref());
- // `#[core::prelude::rust_2024::test]` or `#[std::prelude::rust_2024::test]`.
- if next_segment_text().is_some_and(|it| matches!(&*it.text(), "core" | "std"))
- && next_segment_text().is_some_and(|it| it.text() == "prelude")
- && next_segment_text().is_some()
- && next_segment_text()
- .is_some_and(|it| matches!(&*it.text(), "test" | "bench" | "test_case"))
- {
- return false;
+
+ let Some(item) = ast::Item::cast(ancestor) else {
+ return false;
+ };
+ // Optimization to skip the semantic check.
+ if item.attrs().all(|attr| {
+ attr.simple_name()
+ .is_some_and(|attr| find_builtin_attr_idx(&Symbol::intern(&attr)).is_some())
+ }) {
+ return false;
+ }
+ self.with_ctx(|ctx| {
+ if ctx.item_to_macro_call(token.with_value(&item)).is_some() {
+ return true;
}
- true
+ let adt = match item {
+ ast::Item::Struct(it) => it.into(),
+ ast::Item::Enum(it) => it.into(),
+ ast::Item::Union(it) => it.into(),
+ _ => return false,
+ };
+ ctx.has_derives(token.with_value(&adt))
})
})
}
@@ -1111,16 +1113,7 @@ impl<'db> SemanticsImpl<'db> {
let file_id = match m_cache.get(&mcall) {
Some(&it) => it,
None => {
- let it = token
- .parent()
- .and_then(|parent| {
- self.analyze_impl(
- InFile::new(expansion, &parent),
- None,
- false,
- )
- })?
- .expand(self.db, mcall.as_ref())?;
+ let it = ast::MacroCall::to_def(self, mcall.as_ref())?;
m_cache.insert(mcall, it);
it
}
@@ -1560,14 +1553,9 @@ impl<'db> SemanticsImpl<'db> {
}
pub fn resolve_macro_call2(&self, macro_call: InFile<&ast::MacroCall>) -> Option<Macro> {
- self.with_ctx(|ctx| {
- ctx.macro_call_to_macro_call(macro_call)
- .and_then(|call| macro_call_to_macro_id(ctx, call))
- .map(Into::into)
- })
- .or_else(|| {
- self.analyze(macro_call.value.syntax())?.resolve_macro_call(self.db, macro_call)
- })
+ self.to_def2(macro_call)
+ .and_then(|call| self.with_ctx(|ctx| macro_call_to_macro_id(ctx, call)))
+ .map(Into::into)
}
pub fn is_proc_macro_call(&self, macro_call: InFile<&ast::MacroCall>) -> bool {
@@ -1576,14 +1564,8 @@ impl<'db> SemanticsImpl<'db> {
}
pub fn resolve_macro_call_arm(&self, macro_call: &ast::MacroCall) -> Option<u32> {
- let sa = self.analyze(macro_call.syntax())?;
- self.db
- .parse_macro_expansion(
- sa.expand(self.db, self.wrap_node_infile(macro_call.clone()).as_ref())?,
- )
- .value
- .1
- .matched_arm
+ let file_id = self.to_def(macro_call)?;
+ self.db.parse_macro_expansion(file_id).value.1.matched_arm
}
pub fn get_unsafe_ops(&self, def: DefWithBody) -> FxHashSet<ExprOrPatSource> {
@@ -1688,6 +1670,10 @@ impl<'db> SemanticsImpl<'db> {
T::to_def(self, src)
}
+ pub fn to_def2<T: ToDef>(&self, src: InFile<&T>) -> Option<T::Def> {
+ T::to_def(self, src)
+ }
+
fn file_to_module_defs(&self, file: FileId) -> impl Iterator<Item = Module> {
self.with_ctx(|ctx| ctx.file_to_def(file).to_owned()).into_iter().map(Module::from)
}
diff --git a/crates/hir/src/semantics/child_by_source.rs b/crates/hir/src/semantics/child_by_source.rs
index 9393d08ad3..6accf9b2e9 100644
--- a/crates/hir/src/semantics/child_by_source.rs
+++ b/crates/hir/src/semantics/child_by_source.rs
@@ -36,9 +36,14 @@ impl ChildBySource for TraitId {
fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap, file_id: HirFileId) {
let data = db.trait_items(*self);
- data.attribute_calls().filter(|(ast_id, _)| ast_id.file_id == file_id).for_each(
+ data.macro_calls().filter(|(ast_id, _)| ast_id.file_id == file_id).for_each(
|(ast_id, call_id)| {
- res[keys::ATTR_MACRO_CALL].insert(ast_id.to_ptr(db), call_id);
+ let ptr = ast_id.to_ptr(db);
+ if let Some(ptr) = ptr.cast::<ast::MacroCall>() {
+ res[keys::MACRO_CALL].insert(ptr, call_id);
+ } else {
+ res[keys::ATTR_MACRO_CALL].insert(ptr, call_id);
+ }
},
);
data.items.iter().for_each(|&(_, item)| {
@@ -50,10 +55,14 @@ impl ChildBySource for TraitId {
impl ChildBySource for ImplId {
fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap, file_id: HirFileId) {
let data = db.impl_items(*self);
- // FIXME: Macro calls
- data.attribute_calls().filter(|(ast_id, _)| ast_id.file_id == file_id).for_each(
+ data.macro_calls().filter(|(ast_id, _)| ast_id.file_id == file_id).for_each(
|(ast_id, call_id)| {
- res[keys::ATTR_MACRO_CALL].insert(ast_id.to_ptr(db), call_id);
+ let ptr = ast_id.to_ptr(db);
+ if let Some(ptr) = ptr.cast::<ast::MacroCall>() {
+ res[keys::MACRO_CALL].insert(ptr, call_id);
+ } else {
+ res[keys::ATTR_MACRO_CALL].insert(ptr, call_id);
+ }
},
);
data.items.iter().for_each(|&(_, item)| {
diff --git a/crates/hir/src/semantics/source_to_def.rs b/crates/hir/src/semantics/source_to_def.rs
index 172af456d9..7f6c9af474 100644
--- a/crates/hir/src/semantics/source_to_def.rs
+++ b/crates/hir/src/semantics/source_to_def.rs
@@ -399,19 +399,6 @@ impl SourceToDefCtx<'_, '_> {
Some((container, label?))
}
- pub(super) fn item_to_macro_call(&mut self, src: InFile<&ast::Item>) -> Option<MacroCallId> {
- let map = self.dyn_map(src)?;
- map[keys::ATTR_MACRO_CALL].get(&AstPtr::new(src.value)).copied()
- }
-
- pub(super) fn macro_call_to_macro_call(
- &mut self,
- src: InFile<&ast::MacroCall>,
- ) -> Option<MacroCallId> {
- let map = self.dyn_map(src)?;
- map[keys::MACRO_CALL].get(&AstPtr::new(src.value)).copied()
- }
-
/// (AttrId, derive attribute call id, derive call ids)
pub(super) fn attr_to_derive_macro_call(
&mut self,
@@ -449,6 +436,17 @@ impl SourceToDefCtx<'_, '_> {
.or_insert_with(|| container.child_by_source(db, file_id))
}
+ pub(super) fn item_to_macro_call(&mut self, src: InFile<&ast::Item>) -> Option<MacroCallId> {
+ self.to_def(src, keys::ATTR_MACRO_CALL)
+ }
+
+ pub(super) fn macro_call_to_macro_call(
+ &mut self,
+ src: InFile<&ast::MacroCall>,
+ ) -> Option<MacroCallId> {
+ self.to_def(src, keys::MACRO_CALL)
+ }
+
pub(super) fn type_param_to_def(
&mut self,
src: InFile<&ast::TypeParam>,
diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs
index ea21546f9d..d22812d3c6 100644
--- a/crates/hir/src/source_analyzer.rs
+++ b/crates/hir/src/source_analyzer.rs
@@ -26,12 +26,12 @@ use hir_def::{
},
hir::{BindingId, Expr, ExprId, ExprOrPatId, Pat},
lang_item::LangItem,
- nameres::{MacroSubNs, crate_def_map},
+ nameres::MacroSubNs,
resolver::{HasResolver, Resolver, TypeNs, ValueNs, resolver_for_scope},
type_ref::{Mutability, TypeRefId},
};
use hir_expand::{
- HirFileId, InFile, MacroCallId,
+ HirFileId, InFile,
mod_path::{ModPath, PathKind, path},
name::{AsName, Name},
};
@@ -218,10 +218,6 @@ impl<'db> SourceAnalyzer<'db> {
})
}
- pub(crate) fn expansion(&self, node: InFile<&ast::MacroCall>) -> Option<MacroCallId> {
- self.store_sm()?.expansion(node)
- }
-
fn trait_environment(&self, db: &'db dyn HirDatabase) -> Arc<TraitEnvironment> {
self.body_().map(|(def, ..)| def).map_or_else(
|| TraitEnvironment::empty(self.resolver.krate()),
@@ -745,22 +741,6 @@ impl<'db> SourceAnalyzer<'db> {
))
}
- pub(crate) fn resolve_macro_call(
- &self,
- db: &'db dyn HirDatabase,
- macro_call: InFile<&ast::MacroCall>,
- ) -> Option<Macro> {
- let bs = self.store_sm()?;
- bs.expansion(macro_call).and_then(|it| {
- // FIXME: Block def maps
- let def = it.lookup(db).def;
- crate_def_map(db, def.krate)
- .macro_def_to_macro_id
- .get(&def.kind.erased_ast_id())
- .map(|it| (*it).into())
- })
- }
-
pub(crate) fn resolve_bind_pat_to_const(
&self,
db: &'db dyn HirDatabase,
@@ -1292,18 +1272,6 @@ impl<'db> SourceAnalyzer<'db> {
.collect()
}
- pub(crate) fn expand(
- &self,
- db: &'db dyn HirDatabase,
- macro_call: InFile<&ast::MacroCall>,
- ) -> Option<MacroCallId> {
- self.store_sm().and_then(|bs| bs.expansion(macro_call)).or_else(|| {
- self.resolver.item_scope().macro_invoc(
- macro_call.with_value(db.ast_id_map(macro_call.file_id).ast_id(macro_call.value)),
- )
- })
- }
-
pub(crate) fn resolve_variant(&self, record_lit: ast::RecordExpr) -> Option<VariantId> {
let infer = self.infer()?;
let expr_id = self.expr_id(record_lit.into())?;
diff --git a/crates/ide-assists/src/handlers/desugar_try_expr.rs b/crates/ide-assists/src/handlers/desugar_try_expr.rs
new file mode 100644
index 0000000000..efadde9e36
--- /dev/null
+++ b/crates/ide-assists/src/handlers/desugar_try_expr.rs
@@ -0,0 +1,281 @@
+use std::iter;
+
+use ide_db::{
+ assists::{AssistId, ExprFillDefaultMode},
+ ty_filter::TryEnum,
+};
+use syntax::{
+ AstNode, T,
+ ast::{
+ self,
+ edit::{AstNodeEdit, IndentLevel},
+ make,
+ syntax_factory::SyntaxFactory,
+ },
+};
+
+use crate::assist_context::{AssistContext, Assists};
+
+// Assist: desugar_try_expr_match
+//
+// Replaces a `try` expression with a `match` expression.
+//
+// ```
+// # //- minicore: try, option
+// fn handle() {
+// let pat = Some(true)$0?;
+// }
+// ```
+// ->
+// ```
+// fn handle() {
+// let pat = match Some(true) {
+// Some(it) => it,
+// None => return None,
+// };
+// }
+// ```
+
+// Assist: desugar_try_expr_let_else
+//
+// Replaces a `try` expression with a `let else` statement.
+//
+// ```
+// # //- minicore: try, option
+// fn handle() {
+// let pat = Some(true)$0?;
+// }
+// ```
+// ->
+// ```
+// fn handle() {
+// let Some(pat) = Some(true) else {
+// return None;
+// };
+// }
+// ```
+pub(crate) fn desugar_try_expr(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+ let question_tok = ctx.find_token_syntax_at_offset(T![?])?;
+ let try_expr = question_tok.parent().and_then(ast::TryExpr::cast)?;
+
+ let expr = try_expr.expr()?;
+ let expr_type_info = ctx.sema.type_of_expr(&expr)?;
+
+ let try_enum = TryEnum::from_ty(&ctx.sema, &expr_type_info.original)?;
+
+ let target = try_expr.syntax().text_range();
+ acc.add(
+ AssistId::refactor_rewrite("desugar_try_expr_match"),
+ "Replace try expression with match",
+ target,
+ |edit| {
+ let sad_pat = match try_enum {
+ TryEnum::Option => make::path_pat(make::ext::ident_path("None")),
+ TryEnum::Result => make::tuple_struct_pat(
+ make::ext::ident_path("Err"),
+ iter::once(make::path_pat(make::ext::ident_path("err"))),
+ )
+ .into(),
+ };
+ let sad_expr = match try_enum {
+ TryEnum::Option => {
+ make::expr_return(Some(make::expr_path(make::ext::ident_path("None"))))
+ }
+ TryEnum::Result => make::expr_return(Some(
+ make::expr_call(
+ make::expr_path(make::ext::ident_path("Err")),
+ make::arg_list(iter::once(make::expr_path(make::ext::ident_path("err")))),
+ )
+ .into(),
+ )),
+ };
+
+ let happy_arm = make::match_arm(
+ try_enum.happy_pattern(make::ident_pat(false, false, make::name("it")).into()),
+ None,
+ make::expr_path(make::ext::ident_path("it")),
+ );
+ let sad_arm = make::match_arm(sad_pat, None, sad_expr);
+
+ let match_arm_list = make::match_arm_list([happy_arm, sad_arm]);
+
+ let expr_match = make::expr_match(expr.clone(), match_arm_list)
+ .indent(IndentLevel::from_node(try_expr.syntax()));
+
+ edit.replace_ast::<ast::Expr>(try_expr.clone().into(), expr_match.into());
+ },
+ );
+
+ if let Some(let_stmt) = try_expr.syntax().parent().and_then(ast::LetStmt::cast) {
+ if let_stmt.let_else().is_none() {
+ let pat = let_stmt.pat()?;
+ acc.add(
+ AssistId::refactor_rewrite("desugar_try_expr_let_else"),
+ "Replace try expression with let else",
+ target,
+ |builder| {
+ let make = SyntaxFactory::with_mappings();
+ let mut editor = builder.make_editor(let_stmt.syntax());
+
+ let indent_level = IndentLevel::from_node(let_stmt.syntax());
+ let new_let_stmt = make.let_else_stmt(
+ try_enum.happy_pattern(pat),
+ let_stmt.ty(),
+ expr,
+ make.block_expr(
+ iter::once(
+ make.expr_stmt(
+ make.expr_return(Some(match try_enum {
+ TryEnum::Option => make.expr_path(make.ident_path("None")),
+ TryEnum::Result => make
+ .expr_call(
+ make.expr_path(make.ident_path("Err")),
+ make.arg_list(iter::once(
+ match ctx.config.expr_fill_default {
+ ExprFillDefaultMode::Todo => make
+ .expr_macro(
+ make.ident_path("todo"),
+ make.token_tree(
+ syntax::SyntaxKind::L_PAREN,
+ [],
+ ),
+ )
+ .into(),
+ ExprFillDefaultMode::Underscore => {
+ make.expr_underscore().into()
+ }
+ ExprFillDefaultMode::Default => make
+ .expr_macro(
+ make.ident_path("todo"),
+ make.token_tree(
+ syntax::SyntaxKind::L_PAREN,
+ [],
+ ),
+ )
+ .into(),
+ },
+ )),
+ )
+ .into(),
+ }))
+ .indent(indent_level + 1)
+ .into(),
+ )
+ .into(),
+ ),
+ None,
+ )
+ .indent(indent_level),
+ );
+ editor.replace(let_stmt.syntax(), new_let_stmt.syntax());
+ editor.add_mappings(make.finish_with_mappings());
+ builder.add_file_edits(ctx.vfs_file_id(), editor);
+ },
+ );
+ }
+ }
+ Some(())
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ use crate::tests::{check_assist, check_assist_by_label, check_assist_not_applicable};
+
+ #[test]
+ fn test_desugar_try_expr_not_applicable() {
+ check_assist_not_applicable(
+ desugar_try_expr,
+ r#"
+ fn test() {
+ let pat: u32 = 25$0;
+ }
+ "#,
+ );
+ }
+
+ #[test]
+ fn test_desugar_try_expr_option() {
+ check_assist(
+ desugar_try_expr,
+ r#"
+//- minicore: try, option
+fn test() {
+ let pat = Some(true)$0?;
+}
+ "#,
+ r#"
+fn test() {
+ let pat = match Some(true) {
+ Some(it) => it,
+ None => return None,
+ };
+}
+ "#,
+ );
+ }
+
+ #[test]
+ fn test_desugar_try_expr_result() {
+ check_assist(
+ desugar_try_expr,
+ r#"
+//- minicore: try, from, result
+fn test() {
+ let pat = Ok(true)$0?;
+}
+ "#,
+ r#"
+fn test() {
+ let pat = match Ok(true) {
+ Ok(it) => it,
+ Err(err) => return Err(err),
+ };
+}
+ "#,
+ );
+ }
+
+ #[test]
+ fn test_desugar_try_expr_option_let_else() {
+ check_assist_by_label(
+ desugar_try_expr,
+ r#"
+//- minicore: try, option
+fn test() {
+ let pat = Some(true)$0?;
+}
+ "#,
+ r#"
+fn test() {
+ let Some(pat) = Some(true) else {
+ return None;
+ };
+}
+ "#,
+ "Replace try expression with let else",
+ );
+ }
+
+ #[test]
+ fn test_desugar_try_expr_result_let_else() {
+ check_assist_by_label(
+ desugar_try_expr,
+ r#"
+//- minicore: try, from, result
+fn test() {
+ let pat = Ok(true)$0?;
+}
+ "#,
+ r#"
+fn test() {
+ let Ok(pat) = Ok(true) else {
+ return Err(todo!());
+ };
+}
+ "#,
+ "Replace try expression with let else",
+ );
+ }
+}
diff --git a/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs b/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs
index 2ac960ed7e..bab2ccf3f3 100644
--- a/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs
+++ b/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs
@@ -1,7 +1,7 @@
use ide_db::famous_defs::FamousDefs;
use syntax::{
AstNode,
- ast::{self, make},
+ ast::{self, edit_in_place::Indent, make},
ted,
};
@@ -46,6 +46,7 @@ use crate::{AssistContext, AssistId, Assists};
// ```
pub(crate) fn generate_mut_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
let impl_def = ctx.find_node_at_offset::<ast::Impl>()?.clone_for_update();
+ let indent = impl_def.indent_level();
let trait_ = impl_def.trait_()?;
if let ast::Type::PathType(trait_path) = trait_ {
@@ -97,8 +98,8 @@ pub(crate) fn generate_mut_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_>
})?;
let assoc_list = make::assoc_item_list().clone_for_update();
- assoc_list.add_item(syntax::ast::AssocItem::Fn(fn_));
ted::replace(impl_def.assoc_item_list()?.syntax(), assoc_list.syntax());
+ impl_def.get_or_create_assoc_item_list().add_item(syntax::ast::AssocItem::Fn(fn_));
let target = impl_def.syntax().text_range();
acc.add(
@@ -106,7 +107,7 @@ pub(crate) fn generate_mut_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_>
"Generate `IndexMut` impl from this `Index` trait",
target,
|edit| {
- edit.insert(target.start(), format!("$0{impl_def}\n\n"));
+ edit.insert(target.start(), format!("$0{impl_def}\n\n{indent}"));
},
)
}
@@ -190,6 +191,93 @@ impl<T> core::ops::Index<Axis> for [T; 3] where T: Copy {
}
#[test]
+ fn test_generate_mut_trait_impl_non_zero_indent() {
+ check_assist(
+ generate_mut_trait_impl,
+ r#"
+//- minicore: index
+mod foo {
+ pub enum Axis { X = 0, Y = 1, Z = 2 }
+
+ impl<T> core::ops::Index$0<Axis> for [T; 3] where T: Copy {
+ type Output = T;
+
+ fn index(&self, index: Axis) -> &Self::Output {
+ let var_name = &self[index as usize];
+ var_name
+ }
+ }
+}
+"#,
+ r#"
+mod foo {
+ pub enum Axis { X = 0, Y = 1, Z = 2 }
+
+ $0impl<T> core::ops::IndexMut<Axis> for [T; 3] where T: Copy {
+ fn index_mut(&mut self, index: Axis) -> &mut Self::Output {
+ let var_name = &self[index as usize];
+ var_name
+ }
+ }
+
+ impl<T> core::ops::Index<Axis> for [T; 3] where T: Copy {
+ type Output = T;
+
+ fn index(&self, index: Axis) -> &Self::Output {
+ let var_name = &self[index as usize];
+ var_name
+ }
+ }
+}
+"#,
+ );
+
+ check_assist(
+ generate_mut_trait_impl,
+ r#"
+//- minicore: index
+mod foo {
+ mod bar {
+ pub enum Axis { X = 0, Y = 1, Z = 2 }
+
+ impl<T> core::ops::Index$0<Axis> for [T; 3] where T: Copy {
+ type Output = T;
+
+ fn index(&self, index: Axis) -> &Self::Output {
+ let var_name = &self[index as usize];
+ var_name
+ }
+ }
+ }
+}
+"#,
+ r#"
+mod foo {
+ mod bar {
+ pub enum Axis { X = 0, Y = 1, Z = 2 }
+
+ $0impl<T> core::ops::IndexMut<Axis> for [T; 3] where T: Copy {
+ fn index_mut(&mut self, index: Axis) -> &mut Self::Output {
+ let var_name = &self[index as usize];
+ var_name
+ }
+ }
+
+ impl<T> core::ops::Index<Axis> for [T; 3] where T: Copy {
+ type Output = T;
+
+ fn index(&self, index: Axis) -> &Self::Output {
+ let var_name = &self[index as usize];
+ var_name
+ }
+ }
+ }
+}
+"#,
+ );
+ }
+
+ #[test]
fn test_generate_mut_trait_impl_not_applicable() {
check_assist_not_applicable(
generate_mut_trait_impl,
diff --git a/crates/ide-assists/src/handlers/generate_new.rs b/crates/ide-assists/src/handlers/generate_new.rs
index f963f48d62..4837f92f93 100644
--- a/crates/ide-assists/src/handlers/generate_new.rs
+++ b/crates/ide-assists/src/handlers/generate_new.rs
@@ -129,17 +129,23 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option
// Get the mutable version of the impl to modify
let impl_def = if let Some(impl_def) = impl_def {
+ fn_.indent(impl_def.indent_level());
builder.make_mut(impl_def)
} else {
// Generate a new impl to add the method to
let impl_def = generate_impl(&ast::Adt::Struct(strukt.clone()));
+ let indent_level = strukt.indent_level();
+ fn_.indent(indent_level);
// Insert it after the adt
let strukt = builder.make_mut(strukt.clone());
ted::insert_all_raw(
ted::Position::after(strukt.syntax()),
- vec![make::tokens::blank_line().into(), impl_def.syntax().clone().into()],
+ vec![
+ make::tokens::whitespace(&format!("\n\n{indent_level}")).into(),
+ impl_def.syntax().clone().into(),
+ ],
);
impl_def
@@ -426,6 +432,135 @@ impl Foo {
}
#[test]
+ fn non_zero_indent() {
+ check_assist(
+ generate_new,
+ r#"
+mod foo {
+ struct $0Foo {}
+}
+"#,
+ r#"
+mod foo {
+ struct Foo {}
+
+ impl Foo {
+ fn $0new() -> Self {
+ Self { }
+ }
+ }
+}
+"#,
+ );
+ check_assist(
+ generate_new,
+ r#"
+mod foo {
+ mod bar {
+ struct $0Foo {}
+ }
+}
+"#,
+ r#"
+mod foo {
+ mod bar {
+ struct Foo {}
+
+ impl Foo {
+ fn $0new() -> Self {
+ Self { }
+ }
+ }
+ }
+}
+"#,
+ );
+ check_assist(
+ generate_new,
+ r#"
+mod foo {
+ struct $0Foo {}
+
+ impl Foo {
+ fn some() {}
+ }
+}
+"#,
+ r#"
+mod foo {
+ struct Foo {}
+
+ impl Foo {
+ fn $0new() -> Self {
+ Self { }
+ }
+
+ fn some() {}
+ }
+}
+"#,
+ );
+ check_assist(
+ generate_new,
+ r#"
+mod foo {
+ mod bar {
+ struct $0Foo {}
+
+ impl Foo {
+ fn some() {}
+ }
+ }
+}
+"#,
+ r#"
+mod foo {
+ mod bar {
+ struct Foo {}
+
+ impl Foo {
+ fn $0new() -> Self {
+ Self { }
+ }
+
+ fn some() {}
+ }
+ }
+}
+"#,
+ );
+ check_assist(
+ generate_new,
+ r#"
+mod foo {
+ mod bar {
+struct $0Foo {}
+
+ impl Foo {
+ fn some() {}
+ }
+ }
+}
+"#,
+ r#"
+mod foo {
+ mod bar {
+struct Foo {}
+
+ impl Foo {
+ fn $0new() -> Self {
+ Self { }
+ }
+
+ fn some() {}
+ }
+ }
+}
+"#,
+ );
+ }
+
+ #[test]
fn check_visibility_of_new_fn_based_on_struct() {
check_assist(
generate_new,
diff --git a/crates/ide-assists/src/handlers/replace_try_expr_with_match.rs b/crates/ide-assists/src/handlers/replace_try_expr_with_match.rs
deleted file mode 100644
index c6e864fcfd..0000000000
--- a/crates/ide-assists/src/handlers/replace_try_expr_with_match.rs
+++ /dev/null
@@ -1,148 +0,0 @@
-use std::iter;
-
-use ide_db::{assists::AssistId, ty_filter::TryEnum};
-use syntax::{
- AstNode, T,
- ast::{
- self,
- edit::{AstNodeEdit, IndentLevel},
- make,
- },
-};
-
-use crate::assist_context::{AssistContext, Assists};
-
-// Assist: replace_try_expr_with_match
-//
-// Replaces a `try` expression with a `match` expression.
-//
-// ```
-// # //- minicore: try, option
-// fn handle() {
-// let pat = Some(true)$0?;
-// }
-// ```
-// ->
-// ```
-// fn handle() {
-// let pat = match Some(true) {
-// Some(it) => it,
-// None => return None,
-// };
-// }
-// ```
-pub(crate) fn replace_try_expr_with_match(
- acc: &mut Assists,
- ctx: &AssistContext<'_>,
-) -> Option<()> {
- let qm_kw = ctx.find_token_syntax_at_offset(T![?])?;
- let qm_kw_parent = qm_kw.parent().and_then(ast::TryExpr::cast)?;
-
- let expr = qm_kw_parent.expr()?;
- let expr_type_info = ctx.sema.type_of_expr(&expr)?;
-
- let try_enum = TryEnum::from_ty(&ctx.sema, &expr_type_info.original)?;
-
- let target = qm_kw_parent.syntax().text_range();
- acc.add(
- AssistId::refactor_rewrite("replace_try_expr_with_match"),
- "Replace try expression with match",
- target,
- |edit| {
- let sad_pat = match try_enum {
- TryEnum::Option => make::path_pat(make::ext::ident_path("None")),
- TryEnum::Result => make::tuple_struct_pat(
- make::ext::ident_path("Err"),
- iter::once(make::path_pat(make::ext::ident_path("err"))),
- )
- .into(),
- };
- let sad_expr = match try_enum {
- TryEnum::Option => {
- make::expr_return(Some(make::expr_path(make::ext::ident_path("None"))))
- }
- TryEnum::Result => make::expr_return(Some(
- make::expr_call(
- make::expr_path(make::ext::ident_path("Err")),
- make::arg_list(iter::once(make::expr_path(make::ext::ident_path("err")))),
- )
- .into(),
- )),
- };
-
- let happy_arm = make::match_arm(
- try_enum.happy_pattern(make::ident_pat(false, false, make::name("it")).into()),
- None,
- make::expr_path(make::ext::ident_path("it")),
- );
- let sad_arm = make::match_arm(sad_pat, None, sad_expr);
-
- let match_arm_list = make::match_arm_list([happy_arm, sad_arm]);
-
- let expr_match = make::expr_match(expr, match_arm_list)
- .indent(IndentLevel::from_node(qm_kw_parent.syntax()));
- edit.replace_ast::<ast::Expr>(qm_kw_parent.into(), expr_match.into());
- },
- )
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
-
- use crate::tests::{check_assist, check_assist_not_applicable};
-
- #[test]
- fn test_replace_try_expr_with_match_not_applicable() {
- check_assist_not_applicable(
- replace_try_expr_with_match,
- r#"
- fn test() {
- let pat: u32 = 25$0;
- }
- "#,
- );
- }
-
- #[test]
- fn test_replace_try_expr_with_match_option() {
- check_assist(
- replace_try_expr_with_match,
- r#"
-//- minicore: try, option
-fn test() {
- let pat = Some(true)$0?;
-}
- "#,
- r#"
-fn test() {
- let pat = match Some(true) {
- Some(it) => it,
- None => return None,
- };
-}
- "#,
- );
- }
-
- #[test]
- fn test_replace_try_expr_with_match_result() {
- check_assist(
- replace_try_expr_with_match,
- r#"
-//- minicore: try, from, result
-fn test() {
- let pat = Ok(true)$0?;
-}
- "#,
- r#"
-fn test() {
- let pat = match Ok(true) {
- Ok(it) => it,
- Err(err) => return Err(err),
- };
-}
- "#,
- );
- }
-}
diff --git a/crates/ide-assists/src/lib.rs b/crates/ide-assists/src/lib.rs
index 2395091b6f..c260443203 100644
--- a/crates/ide-assists/src/lib.rs
+++ b/crates/ide-assists/src/lib.rs
@@ -139,6 +139,7 @@ mod handlers {
mod destructure_struct_binding;
mod destructure_tuple_binding;
mod desugar_doc_comment;
+ mod desugar_try_expr;
mod expand_glob_import;
mod expand_rest_pattern;
mod extract_expressions_from_format_string;
@@ -214,7 +215,6 @@ mod handlers {
mod replace_named_generic_with_impl;
mod replace_qualified_name_with_use;
mod replace_string_with_char;
- mod replace_try_expr_with_match;
mod replace_turbofish_with_explicit_type;
mod sort_items;
mod split_import;
@@ -273,6 +273,7 @@ mod handlers {
destructure_struct_binding::destructure_struct_binding,
destructure_tuple_binding::destructure_tuple_binding,
desugar_doc_comment::desugar_doc_comment,
+ desugar_try_expr::desugar_try_expr,
expand_glob_import::expand_glob_import,
expand_glob_import::expand_glob_reexport,
expand_rest_pattern::expand_rest_pattern,
@@ -354,7 +355,6 @@ mod handlers {
replace_method_eager_lazy::replace_with_lazy_method,
replace_named_generic_with_impl::replace_named_generic_with_impl,
replace_qualified_name_with_use::replace_qualified_name_with_use,
- 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,
diff --git a/crates/ide-assists/src/tests/generated.rs b/crates/ide-assists/src/tests/generated.rs
index 76134acb36..72f7195cbd 100644
--- a/crates/ide-assists/src/tests/generated.rs
+++ b/crates/ide-assists/src/tests/generated.rs
@@ -930,6 +930,47 @@ comment"]
}
#[test]
+fn doctest_desugar_try_expr_let_else() {
+ check_doc_test(
+ "desugar_try_expr_let_else",
+ r#####"
+//- minicore: try, option
+fn handle() {
+ let pat = Some(true)$0?;
+}
+"#####,
+ r#####"
+fn handle() {
+ let Some(pat) = Some(true) else {
+ return None;
+ };
+}
+"#####,
+ )
+}
+
+#[test]
+fn doctest_desugar_try_expr_match() {
+ check_doc_test(
+ "desugar_try_expr_match",
+ r#####"
+//- minicore: try, option
+fn handle() {
+ let pat = Some(true)$0?;
+}
+"#####,
+ r#####"
+fn handle() {
+ let pat = match Some(true) {
+ Some(it) => it,
+ None => return None,
+ };
+}
+"#####,
+ )
+}
+
+#[test]
fn doctest_expand_glob_import() {
check_doc_test(
"expand_glob_import",
@@ -3097,27 +3138,6 @@ fn main() {
}
#[test]
-fn doctest_replace_try_expr_with_match() {
- check_doc_test(
- "replace_try_expr_with_match",
- r#####"
-//- minicore: try, option
-fn handle() {
- let pat = Some(true)$0?;
-}
-"#####,
- r#####"
-fn handle() {
- let pat = match Some(true) {
- Some(it) => it,
- None => return None,
- };
-}
-"#####,
- )
-}
-
-#[test]
fn doctest_replace_turbofish_with_explicit_type() {
check_doc_test(
"replace_turbofish_with_explicit_type",
diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs
index 3baf1f3de6..5287627790 100644
--- a/crates/ide-completion/src/context.rs
+++ b/crates/ide-completion/src/context.rs
@@ -8,8 +8,8 @@ use std::{iter, ops::ControlFlow};
use base_db::RootQueryDb as _;
use hir::{
- DisplayTarget, HasAttrs, Local, ModuleDef, ModuleSource, Name, PathResolution, ScopeDef,
- Semantics, SemanticsScope, Symbol, Type, TypeInfo,
+ DisplayTarget, HasAttrs, InFile, Local, ModuleDef, ModuleSource, Name, PathResolution,
+ ScopeDef, Semantics, SemanticsScope, Symbol, Type, TypeInfo,
};
use ide_db::{
FilePosition, FxHashMap, FxHashSet, RootDatabase, famous_defs::FamousDefs,
@@ -751,7 +751,7 @@ impl<'a> CompletionContext<'a> {
original_offset,
} = expand_and_analyze(
&sema,
- original_file.syntax().clone(),
+ InFile::new(editioned_file_id.into(), original_file.syntax().clone()),
file_with_fake_ident.syntax().clone(),
offset,
&original_token,
diff --git a/crates/ide-completion/src/context/analysis.rs b/crates/ide-completion/src/context/analysis.rs
index 284876ffc8..7a2230b3e3 100644
--- a/crates/ide-completion/src/context/analysis.rs
+++ b/crates/ide-completion/src/context/analysis.rs
@@ -1,7 +1,7 @@
//! Module responsible for analyzing the code surrounding the cursor for completion.
use std::iter;
-use hir::{ExpandResult, Semantics, Type, TypeInfo, Variant};
+use hir::{ExpandResult, InFile, Semantics, Type, TypeInfo, Variant};
use ide_db::{RootDatabase, active_parameter::ActiveParameter};
use itertools::Either;
use syntax::{
@@ -50,7 +50,7 @@ pub(super) struct AnalysisResult {
pub(super) fn expand_and_analyze(
sema: &Semantics<'_, RootDatabase>,
- original_file: SyntaxNode,
+ original_file: InFile<SyntaxNode>,
speculative_file: SyntaxNode,
offset: TextSize,
original_token: &SyntaxToken,
@@ -72,7 +72,7 @@ pub(super) fn expand_and_analyze(
relative_offset,
)
.unwrap_or(ExpansionResult {
- original_file,
+ original_file: original_file.value,
speculative_file,
original_offset: offset,
speculative_offset: fake_ident_token.text_range().start(),
@@ -125,7 +125,7 @@ fn token_at_offset_ignore_whitespace(file: &SyntaxNode, offset: TextSize) -> Opt
/// the best we can do.
fn expand_maybe_stop(
sema: &Semantics<'_, RootDatabase>,
- original_file: SyntaxNode,
+ original_file: InFile<SyntaxNode>,
speculative_file: SyntaxNode,
original_offset: TextSize,
fake_ident_token: SyntaxToken,
@@ -142,17 +142,16 @@ fn expand_maybe_stop(
return result;
}
- // This needs to come after the recursive call, because our "inside macro" detection is subtly wrong
- // with regard to attribute macros named `test` that are not std's test. So hopefully we will expand
- // them successfully above and be able to analyze.
- // Left biased since there may already be an identifier token there, and we appended to it.
- if !sema.might_be_inside_macro_call(&fake_ident_token)
- && token_at_offset_ignore_whitespace(&original_file, original_offset + relative_offset)
- .is_some_and(|original_token| !sema.might_be_inside_macro_call(&original_token))
+ // We can't check whether the fake expansion is inside macro call, because that requires semantic info.
+ // But hopefully checking just the real one should be enough.
+ if token_at_offset_ignore_whitespace(&original_file.value, original_offset + relative_offset)
+ .is_some_and(|original_token| {
+ !sema.is_inside_macro_call(original_file.with_value(&original_token))
+ })
{
// Recursion base case.
Some(ExpansionResult {
- original_file,
+ original_file: original_file.value,
speculative_file,
original_offset,
speculative_offset: fake_ident_token.text_range().start(),
@@ -166,7 +165,7 @@ fn expand_maybe_stop(
fn expand(
sema: &Semantics<'_, RootDatabase>,
- original_file: SyntaxNode,
+ original_file: InFile<SyntaxNode>,
speculative_file: SyntaxNode,
original_offset: TextSize,
fake_ident_token: SyntaxToken,
@@ -176,7 +175,7 @@ fn expand(
let parent_item =
|item: &ast::Item| item.syntax().ancestors().skip(1).find_map(ast::Item::cast);
- let original_node = token_at_offset_ignore_whitespace(&original_file, original_offset)
+ let original_node = token_at_offset_ignore_whitespace(&original_file.value, original_offset)
.and_then(|token| token.parent_ancestors().find_map(ast::Item::cast));
let ancestor_items = iter::successors(
Option::zip(
@@ -249,7 +248,7 @@ fn expand(
}
// No attributes have been expanded, so look for macro_call! token trees or derive token trees
- let orig_tt = ancestors_at_offset(&original_file, original_offset)
+ let orig_tt = ancestors_at_offset(&original_file.value, original_offset)
.map_while(Either::<ast::TokenTree, ast::Meta>::cast)
.last()?;
let spec_tt = ancestors_at_offset(&speculative_file, fake_ident_token.text_range().start())
@@ -292,7 +291,7 @@ fn expand(
fake_mapped_tokens.into_iter().min_by_key(|(_, rank)| *rank)
{
return Some(ExpansionResult {
- original_file,
+ original_file: original_file.value,
speculative_file,
original_offset,
speculative_offset: fake_ident_token.text_range().start(),
@@ -349,7 +348,7 @@ fn expand(
}
let result = expand_maybe_stop(
sema,
- actual_expansion.clone(),
+ InFile::new(file.into(), actual_expansion.clone()),
fake_expansion.clone(),
new_offset,
fake_mapped_token,
diff --git a/crates/ide-completion/src/tests/expression.rs b/crates/ide-completion/src/tests/expression.rs
index e5467767d4..b46e4c3206 100644
--- a/crates/ide-completion/src/tests/expression.rs
+++ b/crates/ide-completion/src/tests/expression.rs
@@ -2112,6 +2112,56 @@ fn foo() {
}
#[test]
+fn cfg_attr_attr_macro() {
+ check(
+ r#"
+//- proc_macros: identity
+#[cfg_attr(test, proc_macros::identity)]
+fn foo() {
+ $0
+}
+ "#,
+ expect![[r#"
+ fn foo() fn()
+ md proc_macros
+ bt u32 u32
+ kw async
+ kw const
+ kw crate::
+ kw enum
+ kw extern
+ kw false
+ kw fn
+ kw for
+ kw if
+ kw if let
+ kw impl
+ kw impl for
+ kw let
+ kw letm
+ kw loop
+ kw match
+ kw mod
+ kw return
+ kw self::
+ kw static
+ kw struct
+ kw trait
+ kw true
+ kw type
+ kw union
+ kw unsafe
+ kw use
+ kw while
+ kw while let
+ sn macro_rules
+ sn pd
+ sn ppd
+ "#]],
+ );
+}
+
+#[test]
fn escaped_label() {
check(
r#"
diff --git a/crates/ide-completion/src/tests/item.rs b/crates/ide-completion/src/tests/item.rs
index 55689034fb..ed87b339fe 100644
--- a/crates/ide-completion/src/tests/item.rs
+++ b/crates/ide-completion/src/tests/item.rs
@@ -4,7 +4,7 @@
//! in [crate::completions::mod_].
use expect_test::expect;
-use crate::tests::{check_edit, check_with_base_items};
+use crate::tests::{check, check_edit, check_with_base_items};
#[test]
fn target_type_or_trait_in_impl_block() {
@@ -308,3 +308,39 @@ fn bar() {
"#]],
);
}
+
+#[test]
+fn expression_in_item_macro() {
+ check(
+ r#"
+fn foo() -> u8 { 0 }
+
+macro_rules! foo {
+ ($expr:expr) => {
+ const BAR: u8 = $expr;
+ };
+}
+
+foo!(f$0);
+ "#,
+ expect![[r#"
+ ct BAR u8
+ fn foo() fn() -> u8
+ ma foo!(…) macro_rules! foo
+ bt u32 u32
+ kw const
+ kw crate::
+ kw false
+ kw for
+ kw if
+ kw if let
+ kw loop
+ kw match
+ kw self::
+ kw true
+ kw unsafe
+ kw while
+ kw while let
+ "#]],
+ );
+}
diff --git a/crates/ide-db/src/lib.rs b/crates/ide-db/src/lib.rs
index 63cc7cde28..c94be7e164 100644
--- a/crates/ide-db/src/lib.rs
+++ b/crates/ide-db/src/lib.rs
@@ -92,9 +92,7 @@ pub struct RootDatabase {
impl std::panic::RefUnwindSafe for RootDatabase {}
#[salsa_macros::db]
-impl salsa::Database for RootDatabase {
- fn salsa_event(&self, _event: &dyn Fn() -> salsa::Event) {}
-}
+impl salsa::Database for RootDatabase {}
impl Drop for RootDatabase {
fn drop(&mut self) {
diff --git a/crates/ide-db/src/search.rs b/crates/ide-db/src/search.rs
index 30be5bc21b..d4ab759292 100644
--- a/crates/ide-db/src/search.rs
+++ b/crates/ide-db/src/search.rs
@@ -524,6 +524,7 @@ impl<'a> FindUsages<'a> {
fn find_nodes<'b>(
sema: &'b Semantics<'_, RootDatabase>,
name: &str,
+ file_id: EditionedFileId,
node: &syntax::SyntaxNode,
offset: TextSize,
) -> impl Iterator<Item = SyntaxNode> + 'b {
@@ -534,7 +535,7 @@ impl<'a> FindUsages<'a> {
})
.into_iter()
.flat_map(move |token| {
- if sema.might_be_inside_macro_call(&token) {
+ if sema.is_inside_macro_call(InFile::new(file_id.into(), &token)) {
sema.descend_into_macros_exact(token)
} else {
<_>::from([token])
@@ -654,11 +655,14 @@ impl<'a> FindUsages<'a> {
let tree = LazyCell::new(move || sema.parse(file_id).syntax().clone());
for offset in FindUsages::match_indices(&file_text, &finder, search_range) {
- let usages =
- FindUsages::find_nodes(sema, &current_to_process, &tree, offset)
- .filter(|it| {
- matches!(it.kind(), SyntaxKind::NAME | SyntaxKind::NAME_REF)
- });
+ let usages = FindUsages::find_nodes(
+ sema,
+ &current_to_process,
+ file_id,
+ &tree,
+ offset,
+ )
+ .filter(|it| matches!(it.kind(), SyntaxKind::NAME | SyntaxKind::NAME_REF));
for usage in usages {
if let Some(alias) = usage.parent().and_then(|it| {
let path = ast::PathSegment::cast(it)?.parent_path();
@@ -813,7 +817,7 @@ impl<'a> FindUsages<'a> {
let tree = LazyCell::new(move || this.sema.parse(file_id).syntax().clone());
for offset in FindUsages::match_indices(&file_text, finder, search_range) {
- let usages = FindUsages::find_nodes(this.sema, name, &tree, offset)
+ let usages = FindUsages::find_nodes(this.sema, name, file_id, &tree, offset)
.filter_map(ast::NameRef::cast);
for usage in usages {
let found_usage = usage
@@ -970,8 +974,8 @@ impl<'a> FindUsages<'a> {
return;
}
- for name in
- Self::find_nodes(sema, name, &tree, offset).filter_map(ast::NameLike::cast)
+ for name in Self::find_nodes(sema, name, file_id, &tree, offset)
+ .filter_map(ast::NameLike::cast)
{
if match name {
ast::NameLike::NameRef(name_ref) => self.found_name_ref(&name_ref, sink),
@@ -985,8 +989,8 @@ impl<'a> FindUsages<'a> {
// Search for occurrences of the `Self` referring to our type
if let Some((self_ty, finder)) = &include_self_kw_refs {
for offset in Self::match_indices(&text, finder, search_range) {
- for name_ref in
- Self::find_nodes(sema, "Self", &tree, offset).filter_map(ast::NameRef::cast)
+ for name_ref in Self::find_nodes(sema, "Self", file_id, &tree, offset)
+ .filter_map(ast::NameRef::cast)
{
if self.found_self_ty_name_ref(self_ty, &name_ref, sink) {
return;
@@ -1010,7 +1014,7 @@ impl<'a> FindUsages<'a> {
let tree = LazyCell::new(move || sema.parse(file_id).syntax().clone());
for offset in Self::match_indices(&text, finder, search_range) {
- for name_ref in Self::find_nodes(sema, "super", &tree, offset)
+ for name_ref in Self::find_nodes(sema, "super", file_id, &tree, offset)
.filter_map(ast::NameRef::cast)
{
if self.found_name_ref(&name_ref, sink) {
@@ -1020,7 +1024,7 @@ impl<'a> FindUsages<'a> {
}
if let Some(finder) = &is_crate_root {
for offset in Self::match_indices(&text, finder, search_range) {
- for name_ref in Self::find_nodes(sema, "crate", &tree, offset)
+ for name_ref in Self::find_nodes(sema, "crate", file_id, &tree, offset)
.filter_map(ast::NameRef::cast)
{
if self.found_name_ref(&name_ref, sink) {
@@ -1064,8 +1068,8 @@ impl<'a> FindUsages<'a> {
let finder = &Finder::new("self");
for offset in Self::match_indices(&text, finder, search_range) {
- for name_ref in
- Self::find_nodes(sema, "self", &tree, offset).filter_map(ast::NameRef::cast)
+ for name_ref in Self::find_nodes(sema, "self", file_id, &tree, offset)
+ .filter_map(ast::NameRef::cast)
{
if self.found_self_module_name_ref(&name_ref, sink) {
return;
diff --git a/crates/ide-diagnostics/src/handlers/missing_unsafe.rs b/crates/ide-diagnostics/src/handlers/missing_unsafe.rs
index 364bead34e..6bd5417b25 100644
--- a/crates/ide-diagnostics/src/handlers/missing_unsafe.rs
+++ b/crates/ide-diagnostics/src/handlers/missing_unsafe.rs
@@ -915,4 +915,47 @@ fn foo() {
"#,
);
}
+
+ #[test]
+ fn regression_19823() {
+ check_diagnostics(
+ r#"
+pub trait FooTrait {
+ unsafe fn method1();
+ unsafe fn method2();
+}
+
+unsafe fn some_unsafe_fn() {}
+
+macro_rules! impl_foo {
+ () => {
+ unsafe fn method1() {
+ some_unsafe_fn();
+ }
+ unsafe fn method2() {
+ some_unsafe_fn();
+ }
+ };
+}
+
+pub struct S1;
+#[allow(unsafe_op_in_unsafe_fn)]
+impl FooTrait for S1 {
+ unsafe fn method1() {
+ some_unsafe_fn();
+ }
+
+ unsafe fn method2() {
+ some_unsafe_fn();
+ }
+}
+
+pub struct S2;
+#[allow(unsafe_op_in_unsafe_fn)]
+impl FooTrait for S2 {
+ impl_foo!();
+}
+ "#,
+ );
+ }
}
diff --git a/crates/ide-diagnostics/src/handlers/non_exhaustive_let.rs b/crates/ide-diagnostics/src/handlers/non_exhaustive_let.rs
index 35cefd2397..f20b6dea12 100644
--- a/crates/ide-diagnostics/src/handlers/non_exhaustive_let.rs
+++ b/crates/ide-diagnostics/src/handlers/non_exhaustive_let.rs
@@ -106,4 +106,29 @@ fn test(x: Result<i32, &'static !>) {
"#,
);
}
+
+ #[test]
+ fn empty_patterns_normalize() {
+ check_diagnostics(
+ r#"
+enum Infallible {}
+
+trait Foo {
+ type Assoc;
+}
+enum Enum<T: Foo> {
+ A,
+ B(T::Assoc),
+}
+
+impl Foo for () {
+ type Assoc = Infallible;
+}
+
+fn foo(v: Enum<()>) {
+ let Enum::A = v;
+}
+ "#,
+ );
+ }
}
diff --git a/crates/ide-diagnostics/src/handlers/type_mismatch.rs b/crates/ide-diagnostics/src/handlers/type_mismatch.rs
index 5253734867..076df1ab0f 100644
--- a/crates/ide-diagnostics/src/handlers/type_mismatch.rs
+++ b/crates/ide-diagnostics/src/handlers/type_mismatch.rs
@@ -1243,4 +1243,18 @@ fn foo(v: &Enum) {
"#,
);
}
+
+ #[test]
+ fn regression_19844() {
+ check_diagnostics(
+ r#"
+fn main() {
+ struct S {}
+ enum E { V() }
+ let E::V() = &S {};
+ // ^^^^^^ error: expected S, found E
+}
+"#,
+ );
+ }
}
diff --git a/crates/ide-diagnostics/src/lib.rs b/crates/ide-diagnostics/src/lib.rs
index 2af14ca949..72bd66d1c8 100644
--- a/crates/ide-diagnostics/src/lib.rs
+++ b/crates/ide-diagnostics/src/lib.rs
@@ -83,12 +83,11 @@ mod handlers {
#[cfg(test)]
mod tests;
-use std::{collections::hash_map, iter, sync::LazyLock};
+use std::{iter, sync::LazyLock};
use either::Either;
use hir::{
- Crate, DisplayTarget, HirFileId, InFile, Semantics, db::ExpandDatabase,
- diagnostics::AnyDiagnostic,
+ Crate, DisplayTarget, InFile, Semantics, db::ExpandDatabase, diagnostics::AnyDiagnostic,
};
use ide_db::{
EditionedFileId, FileId, FileRange, FxHashMap, FxHashSet, RootDatabase, Severity, SnippetCap,
@@ -513,13 +512,7 @@ pub fn semantic_diagnostics(
// The edition isn't accurate (each diagnostics may have its own edition due to macros),
// but it's okay as it's only being used for error recovery.
- handle_lints(
- &ctx.sema,
- &mut FxHashMap::default(),
- &mut lints,
- &mut Vec::new(),
- editioned_file_id.edition(db),
- );
+ handle_lints(&ctx.sema, &mut lints, editioned_file_id.edition(db));
res.retain(|d| d.severity != Severity::Allow);
@@ -584,8 +577,6 @@ fn handle_diag_from_macros(
true
}
-// `__RA_EVERY_LINT` is a fake lint group to allow every lint in proc macros
-
struct BuiltLint {
lint: &'static Lint,
groups: Vec<&'static str>,
@@ -629,9 +620,7 @@ fn build_lints_map(
fn handle_lints(
sema: &Semantics<'_, RootDatabase>,
- cache: &mut FxHashMap<HirFileId, FxHashMap<SmolStr, SeverityAttr>>,
diagnostics: &mut [(InFile<SyntaxNode>, &mut Diagnostic)],
- cache_stack: &mut Vec<HirFileId>,
edition: Edition,
) {
for (node, diag) in diagnostics {
@@ -645,7 +634,8 @@ fn handle_lints(
diag.severity = default_severity;
}
- let mut diag_severity = fill_lint_attrs(sema, node, cache, cache_stack, diag, edition);
+ let mut diag_severity =
+ lint_severity_at(sema, node, &lint_groups(&diag.code, edition), edition);
if let outline_diag_severity @ Some(_) =
find_outline_mod_lint_severity(sema, node, diag, edition)
@@ -698,155 +688,22 @@ fn find_outline_mod_lint_severity(
result
}
-#[derive(Debug, Clone, Copy)]
-struct SeverityAttr {
- severity: Severity,
- /// This field counts how far we are from the main node. Bigger values mean more far.
- ///
- /// Note this isn't accurate: there can be gaps between values (created when merging severity maps).
- /// The important thing is that if an attr is closer to the main node, it will have smaller value.
- ///
- /// This is necessary even though we take care to never overwrite a value from deeper nesting
- /// because of lint groups. For example, in the following code:
- /// ```
- /// #[warn(non_snake_case)]
- /// mod foo {
- /// #[allow(nonstandard_style)]
- /// mod bar {}
- /// }
- /// ```
- /// We want to not warn on non snake case inside `bar`. If we are traversing this for the first
- /// time, everything will be fine, because we will set `diag_severity` on the first matching group
- /// and never overwrite it since then. But if `bar` is cached, the cache will contain both
- /// `#[warn(non_snake_case)]` and `#[allow(nonstandard_style)]`, and without this field, we have
- /// no way of differentiating between the two.
- depth: u32,
-}
-
-fn fill_lint_attrs(
+fn lint_severity_at(
sema: &Semantics<'_, RootDatabase>,
node: &InFile<SyntaxNode>,
- cache: &mut FxHashMap<HirFileId, FxHashMap<SmolStr, SeverityAttr>>,
- cache_stack: &mut Vec<HirFileId>,
- diag: &Diagnostic,
+ lint_groups: &LintGroups,
edition: Edition,
) -> Option<Severity> {
- let mut collected_lint_attrs = FxHashMap::<SmolStr, SeverityAttr>::default();
- let mut diag_severity = None;
-
- let mut ancestors = node.value.ancestors().peekable();
- let mut depth = 0;
- loop {
- let ancestor = ancestors.next().expect("we always return from top-level nodes");
- depth += 1;
-
- if ancestors.peek().is_none() {
- // We don't want to insert too many nodes into cache, but top level nodes (aka. outline modules
- // or macro expansions) need to touch the database so they seem like a good fit to cache.
-
- if let Some(cached) = cache.get_mut(&node.file_id) {
- // This node (and everything above it) is already cached; the attribute is either here or nowhere.
-
- // Workaround for the borrow checker.
- let cached = std::mem::take(cached);
-
- cached.iter().for_each(|(lint, severity)| {
- for item in &*cache_stack {
- let node_cache_entry = cache
- .get_mut(item)
- .expect("we always insert cached nodes into the cache map");
- let lint_cache_entry = node_cache_entry.entry(lint.clone());
- if let hash_map::Entry::Vacant(lint_cache_entry) = lint_cache_entry {
- // Do not overwrite existing lint attributes, as we go bottom to top and bottom attrs
- // overwrite top attrs.
- lint_cache_entry.insert(SeverityAttr {
- severity: severity.severity,
- depth: severity.depth + depth,
- });
- }
- }
- });
-
- let lints = lint_groups(&diag.code, edition);
- let all_matching_groups =
- lints.iter().filter_map(|lint_group| cached.get(lint_group));
- let cached_severity =
- all_matching_groups.min_by_key(|it| it.depth).map(|it| it.severity);
-
- cache.insert(node.file_id, cached);
-
- return diag_severity.or(cached_severity);
- }
-
- // Insert this node's descendants' attributes into any outline descendant, but not including this node.
- // This must come before inserting this node's own attributes to preserve order.
- collected_lint_attrs.drain().for_each(|(lint, severity)| {
- if diag_severity.is_none() && lint_groups(&diag.code, edition).contains(&lint) {
- diag_severity = Some(severity.severity);
- }
-
- for item in &*cache_stack {
- let node_cache_entry = cache
- .get_mut(item)
- .expect("we always insert cached nodes into the cache map");
- let lint_cache_entry = node_cache_entry.entry(lint.clone());
- if let hash_map::Entry::Vacant(lint_cache_entry) = lint_cache_entry {
- // Do not overwrite existing lint attributes, as we go bottom to top and bottom attrs
- // overwrite top attrs.
- lint_cache_entry.insert(severity);
- }
- }
- });
-
- cache_stack.push(node.file_id);
- cache.insert(node.file_id, FxHashMap::default());
-
- if let Some(ancestor) = ast::AnyHasAttrs::cast(ancestor) {
- // Insert this node's attributes into any outline descendant, including this node.
- lint_attrs(sema, ancestor, edition).for_each(|(lint, severity)| {
- if diag_severity.is_none() && lint_groups(&diag.code, edition).contains(&lint) {
- diag_severity = Some(severity);
- }
-
- for item in &*cache_stack {
- let node_cache_entry = cache
- .get_mut(item)
- .expect("we always insert cached nodes into the cache map");
- let lint_cache_entry = node_cache_entry.entry(lint.clone());
- if let hash_map::Entry::Vacant(lint_cache_entry) = lint_cache_entry {
- // Do not overwrite existing lint attributes, as we go bottom to top and bottom attrs
- // overwrite top attrs.
- lint_cache_entry.insert(SeverityAttr { severity, depth });
- }
- }
- });
- }
-
- let parent_node = sema.find_parent_file(node.file_id);
- if let Some(parent_node) = parent_node {
- let parent_severity =
- fill_lint_attrs(sema, &parent_node, cache, cache_stack, diag, edition);
- if diag_severity.is_none() {
- diag_severity = parent_severity;
- }
- }
- cache_stack.pop();
- return diag_severity;
- } else if let Some(ancestor) = ast::AnyHasAttrs::cast(ancestor) {
- lint_attrs(sema, ancestor, edition).for_each(|(lint, severity)| {
- if diag_severity.is_none() && lint_groups(&diag.code, edition).contains(&lint) {
- diag_severity = Some(severity);
- }
-
- let lint_cache_entry = collected_lint_attrs.entry(lint);
- if let hash_map::Entry::Vacant(lint_cache_entry) = lint_cache_entry {
- // Do not overwrite existing lint attributes, as we go bottom to top and bottom attrs
- // overwrite top attrs.
- lint_cache_entry.insert(SeverityAttr { severity, depth });
- }
- });
- }
- }
+ node.value
+ .ancestors()
+ .filter_map(ast::AnyHasAttrs::cast)
+ .find_map(|ancestor| {
+ lint_attrs(sema, ancestor, edition)
+ .find_map(|(lint, severity)| lint_groups.contains(&lint).then_some(severity))
+ })
+ .or_else(|| {
+ lint_severity_at(sema, &sema.find_parent_file(node.file_id)?, lint_groups, edition)
+ })
}
fn lint_attrs<'a>(
@@ -945,10 +802,6 @@ impl LintGroups {
fn contains(&self, group: &str) -> bool {
self.groups.contains(&group) || (self.inside_warnings && group == "warnings")
}
-
- fn iter(&self) -> impl Iterator<Item = &'static str> {
- self.groups.iter().copied().chain(self.inside_warnings.then_some("warnings"))
- }
}
fn lint_groups(lint: &DiagnosticCode, edition: Edition) -> LintGroups {
diff --git a/crates/ide-ssr/src/lib.rs b/crates/ide-ssr/src/lib.rs
index 339c199ec2..43c56ac8be 100644
--- a/crates/ide-ssr/src/lib.rs
+++ b/crates/ide-ssr/src/lib.rs
@@ -287,7 +287,7 @@ impl<'db> MatchFinder<'db> {
if let Some(expanded) = self.sema.expand_macro_call(&macro_call) {
if let Some(tt) = macro_call.token_tree() {
self.output_debug_for_nodes_at_range(
- &expanded,
+ &expanded.value,
range,
&Some(self.sema.original_range(tt.syntax())),
out,
diff --git a/crates/ide-ssr/src/search.rs b/crates/ide-ssr/src/search.rs
index d89911fca4..9afbedbb1a 100644
--- a/crates/ide-ssr/src/search.rs
+++ b/crates/ide-ssr/src/search.rs
@@ -194,7 +194,7 @@ impl MatchFinder<'_> {
// nodes that originated entirely from within the token tree of the macro call.
// i.e. we don't want to match something that came from the macro itself.
if let Some(range) = self.sema.original_range_opt(tt.syntax()) {
- self.slow_scan_node(&expanded, rule, &Some(range), matches_out);
+ self.slow_scan_node(&expanded.value, rule, &Some(range), matches_out);
}
}
}
diff --git a/crates/ide/src/expand_macro.rs b/crates/ide/src/expand_macro.rs
index 241a702038..7c396339c1 100644
--- a/crates/ide/src/expand_macro.rs
+++ b/crates/ide/src/expand_macro.rs
@@ -1,10 +1,10 @@
use hir::db::ExpandDatabase;
-use hir::{ExpandResult, InFile, Semantics};
+use hir::{ExpandResult, InFile, InRealFile, Semantics};
use ide_db::{
FileId, RootDatabase, base_db::Crate, helpers::pick_best_token,
syntax_helpers::prettify_macro_expansion,
};
-use span::{Edition, SpanMap, SyntaxContext, TextRange, TextSize};
+use span::{SpanMap, SyntaxContext, TextRange, TextSize};
use stdx::format_to;
use syntax::{AstNode, NodeOrToken, SyntaxKind, SyntaxNode, T, ast, ted};
@@ -26,8 +26,9 @@ pub struct ExpandedMacro {
// ![Expand Macro Recursively](https://user-images.githubusercontent.com/48062697/113020648-b3973180-917a-11eb-84a9-ecb921293dc5.gif)
pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<ExpandedMacro> {
let sema = Semantics::new(db);
- let file = sema.parse_guess_edition(position.file_id);
- let krate = sema.file_to_module_def(position.file_id)?.krate().into();
+ let file_id = sema.attach_first_edition(position.file_id)?;
+ let file = sema.parse(file_id);
+ let krate = sema.file_to_module_def(file_id.file_id(db))?.krate().into();
let tok = pick_best_token(file.syntax().token_at_offset(position.offset), |kind| match kind {
SyntaxKind::IDENT => 1,
@@ -86,7 +87,10 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<
return derive;
}
- let mut anc = tok.parent_ancestors();
+ let mut anc = sema
+ .descend_token_into_include_expansion(InRealFile::new(file_id, tok))
+ .value
+ .parent_ancestors();
let mut span_map = SpanMap::empty();
let mut error = String::new();
let (name, expanded, kind) = loop {
@@ -95,14 +99,7 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<
if let Some(item) = ast::Item::cast(node.clone()) {
if let Some(def) = sema.resolve_attr_macro_call(&item) {
break (
- def.name(db)
- .display(
- db,
- sema.attach_first_edition(position.file_id)
- .map(|it| it.edition(db))
- .unwrap_or(Edition::CURRENT),
- )
- .to_string(),
+ def.name(db).display(db, file_id.edition(db)).to_string(),
expand_macro_recur(&sema, &item, &mut error, &mut span_map, TextSize::new(0))?,
SyntaxKind::MACRO_ITEMS,
);
@@ -146,10 +143,11 @@ fn expand_macro_recur(
offset_in_original_node: TextSize,
) -> Option<SyntaxNode> {
let ExpandResult { value: expanded, err } = match macro_call {
- item @ ast::Item::MacroCall(macro_call) => {
- sema.expand_attr_macro(item).or_else(|| sema.expand_allowed_builtins(macro_call))?
- }
- item => sema.expand_attr_macro(item)?,
+ item @ ast::Item::MacroCall(macro_call) => sema
+ .expand_attr_macro(item)
+ .map(|it| it.map(|it| it.value))
+ .or_else(|| sema.expand_allowed_builtins(macro_call))?,
+ item => sema.expand_attr_macro(item)?.map(|it| it.value),
};
let expanded = expanded.clone_for_update();
if let Some(err) = err {
@@ -718,4 +716,88 @@ __log!(written:%; "Test"$0);
"#]],
);
}
+
+ #[test]
+ fn assoc_call() {
+ check(
+ r#"
+macro_rules! mac {
+ () => { fn assoc() {} }
+}
+impl () {
+ mac$0!();
+}
+ "#,
+ expect![[r#"
+ mac!
+ fn assoc(){}"#]],
+ );
+ }
+
+ #[test]
+ fn eager() {
+ check(
+ r#"
+//- minicore: concat
+macro_rules! my_concat {
+ ($head:expr, $($tail:tt)*) => { concat!($head, $($tail)*) };
+}
+
+
+fn test() {
+ _ = my_concat!(
+ conc$0at!("<", ">"),
+ "hi",
+ );
+}
+ "#,
+ expect![[r#"
+ my_concat!
+ "<>hi""#]],
+ );
+ }
+
+ #[test]
+ fn in_included() {
+ check(
+ r#"
+//- minicore: include
+//- /main.rs crate:main
+include!("./included.rs");
+//- /included.rs
+macro_rules! foo {
+ () => { fn item() {} };
+}
+foo$0!();
+"#,
+ expect![[r#"
+ foo!
+ fn item(){}"#]],
+ );
+ }
+
+ #[test]
+ fn include() {
+ check(
+ r#"
+//- minicore: include
+//- /main.rs crate:main
+include$0!("./included.rs");
+//- /included.rs
+macro_rules! foo {
+ () => { fn item() {} };
+}
+foo();
+"#,
+ expect![[r#"
+ include!
+ macro_rules! foo {
+ () => {
+ fn item(){}
+
+ };
+ }
+ foo();"#]],
+ );
+ }
}
diff --git a/crates/ide/src/highlight_related.rs b/crates/ide/src/highlight_related.rs
index fb8dbcfc73..520ba39a23 100644
--- a/crates/ide/src/highlight_related.rs
+++ b/crates/ide/src/highlight_related.rs
@@ -653,7 +653,7 @@ impl<'a> WalkExpandedExprCtx<'a> {
expr.macro_call().and_then(|call| self.sema.expand_macro_call(&call))
{
match_ast! {
- match expanded {
+ match (expanded.value) {
ast::MacroStmts(it) => {
self.handle_expanded(it, cb);
},
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs
index 873e31b4a3..8bb1c708e2 100644
--- a/crates/ide/src/hover.rs
+++ b/crates/ide/src/hover.rs
@@ -58,6 +58,7 @@ pub struct MemoryLayoutHoverConfig {
pub size: Option<MemoryLayoutHoverRenderKind>,
pub offset: Option<MemoryLayoutHoverRenderKind>,
pub alignment: Option<MemoryLayoutHoverRenderKind>,
+ pub padding: Option<MemoryLayoutHoverRenderKind>,
pub niches: bool,
}
diff --git a/crates/ide/src/hover/render.rs b/crates/ide/src/hover/render.rs
index ad720c8a62..c24864a18b 100644
--- a/crates/ide/src/hover/render.rs
+++ b/crates/ide/src/hover/render.rs
@@ -630,27 +630,57 @@ pub(super) fn definition(
}
},
|_| None,
+ |_| None,
+ ),
+ Definition::Adt(it @ Adt::Struct(strukt)) => render_memory_layout(
+ config.memory_layout,
+ || it.layout(db),
+ |_| None,
+ |layout| {
+ let mut field_size =
+ |i: usize| Some(strukt.fields(db).get(i)?.layout(db).ok()?.size());
+ if strukt.repr(db).is_some_and(|it| it.inhibit_struct_field_reordering()) {
+ Some(("tail padding", layout.tail_padding(&mut field_size)?))
+ } else {
+ Some(("largest padding", layout.largest_padding(&mut field_size)?))
+ }
+ },
+ |_| None,
+ ),
+ Definition::Adt(it) => render_memory_layout(
+ config.memory_layout,
+ || it.layout(db),
+ |_| None,
+ |_| None,
+ |_| None,
),
- Definition::Adt(it) => {
- render_memory_layout(config.memory_layout, || it.layout(db), |_| None, |_| None)
- }
Definition::Variant(it) => render_memory_layout(
config.memory_layout,
|| it.layout(db),
|_| None,
+ |_| None,
|layout| layout.enum_tag_size(),
),
- Definition::TypeAlias(it) => {
- render_memory_layout(config.memory_layout, || it.ty(db).layout(db), |_| None, |_| None)
- }
- Definition::Local(it) => {
- render_memory_layout(config.memory_layout, || it.ty(db).layout(db), |_| None, |_| None)
- }
+ Definition::TypeAlias(it) => render_memory_layout(
+ config.memory_layout,
+ || it.ty(db).layout(db),
+ |_| None,
+ |_| None,
+ |_| None,
+ ),
+ Definition::Local(it) => render_memory_layout(
+ config.memory_layout,
+ || it.ty(db).layout(db),
+ |_| None,
+ |_| None,
+ |_| None,
+ ),
Definition::SelfType(it) => render_memory_layout(
config.memory_layout,
|| it.self_ty(db).layout(db),
|_| None,
|_| None,
+ |_| None,
),
_ => None,
};
@@ -1055,9 +1085,13 @@ fn closure_ty(
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())
}
- if let Some(layout) =
- render_memory_layout(config.memory_layout, || original.layout(sema.db), |_| None, |_| None)
- {
+ if let Some(layout) = render_memory_layout(
+ config.memory_layout,
+ || original.layout(sema.db),
+ |_| None,
+ |_| None,
+ |_| None,
+ ) {
format_to!(markup, "\n___\n{layout}");
}
format_to!(markup, "{adjusted}\n\n## Captures\n{}", captures_rendered,);
@@ -1142,6 +1176,7 @@ fn render_memory_layout(
config: Option<MemoryLayoutHoverConfig>,
layout: impl FnOnce() -> Result<Layout, LayoutError>,
offset: impl FnOnce(&Layout) -> Option<u64>,
+ padding: impl FnOnce(&Layout) -> Option<(&str, u64)>,
tag: impl FnOnce(&Layout) -> Option<usize>,
) -> Option<String> {
let config = config?;
@@ -1199,6 +1234,23 @@ fn render_memory_layout(
}
}
+ if let Some(render) = config.padding {
+ if let Some((padding_name, padding)) = padding(&layout) {
+ format_to!(label, "{padding_name} = ");
+ match render {
+ MemoryLayoutHoverRenderKind::Decimal => format_to!(label, "{padding}"),
+ MemoryLayoutHoverRenderKind::Hexadecimal => format_to!(label, "{padding:#X}"),
+ MemoryLayoutHoverRenderKind::Both if padding >= 10 => {
+ format_to!(label, "{padding} ({padding:#X})")
+ }
+ MemoryLayoutHoverRenderKind::Both => {
+ format_to!(label, "{padding}")
+ }
+ }
+ format_to!(label, ", ");
+ }
+ }
+
if config.niches {
if let Some(niches) = layout.niches() {
if niches > 1024 {
diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs
index 06ca24c3ec..a281a49152 100644
--- a/crates/ide/src/hover/tests.rs
+++ b/crates/ide/src/hover/tests.rs
@@ -12,6 +12,7 @@ const HOVER_BASE_CONFIG: HoverConfig = HoverConfig {
size: Some(MemoryLayoutHoverRenderKind::Both),
offset: Some(MemoryLayoutHoverRenderKind::Both),
alignment: Some(MemoryLayoutHoverRenderKind::Both),
+ padding: Some(MemoryLayoutHoverRenderKind::Both),
niches: true,
}),
documentation: true,
@@ -933,7 +934,7 @@ struct Foo$0(pub u32) where u32: Copy;
---
- size = 4, align = 4, no Drop
+ size = 4, align = 4, largest padding = 0, no Drop
"#]],
);
}
@@ -959,7 +960,7 @@ struct Foo$0 { field: u32 }
---
- size = 4, align = 4, no Drop
+ size = 4, align = 4, largest padding = 0, no Drop
"#]],
);
check(
@@ -984,7 +985,7 @@ struct Foo$0 where u32: Copy { field: u32 }
---
- size = 4, align = 4, no Drop
+ size = 4, align = 4, largest padding = 0, no Drop
"#]],
);
}
@@ -1013,7 +1014,7 @@ fn hover_record_struct_limit() {
---
- size = 12 (0xC), align = 4, no Drop
+ size = 12 (0xC), align = 4, largest padding = 0, no Drop
"#]],
);
check_hover_fields_limit(
@@ -1036,7 +1037,7 @@ fn hover_record_struct_limit() {
---
- size = 4, align = 4, no Drop
+ size = 4, align = 4, largest padding = 0, no Drop
"#]],
);
check_hover_fields_limit(
@@ -1062,7 +1063,7 @@ fn hover_record_struct_limit() {
---
- size = 16 (0x10), align = 4, no Drop
+ size = 16 (0x10), align = 4, largest padding = 0, no Drop
"#]],
);
check_hover_fields_limit(
@@ -1083,7 +1084,7 @@ fn hover_record_struct_limit() {
---
- size = 12 (0xC), align = 4, no Drop
+ size = 12 (0xC), align = 4, largest padding = 0, no Drop
"#]],
);
check_hover_fields_limit(
@@ -1104,7 +1105,7 @@ fn hover_record_struct_limit() {
---
- size = 12 (0xC), align = 4, no Drop
+ size = 12 (0xC), align = 4, largest padding = 0, no Drop
"#]],
);
@@ -3114,7 +3115,7 @@ struct S$0<T>(core::marker::PhantomData<T>);
---
- size = 0, align = 1, no Drop
+ size = 0, align = 1, largest padding = 0, no Drop
"#]],
);
}
@@ -3148,6 +3149,111 @@ fn test_hover_layout_of_enum() {
}
#[test]
+fn test_hover_layout_padding_info() {
+ check(
+ r#"struct $0Foo {
+ x: bool,
+ y: i64,
+ z: u32,
+ }"#,
+ expect![[r#"
+ *Foo*
+
+ ```rust
+ ra_test_fixture
+ ```
+
+ ```rust
+ struct Foo {
+ x: bool,
+ y: i64,
+ z: u32,
+ }
+ ```
+
+ ---
+
+ size = 16 (0x10), align = 8, largest padding = 3, niches = 254, no Drop
+ "#]],
+ );
+
+ check(
+ r#"#[repr(align(32))]
+ struct $0Foo {
+ x: bool,
+ y: i64,
+ z: u32,
+ }"#,
+ expect![[r#"
+ *Foo*
+
+ ```rust
+ ra_test_fixture
+ ```
+
+ ```rust
+ struct Foo {
+ x: bool,
+ y: i64,
+ z: u32,
+ }
+ ```
+
+ ---
+
+ size = 32 (0x20), align = 32 (0x20), largest padding = 19 (0x13), niches = 254, no Drop
+ "#]],
+ );
+
+ check(
+ r#"#[repr(C)]
+ struct $0Foo {
+ x: bool,
+ y: i64,
+ z: u32,
+ }"#,
+ expect![[r#"
+ *Foo*
+
+ ```rust
+ ra_test_fixture
+ ```
+
+ ```rust
+ struct Foo {
+ x: bool,
+ y: i64,
+ z: u32,
+ }
+ ```
+
+ ---
+
+ size = 24 (0x18), align = 8, tail padding = 4, niches = 254, no Drop
+ "#]],
+ );
+
+ check(
+ r#"struct $0Foo(i16, u128, u64)"#,
+ expect![[r#"
+ *Foo*
+
+ ```rust
+ ra_test_fixture
+ ```
+
+ ```rust
+ struct Foo(i16, u128, u64)
+ ```
+
+ ---
+
+ size = 32 (0x20), align = 8, largest padding = 6, no Drop
+ "#]],
+ );
+}
+
+#[test]
fn test_hover_no_memory_layout() {
check_hover_no_memory_layout(
r#"struct Foo { fiel$0d_a: u8, field_b: i32, field_c: i16 }"#,
@@ -9198,7 +9304,7 @@ struct Pedro$0<'a> {
---
- size = 16 (0x10), align = 8, niches = 1, no Drop
+ size = 16 (0x10), align = 8, largest padding = 0, niches = 1, no Drop
"#]],
)
}
@@ -10559,7 +10665,7 @@ struct DropField$0 {
---
- size = 4, align = 4, needs Drop
+ size = 4, align = 4, largest padding = 0, needs Drop
"#]],
);
check(
diff --git a/crates/intern/src/symbol/symbols.rs b/crates/intern/src/symbol/symbols.rs
index abde48d151..fc922dd849 100644
--- a/crates/intern/src/symbol/symbols.rs
+++ b/crates/intern/src/symbol/symbols.rs
@@ -164,7 +164,6 @@ define_symbols! {
completion,
compile_error,
concat_bytes,
- concat_idents,
concat,
const_format_args,
const_panic_fmt,
diff --git a/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs b/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs
index 64b40e7b94..e0c6e68f80 100644
--- a/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs
+++ b/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs
@@ -258,7 +258,9 @@ impl server::TokenStream for RaSpanServer {
&mut self,
stream: Self::TokenStream,
) -> Vec<bridge::TokenTree<Self::TokenStream, Self::Span, Self::Symbol>> {
- stream.into_bridge()
+ stream.into_bridge(&mut |first, second| {
+ server::Span::join(self, first, second).unwrap_or(first)
+ })
}
}
diff --git a/crates/proc-macro-srv/src/server_impl/token_id.rs b/crates/proc-macro-srv/src/server_impl/token_id.rs
index 24a67bf45c..d55b269f86 100644
--- a/crates/proc-macro-srv/src/server_impl/token_id.rs
+++ b/crates/proc-macro-srv/src/server_impl/token_id.rs
@@ -238,7 +238,8 @@ impl server::TokenStream for TokenIdServer {
&mut self,
stream: Self::TokenStream,
) -> Vec<bridge::TokenTree<Self::TokenStream, Self::Span, Self::Symbol>> {
- stream.into_bridge()
+ // Can't join with `TokenId`.
+ stream.into_bridge(&mut |first, _second| first)
}
}
diff --git a/crates/proc-macro-srv/src/server_impl/token_stream.rs b/crates/proc-macro-srv/src/server_impl/token_stream.rs
index 072557913c..c5019a5917 100644
--- a/crates/proc-macro-srv/src/server_impl/token_stream.rs
+++ b/crates/proc-macro-srv/src/server_impl/token_stream.rs
@@ -56,7 +56,10 @@ impl<S: Copy> TokenStream<S> {
self.token_trees.is_empty()
}
- pub(crate) fn into_bridge(self) -> Vec<bridge::TokenTree<Self, S, intern::Symbol>> {
+ pub(crate) fn into_bridge(
+ self,
+ join_spans: &mut dyn FnMut(S, S) -> S,
+ ) -> Vec<bridge::TokenTree<Self, S, intern::Symbol>> {
let mut result = Vec::new();
let mut iter = self.token_trees.into_iter();
while let Some(tree) = iter.next() {
@@ -98,7 +101,11 @@ impl<S: Copy> TokenStream<S> {
token_trees: iter.by_ref().take(subtree.usize_len()).collect(),
})
},
- span: bridge::DelimSpan::from_single(subtree.delimiter.open),
+ span: bridge::DelimSpan {
+ open: subtree.delimiter.open,
+ close: subtree.delimiter.close,
+ entire: join_spans(subtree.delimiter.open, subtree.delimiter.close),
+ },
}))
}
}
diff --git a/crates/proc-macro-srv/src/tests/mod.rs b/crates/proc-macro-srv/src/tests/mod.rs
index 3868fee40f..3a6ce639d1 100644
--- a/crates/proc-macro-srv/src/tests/mod.rs
+++ b/crates/proc-macro-srv/src/tests/mod.rs
@@ -144,7 +144,7 @@ fn test_fn_like_macro_clone_ident_subtree() {
SUBTREE $$ 42:[email protected]#ROOT2024 42:[email protected]#ROOT2024
IDENT ident 42:[email protected]#ROOT2024
PUNCH , [alone] 42:[email protected]#ROOT2024
- SUBTREE [] 42:[email protected]#ROOT2024 42:[email protected]#ROOT2024"#]],
+ SUBTREE [] 42:[email protected]#ROOT2024 42:[email protected]#ROOT2024"#]],
);
}
diff --git a/crates/query-group-macro/tests/logger_db.rs b/crates/query-group-macro/tests/logger_db.rs
index bade0c2cd6..71af63a0d3 100644
--- a/crates/query-group-macro/tests/logger_db.rs
+++ b/crates/query-group-macro/tests/logger_db.rs
@@ -1,33 +1,41 @@
use std::sync::{Arc, Mutex};
#[salsa_macros::db]
-#[derive(Default, Clone)]
+#[derive(Clone)]
pub(crate) struct LoggerDb {
storage: salsa::Storage<Self>,
logger: Logger,
}
+impl Default for LoggerDb {
+ fn default() -> Self {
+ let logger = Logger::default();
+ Self {
+ storage: salsa::Storage::new(Some(Box::new({
+ let logger = logger.clone();
+ move |event| match event.kind {
+ salsa::EventKind::WillExecute { .. }
+ | salsa::EventKind::WillCheckCancellation
+ | salsa::EventKind::DidValidateMemoizedValue { .. }
+ | salsa::EventKind::WillDiscardStaleOutput { .. }
+ | salsa::EventKind::DidDiscard { .. } => {
+ logger.logs.lock().unwrap().push(format!("salsa_event({:?})", event.kind));
+ }
+ _ => {}
+ }
+ }))),
+ logger,
+ }
+ }
+}
+
#[derive(Default, Clone)]
struct Logger {
logs: Arc<Mutex<Vec<String>>>,
}
#[salsa_macros::db]
-impl salsa::Database for LoggerDb {
- fn salsa_event(&self, event: &dyn Fn() -> salsa::Event) {
- let event = event();
- match event.kind {
- salsa::EventKind::WillExecute { .. }
- | salsa::EventKind::WillCheckCancellation
- | salsa::EventKind::DidValidateMemoizedValue { .. }
- | salsa::EventKind::WillDiscardStaleOutput { .. }
- | salsa::EventKind::DidDiscard { .. } => {
- self.push_log(format!("salsa_event({:?})", event.kind));
- }
- _ => {}
- }
- }
-}
+impl salsa::Database for LoggerDb {}
impl LoggerDb {
/// Log an event from inside a tracked function.
diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs
index 671e838421..12b393b80c 100644
--- a/crates/rust-analyzer/src/cli/analysis_stats.rs
+++ b/crates/rust-analyzer/src/cli/analysis_stats.rs
@@ -1023,7 +1023,7 @@ impl flags::AnalysisStats {
percentage(num_pats_partially_unknown, num_pats),
num_pat_type_mismatches
);
- eprintln!(" panics: {}", panics);
+ eprintln!(" panics: {panics}");
eprintln!("{:<20} {}", "Inference:", inference_time);
report_metric("unknown type", num_exprs_unknown, "#");
report_metric("type mismatches", num_expr_type_mismatches, "#");
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 03e5b1f6f4..d1ca8c1a91 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -149,6 +149,8 @@ config_data! {
hover_memoryLayout_niches: Option<bool> = Some(false),
/// How to render the offset information in a memory layout hover.
hover_memoryLayout_offset: Option<MemoryLayoutHoverRenderKindDef> = Some(MemoryLayoutHoverRenderKindDef::Hexadecimal),
+ /// How to render the padding information in a memory layout hover.
+ hover_memoryLayout_padding: Option<MemoryLayoutHoverRenderKindDef> = None,
/// How to render the size information in a memory layout hover.
hover_memoryLayout_size: Option<MemoryLayoutHoverRenderKindDef> = Some(MemoryLayoutHoverRenderKindDef::Both),
@@ -544,7 +546,7 @@ config_data! {
/// Whether to prefer import paths containing a `prelude` module.
imports_preferPrelude: bool = false,
/// The path structure for newly inserted paths to use.
- imports_prefix: ImportPrefixDef = ImportPrefixDef::Plain,
+ imports_prefix: ImportPrefixDef = ImportPrefixDef::ByCrate,
/// Whether to prefix external (including std, core) crate imports with `::`. e.g. "use ::std::io::Read;".
imports_prefixExternPrelude: bool = false,
}
@@ -1635,6 +1637,7 @@ impl Config {
size: self.hover_memoryLayout_size().map(mem_kind),
offset: self.hover_memoryLayout_offset().map(mem_kind),
alignment: self.hover_memoryLayout_alignment().map(mem_kind),
+ padding: self.hover_memoryLayout_padding().map(mem_kind),
niches: self.hover_memoryLayout_niches().unwrap_or_default(),
}),
documentation: self.hover_documentation_enable().to_owned(),
diff --git a/crates/rust-analyzer/src/integrated_benchmarks.rs b/crates/rust-analyzer/src/integrated_benchmarks.rs
index 49ebffa909..84b7888258 100644
--- a/crates/rust-analyzer/src/integrated_benchmarks.rs
+++ b/crates/rust-analyzer/src/integrated_benchmarks.rs
@@ -147,7 +147,7 @@ fn integrated_completion_benchmark() {
let _it = stdx::timeit("change");
let mut text = host.analysis().file_text(file_id).unwrap().to_string();
let completion_offset =
- patch(&mut text, "db.struct_data(self.id)", "sel;\ndb.struct_data(self.id)")
+ patch(&mut text, "db.struct_signature(self.id)", "sel;\ndb.struct_signature(self.id)")
+ "sel".len();
let mut change = ChangeWithProcMacros::default();
change.change_file(file_id, Some(text));
@@ -197,9 +197,11 @@ fn integrated_completion_benchmark() {
let completion_offset = {
let _it = stdx::timeit("change");
let mut text = host.analysis().file_text(file_id).unwrap().to_string();
- let completion_offset =
- patch(&mut text, "sel;\ndb.struct_data(self.id)", ";sel;\ndb.struct_data(self.id)")
- + ";sel".len();
+ let completion_offset = patch(
+ &mut text,
+ "sel;\ndb.struct_signature(self.id)",
+ ";sel;\ndb.struct_signature(self.id)",
+ ) + ";sel".len();
let mut change = ChangeWithProcMacros::default();
change.change_file(file_id, Some(text));
host.apply_change(change);
@@ -247,9 +249,11 @@ fn integrated_completion_benchmark() {
let completion_offset = {
let _it = stdx::timeit("change");
let mut text = host.analysis().file_text(file_id).unwrap().to_string();
- let completion_offset =
- patch(&mut text, "sel;\ndb.struct_data(self.id)", "self.;\ndb.struct_data(self.id)")
- + "self.".len();
+ let completion_offset = patch(
+ &mut text,
+ "sel;\ndb.struct_signature(self.id)",
+ "self.;\ndb.struct_signature(self.id)",
+ ) + "self.".len();
let mut change = ChangeWithProcMacros::default();
change.change_file(file_id, Some(text));
host.apply_change(change);
@@ -366,7 +370,7 @@ fn integrated_diagnostics_benchmark() {
{
let _it = stdx::timeit("change");
let mut text = host.analysis().file_text(file_id).unwrap().to_string();
- patch(&mut text, "db.struct_data(self.id)", "();\ndb.struct_data(self.id)");
+ patch(&mut text, "db.struct_signature(self.id)", "();\ndb.struct_signature(self.id)");
let mut change = ChangeWithProcMacros::default();
change.change_file(file_id, Some(text));
host.apply_change(change);
diff --git a/crates/syntax/src/ast/syntax_factory/constructors.rs b/crates/syntax/src/ast/syntax_factory/constructors.rs
index 8dee3964d4..429e51ba36 100644
--- a/crates/syntax/src/ast/syntax_factory/constructors.rs
+++ b/crates/syntax/src/ast/syntax_factory/constructors.rs
@@ -585,6 +585,18 @@ impl SyntaxFactory {
ast
}
+ pub fn expr_underscore(&self) -> ast::UnderscoreExpr {
+ let ast::Expr::UnderscoreExpr(ast) = make::ext::expr_underscore().clone_for_update() else {
+ unreachable!()
+ };
+
+ if let Some(mut mapping) = self.mappings() {
+ SyntaxMappingBuilder::new(ast.syntax().clone()).finish(&mut mapping);
+ }
+
+ ast
+ }
+
pub fn expr_if(
&self,
condition: ast::Expr,
diff --git a/docs/book/src/configuration_generated.md b/docs/book/src/configuration_generated.md
index 2ae73df61d..0e07dadfb7 100644
--- a/docs/book/src/configuration_generated.md
+++ b/docs/book/src/configuration_generated.md
@@ -763,6 +763,13 @@ Default: `"hexadecimal"`
How to render the offset information in a memory layout hover.
+## rust-analyzer.hover.memoryLayout.padding {#hover.memoryLayout.padding}
+
+Default: `null`
+
+How to render the padding information in a memory layout hover.
+
+
## rust-analyzer.hover.memoryLayout.size {#hover.memoryLayout.size}
Default: `"both"`
@@ -835,7 +842,7 @@ Whether to prefer import paths containing a `prelude` module.
## rust-analyzer.imports.prefix {#imports.prefix}
-Default: `"plain"`
+Default: `"crate"`
The path structure for newly inserted paths to use.
diff --git a/docs/book/src/other_editors.md b/docs/book/src/other_editors.md
index 1eac7dd2c2..896df52af5 100644
--- a/docs/book/src/other_editors.md
+++ b/docs/book/src/other_editors.md
@@ -364,30 +364,6 @@ binary](./rust_analyzer_binary.html).
There are multiple rust-analyzer extensions for Visual Studio 2022 on
Windows:
-### rust-analyzer.vs
-
-(License: Creative Commons Attribution-NonCommercial-ShareAlike 4.0
-International)
-
-[Visual Studio
-Marketplace](https://marketplace.visualstudio.com/items?itemName=kitamstudios.RustAnalyzer)
-
-[GitHub](https://github.com/kitamstudios/rust-analyzer/)
-
-Support for Rust development in the Visual Studio IDE is enabled by the
-[rust-analyzer](https://marketplace.visualstudio.com/items?itemName=kitamstudios.RustAnalyzer)
-package. Either click on the download link or install from IDE’s
-extension manager. For now [Visual Studio
-2022](https://visualstudio.microsoft.com/downloads/) is required. All
-editions are supported viz. Community, Professional & Enterprise. The
-package aims to provide 0-friction installation and therefore comes
-loaded with most things required including rust-analyzer binary. If
-anything it needs is missing, appropriate errors / warnings will guide
-the user. E.g. cargo.exe needs to be in path and the package will tell
-you as much. This package is under rapid active development. So if you
-encounter any issues please file it at
-[rust-analyzer.vs](https://github.com/kitamstudios/rust-analyzer/).
-
### VS RustAnalyzer
(License: GPL)
diff --git a/editors/code/package.json b/editors/code/package.json
index a282eea999..c8c36cd85c 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -1782,6 +1782,33 @@
{
"title": "hover",
"properties": {
+ "rust-analyzer.hover.memoryLayout.padding": {
+ "markdownDescription": "How to render the padding information in a memory layout hover.",
+ "default": null,
+ "anyOf": [
+ {
+ "type": "null"
+ },
+ {
+ "type": "string",
+ "enum": [
+ "both",
+ "decimal",
+ "hexadecimal"
+ ],
+ "enumDescriptions": [
+ "Render as 12 (0xC)",
+ "Render as 12",
+ "Render as 0xC"
+ ]
+ }
+ ]
+ }
+ }
+ },
+ {
+ "title": "hover",
+ "properties": {
"rust-analyzer.hover.memoryLayout.size": {
"markdownDescription": "How to render the size information in a memory layout hover.",
"default": "both",
@@ -1927,7 +1954,7 @@
"properties": {
"rust-analyzer.imports.prefix": {
"markdownDescription": "The path structure for newly inserted paths to use.",
- "default": "plain",
+ "default": "crate",
"type": "string",
"enum": [
"plain",