Unnamed repository; edit this file 'description' to name the repository.
Merge pull request #2374 from rust-lang/rustc-pull
Rustc pull update
Tshepang Mbambo 12 months ago
parent 8926344 · parent f2e4fec · commit 275a5d6
-rw-r--r--Cargo.lock18
-rw-r--r--Cargo.toml4
-rw-r--r--crates/base-db/Cargo.toml1
-rw-r--r--crates/base-db/src/input.rs2
-rw-r--r--crates/base-db/src/lib.rs94
-rw-r--r--crates/hir-def/Cargo.toml1
-rw-r--r--crates/hir-def/src/attr.rs55
-rw-r--r--crates/hir-def/src/db.rs8
-rw-r--r--crates/hir-def/src/expr_store/expander.rs49
-rw-r--r--crates/hir-def/src/expr_store/lower.rs93
-rw-r--r--crates/hir-def/src/expr_store/lower/asm.rs2
-rw-r--r--crates/hir-def/src/expr_store/lower/generics.rs4
-rw-r--r--crates/hir-def/src/hir/format_args.rs4
-rw-r--r--crates/hir-def/src/item_scope.rs19
-rw-r--r--crates/hir-def/src/item_tree.rs4
-rw-r--r--crates/hir-def/src/lang_item.rs187
-rw-r--r--crates/hir-def/src/lib.rs113
-rw-r--r--crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs2
-rw-r--r--crates/hir-def/src/macro_expansion_tests/mbe.rs10
-rw-r--r--crates/hir-def/src/macro_expansion_tests/mod.rs85
-rw-r--r--crates/hir-def/src/nameres/assoc.rs18
-rw-r--r--crates/hir-def/src/nameres/collector.rs42
-rw-r--r--crates/hir-def/src/nameres/mod_resolution.rs2
-rw-r--r--crates/hir-def/src/nameres/path_resolution.rs5
-rw-r--r--crates/hir-def/src/per_ns.rs12
-rw-r--r--crates/hir-def/src/resolver.rs2
-rw-r--r--crates/hir-def/src/test_db.rs6
-rw-r--r--crates/hir-expand/Cargo.toml1
-rw-r--r--crates/hir-expand/src/attrs.rs158
-rw-r--r--crates/hir-expand/src/change.rs3
-rw-r--r--crates/hir-expand/src/db.rs10
-rw-r--r--crates/hir-expand/src/declarative.rs2
-rw-r--r--crates/hir-expand/src/hygiene.rs60
-rw-r--r--crates/hir-expand/src/lib.rs7
-rw-r--r--crates/hir-expand/src/mod_path.rs4
-rw-r--r--crates/hir-expand/src/name.rs23
-rw-r--r--crates/hir-ty/Cargo.toml1
-rw-r--r--crates/hir-ty/src/autoderef.rs6
-rw-r--r--crates/hir-ty/src/chalk_db.rs29
-rw-r--r--crates/hir-ty/src/chalk_ext.rs7
-rw-r--r--crates/hir-ty/src/db.rs5
-rw-r--r--crates/hir-ty/src/diagnostics/expr.rs5
-rw-r--r--crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs3
-rw-r--r--crates/hir-ty/src/display.rs83
-rw-r--r--crates/hir-ty/src/drop.rs7
-rw-r--r--crates/hir-ty/src/dyn_compatibility.rs8
-rw-r--r--crates/hir-ty/src/infer.rs4
-rw-r--r--crates/hir-ty/src/infer/closure.rs2
-rw-r--r--crates/hir-ty/src/infer/coerce.rs9
-rw-r--r--crates/hir-ty/src/infer/expr.rs4
-rw-r--r--crates/hir-ty/src/infer/mutability.rs12
-rw-r--r--crates/hir-ty/src/infer/pat.rs4
-rw-r--r--crates/hir-ty/src/infer/unify.rs8
-rw-r--r--crates/hir-ty/src/lower.rs19
-rw-r--r--crates/hir-ty/src/method_resolution.rs72
-rw-r--r--crates/hir-ty/src/mir.rs23
-rw-r--r--crates/hir-ty/src/mir/borrowck.rs22
-rw-r--r--crates/hir-ty/src/mir/eval.rs43
-rw-r--r--crates/hir-ty/src/mir/eval/shim.rs21
-rw-r--r--crates/hir-ty/src/mir/lower.rs75
-rw-r--r--crates/hir-ty/src/mir/lower/as_place.rs8
-rw-r--r--crates/hir-ty/src/mir/lower/pattern_matching.rs47
-rw-r--r--crates/hir-ty/src/mir/monomorphization.rs8
-rw-r--r--crates/hir-ty/src/mir/pretty.rs12
-rw-r--r--crates/hir-ty/src/test_db.rs6
-rw-r--r--crates/hir-ty/src/tests/coercion.rs6
-rw-r--r--crates/hir-ty/src/tests/display_source_code.rs18
-rw-r--r--crates/hir-ty/src/tests/method_resolution.rs31
-rw-r--r--crates/hir-ty/src/tests/regression.rs27
-rw-r--r--crates/hir-ty/src/tests/simple.rs10
-rw-r--r--crates/hir-ty/src/tests/traits.rs78
-rw-r--r--crates/hir-ty/src/traits.rs11
-rw-r--r--crates/hir-ty/src/utils.rs3
-rw-r--r--crates/hir-ty/src/variance.rs19
-rw-r--r--crates/hir/src/display.rs3
-rw-r--r--crates/hir/src/lib.rs81
-rw-r--r--crates/hir/src/semantics.rs21
-rw-r--r--crates/hir/src/semantics/source_to_def.rs2
-rw-r--r--crates/hir/src/source_analyzer.rs10
-rw-r--r--crates/hir/src/symbols.rs22
-rw-r--r--crates/ide-assists/src/assist_config.rs3
-rw-r--r--crates/ide-assists/src/handlers/add_missing_impl_members.rs1
-rw-r--r--crates/ide-assists/src/handlers/add_missing_match_arms.rs27
-rw-r--r--crates/ide-assists/src/handlers/convert_bool_then.rs2
-rw-r--r--crates/ide-assists/src/handlers/convert_closure_to_fn.rs6
-rw-r--r--crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs2
-rw-r--r--crates/ide-assists/src/handlers/convert_let_else_to_match.rs299
-rw-r--r--crates/ide-assists/src/handlers/destructure_struct_binding.rs4
-rw-r--r--crates/ide-assists/src/handlers/destructure_tuple_binding.rs2
-rw-r--r--crates/ide-assists/src/handlers/expand_rest_pattern.rs7
-rw-r--r--crates/ide-assists/src/handlers/extract_function.rs2
-rw-r--r--crates/ide-assists/src/handlers/extract_variable.rs28
-rw-r--r--crates/ide-assists/src/handlers/generate_function.rs33
-rw-r--r--crates/ide-assists/src/handlers/merge_imports.rs68
-rw-r--r--crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs15
-rw-r--r--crates/ide-assists/src/handlers/unmerge_imports.rs (renamed from crates/ide-assists/src/handlers/unmerge_use.rs)111
-rw-r--r--crates/ide-assists/src/handlers/unmerge_match_arm.rs10
-rw-r--r--crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs6
-rw-r--r--crates/ide-assists/src/lib.rs4
-rw-r--r--crates/ide-assists/src/tests.rs5
-rw-r--r--crates/ide-assists/src/tests/generated.rs30
-rw-r--r--crates/ide-assists/src/utils.rs16
-rw-r--r--crates/ide-completion/src/completions/expr.rs11
-rw-r--r--crates/ide-completion/src/completions/keyword.rs96
-rw-r--r--crates/ide-completion/src/completions/lifetime.rs4
-rw-r--r--crates/ide-completion/src/context/analysis.rs12
-rw-r--r--crates/ide-completion/src/item.rs2
-rw-r--r--crates/ide-completion/src/tests/expression.rs18
-rw-r--r--crates/ide-completion/src/tests/special.rs4
-rw-r--r--crates/ide-completion/src/tests/type_pos.rs18
-rw-r--r--crates/ide-db/Cargo.toml1
-rw-r--r--crates/ide-db/src/assists.rs12
-rw-r--r--crates/ide-db/src/items_locator.rs4
-rw-r--r--crates/ide-db/src/lib.rs16
-rw-r--r--crates/ide-db/src/prime_caches.rs61
-rw-r--r--crates/ide-diagnostics/src/handlers/expected_function.rs2
-rw-r--r--crates/ide-diagnostics/src/handlers/invalid_cast.rs6
-rw-r--r--crates/ide-diagnostics/src/handlers/missing_fields.rs26
-rw-r--r--crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs19
-rw-r--r--crates/ide-diagnostics/src/handlers/type_mismatch.rs9
-rw-r--r--crates/ide-diagnostics/src/handlers/typed_hole.rs42
-rw-r--r--crates/ide-diagnostics/src/handlers/unresolved_method.rs2
-rw-r--r--crates/ide-diagnostics/src/lib.rs13
-rw-r--r--crates/ide-diagnostics/src/tests.rs55
-rw-r--r--crates/ide/src/doc_links.rs2
-rw-r--r--crates/ide/src/hover.rs4
-rw-r--r--crates/ide/src/hover/tests.rs42
-rw-r--r--crates/ide/src/inlay_hints/bind_pat.rs20
-rw-r--r--crates/ide/src/inlay_hints/closing_brace.rs2
-rw-r--r--crates/ide/src/lib.rs9
-rw-r--r--crates/ide/src/moniker.rs2
-rw-r--r--crates/ide/src/status.rs4
-rw-r--r--crates/ide/src/view_crate_graph.rs2
-rw-r--r--crates/intern/src/symbol.rs34
-rw-r--r--crates/load-cargo/src/lib.rs2
-rw-r--r--crates/parser/src/grammar/expressions.rs2
-rw-r--r--crates/parser/src/grammar/expressions/atom.rs4
-rw-r--r--crates/parser/src/grammar/items.rs3
-rw-r--r--crates/parser/src/grammar/paths.rs2
-rw-r--r--crates/parser/src/grammar/patterns.rs15
-rw-r--r--crates/parser/src/grammar/types.rs5
-rw-r--r--crates/parser/src/lexed_str.rs9
-rw-r--r--crates/parser/src/syntax_kind/generated.rs4
-rw-r--r--crates/parser/test_data/generated/runner.rs4
-rw-r--r--crates/parser/test_data/parser/err/0022_bad_exprs.rast7
-rw-r--r--crates/parser/test_data/parser/inline/err/comma_after_default_values_syntax.rast9
-rw-r--r--crates/parser/test_data/parser/inline/err/struct_field_recover.rast30
-rw-r--r--crates/parser/test_data/parser/inline/err/struct_field_recover.rs1
-rw-r--r--crates/parser/test_data/parser/inline/err/type_in_array_recover.rast15
-rw-r--r--crates/parser/test_data/parser/inline/err/type_in_array_recover.rs1
-rw-r--r--crates/proc-macro-srv/build.rs2
-rw-r--r--crates/project-model/src/env.rs2
-rw-r--r--crates/project-model/src/workspace.rs2
-rw-r--r--crates/query-group-macro/Cargo.toml1
-rw-r--r--crates/query-group-macro/src/lib.rs10
-rw-r--r--crates/query-group-macro/src/queries.rs2
-rw-r--r--crates/query-group-macro/tests/interned.rs2
-rw-r--r--crates/query-group-macro/tests/logger_db.rs6
-rw-r--r--crates/query-group-macro/tests/old_and_new.rs8
-rw-r--r--crates/query-group-macro/tests/supertrait.rs2
-rw-r--r--crates/rust-analyzer/src/bin/main.rs6
-rw-r--r--crates/rust-analyzer/src/cli/analysis_stats.rs13
-rw-r--r--crates/rust-analyzer/src/cli/diagnostics.rs12
-rw-r--r--crates/rust-analyzer/src/cli/rustc_tests.rs3
-rw-r--r--crates/rust-analyzer/src/cli/scip.rs6
-rw-r--r--crates/rust-analyzer/src/cli/unresolved_references.rs14
-rw-r--r--crates/rust-analyzer/src/command.rs8
-rw-r--r--crates/rust-analyzer/src/config.rs18
-rw-r--r--crates/rust-analyzer/src/diagnostics.rs4
-rw-r--r--crates/rust-analyzer/src/discover.rs6
-rw-r--r--crates/rust-analyzer/src/flycheck.rs8
-rw-r--r--crates/rust-analyzer/src/global_state.rs2
-rw-r--r--crates/rust-analyzer/src/handlers/dispatch.rs18
-rw-r--r--crates/rust-analyzer/src/handlers/notification.rs2
-rw-r--r--crates/rust-analyzer/src/handlers/request.rs2
-rw-r--r--crates/rust-analyzer/tests/slow-tests/main.rs4
-rw-r--r--crates/rust-analyzer/tests/slow-tests/ratoml.rs7
-rw-r--r--crates/rust-analyzer/tests/slow-tests/support.rs5
-rw-r--r--crates/span/src/hygiene.rs65
-rw-r--r--crates/stdx/src/thread.rs15
-rw-r--r--crates/stdx/src/thread/pool.rs5
-rw-r--r--crates/syntax/Cargo.toml2
-rw-r--r--crates/syntax/rust.ungram1
-rw-r--r--crates/syntax/src/ast/generated/nodes.rs4
-rw-r--r--crates/syntax/src/ast/make.rs35
-rw-r--r--crates/syntax/src/ast/syntax_factory/constructors.rs124
-rw-r--r--crates/syntax/src/syntax_editor.rs1
-rw-r--r--crates/syntax/src/syntax_editor/edit_algo.rs2
-rw-r--r--crates/syntax/src/syntax_editor/edits.rs50
-rw-r--r--crates/test-fixture/src/lib.rs2
-rw-r--r--crates/tt/src/lib.rs6
-rw-r--r--crates/vfs-notify/src/lib.rs3
-rw-r--r--rust-version2
-rw-r--r--xtask/src/codegen/grammar.rs3
-rw-r--r--xtask/src/codegen/parser_inline_tests.rs4
-rw-r--r--xtask/src/dist.rs109
-rw-r--r--xtask/src/flags.rs52
-rw-r--r--xtask/src/install.rs28
-rw-r--r--xtask/src/main.rs1
-rw-r--r--xtask/src/pgo.rs105
-rw-r--r--xtask/src/util.rs12
201 files changed, 2451 insertions, 1708 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 9f6b80c637..8d6c8284e4 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -85,6 +85,7 @@ dependencies = [
"query-group-macro",
"rustc-hash 2.1.1",
"salsa",
+ "salsa-macros",
"semver",
"span",
"syntax",
@@ -630,6 +631,7 @@ dependencies = [
"rustc-hash 2.1.1",
"rustc_apfloat",
"salsa",
+ "salsa-macros",
"smallvec",
"span",
"stdx",
@@ -660,6 +662,7 @@ dependencies = [
"query-group-macro",
"rustc-hash 2.1.1",
"salsa",
+ "salsa-macros",
"smallvec",
"span",
"stdx",
@@ -700,6 +703,7 @@ dependencies = [
"rustc-hash 2.1.1",
"rustc_apfloat",
"salsa",
+ "salsa-macros",
"scoped-tls",
"smallvec",
"span",
@@ -936,6 +940,7 @@ dependencies = [
"rayon",
"rustc-hash 2.1.1",
"salsa",
+ "salsa-macros",
"span",
"stdx",
"syntax",
@@ -1729,6 +1734,7 @@ dependencies = [
"proc-macro2",
"quote",
"salsa",
+ "salsa-macros",
"syn",
]
@@ -2033,9 +2039,9 @@ checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
[[package]]
name = "salsa"
-version = "0.20.0"
+version = "0.21.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1be22155f8d9732518b2db2bf379fe6f0b2375e76b08b7c8fe6c1b887d548c24"
+checksum = "6f80d5cf3c3fcab2cef898012f242a670477a1baa609267376af9cb4409026c5"
dependencies = [
"boxcar",
"crossbeam-queue",
@@ -2056,15 +2062,15 @@ dependencies = [
[[package]]
name = "salsa-macro-rules"
-version = "0.20.0"
+version = "0.21.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f55a7ef0a84e336f7c5f0332d81727f5629fe042d2aa556c75307afebc9f78a5"
+checksum = "05303d72606fbf2b9c9523cda2039bb8ecb00304027a3cd7e52b02a65c7d9185"
[[package]]
name = "salsa-macros"
-version = "0.20.0"
+version = "0.21.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8d0e88a9c0c0d231a63f83dcd1a2c5e5d11044fac4b65bc9ad3b68ab48b0a0ab"
+checksum = "eb2f0e2a30c65cb3cd63440c491dde68d9af7e1be2b77832ac7057141107db50"
dependencies = [
"heck",
"proc-macro2",
diff --git a/Cargo.toml b/Cargo.toml
index 6fa171702d..c4c2fdf34b 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -131,7 +131,9 @@ process-wrap = { version = "8.2.0", features = ["std"] }
pulldown-cmark-to-cmark = "10.0.4"
pulldown-cmark = { version = "0.9.6", default-features = false }
rayon = "1.10.0"
-salsa = "0.20.0"
+rowan = "=0.15.15"
+salsa = { version = "0.21.1", default-features = false, features = ["rayon","salsa_unstable"] }
+salsa-macros = "0.21.1"
semver = "1.0.26"
serde = { version = "1.0.219" }
serde_derive = { version = "1.0.219" }
diff --git a/crates/base-db/Cargo.toml b/crates/base-db/Cargo.toml
index 441434504c..e2e3253773 100644
--- a/crates/base-db/Cargo.toml
+++ b/crates/base-db/Cargo.toml
@@ -15,6 +15,7 @@ rust-version.workspace = true
la-arena.workspace = true
dashmap.workspace = true
salsa.workspace = true
+salsa-macros.workspace = true
query-group.workspace = true
rustc-hash.workspace = true
triomphe.workspace = true
diff --git a/crates/base-db/src/input.rs b/crates/base-db/src/input.rs
index 499c9b3716..9660e6e87c 100644
--- a/crates/base-db/src/input.rs
+++ b/crates/base-db/src/input.rs
@@ -392,7 +392,7 @@ impl BuiltDependency {
pub type CratesIdMap = FxHashMap<CrateBuilderId, Crate>;
-#[salsa::input]
+#[salsa_macros::input]
#[derive(Debug)]
pub struct Crate {
#[return_ref]
diff --git a/crates/base-db/src/lib.rs b/crates/base-db/src/lib.rs
index 7f7a712577..a67fbf75c0 100644
--- a/crates/base-db/src/lib.rs
+++ b/crates/base-db/src/lib.rs
@@ -1,9 +1,13 @@
//! base_db defines basic database traits. The concrete DB is defined by ide.
+
+pub use salsa;
+pub use salsa_macros;
+
// FIXME: Rename this crate, base db is non descriptive
mod change;
mod input;
-use std::hash::BuildHasherDefault;
+use std::{cell::RefCell, hash::BuildHasherDefault, panic, sync::Once};
pub use crate::{
change::FileChange,
@@ -17,7 +21,6 @@ pub use crate::{
use dashmap::{DashMap, mapref::entry::Entry};
pub use query_group::{self};
use rustc_hash::{FxHashSet, FxHasher};
-pub use salsa::{self};
use salsa::{Durability, Setter};
pub use semver::{BuildMetadata, Prerelease, Version, VersionReq};
use span::Edition;
@@ -28,7 +31,7 @@ pub use vfs::{AnchoredPath, AnchoredPathBuf, FileId, VfsPath, file_set::FileSet}
#[macro_export]
macro_rules! impl_intern_key {
($id:ident, $loc:ident) => {
- #[salsa::interned(no_lifetime)]
+ #[salsa_macros::interned(no_lifetime)]
pub struct $id {
pub loc: $loc,
}
@@ -57,7 +60,12 @@ pub struct Files {
impl Files {
pub fn file_text(&self, file_id: vfs::FileId) -> FileText {
- *self.files.get(&file_id).expect("Unable to fetch file; this is a bug")
+ match self.files.get(&file_id) {
+ Some(text) => *text,
+ None => {
+ panic!("Unable to fetch file text for `vfs::FileId`: {file_id:?}; this is a bug")
+ }
+ }
}
pub fn set_file_text(&self, db: &mut dyn SourceDatabase, file_id: vfs::FileId, text: &str) {
@@ -93,10 +101,12 @@ impl Files {
/// Source root of the file.
pub fn source_root(&self, source_root_id: SourceRootId) -> SourceRootInput {
- let source_root = self
- .source_roots
- .get(&source_root_id)
- .expect("Unable to fetch source root id; this is a bug");
+ let source_root = match self.source_roots.get(&source_root_id) {
+ Some(source_root) => source_root,
+ None => panic!(
+ "Unable to fetch `SourceRootInput` with `SourceRootId` ({source_root_id:?}); this is a bug"
+ ),
+ };
*source_root
}
@@ -121,10 +131,12 @@ impl Files {
}
pub fn file_source_root(&self, id: vfs::FileId) -> FileSourceRootInput {
- let file_source_root = self
- .file_source_roots
- .get(&id)
- .expect("Unable to fetch FileSourceRootInput; this is a bug");
+ let file_source_root = match self.file_source_roots.get(&id) {
+ Some(file_source_root) => file_source_root,
+ None => panic!(
+ "Unable to get `FileSourceRootInput` with `vfs::FileId` ({id:?}); this is a bug",
+ ),
+ };
*file_source_root
}
@@ -152,7 +164,7 @@ impl Files {
}
}
-#[salsa::interned(no_lifetime, debug, constructor=from_span)]
+#[salsa_macros::interned(no_lifetime, debug, constructor=from_span)]
pub struct EditionedFileId {
pub editioned_file_id: span::EditionedFileId,
}
@@ -187,18 +199,18 @@ impl EditionedFileId {
}
}
-#[salsa::input(debug)]
+#[salsa_macros::input(debug)]
pub struct FileText {
pub text: Arc<str>,
pub file_id: vfs::FileId,
}
-#[salsa::input(debug)]
+#[salsa_macros::input(debug)]
pub struct FileSourceRootInput {
pub source_root_id: SourceRootId,
}
-#[salsa::input(debug)]
+#[salsa_macros::input(debug)]
pub struct SourceRootInput {
pub source_root: Arc<SourceRoot>,
}
@@ -265,7 +277,7 @@ pub fn transitive_deps(db: &dyn SourceDatabase, crate_id: Crate) -> FxHashSet<Cr
deps
}
-#[salsa::db]
+#[salsa_macros::db]
pub trait SourceDatabase: salsa::Database {
/// Text of the file.
fn file_text(&self, file_id: vfs::FileId) -> FileText;
@@ -344,7 +356,7 @@ fn parse(db: &dyn RootQueryDb, file_id: EditionedFileId) -> Parse<ast::SourceFil
}
fn parse_errors(db: &dyn RootQueryDb, file_id: EditionedFileId) -> Option<&[SyntaxError]> {
- #[salsa::tracked(return_ref)]
+ #[salsa_macros::tracked(return_ref)]
fn parse_errors(db: &dyn RootQueryDb, file_id: EditionedFileId) -> Option<Box<[SyntaxError]>> {
let errors = db.parse(file_id).errors();
match &*errors {
@@ -373,3 +385,49 @@ fn relevant_crates(db: &dyn RootQueryDb, file_id: FileId) -> Arc<[Crate]> {
let source_root = db.file_source_root(file_id);
db.source_root_crates(source_root.source_root_id(db))
}
+
+#[must_use]
+#[non_exhaustive]
+pub struct DbPanicContext;
+
+impl Drop for DbPanicContext {
+ fn drop(&mut self) {
+ Self::with_ctx(|ctx| assert!(ctx.pop().is_some()));
+ }
+}
+
+impl DbPanicContext {
+ pub fn enter(frame: String) -> DbPanicContext {
+ #[expect(clippy::print_stderr, reason = "already panicking anyway")]
+ fn set_hook() {
+ let default_hook = panic::take_hook();
+ panic::set_hook(Box::new(move |panic_info| {
+ default_hook(panic_info);
+ if let Some(backtrace) = salsa::Backtrace::capture() {
+ eprintln!("{backtrace:#}");
+ }
+ DbPanicContext::with_ctx(|ctx| {
+ if !ctx.is_empty() {
+ eprintln!("additional context:");
+ for (idx, frame) in ctx.iter().enumerate() {
+ eprintln!("{idx:>4}: {frame}\n");
+ }
+ }
+ });
+ }));
+ }
+
+ static SET_HOOK: Once = Once::new();
+ SET_HOOK.call_once(set_hook);
+
+ Self::with_ctx(|ctx| ctx.push(frame));
+ DbPanicContext
+ }
+
+ fn with_ctx(f: impl FnOnce(&mut Vec<String>)) {
+ thread_local! {
+ static CTX: RefCell<Vec<String>> = const { RefCell::new(Vec::new()) };
+ }
+ CTX.with(|ctx| f(&mut ctx.borrow_mut()));
+ }
+}
diff --git a/crates/hir-def/Cargo.toml b/crates/hir-def/Cargo.toml
index f97597ffe5..c1c89e8d1c 100644
--- a/crates/hir-def/Cargo.toml
+++ b/crates/hir-def/Cargo.toml
@@ -28,6 +28,7 @@ triomphe.workspace = true
rustc_apfloat = "0.2.2"
text-size.workspace = true
salsa.workspace = true
+salsa-macros.workspace = true
query-group.workspace = true
ra-ap-rustc_parse_format.workspace = true
diff --git a/crates/hir-def/src/attr.rs b/crates/hir-def/src/attr.rs
index a80313aba3..bb6222b1d4 100644
--- a/crates/hir-def/src/attr.rs
+++ b/crates/hir-def/src/attr.rs
@@ -1,6 +1,6 @@
//! A higher level attributes based on TokenTree, with also some shortcuts.
-use std::{borrow::Cow, hash::Hash, ops};
+use std::{borrow::Cow, convert::identity, hash::Hash, ops};
use base_db::Crate;
use cfg::{CfgExpr, CfgOptions};
@@ -8,6 +8,7 @@ use either::Either;
use hir_expand::{
HirFileId, InFile,
attrs::{Attr, AttrId, RawAttrs, collect_attrs},
+ span_map::SpanMapRef,
};
use intern::{Symbol, sym};
use la_arena::{ArenaMap, Idx, RawIdx};
@@ -45,8 +46,27 @@ impl Attrs {
(**self).iter().find(|attr| attr.id == id)
}
- pub(crate) fn filter(db: &dyn DefDatabase, krate: Crate, raw_attrs: RawAttrs) -> Attrs {
- Attrs(raw_attrs.filter(db, krate))
+ pub(crate) fn expand_cfg_attr(
+ db: &dyn DefDatabase,
+ krate: Crate,
+ raw_attrs: RawAttrs,
+ ) -> Attrs {
+ Attrs(raw_attrs.expand_cfg_attr(db, krate))
+ }
+
+ pub(crate) fn is_cfg_enabled_for(
+ db: &dyn DefDatabase,
+ owner: &dyn ast::HasAttrs,
+ span_map: SpanMapRef<'_>,
+ cfg_options: &CfgOptions,
+ ) -> Result<(), CfgExpr> {
+ RawAttrs::attrs_iter_expanded::<false>(db, owner, span_map, cfg_options)
+ .filter_map(|attr| attr.cfg())
+ .find_map(|cfg| match cfg_options.check(&cfg).is_none_or(identity) {
+ true => None,
+ false => Some(cfg),
+ })
+ .map_or(Ok(()), Err)
}
}
@@ -522,38 +542,41 @@ impl AttrsWithOwner {
GenericParamId::ConstParamId(it) => {
let src = it.parent().child_source(db);
// FIXME: We should be never getting `None` here.
- match src.value.get(it.local_id()) {
- Some(val) => RawAttrs::from_attrs_owner(
+ return Attrs(match src.value.get(it.local_id()) {
+ Some(val) => RawAttrs::new_expanded(
db,
- src.with_value(val),
+ val,
db.span_map(src.file_id).as_ref(),
+ def.krate(db).cfg_options(db),
),
None => RawAttrs::EMPTY,
- }
+ });
}
GenericParamId::TypeParamId(it) => {
let src = it.parent().child_source(db);
// FIXME: We should be never getting `None` here.
- match src.value.get(it.local_id()) {
- Some(val) => RawAttrs::from_attrs_owner(
+ return Attrs(match src.value.get(it.local_id()) {
+ Some(val) => RawAttrs::new_expanded(
db,
- src.with_value(val),
+ val,
db.span_map(src.file_id).as_ref(),
+ def.krate(db).cfg_options(db),
),
None => RawAttrs::EMPTY,
- }
+ });
}
GenericParamId::LifetimeParamId(it) => {
let src = it.parent.child_source(db);
// FIXME: We should be never getting `None` here.
- match src.value.get(it.local_id) {
- Some(val) => RawAttrs::from_attrs_owner(
+ return Attrs(match src.value.get(it.local_id) {
+ Some(val) => RawAttrs::new_expanded(
db,
- src.with_value(val),
+ val,
db.span_map(src.file_id).as_ref(),
+ def.krate(db).cfg_options(db),
),
None => RawAttrs::EMPTY,
- }
+ });
}
},
AttrDefId::ExternBlockId(it) => attrs_from_item_tree_loc(db, it),
@@ -561,7 +584,7 @@ impl AttrsWithOwner {
AttrDefId::UseId(it) => attrs_from_item_tree_loc(db, it),
};
- let attrs = raw_attrs.filter(db, def.krate(db));
+ let attrs = raw_attrs.expand_cfg_attr(db, def.krate(db));
Attrs(attrs)
}
diff --git a/crates/hir-def/src/db.rs b/crates/hir-def/src/db.rs
index 34cf42d02b..2cbdbe16f9 100644
--- a/crates/hir-def/src/db.rs
+++ b/crates/hir-def/src/db.rs
@@ -22,7 +22,7 @@ use crate::{
hir::generics::GenericParams,
import_map::ImportMap,
item_tree::{AttrOwner, ItemTree},
- lang_item::{self, LangItem, LangItemTarget, LangItems},
+ lang_item::{self, LangItem},
nameres::{
DefMap, LocalDefMap,
assoc::{ImplItems, TraitItems},
@@ -325,9 +325,6 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + SourceDatabase {
// endregion:attrs
- #[salsa::invoke(LangItems::lang_item_query)]
- fn lang_item(&self, start_crate: Crate, item: LangItem) -> Option<LangItemTarget>;
-
#[salsa::invoke(ImportMap::import_map_query)]
fn import_map(&self, krate: Crate) -> Arc<ImportMap>;
@@ -349,9 +346,6 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + SourceDatabase {
// endregion:visibilities
- #[salsa::invoke(LangItems::crate_lang_items_query)]
- fn crate_lang_items(&self, krate: Crate) -> Option<Arc<LangItems>>;
-
#[salsa::invoke(crate::lang_item::notable_traits_in_deps)]
fn notable_traits_in_deps(&self, krate: Crate) -> Arc<[Arc<[TraitId]>]>;
#[salsa::invoke(crate::lang_item::crate_notable_traits)]
diff --git a/crates/hir-def/src/expr_store/expander.rs b/crates/hir-def/src/expr_store/expander.rs
index 7eec913dd6..3823fb5a1e 100644
--- a/crates/hir-def/src/expr_store/expander.rs
+++ b/crates/hir-def/src/expr_store/expander.rs
@@ -3,21 +3,24 @@
use std::mem;
use base_db::Crate;
+use cfg::CfgOptions;
use drop_bomb::DropBomb;
+use hir_expand::AstId;
use hir_expand::{
ExpandError, ExpandErrorKind, ExpandResult, HirFileId, InFile, Lookup, MacroCallId,
- attrs::RawAttrs, eager::EagerCallBackFn, mod_path::ModPath, span_map::SpanMap,
+ eager::EagerCallBackFn, mod_path::ModPath, span_map::SpanMap,
};
use span::{AstIdMap, Edition, SyntaxContext};
use syntax::ast::HasAttrs;
-use syntax::{Parse, ast};
+use syntax::{AstNode, Parse, ast};
use triomphe::Arc;
use tt::TextRange;
use crate::attr::Attrs;
use crate::expr_store::HygieneId;
+use crate::macro_call_as_call_id;
use crate::nameres::DefMap;
-use crate::{AsMacroCall, MacroId, UnresolvedMacro, db::DefDatabase};
+use crate::{MacroId, UnresolvedMacro, db::DefDatabase};
#[derive(Debug)]
pub(super) struct Expander {
@@ -64,22 +67,13 @@ impl Expander {
}
}
- pub(super) fn attrs(
- &self,
- db: &dyn DefDatabase,
- krate: Crate,
- has_attrs: &dyn HasAttrs,
- ) -> Attrs {
- Attrs::filter(db, krate, RawAttrs::new(db, has_attrs, self.span_map.as_ref()))
- }
-
pub(super) fn is_cfg_enabled(
&self,
db: &dyn DefDatabase,
- krate: Crate,
has_attrs: &dyn HasAttrs,
- ) -> bool {
- self.attrs(db, krate, has_attrs).is_cfg_enabled(krate.cfg_options(db))
+ cfg_options: &CfgOptions,
+ ) -> Result<(), cfg::CfgExpr> {
+ Attrs::is_cfg_enabled_for(db, has_attrs, self.span_map.as_ref(), cfg_options)
}
pub(super) fn call_syntax_ctx(&self) -> SyntaxContext {
@@ -100,8 +94,31 @@ impl Expander {
let result = self.within_limit(db, |this| {
let macro_call = this.in_file(&macro_call);
- match macro_call.as_call_id_with_errors(
+
+ let expands_to = hir_expand::ExpandTo::from_call_site(macro_call.value);
+ let ast_id = AstId::new(macro_call.file_id, this.ast_id_map().ast_id(macro_call.value));
+ let path = macro_call.value.path().and_then(|path| {
+ let range = path.syntax().text_range();
+ let mod_path = ModPath::from_src(db, path, &mut |range| {
+ this.span_map.span_for_range(range).ctx
+ })?;
+ let call_site = this.span_map.span_for_range(range);
+ Some((call_site, mod_path))
+ });
+
+ let Some((call_site, path)) = path else {
+ return ExpandResult::only_err(ExpandError::other(
+ this.span_map.span_for_range(macro_call.value.syntax().text_range()),
+ "malformed macro invocation",
+ ));
+ };
+
+ match macro_call_as_call_id(
db,
+ ast_id,
+ &path,
+ call_site.ctx,
+ expands_to,
krate,
|path| resolver(path).map(|it| db.macro_def(it)),
eager_callback,
diff --git a/crates/hir-def/src/expr_store/lower.rs b/crates/hir-def/src/expr_store/lower.rs
index 7f907fdba8..50505d54ba 100644
--- a/crates/hir-def/src/expr_store/lower.rs
+++ b/crates/hir-def/src/expr_store/lower.rs
@@ -7,6 +7,7 @@ mod path;
use std::mem;
+use cfg::CfgOptions;
use either::Either;
use hir_expand::{
HirFileId, InFile, Lookup, MacroDefId,
@@ -81,8 +82,6 @@ pub(super) fn lower_body(
// even though they should be the same. Also, when the body comes from multiple expansions, their
// hygiene is different.
- let krate = module.krate();
-
let mut self_param = None;
let mut source_map_self_param = None;
let mut params = vec![];
@@ -100,9 +99,8 @@ pub(super) fn lower_body(
// and skip the body.
if skip_body {
if let Some(param_list) = parameters {
- if let Some(self_param_syn) = param_list
- .self_param()
- .filter(|self_param| collector.expander.is_cfg_enabled(db, krate, self_param))
+ if let Some(self_param_syn) =
+ param_list.self_param().filter(|self_param| collector.check_cfg(self_param))
{
let is_mutable =
self_param_syn.mut_token().is_some() && self_param_syn.amp_token().is_none();
@@ -119,10 +117,7 @@ pub(super) fn lower_body(
source_map_self_param =
Some(collector.expander.in_file(AstPtr::new(&self_param_syn)));
}
- let count = param_list
- .params()
- .filter(|it| collector.expander.is_cfg_enabled(db, krate, it))
- .count();
+ let count = param_list.params().filter(|it| collector.check_cfg(it)).count();
params = (0..count).map(|_| collector.missing_pat()).collect();
};
let body_expr = collector.missing_expr();
@@ -138,9 +133,7 @@ pub(super) fn lower_body(
}
if let Some(param_list) = parameters {
- if let Some(self_param_syn) =
- param_list.self_param().filter(|it| collector.expander.is_cfg_enabled(db, krate, it))
- {
+ if let Some(self_param_syn) = param_list.self_param().filter(|it| collector.check_cfg(it)) {
let is_mutable =
self_param_syn.mut_token().is_some() && self_param_syn.amp_token().is_none();
let hygiene = self_param_syn
@@ -157,7 +150,7 @@ pub(super) fn lower_body(
}
for param in param_list.params() {
- if collector.expander.is_cfg_enabled(db, krate, &param) {
+ if collector.check_cfg(&param) {
let param_pat = collector.collect_pat_top(param.pat());
params.push(param_pat);
}
@@ -346,7 +339,7 @@ pub(crate) fn lower_function(
collector.collect_impl_trait(&mut expr_collector, |collector, mut impl_trait_lower_fn| {
if let Some(param_list) = fn_.value.param_list() {
if let Some(param) = param_list.self_param() {
- let enabled = collector.expander.is_cfg_enabled(db, module.krate(), &param);
+ let enabled = collector.check_cfg(&param);
if enabled {
has_self_param = true;
params.push(match param.ty() {
@@ -381,7 +374,7 @@ pub(crate) fn lower_function(
}
let p = param_list
.params()
- .filter(|param| collector.expander.is_cfg_enabled(db, module.krate(), param))
+ .filter(|param| collector.check_cfg(param))
.filter(|param| {
let is_variadic = param.dotdotdot_token().is_some();
has_variadic |= is_variadic;
@@ -441,6 +434,7 @@ pub(crate) fn lower_function(
pub struct ExprCollector<'db> {
db: &'db dyn DefDatabase,
+ cfg_options: &'db CfgOptions,
expander: Expander,
def_map: Arc<DefMap>,
local_def_map: Arc<LocalDefMap>,
@@ -553,6 +547,7 @@ impl ExprCollector<'_> {
let expander = Expander::new(db, current_file_id, &def_map);
ExprCollector {
db,
+ cfg_options: module.krate().cfg_options(db),
module,
def_map,
local_def_map,
@@ -1026,7 +1021,9 @@ impl ExprCollector<'_> {
/// Returns `None` if and only if the expression is `#[cfg]`d out.
fn maybe_collect_expr(&mut self, expr: ast::Expr) -> Option<ExprId> {
let syntax_ptr = AstPtr::new(&expr);
- self.check_cfg(&expr)?;
+ if !self.check_cfg(&expr) {
+ return None;
+ }
// FIXME: Move some of these arms out into separate methods for clarity
Some(match expr {
@@ -1114,6 +1111,7 @@ impl ExprCollector<'_> {
ast::Expr::WhileExpr(e) => self.collect_while_loop(syntax_ptr, e),
ast::Expr::ForExpr(e) => self.collect_for_loop(syntax_ptr, e),
ast::Expr::CallExpr(e) => {
+ // FIXME: Remove this once we drop support for <1.86, https://github.com/rust-lang/rust/commit/ac9cb908ac4301dfc25e7a2edee574320022ae2c
let is_rustc_box = {
let attrs = e.attrs();
attrs.filter_map(|it| it.as_simple_atom()).any(|it| it == "rustc_box")
@@ -1156,13 +1154,17 @@ impl ExprCollector<'_> {
match_arm_list
.arms()
.filter_map(|arm| {
- self.check_cfg(&arm).map(|()| MatchArm {
- pat: self.collect_pat_top(arm.pat()),
- expr: self.collect_expr_opt(arm.expr()),
- guard: arm
- .guard()
- .map(|guard| self.collect_expr_opt(guard.condition())),
- })
+ if self.check_cfg(&arm) {
+ Some(MatchArm {
+ pat: self.collect_pat_top(arm.pat()),
+ expr: self.collect_expr_opt(arm.expr()),
+ guard: arm
+ .guard()
+ .map(|guard| self.collect_expr_opt(guard.condition())),
+ })
+ } else {
+ None
+ }
})
.collect()
} else {
@@ -1230,7 +1232,9 @@ impl ExprCollector<'_> {
let fields = nfl
.fields()
.filter_map(|field| {
- self.check_cfg(&field)?;
+ if !self.check_cfg(&field) {
+ return None;
+ }
let name = field.field_name()?.as_name();
@@ -1483,7 +1487,9 @@ impl ExprCollector<'_> {
}
fn maybe_collect_expr_as_pat(&mut self, expr: &ast::Expr) -> Option<PatId> {
- self.check_cfg(expr)?;
+ if !self.check_cfg(expr) {
+ return None;
+ }
let syntax_ptr = AstPtr::new(expr);
let result = match expr {
@@ -1558,7 +1564,9 @@ impl ExprCollector<'_> {
let args = record_field_list
.fields()
.filter_map(|f| {
- self.check_cfg(&f)?;
+ if !self.check_cfg(&f) {
+ return None;
+ }
let field_expr = f.expr()?;
let pat = self.collect_expr_as_pat(field_expr);
let name = f.field_name()?.as_name();
@@ -2044,7 +2052,7 @@ impl ExprCollector<'_> {
fn collect_stmt(&mut self, statements: &mut Vec<Statement>, s: ast::Stmt) {
match s {
ast::Stmt::LetStmt(stmt) => {
- if self.check_cfg(&stmt).is_none() {
+ if !self.check_cfg(&stmt) {
return;
}
let pat = self.collect_pat_top(stmt.pat());
@@ -2059,7 +2067,7 @@ impl ExprCollector<'_> {
ast::Stmt::ExprStmt(stmt) => {
let expr = stmt.expr();
match &expr {
- Some(expr) if self.check_cfg(expr).is_none() => return,
+ Some(expr) if !self.check_cfg(expr) => return,
_ => (),
}
let has_semi = stmt.semicolon_token().is_some();
@@ -2074,7 +2082,7 @@ impl ExprCollector<'_> {
}
}
ast::Stmt::Item(ast::Item::MacroDef(macro_)) => {
- if self.check_cfg(&macro_).is_none() {
+ if !self.check_cfg(&macro_) {
return;
}
let Some(name) = macro_.name() else {
@@ -2086,7 +2094,7 @@ impl ExprCollector<'_> {
self.collect_macro_def(statements, macro_id);
}
ast::Stmt::Item(ast::Item::MacroRules(macro_)) => {
- if self.check_cfg(&macro_).is_none() {
+ if !self.check_cfg(&macro_) {
return;
}
let Some(name) = macro_.name() else {
@@ -2360,7 +2368,9 @@ impl ExprCollector<'_> {
let args = record_pat_field_list
.fields()
.filter_map(|f| {
- self.check_cfg(&f)?;
+ if !self.check_cfg(&f) {
+ return None;
+ }
let ast_pat = f.pat()?;
let pat = self.collect_pat(ast_pat, binding_list);
let name = f.field_name()?.as_name();
@@ -2536,25 +2546,18 @@ impl ExprCollector<'_> {
/// Returns `None` (and emits diagnostics) when `owner` if `#[cfg]`d out, and `Some(())` when
/// not.
- fn check_cfg(&mut self, owner: &dyn ast::HasAttrs) -> Option<()> {
- let attrs = self.expander.attrs(self.db, self.module.krate(), owner);
- match attrs.cfg() {
- Some(cfg) => {
- let cfg_options = self.module.krate().cfg_options(self.db);
-
- if cfg_options.check(&cfg) != Some(false) {
- return Some(());
- }
-
+ fn check_cfg(&mut self, owner: &dyn ast::HasAttrs) -> bool {
+ let enabled = self.expander.is_cfg_enabled(self.db, owner, self.cfg_options);
+ match enabled {
+ Ok(()) => true,
+ Err(cfg) => {
self.source_map.diagnostics.push(ExpressionStoreDiagnostics::InactiveCode {
node: self.expander.in_file(SyntaxNodePtr::new(owner.syntax())),
cfg,
- opts: cfg_options.clone(),
+ opts: self.cfg_options.clone(),
});
-
- None
+ false
}
- None => Some(()),
}
}
diff --git a/crates/hir-def/src/expr_store/lower/asm.rs b/crates/hir-def/src/expr_store/lower/asm.rs
index 9ef0306565..d36e5205c7 100644
--- a/crates/hir-def/src/expr_store/lower/asm.rs
+++ b/crates/hir-def/src/expr_store/lower/asm.rs
@@ -224,7 +224,7 @@ impl ExprCollector<'_> {
curarg = parser.curarg;
- let to_span = |inner_span: rustc_parse_format::InnerSpan| {
+ let to_span = |inner_span: std::ops::Range<usize>| {
is_direct_literal.then(|| {
TextRange::new(
inner_span.start.try_into().unwrap(),
diff --git a/crates/hir-def/src/expr_store/lower/generics.rs b/crates/hir-def/src/expr_store/lower/generics.rs
index 9485e703d9..02a1d274fb 100644
--- a/crates/hir-def/src/expr_store/lower/generics.rs
+++ b/crates/hir-def/src/expr_store/lower/generics.rs
@@ -110,7 +110,7 @@ impl GenericParamsCollector {
fn lower_param_list(&mut self, ec: &mut ExprCollector<'_>, params: ast::GenericParamList) {
for generic_param in params.generic_params() {
- let enabled = ec.expander.is_cfg_enabled(ec.db, ec.module.krate(), &generic_param);
+ let enabled = ec.check_cfg(&generic_param);
if !enabled {
continue;
}
@@ -270,7 +270,7 @@ impl GenericParamsCollector {
let self_ = Name::new_symbol_root(sym::Self_);
let idx = self.type_or_consts.alloc(
TypeParamData {
- name: Some(self_.clone()),
+ name: Some(self_),
default: None,
provenance: TypeParamProvenance::TraitSelf,
}
diff --git a/crates/hir-def/src/hir/format_args.rs b/crates/hir-def/src/hir/format_args.rs
index 2fd21bb0ed..f27a4062a6 100644
--- a/crates/hir-def/src/hir/format_args.rs
+++ b/crates/hir-def/src/hir/format_args.rs
@@ -214,7 +214,7 @@ pub(crate) fn parse(
};
}
- let to_span = |inner_span: parse::InnerSpan| {
+ let to_span = |inner_span: std::ops::Range<usize>| {
is_source_literal.then(|| {
TextRange::new(inner_span.start.try_into().unwrap(), inner_span.end.try_into().unwrap())
})
@@ -297,7 +297,7 @@ pub(crate) fn parse(
unfinished_literal.clear();
}
- let span = parser.arg_places.get(placeholder_index).and_then(|&s| to_span(s));
+ let span = parser.arg_places.get(placeholder_index).and_then(|s| to_span(s.clone()));
placeholder_index += 1;
let position_span = to_span(position_span);
diff --git a/crates/hir-def/src/item_scope.rs b/crates/hir-def/src/item_scope.rs
index bece940950..5362c0588d 100644
--- a/crates/hir-def/src/item_scope.rs
+++ b/crates/hir-def/src/item_scope.rs
@@ -167,7 +167,7 @@ pub struct ItemScope {
// the resolutions of the imports of this scope
use_imports_types: FxHashMap<ImportOrExternCrate, ImportOrDef>,
use_imports_values: FxHashMap<ImportOrGlob, ImportOrDef>,
- use_imports_macros: FxHashMap<ImportOrGlob, ImportOrDef>,
+ use_imports_macros: FxHashMap<ImportOrExternCrate, ImportOrDef>,
use_decls: Vec<UseId>,
extern_crate_decls: Vec<ExternCrateId>,
@@ -242,7 +242,7 @@ impl ItemScope {
self.types.iter().map(|(n, &i)| (n, i))
}
- pub fn macros(&self) -> impl Iterator<Item = (&Name, Item<MacroId, ImportOrGlob>)> + '_ {
+ pub fn macros(&self) -> impl Iterator<Item = (&Name, Item<MacroId, ImportOrExternCrate>)> + '_ {
self.macros.iter().map(|(n, &i)| (n, i))
}
@@ -250,9 +250,9 @@ impl ItemScope {
self.use_imports_types
.keys()
.copied()
+ .chain(self.use_imports_macros.keys().copied())
.filter_map(ImportOrExternCrate::import_or_glob)
.chain(self.use_imports_values.keys().copied())
- .chain(self.use_imports_macros.keys().copied())
.filter_map(ImportOrGlob::into_import)
.sorted()
.dedup()
@@ -263,7 +263,7 @@ impl ItemScope {
let mut def_map;
let mut scope = self;
- while let Some(&m) = scope.use_imports_macros.get(&ImportOrGlob::Import(import)) {
+ while let Some(&m) = scope.use_imports_macros.get(&ImportOrExternCrate::Import(import)) {
match m {
ImportOrDef::Import(i) => {
let module_id = i.use_.lookup(db).container;
@@ -682,7 +682,6 @@ impl ItemScope {
}
_ => _ = glob_imports.macros.remove(&lookup),
}
- let import = import.and_then(ImportOrExternCrate::import_or_glob);
let prev = std::mem::replace(&mut fld.import, import);
if let Some(import) = import {
self.use_imports_macros.insert(
@@ -698,7 +697,6 @@ impl ItemScope {
{
if glob_imports.macros.remove(&lookup) {
cov_mark::hit!(import_shadowed);
- let import = import.and_then(ImportOrExternCrate::import_or_glob);
let prev = std::mem::replace(&mut fld.import, import);
if let Some(import) = import {
self.use_imports_macros.insert(
@@ -783,8 +781,9 @@ impl ItemScope {
if let Some(Item { import, .. }) = def.macros {
buf.push_str(" m");
match import {
- Some(ImportOrGlob::Import(_)) => buf.push('i'),
- Some(ImportOrGlob::Glob(_)) => buf.push('g'),
+ Some(ImportOrExternCrate::Import(_)) => buf.push('i'),
+ Some(ImportOrExternCrate::Glob(_)) => buf.push('g'),
+ Some(ImportOrExternCrate::ExternCrate(_)) => buf.push('e'),
None => (),
}
}
@@ -893,9 +892,7 @@ impl PerNs {
ModuleDefId::TraitAliasId(_) => PerNs::types(def, v, import),
ModuleDefId::TypeAliasId(_) => PerNs::types(def, v, import),
ModuleDefId::BuiltinType(_) => PerNs::types(def, v, import),
- ModuleDefId::MacroId(mac) => {
- PerNs::macros(mac, v, import.and_then(ImportOrExternCrate::import_or_glob))
- }
+ ModuleDefId::MacroId(mac) => PerNs::macros(mac, v, import),
}
}
}
diff --git a/crates/hir-def/src/item_tree.rs b/crates/hir-def/src/item_tree.rs
index 01d340cea6..1b97eb72b6 100644
--- a/crates/hir-def/src/item_tree.rs
+++ b/crates/hir-def/src/item_tree.rs
@@ -179,7 +179,7 @@ impl ItemTree {
/// Returns the inner attributes of the source file.
pub fn top_level_attrs(&self, db: &dyn DefDatabase, krate: Crate) -> Attrs {
- Attrs::filter(
+ Attrs::expand_cfg_attr(
db,
krate,
self.attrs.get(&AttrOwner::TopLevel).unwrap_or(&RawAttrs::EMPTY).clone(),
@@ -191,7 +191,7 @@ impl ItemTree {
}
pub(crate) fn attrs(&self, db: &dyn DefDatabase, krate: Crate, of: AttrOwner) -> Attrs {
- Attrs::filter(db, krate, self.raw_attrs(of).clone())
+ Attrs::expand_cfg_attr(db, krate, self.raw_attrs(of).clone())
}
/// Returns a count of a few, expensive items.
diff --git a/crates/hir-def/src/lang_item.rs b/crates/hir-def/src/lang_item.rs
index 5431ec9679..51a833b5f1 100644
--- a/crates/hir-def/src/lang_item.rs
+++ b/crates/hir-def/src/lang_item.rs
@@ -83,104 +83,99 @@ impl LangItemTarget {
}
}
-#[derive(Default, Debug, Clone, PartialEq, Eq)]
-pub struct LangItems {
- items: FxHashMap<LangItem, LangItemTarget>,
-}
+/// Salsa query. This will look for lang items in a specific crate.
+#[salsa_macros::tracked(return_ref)]
+pub fn crate_lang_items(db: &dyn DefDatabase, krate: Crate) -> Option<Box<LangItems>> {
+ let _p = tracing::info_span!("crate_lang_items_query").entered();
-impl LangItems {
- pub fn target(&self, item: LangItem) -> Option<LangItemTarget> {
- self.items.get(&item).copied()
- }
+ let mut lang_items = LangItems::default();
- /// Salsa query. This will look for lang items in a specific crate.
- pub(crate) fn crate_lang_items_query(
- db: &dyn DefDatabase,
- krate: Crate,
- ) -> Option<Arc<LangItems>> {
- let _p = tracing::info_span!("crate_lang_items_query").entered();
-
- let mut lang_items = LangItems::default();
+ let crate_def_map = db.crate_def_map(krate);
- let crate_def_map = db.crate_def_map(krate);
+ for (_, module_data) in crate_def_map.modules() {
+ for impl_def in module_data.scope.impls() {
+ lang_items.collect_lang_item(db, impl_def, LangItemTarget::ImplDef);
+ for &(_, assoc) in db.impl_items(impl_def).items.iter() {
+ match assoc {
+ AssocItemId::FunctionId(f) => {
+ lang_items.collect_lang_item(db, f, LangItemTarget::Function)
+ }
+ AssocItemId::TypeAliasId(t) => {
+ lang_items.collect_lang_item(db, t, LangItemTarget::TypeAlias)
+ }
+ AssocItemId::ConstId(_) => (),
+ }
+ }
+ }
- for (_, module_data) in crate_def_map.modules() {
- for impl_def in module_data.scope.impls() {
- lang_items.collect_lang_item(db, impl_def, LangItemTarget::ImplDef);
- for &(_, assoc) in db.impl_items(impl_def).items.iter() {
- match assoc {
+ for def in module_data.scope.declarations() {
+ match def {
+ ModuleDefId::TraitId(trait_) => {
+ lang_items.collect_lang_item(db, trait_, LangItemTarget::Trait);
+ db.trait_items(trait_).items.iter().for_each(|&(_, assoc_id)| match assoc_id {
AssocItemId::FunctionId(f) => {
- lang_items.collect_lang_item(db, f, LangItemTarget::Function)
+ lang_items.collect_lang_item(db, f, LangItemTarget::Function);
}
- AssocItemId::TypeAliasId(t) => {
- lang_items.collect_lang_item(db, t, LangItemTarget::TypeAlias)
+ AssocItemId::TypeAliasId(alias) => {
+ lang_items.collect_lang_item(db, alias, LangItemTarget::TypeAlias)
}
- AssocItemId::ConstId(_) => (),
- }
+ AssocItemId::ConstId(_) => {}
+ });
}
- }
-
- for def in module_data.scope.declarations() {
- match def {
- ModuleDefId::TraitId(trait_) => {
- lang_items.collect_lang_item(db, trait_, LangItemTarget::Trait);
- db.trait_items(trait_).items.iter().for_each(
- |&(_, assoc_id)| match assoc_id {
- AssocItemId::FunctionId(f) => {
- lang_items.collect_lang_item(db, f, LangItemTarget::Function);
- }
- AssocItemId::TypeAliasId(alias) => lang_items.collect_lang_item(
- db,
- alias,
- LangItemTarget::TypeAlias,
- ),
- AssocItemId::ConstId(_) => {}
- },
- );
- }
- ModuleDefId::AdtId(AdtId::EnumId(e)) => {
- lang_items.collect_lang_item(db, e, LangItemTarget::EnumId);
- db.enum_variants(e).variants.iter().for_each(|&(id, _)| {
- lang_items.collect_lang_item(db, id, LangItemTarget::EnumVariant);
- });
- }
- ModuleDefId::AdtId(AdtId::StructId(s)) => {
- lang_items.collect_lang_item(db, s, LangItemTarget::Struct);
- }
- ModuleDefId::AdtId(AdtId::UnionId(u)) => {
- lang_items.collect_lang_item(db, u, LangItemTarget::Union);
- }
- ModuleDefId::FunctionId(f) => {
- lang_items.collect_lang_item(db, f, LangItemTarget::Function);
- }
- ModuleDefId::StaticId(s) => {
- lang_items.collect_lang_item(db, s, LangItemTarget::Static);
- }
- ModuleDefId::TypeAliasId(t) => {
- lang_items.collect_lang_item(db, t, LangItemTarget::TypeAlias);
- }
- _ => {}
+ ModuleDefId::AdtId(AdtId::EnumId(e)) => {
+ lang_items.collect_lang_item(db, e, LangItemTarget::EnumId);
+ db.enum_variants(e).variants.iter().for_each(|&(id, _)| {
+ lang_items.collect_lang_item(db, id, LangItemTarget::EnumVariant);
+ });
+ }
+ ModuleDefId::AdtId(AdtId::StructId(s)) => {
+ lang_items.collect_lang_item(db, s, LangItemTarget::Struct);
+ }
+ ModuleDefId::AdtId(AdtId::UnionId(u)) => {
+ lang_items.collect_lang_item(db, u, LangItemTarget::Union);
}
+ ModuleDefId::FunctionId(f) => {
+ lang_items.collect_lang_item(db, f, LangItemTarget::Function);
+ }
+ ModuleDefId::StaticId(s) => {
+ lang_items.collect_lang_item(db, s, LangItemTarget::Static);
+ }
+ ModuleDefId::TypeAliasId(t) => {
+ lang_items.collect_lang_item(db, t, LangItemTarget::TypeAlias);
+ }
+ _ => {}
}
}
+ }
- if lang_items.items.is_empty() { None } else { Some(Arc::new(lang_items)) }
+ if lang_items.items.is_empty() { None } else { Some(Box::new(lang_items)) }
+}
+
+/// Salsa query. Look for a lang item, starting from the specified crate and recursively
+/// traversing its dependencies.
+#[salsa_macros::tracked]
+pub fn lang_item(
+ db: &dyn DefDatabase,
+ start_crate: Crate,
+ item: LangItem,
+) -> Option<LangItemTarget> {
+ let _p = tracing::info_span!("lang_item_query").entered();
+ if let Some(target) =
+ crate_lang_items(db, start_crate).as_ref().and_then(|it| it.items.get(&item).copied())
+ {
+ return Some(target);
}
+ start_crate.data(db).dependencies.iter().find_map(|dep| lang_item(db, dep.crate_id, item))
+}
- /// Salsa query. Look for a lang item, starting from the specified crate and recursively
- /// traversing its dependencies.
- pub(crate) fn lang_item_query(
- db: &dyn DefDatabase,
- start_crate: Crate,
- item: LangItem,
- ) -> Option<LangItemTarget> {
- let _p = tracing::info_span!("lang_item_query").entered();
- if let Some(target) =
- db.crate_lang_items(start_crate).and_then(|it| it.items.get(&item).copied())
- {
- return Some(target);
- }
- start_crate.data(db).dependencies.iter().find_map(|dep| db.lang_item(dep.crate_id, item))
+#[derive(Default, Debug, Clone, PartialEq, Eq)]
+pub struct LangItems {
+ items: FxHashMap<LangItem, LangItemTarget>,
+}
+
+impl LangItems {
+ pub fn target(&self, item: LangItem) -> Option<LangItemTarget> {
+ self.items.get(&item).copied()
}
fn collect_lang_item<T>(
@@ -269,18 +264,38 @@ macro_rules! language_item_table {
}
impl LangItem {
+ pub fn resolve_function(self, db: &dyn DefDatabase, start_crate: Crate) -> Option<FunctionId> {
+ lang_item(db, start_crate, self).and_then(|t| t.as_function())
+ }
+
+ pub fn resolve_trait(self, db: &dyn DefDatabase, start_crate: Crate) -> Option<TraitId> {
+ lang_item(db, start_crate, self).and_then(|t| t.as_trait())
+ }
+
+ pub fn resolve_enum(self, db: &dyn DefDatabase, start_crate: Crate) -> Option<EnumId> {
+ lang_item(db, start_crate, self).and_then(|t| t.as_enum())
+ }
+
+ pub fn resolve_type_alias(
+ self,
+ db: &dyn DefDatabase,
+ start_crate: Crate,
+ ) -> Option<TypeAliasId> {
+ lang_item(db, start_crate, self).and_then(|t| t.as_type_alias())
+ }
+
/// Opposite of [`LangItem::name`]
pub fn from_name(name: &hir_expand::name::Name) -> Option<Self> {
Self::from_symbol(name.symbol())
}
pub fn path(&self, db: &dyn DefDatabase, start_crate: Crate) -> Option<Path> {
- let t = db.lang_item(start_crate, *self)?;
+ let t = lang_item(db, start_crate, *self)?;
Some(Path::LangItem(t, None))
}
pub fn ty_rel_path(&self, db: &dyn DefDatabase, start_crate: Crate, seg: Name) -> Option<Path> {
- let t = db.lang_item(start_crate, *self)?;
+ let t = lang_item(db, start_crate, *self)?;
Some(Path::LangItem(t, Some(seg)))
}
}
diff --git a/crates/hir-def/src/lib.rs b/crates/hir-def/src/lib.rs
index 737941dba0..28011bda7c 100644
--- a/crates/hir-def/src/lib.rs
+++ b/crates/hir-def/src/lib.rs
@@ -64,8 +64,8 @@ use std::hash::{Hash, Hasher};
use base_db::{Crate, impl_intern_key};
use hir_expand::{
- AstId, ExpandError, ExpandResult, ExpandTo, HirFileId, InFile, MacroCallId, MacroCallKind,
- MacroDefId, MacroDefKind,
+ AstId, ExpandResult, ExpandTo, HirFileId, InFile, MacroCallId, MacroCallKind, MacroDefId,
+ MacroDefKind,
builtin::{BuiltinAttrExpander, BuiltinDeriveExpander, BuiltinFnLikeExpander, EagerExpander},
db::ExpandDatabase,
eager::expand_eager_macro_input,
@@ -79,7 +79,7 @@ use la_arena::Idx;
use nameres::DefMap;
use span::{AstIdNode, Edition, FileAstId, SyntaxContext};
use stdx::impl_from;
-use syntax::{AstNode, ast};
+use syntax::ast;
pub use hir_expand::{Intern, Lookup, tt};
@@ -554,7 +554,7 @@ pub enum ItemContainerId {
impl_from!(ModuleId for ItemContainerId);
/// A Data Type
-#[derive(Debug, PartialOrd, Ord, Clone, Copy, PartialEq, Eq, Hash, salsa::Supertype)]
+#[derive(Debug, PartialOrd, Ord, Clone, Copy, PartialEq, Eq, Hash, salsa_macros::Supertype)]
pub enum AdtId {
StructId(StructId),
UnionId(UnionId),
@@ -563,7 +563,7 @@ pub enum AdtId {
impl_from!(StructId, UnionId, EnumId for AdtId);
/// A macro
-#[derive(Debug, PartialOrd, Ord, Clone, Copy, PartialEq, Eq, Hash, salsa::Supertype)]
+#[derive(Debug, PartialOrd, Ord, Clone, Copy, PartialEq, Eq, Hash, salsa_macros::Supertype)]
pub enum MacroId {
Macro2Id(Macro2Id),
MacroRulesId(MacroRulesId),
@@ -619,7 +619,7 @@ impl_from!(
/// A constant, which might appears as a const item, an anonymous const block in expressions
/// or patterns, or as a constant in types with const generics.
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, salsa::Supertype)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, salsa_macros::Supertype)]
pub enum GeneralConstId {
ConstId(ConstId),
StaticId(StaticId),
@@ -656,7 +656,7 @@ impl GeneralConstId {
}
/// The defs which have a body (have root expressions for type inference).
-#[derive(Debug, PartialOrd, Ord, Clone, Copy, PartialEq, Eq, Hash, salsa::Supertype)]
+#[derive(Debug, PartialOrd, Ord, Clone, Copy, PartialEq, Eq, Hash, salsa_macros::Supertype)]
pub enum DefWithBodyId {
FunctionId(FunctionId),
StaticId(StaticId),
@@ -701,7 +701,7 @@ pub enum AssocItemId {
// casting them, and somehow making the constructors private, which would be annoying.
impl_from!(FunctionId, ConstId, TypeAliasId for AssocItemId);
-#[derive(Debug, PartialOrd, Ord, Clone, Copy, PartialEq, Eq, Hash, salsa::Supertype)]
+#[derive(Debug, PartialOrd, Ord, Clone, Copy, PartialEq, Eq, Hash, salsa_macros::Supertype)]
pub enum GenericDefId {
AdtId(AdtId),
// consts can have type parameters from their parents (i.e. associated consts of traits)
@@ -790,7 +790,7 @@ impl From<AssocItemId> for GenericDefId {
}
}
-#[derive(Debug, PartialOrd, Ord, Clone, Copy, PartialEq, Eq, Hash, salsa::Supertype)]
+#[derive(Debug, PartialOrd, Ord, Clone, Copy, PartialEq, Eq, Hash, salsa_macros::Supertype)]
pub enum CallableDefId {
FunctionId(FunctionId),
StructId(StructId),
@@ -906,7 +906,7 @@ impl From<VariantId> for AttrDefId {
}
}
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, salsa::Supertype)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, salsa_macros::Supertype)]
pub enum VariantId {
EnumVariantId(EnumVariantId),
StructId(StructId),
@@ -1166,66 +1166,6 @@ impl ModuleDefId {
})
}
}
-
-// FIXME: Replace this with a plain function, it only has one impl
-/// A helper trait for converting to MacroCallId
-trait AsMacroCall {
- fn as_call_id_with_errors(
- &self,
- db: &dyn ExpandDatabase,
- krate: Crate,
- resolver: impl Fn(&ModPath) -> Option<MacroDefId> + Copy,
- eager_callback: &mut dyn FnMut(
- InFile<(syntax::AstPtr<ast::MacroCall>, span::FileAstId<ast::MacroCall>)>,
- MacroCallId,
- ),
- ) -> Result<ExpandResult<Option<MacroCallId>>, UnresolvedMacro>;
-}
-
-impl AsMacroCall for InFile<&ast::MacroCall> {
- fn as_call_id_with_errors(
- &self,
- db: &dyn ExpandDatabase,
- krate: Crate,
- resolver: impl Fn(&ModPath) -> Option<MacroDefId> + Copy,
- eager_callback: &mut dyn FnMut(
- InFile<(syntax::AstPtr<ast::MacroCall>, span::FileAstId<ast::MacroCall>)>,
- MacroCallId,
- ),
- ) -> Result<ExpandResult<Option<MacroCallId>>, UnresolvedMacro> {
- let expands_to = hir_expand::ExpandTo::from_call_site(self.value);
- let ast_id = AstId::new(self.file_id, db.ast_id_map(self.file_id).ast_id(self.value));
- let span_map = db.span_map(self.file_id);
- let path = self.value.path().and_then(|path| {
- let range = path.syntax().text_range();
- let mod_path = ModPath::from_src(db, path, &mut |range| {
- span_map.as_ref().span_for_range(range).ctx
- })?;
- let call_site = span_map.span_for_range(range);
- Some((call_site, mod_path))
- });
-
- let Some((call_site, path)) = path else {
- return Ok(ExpandResult::only_err(ExpandError::other(
- span_map.span_for_range(self.value.syntax().text_range()),
- "malformed macro invocation",
- )));
- };
-
- macro_call_as_call_id_with_eager(
- db,
- ast_id,
- &path,
- call_site.ctx,
- expands_to,
- krate,
- resolver,
- resolver,
- eager_callback,
- )
- }
-}
-
/// Helper wrapper for `AstId` with `ModPath`
#[derive(Clone, Debug, Eq, PartialEq)]
struct AstIdWithPath<T: AstIdNode> {
@@ -1239,41 +1179,14 @@ impl<T: AstIdNode> AstIdWithPath<T> {
}
}
-fn macro_call_as_call_id(
- db: &dyn ExpandDatabase,
- call: &AstIdWithPath<ast::MacroCall>,
- call_site: SyntaxContext,
- expand_to: ExpandTo,
- krate: Crate,
- resolver: impl Fn(&ModPath) -> Option<MacroDefId> + Copy,
- eager_callback: &mut dyn FnMut(
- InFile<(syntax::AstPtr<ast::MacroCall>, span::FileAstId<ast::MacroCall>)>,
- MacroCallId,
- ),
-) -> Result<Option<MacroCallId>, UnresolvedMacro> {
- macro_call_as_call_id_with_eager(
- db,
- call.ast_id,
- &call.path,
- call_site,
- expand_to,
- krate,
- resolver,
- resolver,
- eager_callback,
- )
- .map(|res| res.value)
-}
-
-fn macro_call_as_call_id_with_eager(
+pub fn macro_call_as_call_id(
db: &dyn ExpandDatabase,
ast_id: AstId<ast::MacroCall>,
path: &ModPath,
call_site: SyntaxContext,
expand_to: ExpandTo,
krate: Crate,
- resolver: impl FnOnce(&ModPath) -> Option<MacroDefId>,
- eager_resolver: impl Fn(&ModPath) -> Option<MacroDefId>,
+ resolver: impl Fn(&ModPath) -> Option<MacroDefId> + Copy,
eager_callback: &mut dyn FnMut(
InFile<(syntax::AstPtr<ast::MacroCall>, span::FileAstId<ast::MacroCall>)>,
MacroCallId,
@@ -1289,7 +1202,7 @@ fn macro_call_as_call_id_with_eager(
ast_id,
def,
call_site,
- &|path| eager_resolver(path).filter(MacroDefId::is_fn_like),
+ &|path| resolver(path).filter(MacroDefId::is_fn_like),
eager_callback,
),
_ if def.is_fn_like() => ExpandResult {
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 a3b48831a0..e21d1415aa 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
@@ -285,8 +285,6 @@ fn main() {
/* parse error: expected expression */
builtin #format_args (x = );
/* parse error: expected expression */
-/* parse error: expected R_PAREN */
-/* parse error: expected expression, item or let statement */
builtin #format_args (x = , x = 2);
/* parse error: expected expression */
builtin #format_args ("{}", x = );
diff --git a/crates/hir-def/src/macro_expansion_tests/mbe.rs b/crates/hir-def/src/macro_expansion_tests/mbe.rs
index abb5bd5ed7..38fc4b3d11 100644
--- a/crates/hir-def/src/macro_expansion_tests/mbe.rs
+++ b/crates/hir-def/src/macro_expansion_tests/mbe.rs
@@ -2001,8 +2001,9 @@ macro_rules! bug {
true
};
}
-
-let _ = bug!(a;;;test);
+fn f() {
+ let _ = bug!(a;;;test);
+}
"#,
expect![[r#"
macro_rules! bug {
@@ -2022,8 +2023,9 @@ macro_rules! bug {
true
};
}
-
-let _ = true;
+fn f() {
+ let _ = true;
+}
"#]],
);
}
diff --git a/crates/hir-def/src/macro_expansion_tests/mod.rs b/crates/hir-def/src/macro_expansion_tests/mod.rs
index 143b5df773..800c96ebda 100644
--- a/crates/hir-def/src/macro_expansion_tests/mod.rs
+++ b/crates/hir-def/src/macro_expansion_tests/mod.rs
@@ -19,7 +19,7 @@ use std::{iter, ops::Range, sync};
use base_db::RootQueryDb;
use expect_test::Expect;
use hir_expand::{
- InFile, MacroCallKind, MacroKind,
+ AstId, InFile, MacroCallId, MacroCallKind, MacroKind,
db::ExpandDatabase,
proc_macro::{ProcMacro, ProcMacroExpander, ProcMacroExpansionError, ProcMacroKind},
span_map::SpanMapRef,
@@ -29,7 +29,7 @@ use itertools::Itertools;
use span::{Edition, Span};
use stdx::{format_to, format_to_acc};
use syntax::{
- AstNode,
+ AstNode, AstPtr,
SyntaxKind::{COMMENT, EOF, IDENT, LIFETIME_IDENT},
SyntaxNode, T,
ast::{self, edit::IndentLevel},
@@ -37,10 +37,9 @@ use syntax::{
use test_fixture::WithFixture;
use crate::{
- AdtId, AsMacroCall, Lookup, ModuleDefId,
+ AdtId, Lookup, ModuleDefId,
db::DefDatabase,
- nameres::{DefMap, MacroSubNs, ModuleSource},
- resolver::HasResolver,
+ nameres::{DefMap, ModuleSource},
src::HasSource,
test_db::TestDB,
tt::TopSubtree,
@@ -78,7 +77,6 @@ fn check_errors(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect)
expect.assert_eq(&errors);
}
-#[track_caller]
fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str, mut expect: Expect) {
let extra_proc_macros = vec![(
r#"
@@ -95,54 +93,59 @@ pub fn identity_when_valid(_attr: TokenStream, item: TokenStream) -> TokenStream
disabled: false,
},
)];
+
+ fn resolve(
+ db: &dyn DefDatabase,
+ def_map: &DefMap,
+ ast_id: AstId<ast::MacroCall>,
+ ast_ptr: InFile<AstPtr<ast::MacroCall>>,
+ ) -> Option<MacroCallId> {
+ def_map.modules().find_map(|module| {
+ for decl in
+ module.1.scope.declarations().chain(module.1.scope.unnamed_consts().map(Into::into))
+ {
+ let body = match decl {
+ ModuleDefId::FunctionId(it) => it.into(),
+ ModuleDefId::ConstId(it) => it.into(),
+ ModuleDefId::StaticId(it) => it.into(),
+ _ => continue,
+ };
+
+ let (body, sm) = db.body_with_source_map(body);
+ if let Some(it) =
+ body.blocks(db).find_map(|block| resolve(db, &block.1, ast_id, ast_ptr))
+ {
+ return Some(it);
+ }
+ if let Some((_, res)) = sm.macro_calls().find(|it| it.0 == ast_ptr) {
+ return Some(res);
+ }
+ }
+ module.1.scope.macro_invoc(ast_id)
+ })
+ }
+
let db = TestDB::with_files_extra_proc_macros(ra_fixture, extra_proc_macros);
let krate = db.fetch_test_crate();
let def_map = db.crate_def_map(krate);
let local_id = DefMap::ROOT;
- let module = def_map.module_id(local_id);
- let resolver = module.resolver(&db);
let source = def_map[local_id].definition_source(&db);
let source_file = match source.value {
ModuleSource::SourceFile(it) => it,
ModuleSource::Module(_) | ModuleSource::BlockExpr(_) => panic!(),
};
- // What we want to do is to replace all macros (fn-like, derive, attr) with
- // their expansions. Turns out, we don't actually store enough information
- // to do this precisely though! Specifically, if a macro expands to nothing,
- // it leaves zero traces in def-map, so we can't get its expansion after the
- // fact.
- //
- // This is the usual
- // <https://github.com/rust-lang/rust-analyzer/issues/3407>
- // resolve/record tension!
- //
- // So here we try to do a resolve, which is necessary a heuristic. For macro
- // calls, we use `as_call_id_with_errors`. For derives, we look at the impls
- // in the module and assume that, if impls's source is a different
- // `HirFileId`, than it came from macro expansion.
-
let mut text_edits = Vec::new();
let mut expansions = Vec::new();
- for macro_call in source_file.syntax().descendants().filter_map(ast::MacroCall::cast) {
- let macro_call = InFile::new(source.file_id, &macro_call);
- let res = macro_call
- .as_call_id_with_errors(
- &db,
- krate,
- |path| {
- resolver
- .resolve_path_as_macro(&db, path, Some(MacroSubNs::Bang))
- .map(|(it, _)| db.macro_def(it))
- },
- &mut |_, _| (),
- )
- .unwrap();
- let macro_call_id = res.value.unwrap();
- let mut expansion_result = db.parse_macro_expansion(macro_call_id);
- expansion_result.err = expansion_result.err.or(res.err);
- expansions.push((macro_call.value.clone(), expansion_result));
+ for macro_call_node in source_file.syntax().descendants().filter_map(ast::MacroCall::cast) {
+ let ast_id = db.ast_id_map(source.file_id).ast_id(&macro_call_node);
+ let ast_id = InFile::new(source.file_id, ast_id);
+ let ptr = InFile::new(source.file_id, AstPtr::new(&macro_call_node));
+ let macro_call_id = resolve(&db, &def_map, ast_id, ptr)
+ .unwrap_or_else(|| panic!("unable to find semantic macro call {macro_call_node}"));
+ let expansion_result = db.parse_macro_expansion(macro_call_id);
+ expansions.push((macro_call_node.clone(), expansion_result));
}
for (call, exp) in expansions.into_iter().rev() {
diff --git a/crates/hir-def/src/nameres/assoc.rs b/crates/hir-def/src/nameres/assoc.rs
index b097065529..448b908936 100644
--- a/crates/hir-def/src/nameres/assoc.rs
+++ b/crates/hir-def/src/nameres/assoc.rs
@@ -259,7 +259,8 @@ impl<'a> AssocItemCollector<'a> {
};
match macro_call_as_call_id(
self.db,
- &AstIdWithPath::new(tree_id.file_id(), ast_id, Clone::clone(path)),
+ InFile::new(tree_id.file_id(), ast_id),
+ path,
ctxt,
expand_to,
self.module_id.krate(),
@@ -268,12 +269,15 @@ impl<'a> AssocItemCollector<'a> {
self.macro_calls.push((ptr.map(|(_, it)| it.upcast()), call_id))
},
) {
- Ok(Some(call_id)) => {
- self.macro_calls
- .push((InFile::new(tree_id.file_id(), ast_id.upcast()), call_id));
- self.collect_macro_items(call_id);
- }
- Ok(None) => (),
+ // FIXME: Expansion error?
+ Ok(call_id) => match call_id.value {
+ Some(call_id) => {
+ self.macro_calls
+ .push((InFile::new(tree_id.file_id(), ast_id.upcast()), call_id));
+ self.collect_macro_items(call_id);
+ }
+ None => (),
+ },
Err(_) => {
self.diagnostics.push(DefDiagnostic::unresolved_macro_call(
self.module_id.local_id,
diff --git a/crates/hir-def/src/nameres/collector.rs b/crates/hir-def/src/nameres/collector.rs
index 77effbcc88..8df0f092cd 100644
--- a/crates/hir-def/src/nameres/collector.rs
+++ b/crates/hir-def/src/nameres/collector.rs
@@ -39,7 +39,7 @@ use crate::{
ItemTreeId, ItemTreeNode, Macro2, MacroCall, MacroRules, Mod, ModItem, ModKind, TreeId,
UseTreeKind,
},
- macro_call_as_call_id, macro_call_as_call_id_with_eager,
+ macro_call_as_call_id,
nameres::{
BuiltinShadowMode, DefMap, LocalDefMap, MacroSubNs, ModuleData, ModuleOrigin, ResolveMode,
attr_resolution::{attr_macro_as_call_id, derive_macro_as_call_id},
@@ -1256,7 +1256,8 @@ impl DefCollector<'_> {
MacroDirectiveKind::FnLike { ast_id, expand_to, ctxt: call_site } => {
let call_id = macro_call_as_call_id(
self.db,
- ast_id,
+ ast_id.ast_id,
+ &ast_id.path,
*call_site,
*expand_to,
self.def_map.krate,
@@ -1265,15 +1266,18 @@ impl DefCollector<'_> {
eager_callback_buffer.push((directive.module_id, ptr, call_id));
},
);
- if let Ok(Some(call_id)) = call_id {
- self.def_map.modules[directive.module_id]
- .scope
- .add_macro_invoc(ast_id.ast_id, call_id);
+ if let Ok(call_id) = call_id {
+ // FIXME: Expansion error
+ if let Some(call_id) = call_id.value {
+ self.def_map.modules[directive.module_id]
+ .scope
+ .add_macro_invoc(ast_id.ast_id, call_id);
- push_resolved(directive, call_id);
+ push_resolved(directive, call_id);
- res = ReachedFixedPoint::No;
- return Resolved::Yes;
+ res = ReachedFixedPoint::No;
+ return Resolved::Yes;
+ }
}
}
MacroDirectiveKind::Derive {
@@ -1542,7 +1546,8 @@ impl DefCollector<'_> {
// FIXME: we shouldn't need to re-resolve the macro here just to get the unresolved error!
let macro_call_as_call_id = macro_call_as_call_id(
self.db,
- ast_id,
+ ast_id.ast_id,
+ &ast_id.path,
*call_site,
*expand_to,
self.def_map.krate,
@@ -2420,7 +2425,7 @@ impl ModCollector<'_, '_> {
let mut eager_callback_buffer = vec![];
// Case 1: try to resolve macro calls with single-segment name and expand macro_rules
- if let Ok(res) = macro_call_as_call_id_with_eager(
+ if let Ok(res) = macro_call_as_call_id(
db,
ast_id.ast_id,
&ast_id.path,
@@ -2445,21 +2450,6 @@ impl ModCollector<'_, '_> {
.map(|it| self.def_collector.db.macro_def(it))
})
},
- |path| {
- let resolved_res = self.def_collector.def_map.resolve_path_fp_with_macro(
- self.def_collector
- .crate_local_def_map
- .as_deref()
- .unwrap_or(&self.def_collector.local_def_map),
- db,
- ResolveMode::Other,
- self.module_id,
- path,
- BuiltinShadowMode::Module,
- Some(MacroSubNs::Bang),
- );
- resolved_res.resolved_def.take_macros().map(|it| db.macro_def(it))
- },
&mut |ptr, call_id| eager_callback_buffer.push((ptr, call_id)),
) {
for (ptr, call_id) in eager_callback_buffer {
diff --git a/crates/hir-def/src/nameres/mod_resolution.rs b/crates/hir-def/src/nameres/mod_resolution.rs
index d6c9f5a00c..0c50f13edf 100644
--- a/crates/hir-def/src/nameres/mod_resolution.rs
+++ b/crates/hir-def/src/nameres/mod_resolution.rs
@@ -86,7 +86,7 @@ impl ModDir {
let dir_path = if root_dir_owner {
DirPath::empty()
} else {
- DirPath::new(format!("{}/", name))
+ DirPath::new(format!("{name}/"))
};
if let Some(mod_dir) = self.child(dir_path, !root_dir_owner) {
return Ok((
diff --git a/crates/hir-def/src/nameres/path_resolution.rs b/crates/hir-def/src/nameres/path_resolution.rs
index f8b2c73a8f..a49155d878 100644
--- a/crates/hir-def/src/nameres/path_resolution.rs
+++ b/crates/hir-def/src/nameres/path_resolution.rs
@@ -673,12 +673,11 @@ impl DefMap {
}
fn resolve_in_macro_use_prelude(&self, name: &Name) -> PerNs {
- self.macro_use_prelude.get(name).map_or(PerNs::none(), |&(it, _extern_crate)| {
+ self.macro_use_prelude.get(name).map_or(PerNs::none(), |&(it, extern_crate)| {
PerNs::macros(
it,
Visibility::Public,
- // FIXME?
- None, // extern_crate.map(ImportOrExternCrate::ExternCrate),
+ extern_crate.map(ImportOrExternCrate::ExternCrate),
)
})
}
diff --git a/crates/hir-def/src/per_ns.rs b/crates/hir-def/src/per_ns.rs
index 1f7dd6f0c4..8721cd65db 100644
--- a/crates/hir-def/src/per_ns.rs
+++ b/crates/hir-def/src/per_ns.rs
@@ -37,7 +37,8 @@ pub struct Item<Def, Import = ImportId> {
pub type TypesItem = Item<ModuleDefId, ImportOrExternCrate>;
pub type ValuesItem = Item<ModuleDefId, ImportOrGlob>;
-pub type MacrosItem = Item<MacroId, ImportOrGlob>;
+// May be Externcrate for `[macro_use]`'d macros
+pub type MacrosItem = Item<MacroId, ImportOrExternCrate>;
#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
pub struct PerNs {
@@ -84,7 +85,7 @@ impl PerNs {
}
}
- pub fn macros(def: MacroId, vis: Visibility, import: Option<ImportOrGlob>) -> PerNs {
+ pub fn macros(def: MacroId, vis: Visibility, import: Option<ImportOrExternCrate>) -> PerNs {
PerNs { types: None, values: None, macros: Some(Item { def, vis, import }) }
}
@@ -116,7 +117,7 @@ impl PerNs {
self.macros.map(|it| it.def)
}
- pub fn take_macros_import(self) -> Option<(MacroId, Option<ImportOrGlob>)> {
+ pub fn take_macros_import(self) -> Option<(MacroId, Option<ImportOrExternCrate>)> {
self.macros.map(|it| (it.def, it.import))
}
@@ -158,9 +159,6 @@ impl PerNs {
self.values
.map(|it| (ItemInNs::Values(it.def), it.import.map(ImportOrExternCrate::from))),
)
- .chain(
- self.macros
- .map(|it| (ItemInNs::Macros(it.def), it.import.map(ImportOrExternCrate::from))),
- )
+ .chain(self.macros.map(|it| (ItemInNs::Macros(it.def), it.import)))
}
}
diff --git a/crates/hir-def/src/resolver.rs b/crates/hir-def/src/resolver.rs
index 46c12574fd..8a8d17018c 100644
--- a/crates/hir-def/src/resolver.rs
+++ b/crates/hir-def/src/resolver.rs
@@ -493,7 +493,7 @@ impl Resolver {
db: &dyn DefDatabase,
path: &ModPath,
expected_macro_kind: Option<MacroSubNs>,
- ) -> Option<(MacroId, Option<ImportOrGlob>)> {
+ ) -> Option<(MacroId, Option<ImportOrExternCrate>)> {
let (item_map, item_local_map, module) = self.item_scope_();
item_map
.resolve_path(
diff --git a/crates/hir-def/src/test_db.rs b/crates/hir-def/src/test_db.rs
index 2f7675134c..4709754829 100644
--- a/crates/hir-def/src/test_db.rs
+++ b/crates/hir-def/src/test_db.rs
@@ -19,7 +19,7 @@ use crate::{
src::HasSource,
};
-#[salsa::db]
+#[salsa_macros::db]
#[derive(Clone)]
pub(crate) struct TestDB {
storage: salsa::Storage<Self>,
@@ -44,7 +44,7 @@ impl Default for TestDB {
}
}
-#[salsa::db]
+#[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();
@@ -63,7 +63,7 @@ impl fmt::Debug for TestDB {
impl panic::RefUnwindSafe for TestDB {}
-#[salsa::db]
+#[salsa_macros::db]
impl SourceDatabase for TestDB {
fn file_text(&self, file_id: base_db::FileId) -> FileText {
self.files.file_text(file_id)
diff --git a/crates/hir-expand/Cargo.toml b/crates/hir-expand/Cargo.toml
index b83efca255..ed818c5be3 100644
--- a/crates/hir-expand/Cargo.toml
+++ b/crates/hir-expand/Cargo.toml
@@ -21,6 +21,7 @@ smallvec.workspace = true
triomphe.workspace = true
query-group.workspace = true
salsa.workspace = true
+salsa-macros.workspace = true
# local deps
stdx.workspace = true
diff --git a/crates/hir-expand/src/attrs.rs b/crates/hir-expand/src/attrs.rs
index 5dae27f7a1..bb17eb0627 100644
--- a/crates/hir-expand/src/attrs.rs
+++ b/crates/hir-expand/src/attrs.rs
@@ -2,7 +2,7 @@
use std::{borrow::Cow, fmt, ops};
use base_db::Crate;
-use cfg::CfgExpr;
+use cfg::{CfgExpr, CfgOptions};
use either::Either;
use intern::{Interned, Symbol, sym};
@@ -14,11 +14,10 @@ use syntax::{AstNode, AstToken, SyntaxNode, ast, match_ast};
use syntax_bridge::{DocCommentDesugarMode, desugar_doc_comment_text, syntax_node_to_token_tree};
use triomphe::ThinArc;
-use crate::name::Name;
use crate::{
- InFile,
db::ExpandDatabase,
mod_path::ModPath,
+ name::Name,
span_map::SpanMapRef,
tt::{self, TopSubtree, token_to_literal},
};
@@ -49,29 +48,7 @@ impl RawAttrs {
owner: &dyn ast::HasAttrs,
span_map: SpanMapRef<'_>,
) -> Self {
- let entries: Vec<_> = collect_attrs(owner)
- .filter_map(|(id, attr)| match attr {
- Either::Left(attr) => {
- attr.meta().and_then(|meta| Attr::from_src(db, meta, span_map, id))
- }
- Either::Right(comment) => comment.doc_comment().map(|doc| {
- let span = span_map.span_for_range(comment.syntax().text_range());
- let (text, kind) =
- desugar_doc_comment_text(doc, DocCommentDesugarMode::ProcMacro);
- Attr {
- id,
- input: Some(Box::new(AttrInput::Literal(tt::Literal {
- symbol: text,
- span,
- kind,
- suffix: None,
- }))),
- path: Interned::new(ModPath::from(Name::new_symbol(sym::doc, span.ctx))),
- ctxt: span.ctx,
- }
- }),
- })
- .collect();
+ let entries: Vec<_> = Self::attrs_iter::<true>(db, owner, span_map).collect();
let entries = if entries.is_empty() {
None
@@ -82,12 +59,61 @@ impl RawAttrs {
RawAttrs { entries }
}
- pub fn from_attrs_owner(
+ /// A [`RawAttrs`] that has its `#[cfg_attr(...)]` attributes expanded.
+ pub fn new_expanded(
db: &dyn ExpandDatabase,
- owner: InFile<&dyn ast::HasAttrs>,
+ owner: &dyn ast::HasAttrs,
span_map: SpanMapRef<'_>,
+ cfg_options: &CfgOptions,
) -> Self {
- Self::new(db, owner.value, span_map)
+ let entries: Vec<_> =
+ Self::attrs_iter_expanded::<true>(db, owner, span_map, cfg_options).collect();
+
+ let entries = if entries.is_empty() {
+ None
+ } else {
+ Some(ThinArc::from_header_and_iter((), entries.into_iter()))
+ };
+
+ RawAttrs { entries }
+ }
+
+ pub fn attrs_iter<const DESUGAR_COMMENTS: bool>(
+ db: &dyn ExpandDatabase,
+ owner: &dyn ast::HasAttrs,
+ span_map: SpanMapRef<'_>,
+ ) -> impl Iterator<Item = Attr> {
+ collect_attrs(owner).filter_map(move |(id, attr)| match attr {
+ Either::Left(attr) => {
+ attr.meta().and_then(|meta| Attr::from_src(db, meta, span_map, id))
+ }
+ Either::Right(comment) if DESUGAR_COMMENTS => comment.doc_comment().map(|doc| {
+ let span = span_map.span_for_range(comment.syntax().text_range());
+ let (text, kind) = desugar_doc_comment_text(doc, DocCommentDesugarMode::ProcMacro);
+ Attr {
+ id,
+ input: Some(Box::new(AttrInput::Literal(tt::Literal {
+ symbol: text,
+ span,
+ kind,
+ suffix: None,
+ }))),
+ path: Interned::new(ModPath::from(Name::new_symbol(sym::doc, span.ctx))),
+ ctxt: span.ctx,
+ }
+ }),
+ Either::Right(_) => None,
+ })
+ }
+
+ pub fn attrs_iter_expanded<const DESUGAR_COMMENTS: bool>(
+ db: &dyn ExpandDatabase,
+ owner: &dyn ast::HasAttrs,
+ span_map: SpanMapRef<'_>,
+ cfg_options: &CfgOptions,
+ ) -> impl Iterator<Item = Attr> {
+ Self::attrs_iter::<DESUGAR_COMMENTS>(db, owner, span_map)
+ .flat_map(|attr| attr.expand_cfg_attr(db, cfg_options))
}
pub fn merge(&self, other: Self) -> Self {
@@ -114,9 +140,8 @@ impl RawAttrs {
}
}
- /// Processes `cfg_attr`s, returning the resulting semantic `Attrs`.
- // FIXME: This should return a different type, signaling it was filtered?
- pub fn filter(self, db: &dyn ExpandDatabase, krate: Crate) -> RawAttrs {
+ /// Processes `cfg_attr`s
+ pub fn expand_cfg_attr(self, db: &dyn ExpandDatabase, krate: Crate) -> RawAttrs {
let has_cfg_attrs =
self.iter().any(|attr| attr.path.as_ident().is_some_and(|name| *name == sym::cfg_attr));
if !has_cfg_attrs {
@@ -126,37 +151,8 @@ impl RawAttrs {
let cfg_options = krate.cfg_options(db);
let new_attrs = self
.iter()
- .flat_map(|attr| -> SmallVec<[_; 1]> {
- let is_cfg_attr = attr.path.as_ident().is_some_and(|name| *name == sym::cfg_attr);
- if !is_cfg_attr {
- return smallvec![attr.clone()];
- }
-
- let subtree = match attr.token_tree_value() {
- Some(it) => it,
- _ => return smallvec![attr.clone()],
- };
-
- let (cfg, parts) = match parse_cfg_attr_input(subtree) {
- Some(it) => it,
- None => return smallvec![attr.clone()],
- };
- let index = attr.id;
- let attrs = parts
- .enumerate()
- .take(1 << AttrId::CFG_ATTR_BITS)
- .filter_map(|(idx, attr)| Attr::from_tt(db, attr, index.with_cfg_attr(idx)));
-
- let cfg = TopSubtree::from_token_trees(subtree.top_subtree().delimiter, cfg);
- let cfg = CfgExpr::parse(&cfg);
- if cfg_options.check(&cfg) == Some(false) {
- smallvec![]
- } else {
- cov_mark::hit!(cfg_attr_active);
-
- attrs.collect()
- }
- })
+ .cloned()
+ .flat_map(|attr| attr.expand_cfg_attr(db, cfg_options))
.collect::<Vec<_>>();
let entries = if new_attrs.is_empty() {
None
@@ -316,6 +312,42 @@ impl Attr {
pub fn path(&self) -> &ModPath {
&self.path
}
+
+ pub fn expand_cfg_attr(
+ self,
+ db: &dyn ExpandDatabase,
+ cfg_options: &CfgOptions,
+ ) -> impl IntoIterator<Item = Self> {
+ let is_cfg_attr = self.path.as_ident().is_some_and(|name| *name == sym::cfg_attr);
+ if !is_cfg_attr {
+ return smallvec![self];
+ }
+
+ let subtree = match self.token_tree_value() {
+ Some(it) => it,
+ _ => return smallvec![self.clone()],
+ };
+
+ let (cfg, parts) = match parse_cfg_attr_input(subtree) {
+ Some(it) => it,
+ None => return smallvec![self.clone()],
+ };
+ let index = self.id;
+ let attrs = parts
+ .enumerate()
+ .take(1 << AttrId::CFG_ATTR_BITS)
+ .filter_map(|(idx, attr)| Attr::from_tt(db, attr, index.with_cfg_attr(idx)));
+
+ let cfg = TopSubtree::from_token_trees(subtree.top_subtree().delimiter, cfg);
+ let cfg = CfgExpr::parse(&cfg);
+ if cfg_options.check(&cfg) == Some(false) {
+ smallvec![]
+ } else {
+ cov_mark::hit!(cfg_attr_active);
+
+ attrs.collect::<SmallVec<[_; 1]>>()
+ }
+ }
}
impl Attr {
diff --git a/crates/hir-expand/src/change.rs b/crates/hir-expand/src/change.rs
index 6873cb7eaf..3959741e6f 100644
--- a/crates/hir-expand/src/change.rs
+++ b/crates/hir-expand/src/change.rs
@@ -1,7 +1,6 @@
//! Defines a unit of change that can applied to the database to get the next
//! state. Changes are transactional.
-use base_db::{CrateGraphBuilder, FileChange, SourceRoot};
-use salsa::Durability;
+use base_db::{CrateGraphBuilder, FileChange, SourceRoot, salsa::Durability};
use span::FileId;
use triomphe::Arc;
diff --git a/crates/hir-expand/src/db.rs b/crates/hir-expand/src/db.rs
index 29b7b33fd0..7cb1b6c020 100644
--- a/crates/hir-expand/src/db.rs
+++ b/crates/hir-expand/src/db.rs
@@ -18,10 +18,7 @@ use crate::{
cfg_process,
declarative::DeclarativeMacroExpander,
fixup::{self, SyntaxFixupUndoInfo},
- hygiene::{
- SyntaxContextExt as _, span_with_call_site_ctxt, span_with_def_site_ctxt,
- span_with_mixed_site_ctxt,
- },
+ hygiene::{span_with_call_site_ctxt, span_with_def_site_ctxt, span_with_mixed_site_ctxt},
proc_macro::{CrateProcMacros, CustomProcMacroExpander, ProcMacros},
span_map::{ExpansionSpanMap, RealSpanMap, SpanMap, SpanMapRef},
tt,
@@ -147,7 +144,7 @@ pub trait ExpandDatabase: RootQueryDb {
fn syntax_context(&self, file: HirFileId, edition: Edition) -> SyntaxContext;
}
-#[salsa::interned(no_lifetime, id = span::SyntaxContext)]
+#[salsa_macros::interned(no_lifetime, id = span::SyntaxContext)]
pub struct SyntaxContextWrapper {
pub data: SyntaxContext,
}
@@ -752,8 +749,7 @@ fn check_tt_count(tt: &tt::TopSubtree) -> Result<(), ExpandResult<()>> {
err: Some(ExpandError::other(
tt.delimiter.open,
format!(
- "macro invocation exceeds token limit: produced {} tokens, limit is {}",
- count, TOKEN_LIMIT,
+ "macro invocation exceeds token limit: produced {count} tokens, limit is {TOKEN_LIMIT}",
),
)),
})
diff --git a/crates/hir-expand/src/declarative.rs b/crates/hir-expand/src/declarative.rs
index 1fa682ce3a..0d100c1364 100644
--- a/crates/hir-expand/src/declarative.rs
+++ b/crates/hir-expand/src/declarative.rs
@@ -82,7 +82,7 @@ impl DeclarativeMacroExpander {
let transparency = |node| {
// ... would be nice to have the item tree here
- let attrs = RawAttrs::new(db, node, map.as_ref()).filter(db, def_crate);
+ let attrs = RawAttrs::new_expanded(db, node, map.as_ref(), def_crate.cfg_options(db));
match attrs
.iter()
.find(|it| {
diff --git a/crates/hir-expand/src/hygiene.rs b/crates/hir-expand/src/hygiene.rs
index e7856920bc..28800c6fab 100644
--- a/crates/hir-expand/src/hygiene.rs
+++ b/crates/hir-expand/src/hygiene.rs
@@ -22,7 +22,7 @@
// FIXME: Move this into the span crate? Not quite possible today as that depends on `MacroCallLoc`
// which contains a bunch of unrelated things
-use std::{convert::identity, iter};
+use std::convert::identity;
use span::{Edition, MacroCallId, Span, SyntaxContext};
@@ -141,61 +141,3 @@ fn apply_mark_internal(
|_| opaque_and_semitransparent,
)
}
-
-pub trait SyntaxContextExt {
- fn normalize_to_macro_rules(self, db: &dyn ExpandDatabase) -> span::SyntaxContext;
- fn normalize_to_macros_2_0(self, db: &dyn ExpandDatabase) -> span::SyntaxContext;
- fn parent_ctxt(self, db: &dyn ExpandDatabase) -> span::SyntaxContext;
- fn remove_mark(&mut self, db: &dyn ExpandDatabase)
- -> (Option<span::MacroCallId>, Transparency);
- fn outer_mark(self, db: &dyn ExpandDatabase) -> (Option<span::MacroCallId>, Transparency);
- fn marks(self, db: &dyn ExpandDatabase) -> Vec<(span::MacroCallId, Transparency)>;
- fn is_opaque(self, db: &dyn ExpandDatabase) -> bool;
-}
-
-impl SyntaxContextExt for SyntaxContext {
- fn normalize_to_macro_rules(self, db: &dyn ExpandDatabase) -> span::SyntaxContext {
- self.opaque_and_semitransparent(db)
- }
- fn normalize_to_macros_2_0(self, db: &dyn ExpandDatabase) -> span::SyntaxContext {
- self.opaque(db)
- }
- fn parent_ctxt(self, db: &dyn ExpandDatabase) -> span::SyntaxContext {
- self.parent(db)
- }
- fn outer_mark(self, db: &dyn ExpandDatabase) -> (Option<span::MacroCallId>, Transparency) {
- let data = self;
- (data.outer_expn(db), data.outer_transparency(db))
- }
- fn remove_mark(
- &mut self,
- db: &dyn ExpandDatabase,
- ) -> (Option<span::MacroCallId>, Transparency) {
- let data = *self;
- *self = data.parent(db);
- (data.outer_expn(db), data.outer_transparency(db))
- }
- fn marks(self, db: &dyn ExpandDatabase) -> Vec<(span::MacroCallId, Transparency)> {
- let mut marks = marks_rev(self, db).collect::<Vec<_>>();
- marks.reverse();
- marks
- }
- fn is_opaque(self, db: &dyn ExpandDatabase) -> bool {
- !self.is_root() && self.outer_transparency(db).is_opaque()
- }
-}
-
-// FIXME: Make this a SyntaxContextExt method once we have RPIT
-pub fn marks_rev(
- ctxt: SyntaxContext,
- db: &dyn ExpandDatabase,
-) -> impl Iterator<Item = (span::MacroCallId, Transparency)> + '_ {
- iter::successors(Some(ctxt), move |&mark| Some(mark.parent_ctxt(db)))
- .take_while(|&it| !it.is_root())
- .map(|ctx| {
- let mark = ctx.outer_mark(db);
- // We stop before taking the root expansion, as such we cannot encounter a `None` outer
- // expansion, as only the ROOT has it.
- (mark.0.unwrap(), mark.1)
- })
-}
diff --git a/crates/hir-expand/src/lib.rs b/crates/hir-expand/src/lib.rs
index f0a9a2ad52..d844d8f41e 100644
--- a/crates/hir-expand/src/lib.rs
+++ b/crates/hir-expand/src/lib.rs
@@ -206,8 +206,7 @@ impl ExpandErrorKind {
},
None => RenderedExpandError {
message: format!(
- "internal error: proc-macro map is missing error entry for crate {:?}",
- def_crate
+ "internal error: proc-macro map is missing error entry for crate {def_crate:?}"
),
error: true,
kind: RenderedExpandError::GENERAL_KIND,
@@ -1051,7 +1050,7 @@ impl ExpandTo {
intern::impl_internable!(ModPath, attrs::AttrInput);
-#[salsa::interned(no_lifetime, debug)]
+#[salsa_macros::interned(no_lifetime, debug)]
#[doc(alias = "MacroFileId")]
pub struct MacroCallId {
pub loc: MacroCallLoc,
@@ -1071,7 +1070,7 @@ impl From<MacroCallId> for span::MacroCallId {
}
}
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, salsa::Supertype)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, salsa_macros::Supertype)]
pub enum HirFileId {
FileId(EditionedFileId),
MacroFile(MacroCallId),
diff --git a/crates/hir-expand/src/mod_path.rs b/crates/hir-expand/src/mod_path.rs
index 72a5627636..9f1e3879e1 100644
--- a/crates/hir-expand/src/mod_path.rs
+++ b/crates/hir-expand/src/mod_path.rs
@@ -7,7 +7,7 @@ use std::{
use crate::{
db::ExpandDatabase,
- hygiene::{SyntaxContextExt, Transparency, marks_rev},
+ hygiene::Transparency,
name::{AsName, Name},
tt,
};
@@ -340,7 +340,7 @@ pub fn resolve_crate_root(db: &dyn ExpandDatabase, mut ctxt: SyntaxContext) -> O
// definitions actually produced by `macro` and `macro` definitions produced by
// `macro_rules!`, but at least such configurations are not stable yet.
ctxt = ctxt.normalize_to_macro_rules(db);
- let mut iter = marks_rev(ctxt, db).peekable();
+ let mut iter = ctxt.marks_rev(db).peekable();
let mut result_mark = None;
// Find the last opaque mark from the end if it exists.
while let Some(&(mark, Transparency::Opaque)) = iter.peek() {
diff --git a/crates/hir-expand/src/name.rs b/crates/hir-expand/src/name.rs
index d43ef38f29..217d991d11 100644
--- a/crates/hir-expand/src/name.rs
+++ b/crates/hir-expand/src/name.rs
@@ -191,7 +191,7 @@ impl Name {
// FIXME: Remove this in favor of `display`, see fixme on `as_str`
#[doc(hidden)]
pub fn display_no_db(&self, edition: Edition) -> impl fmt::Display + '_ {
- Display { name: self, needs_escaping: is_raw_identifier(self.symbol.as_str(), edition) }
+ Display { name: self, edition }
}
pub fn symbol(&self) -> &Symbol {
@@ -201,15 +201,28 @@ impl Name {
struct Display<'a> {
name: &'a Name,
- needs_escaping: bool,
+ edition: Edition,
}
impl fmt::Display for Display<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- if self.needs_escaping {
- write!(f, "r#")?;
+ let mut symbol = self.name.symbol.as_str();
+
+ if symbol == "'static" {
+ // FIXME: '`static` can also be a label, and there it does need escaping.
+ // But knowing where it is will require adding a parameter to `display()`,
+ // and that is an infectious change.
+ return f.write_str(symbol);
+ }
+
+ if let Some(s) = symbol.strip_prefix('\'') {
+ f.write_str("'")?;
+ symbol = s;
+ }
+ if is_raw_identifier(symbol, self.edition) {
+ f.write_str("r#")?;
}
- fmt::Display::fmt(self.name.symbol.as_str(), f)
+ f.write_str(symbol)
}
}
diff --git a/crates/hir-ty/Cargo.toml b/crates/hir-ty/Cargo.toml
index 69ad7703c2..efa544cf39 100644
--- a/crates/hir-ty/Cargo.toml
+++ b/crates/hir-ty/Cargo.toml
@@ -34,6 +34,7 @@ indexmap.workspace = true
rustc_apfloat = "0.2.2"
query-group.workspace = true
salsa.workspace = true
+salsa-macros.workspace = true
ra-ap-rustc_abi.workspace = true
ra-ap-rustc_index.workspace = true
diff --git a/crates/hir-ty/src/autoderef.rs b/crates/hir-ty/src/autoderef.rs
index 7115445456..7acc9456ec 100644
--- a/crates/hir-ty/src/autoderef.rs
+++ b/crates/hir-ty/src/autoderef.rs
@@ -198,15 +198,13 @@ pub(crate) fn deref_by_trait(
// blanked impl on `Deref`.
#[expect(clippy::overly_complex_bool_expr)]
if use_receiver_trait && false {
- if let Some(receiver) =
- db.lang_item(table.trait_env.krate, LangItem::Receiver).and_then(|l| l.as_trait())
- {
+ if let Some(receiver) = LangItem::Receiver.resolve_trait(db, table.trait_env.krate) {
return Some(receiver);
}
}
// Old rustc versions might not have `Receiver` trait.
// Fallback to `Deref` if they don't
- db.lang_item(table.trait_env.krate, LangItem::Deref).and_then(|l| l.as_trait())
+ LangItem::Deref.resolve_trait(db, table.trait_env.krate)
};
let trait_id = trait_id()?;
let target =
diff --git a/crates/hir-ty/src/chalk_db.rs b/crates/hir-ty/src/chalk_db.rs
index 2aa9401eef..cd799c03dd 100644
--- a/crates/hir-ty/src/chalk_db.rs
+++ b/crates/hir-ty/src/chalk_db.rs
@@ -16,7 +16,7 @@ use hir_def::{
AssocItemId, BlockId, CallableDefId, GenericDefId, HasModule, ItemContainerId, Lookup,
TypeAliasId, VariantId,
hir::Movability,
- lang_item::{LangItem, LangItemTarget},
+ lang_item::LangItem,
signatures::{ImplFlags, StructFlags, TraitFlags},
};
@@ -262,10 +262,7 @@ impl chalk_solve::RustIrDatabase<Interner> for ChalkContext<'_> {
well_known_trait: rust_ir::WellKnownTrait,
) -> Option<chalk_ir::TraitId<Interner>> {
let lang_attr = lang_item_from_well_known_trait(well_known_trait);
- let trait_ = match self.db.lang_item(self.krate, lang_attr) {
- Some(LangItemTarget::Trait(trait_)) => trait_,
- _ => return None,
- };
+ let trait_ = lang_attr.resolve_trait(self.db, self.krate)?;
Some(to_chalk_trait_id(trait_))
}
@@ -306,11 +303,8 @@ impl chalk_solve::RustIrDatabase<Interner> for ChalkContext<'_> {
chalk_ir::Binders::new(binders, bound)
}
crate::ImplTraitId::AsyncBlockTypeImplTrait(..) => {
- if let Some((future_trait, future_output)) = self
- .db
- .lang_item(self.krate, LangItem::Future)
- .and_then(|item| item.as_trait())
- .and_then(|trait_| {
+ if let Some((future_trait, future_output)) =
+ LangItem::Future.resolve_trait(self.db, self.krate).and_then(|trait_| {
let alias = self
.db
.trait_items(trait_)
@@ -338,10 +332,7 @@ impl chalk_solve::RustIrDatabase<Interner> for ChalkContext<'_> {
});
let mut binder = vec![];
binder.push(crate::wrap_empty_binders(impl_bound));
- let sized_trait = self
- .db
- .lang_item(self.krate, LangItem::Sized)
- .and_then(|item| item.as_trait());
+ let sized_trait = LangItem::Sized.resolve_trait(self.db, self.krate);
if let Some(sized_trait_) = sized_trait {
let sized_bound = WhereClause::Implemented(TraitRef {
trait_id: to_chalk_trait_id(sized_trait_),
@@ -646,7 +637,10 @@ pub(crate) fn associated_ty_data_query(
.fill_with_bound_vars(crate::DebruijnIndex::INNERMOST, 0)
.build();
let pro_ty = TyBuilder::assoc_type_projection(db, type_alias, Some(trait_subst))
- .fill_with_bound_vars(crate::DebruijnIndex::INNERMOST, generic_params.len_self())
+ .fill_with_bound_vars(
+ crate::DebruijnIndex::INNERMOST,
+ generic_params.parent_generics().map_or(0, |it| it.len()),
+ )
.build();
let self_ty = TyKind::Alias(AliasTy::Projection(pro_ty)).intern(Interner);
@@ -660,9 +654,8 @@ pub(crate) fn associated_ty_data_query(
}
if !ctx.unsized_types.contains(&self_ty) {
- let sized_trait = db
- .lang_item(resolver.krate(), LangItem::Sized)
- .and_then(|lang_item| lang_item.as_trait().map(to_chalk_trait_id));
+ let sized_trait =
+ LangItem::Sized.resolve_trait(db, resolver.krate()).map(to_chalk_trait_id);
let sized_bound = sized_trait.into_iter().map(|sized_trait| {
let trait_bound =
rust_ir::TraitBound { trait_id: sized_trait, args_no_self: Default::default() };
diff --git a/crates/hir-ty/src/chalk_ext.rs b/crates/hir-ty/src/chalk_ext.rs
index 0f0cf6ae7a..aabc4c4234 100644
--- a/crates/hir-ty/src/chalk_ext.rs
+++ b/crates/hir-ty/src/chalk_ext.rs
@@ -251,9 +251,7 @@ impl TyExt for Ty {
match db.lookup_intern_impl_trait_id((*opaque_ty_id).into()) {
ImplTraitId::AsyncBlockTypeImplTrait(def, _expr) => {
let krate = def.module(db).krate();
- if let Some(future_trait) =
- db.lang_item(krate, LangItem::Future).and_then(|item| item.as_trait())
- {
+ if let Some(future_trait) = LangItem::Future.resolve_trait(db, krate) {
// This is only used by type walking.
// Parameters will be walked outside, and projection predicate is not used.
// So just provide the Future trait.
@@ -364,8 +362,7 @@ impl TyExt for Ty {
fn is_copy(self, db: &dyn HirDatabase, owner: DefWithBodyId) -> bool {
let crate_id = owner.module(db).krate();
- let Some(copy_trait) = db.lang_item(crate_id, LangItem::Copy).and_then(|it| it.as_trait())
- else {
+ let Some(copy_trait) = LangItem::Copy.resolve_trait(db, crate_id) else {
return false;
};
let trait_ref = TyBuilder::trait_ref(db, copy_trait).push(self).build();
diff --git a/crates/hir-ty/src/db.rs b/crates/hir-ty/src/db.rs
index c24ef16b49..980ee264b0 100644
--- a/crates/hir-ty/src/db.rs
+++ b/crates/hir-ty/src/db.rs
@@ -283,8 +283,9 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug {
#[salsa::invoke(crate::variance::variances_of)]
#[salsa::cycle(
- cycle_fn = crate::variance::variances_of_cycle_fn,
- cycle_initial = crate::variance::variances_of_cycle_initial,
+ // cycle_fn = crate::variance::variances_of_cycle_fn,
+ // cycle_initial = crate::variance::variances_of_cycle_initial,
+ cycle_result = crate::variance::variances_of_cycle_initial,
)]
fn variances_of(&self, def: GenericDefId) -> Option<Arc<[crate::variance::Variance]>>;
diff --git a/crates/hir-ty/src/diagnostics/expr.rs b/crates/hir-ty/src/diagnostics/expr.rs
index 5e3d880589..5710641276 100644
--- a/crates/hir-ty/src/diagnostics/expr.rs
+++ b/crates/hir-ty/src/diagnostics/expr.rs
@@ -482,9 +482,8 @@ struct FilterMapNextChecker {
impl FilterMapNextChecker {
fn new(resolver: &hir_def::resolver::Resolver, db: &dyn HirDatabase) -> Self {
// Find and store the FunctionIds for Iterator::filter_map and Iterator::next
- let (next_function_id, filter_map_function_id) = match db
- .lang_item(resolver.krate(), LangItem::IteratorNext)
- .and_then(|it| it.as_function())
+ let (next_function_id, filter_map_function_id) = match LangItem::IteratorNext
+ .resolve_function(db, resolver.krate())
{
Some(next_function_id) => (
Some(next_function_id),
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 6323d8b71b..068fc22f2c 100644
--- a/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs
+++ b/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs
@@ -301,6 +301,7 @@ impl<'db> MatchCheckCtx<'db> {
// ignore this issue.
Ref => PatKind::Deref { subpattern: subpatterns.next().unwrap() },
Slice(_) => unimplemented!(),
+ DerefPattern(_) => unimplemented!(),
&Str(void) => match void {},
Wildcard | NonExhaustive | Hidden | PrivateUninhabited => PatKind::Wild,
Never => PatKind::Never,
@@ -351,6 +352,7 @@ impl PatCx for MatchCheckCtx<'_> {
},
Ref => 1,
Slice(..) => unimplemented!(),
+ DerefPattern(..) => unimplemented!(),
Never | Bool(..) | IntRange(..) | F16Range(..) | F32Range(..) | F64Range(..)
| F128Range(..) | Str(..) | Opaque(..) | NonExhaustive | PrivateUninhabited
| Hidden | Missing | Wildcard => 0,
@@ -411,6 +413,7 @@ impl PatCx for MatchCheckCtx<'_> {
}
},
Slice(_) => unreachable!("Found a `Slice` constructor in match checking"),
+ DerefPattern(_) => unreachable!("Found a `DerefPattern` constructor in match checking"),
Never | Bool(..) | IntRange(..) | F16Range(..) | F32Range(..) | F64Range(..)
| F128Range(..) | Str(..) | Opaque(..) | NonExhaustive | PrivateUninhabited
| Hidden | Missing | Wildcard => {
diff --git a/crates/hir-ty/src/display.rs b/crates/hir-ty/src/display.rs
index f62e4bb4f8..f0989d9de9 100644
--- a/crates/hir-ty/src/display.rs
+++ b/crates/hir-ty/src/display.rs
@@ -19,7 +19,7 @@ use hir_def::{
hir::generics::{TypeOrConstParamData, TypeParamProvenance, WherePredicate},
item_scope::ItemInNs,
item_tree::FieldsShape,
- lang_item::{LangItem, LangItemTarget},
+ lang_item::LangItem,
nameres::DefMap,
signatures::VariantFields,
type_ref::{
@@ -90,11 +90,26 @@ pub struct HirFormatter<'a> {
show_container_bounds: bool,
omit_verbose_types: bool,
closure_style: ClosureStyle,
+ display_lifetimes: DisplayLifetime,
display_kind: DisplayKind,
display_target: DisplayTarget,
bounds_formatting_ctx: BoundsFormattingCtx,
}
+// FIXME: To consider, ref and dyn trait lifetimes can be omitted if they are `'_`, path args should
+// not be when in signatures
+// So this enum does not encode this well enough
+// Also 'static can be omitted for ref and dyn trait lifetimes in static/const item types
+// FIXME: Also named lifetimes may be rendered in places where their name is not in scope?
+#[derive(Copy, Clone)]
+pub enum DisplayLifetime {
+ Always,
+ OnlyStatic,
+ OnlyNamed,
+ OnlyNamedOrStatic,
+ Never,
+}
+
#[derive(Default)]
enum BoundsFormattingCtx {
Entered {
@@ -155,6 +170,21 @@ impl HirFormatter<'_> {
}
}
}
+
+ fn render_lifetime(&self, lifetime: &Lifetime) -> bool {
+ match self.display_lifetimes {
+ DisplayLifetime::Always => true,
+ DisplayLifetime::OnlyStatic => matches!(***lifetime.interned(), LifetimeData::Static),
+ DisplayLifetime::OnlyNamed => {
+ matches!(***lifetime.interned(), LifetimeData::Placeholder(_))
+ }
+ DisplayLifetime::OnlyNamedOrStatic => matches!(
+ ***lifetime.interned(),
+ LifetimeData::Static | LifetimeData::Placeholder(_)
+ ),
+ DisplayLifetime::Never => false,
+ }
+ }
}
pub trait HirDisplay {
@@ -189,6 +219,7 @@ pub trait HirDisplay {
display_kind,
closure_style,
show_container_bounds,
+ display_lifetimes: DisplayLifetime::OnlyNamedOrStatic,
}
}
@@ -212,6 +243,7 @@ pub trait HirDisplay {
display_target,
display_kind: DisplayKind::Diagnostics,
show_container_bounds: false,
+ display_lifetimes: DisplayLifetime::OnlyNamedOrStatic,
}
}
@@ -236,6 +268,7 @@ pub trait HirDisplay {
display_target,
display_kind: DisplayKind::Diagnostics,
show_container_bounds: false,
+ display_lifetimes: DisplayLifetime::OnlyNamedOrStatic,
}
}
@@ -260,6 +293,7 @@ pub trait HirDisplay {
display_target,
display_kind: DisplayKind::Diagnostics,
show_container_bounds: false,
+ display_lifetimes: DisplayLifetime::OnlyNamedOrStatic,
}
}
@@ -284,6 +318,7 @@ pub trait HirDisplay {
display_target: DisplayTarget::from_crate(db, module_id.krate()),
display_kind: DisplayKind::SourceCode { target_module_id: module_id, allow_opaque },
show_container_bounds: false,
+ display_lifetimes: DisplayLifetime::OnlyNamedOrStatic,
bounds_formatting_ctx: Default::default(),
}) {
Ok(()) => {}
@@ -312,6 +347,7 @@ pub trait HirDisplay {
display_target,
display_kind: DisplayKind::Test,
show_container_bounds: false,
+ display_lifetimes: DisplayLifetime::Always,
}
}
@@ -336,6 +372,7 @@ pub trait HirDisplay {
display_target,
display_kind: DisplayKind::Diagnostics,
show_container_bounds,
+ display_lifetimes: DisplayLifetime::OnlyNamedOrStatic,
}
}
}
@@ -480,6 +517,7 @@ pub struct HirDisplayWrapper<'a, T> {
display_kind: DisplayKind,
display_target: DisplayTarget,
show_container_bounds: bool,
+ display_lifetimes: DisplayLifetime,
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
@@ -502,7 +540,7 @@ impl<T: HirDisplay> HirDisplayWrapper<'_, T> {
self.t.hir_fmt(&mut HirFormatter {
db: self.db,
fmt: f,
- buf: String::with_capacity(20),
+ buf: String::with_capacity(self.max_size.unwrap_or(20)),
curr_size: 0,
max_size: self.max_size,
entity_limit: self.limited_size,
@@ -511,6 +549,7 @@ impl<T: HirDisplay> HirDisplayWrapper<'_, T> {
display_target: self.display_target,
closure_style: self.closure_style,
show_container_bounds: self.show_container_bounds,
+ display_lifetimes: self.display_lifetimes,
bounds_formatting_ctx: Default::default(),
})
}
@@ -519,6 +558,11 @@ impl<T: HirDisplay> HirDisplayWrapper<'_, T> {
self.closure_style = c;
self
}
+
+ pub fn with_lifetime_display(mut self, l: DisplayLifetime) -> Self {
+ self.display_lifetimes = l;
+ self
+ }
}
impl<T> fmt::Display for HirDisplayWrapper<'_, T>
@@ -1022,9 +1066,7 @@ impl HirDisplay for Ty {
kind @ (TyKind::Raw(m, t) | TyKind::Ref(m, _, t)) => {
if let TyKind::Ref(_, l, _) = kind {
f.write_char('&')?;
- if cfg!(test) {
- // rendering these unconditionally is probably too much (at least for inlay
- // hints) so we gate it to testing only for the time being
+ if f.render_lifetime(l) {
l.hir_fmt(f)?;
f.write_char(' ')?;
}
@@ -1055,9 +1097,10 @@ impl HirDisplay for Ty {
})
};
let (preds_to_print, has_impl_fn_pred) = match t.kind(Interner) {
- TyKind::Dyn(dyn_ty) if dyn_ty.bounds.skip_binders().interned().len() > 1 => {
+ TyKind::Dyn(dyn_ty) => {
let bounds = dyn_ty.bounds.skip_binders().interned();
- (bounds.len(), contains_impl_fn(bounds))
+ let render_lifetime = f.render_lifetime(&dyn_ty.lifetime);
+ (bounds.len() + render_lifetime as usize, contains_impl_fn(bounds))
}
TyKind::Alias(AliasTy::Opaque(OpaqueTy {
opaque_ty_id,
@@ -1348,9 +1391,8 @@ impl HirDisplay for Ty {
)?;
}
ImplTraitId::AsyncBlockTypeImplTrait(body, ..) => {
- let future_trait = db
- .lang_item(body.module(db).krate(), LangItem::Future)
- .and_then(LangItemTarget::as_trait);
+ let future_trait =
+ LangItem::Future.resolve_trait(db, body.module(db).krate());
let output = future_trait.and_then(|t| {
db.trait_items(t)
.associated_type_by_name(&Name::new_symbol_root(sym::Output))
@@ -1480,7 +1522,7 @@ impl HirDisplay for Ty {
TyKind::BoundVar(idx) => idx.hir_fmt(f)?,
TyKind::Dyn(dyn_ty) => {
// Reorder bounds to satisfy `write_bounds_like_dyn_trait()`'s expectation.
- // FIXME: `Iterator::partition_in_place()` or `Vec::drain_filter()` may make it
+ // FIXME: `Iterator::partition_in_place()` or `Vec::extract_if()` may make it
// more efficient when either of them hits stable.
let mut bounds: SmallVec<[_; 4]> =
dyn_ty.bounds.skip_binders().iter(Interner).cloned().collect();
@@ -1489,6 +1531,17 @@ impl HirDisplay for Ty {
bounds.extend(others);
bounds.extend(auto_traits);
+ if f.render_lifetime(&dyn_ty.lifetime) {
+ // we skip the binders in `write_bounds_like_dyn_trait_with_prefix`
+ bounds.push(Binders::empty(
+ Interner,
+ chalk_ir::WhereClause::TypeOutlives(chalk_ir::TypeOutlives {
+ ty: self.clone(),
+ lifetime: dyn_ty.lifetime.clone(),
+ }),
+ ));
+ }
+
write_bounds_like_dyn_trait_with_prefix(
f,
"dyn",
@@ -1728,9 +1781,7 @@ impl SizedByDefault {
match self {
Self::NotSized => false,
Self::Sized { anchor } => {
- let sized_trait = db
- .lang_item(anchor, LangItem::Sized)
- .and_then(|lang_item| lang_item.as_trait());
+ let sized_trait = LangItem::Sized.resolve_trait(db, anchor);
Some(trait_) == sized_trait
}
}
@@ -1895,8 +1946,7 @@ fn write_bounds_like_dyn_trait(
write!(f, ">")?;
}
if let SizedByDefault::Sized { anchor } = default_sized {
- let sized_trait =
- f.db.lang_item(anchor, LangItem::Sized).and_then(|lang_item| lang_item.as_trait());
+ let sized_trait = LangItem::Sized.resolve_trait(f.db, anchor);
if !is_sized {
if !first {
write!(f, " + ")?;
@@ -1993,7 +2043,6 @@ impl HirDisplay for LifetimeData {
write!(f, "{}", param_data.name.display(f.db, f.edition()))?;
Ok(())
}
- _ if f.display_kind.is_source_code() => write!(f, "'_"),
LifetimeData::BoundVar(idx) => idx.hir_fmt(f),
LifetimeData::InferenceVar(_) => write!(f, "_"),
LifetimeData::Static => write!(f, "'static"),
diff --git a/crates/hir-ty/src/drop.rs b/crates/hir-ty/src/drop.rs
index 9823c854d5..70763759ef 100644
--- a/crates/hir-ty/src/drop.rs
+++ b/crates/hir-ty/src/drop.rs
@@ -19,9 +19,7 @@ fn has_destructor(db: &dyn HirDatabase, adt: AdtId) -> bool {
AdtId::StructId(id) => db.lookup_intern_struct(id).container,
AdtId::UnionId(id) => db.lookup_intern_union(id).container,
};
- let Some(drop_trait) =
- db.lang_item(module.krate(), LangItem::Drop).and_then(|it| it.as_trait())
- else {
+ let Some(drop_trait) = LangItem::Drop.resolve_trait(db, module.krate()) else {
return false;
};
let impls = match module.containing_block() {
@@ -181,8 +179,7 @@ fn projection_has_drop_glue(
}
fn is_copy(db: &dyn HirDatabase, ty: Ty, env: Arc<TraitEnvironment>) -> bool {
- let Some(copy_trait) = db.lang_item(env.krate, LangItem::Copy).and_then(|it| it.as_trait())
- else {
+ let Some(copy_trait) = LangItem::Copy.resolve_trait(db, env.krate) else {
return false;
};
let trait_ref = TyBuilder::trait_ref(db, copy_trait).push(ty).build();
diff --git a/crates/hir-ty/src/dyn_compatibility.rs b/crates/hir-ty/src/dyn_compatibility.rs
index 80b1847390..106b996b13 100644
--- a/crates/hir-ty/src/dyn_compatibility.rs
+++ b/crates/hir-ty/src/dyn_compatibility.rs
@@ -124,7 +124,7 @@ pub fn dyn_compatibility_of_trait_query(
fn generics_require_sized_self(db: &dyn HirDatabase, def: GenericDefId) -> bool {
let krate = def.module(db).krate();
- let Some(sized) = db.lang_item(krate, LangItem::Sized).and_then(|l| l.as_trait()) else {
+ let Some(sized) = LangItem::Sized.resolve_trait(db, krate) else {
return false;
};
@@ -491,8 +491,8 @@ fn receiver_is_dispatchable(
let krate = func.module(db).krate();
let traits = (
- db.lang_item(krate, LangItem::Unsize).and_then(|it| it.as_trait()),
- db.lang_item(krate, LangItem::DispatchFromDyn).and_then(|it| it.as_trait()),
+ LangItem::Unsize.resolve_trait(db, krate),
+ LangItem::DispatchFromDyn.resolve_trait(db, krate),
);
let (Some(unsize_did), Some(dispatch_from_dyn_did)) = traits else {
return false;
@@ -515,7 +515,7 @@ fn receiver_is_dispatchable(
trait_id: to_chalk_trait_id(trait_),
substitution: Substitution::from_iter(
Interner,
- std::iter::once(unsized_self_ty.clone().cast(Interner))
+ std::iter::once(unsized_self_ty.cast(Interner))
.chain(placeholder_subst.iter(Interner).skip(1).cloned()),
),
});
diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs
index 790914fdaf..f0ec31db8b 100644
--- a/crates/hir-ty/src/infer.rs
+++ b/crates/hir-ty/src/infer.rs
@@ -39,7 +39,7 @@ use hir_def::{
builtin_type::{BuiltinInt, BuiltinType, BuiltinUint},
expr_store::{Body, ExpressionStore, HygieneId, path::Path},
hir::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, LabelId, PatId},
- lang_item::{LangItem, LangItemTarget},
+ lang_item::{LangItem, LangItemTarget, lang_item},
layout::Integer,
resolver::{HasResolver, ResolveValueResult, Resolver, TypeNs, ValueNs},
signatures::{ConstSignature, StaticSignature},
@@ -1801,7 +1801,7 @@ impl<'a> InferenceContext<'a> {
fn resolve_lang_item(&self, item: LangItem) -> Option<LangItemTarget> {
let krate = self.resolver.krate();
- self.db.lang_item(krate, item)
+ lang_item(self.db, krate, item)
}
fn resolve_output_on(&self, trait_: TraitId) -> Option<TypeAliasId> {
diff --git a/crates/hir-ty/src/infer/closure.rs b/crates/hir-ty/src/infer/closure.rs
index cf3b15d2a6..800897c6fc 100644
--- a/crates/hir-ty/src/infer/closure.rs
+++ b/crates/hir-ty/src/infer/closure.rs
@@ -127,7 +127,7 @@ impl InferenceContext<'_> {
let prev_diverges = mem::replace(&mut self.diverges, Diverges::Maybe);
let prev_closure = mem::replace(&mut self.current_closure, id);
let prev_ret_ty = mem::replace(&mut self.return_ty, body_ret_ty.clone());
- let prev_ret_coercion = self.return_coercion.replace(CoerceMany::new(body_ret_ty.clone()));
+ let prev_ret_coercion = self.return_coercion.replace(CoerceMany::new(body_ret_ty));
let prev_resume_yield_tys = mem::replace(&mut self.resume_yield_tys, resume_yield_tys);
self.with_breakable_ctx(BreakableKind::Border, None, None, |this| {
diff --git a/crates/hir-ty/src/infer/coerce.rs b/crates/hir-ty/src/infer/coerce.rs
index 847dd43a02..39bd90849f 100644
--- a/crates/hir-ty/src/infer/coerce.rs
+++ b/crates/hir-ty/src/infer/coerce.rs
@@ -8,10 +8,7 @@
use std::iter;
use chalk_ir::{BoundVar, Goal, Mutability, TyKind, TyVariableKind, cast::Cast};
-use hir_def::{
- hir::ExprId,
- lang_item::{LangItem, LangItemTarget},
-};
+use hir_def::{hir::ExprId, lang_item::LangItem};
use stdx::always;
use triomphe::Arc;
@@ -701,8 +698,8 @@ impl InferenceTable<'_> {
reborrow.as_ref().map_or_else(|| from_ty.clone(), |(_, adj)| adj.target.clone());
let krate = self.trait_env.krate;
- let coerce_unsized_trait = match self.db.lang_item(krate, LangItem::CoerceUnsized) {
- Some(LangItemTarget::Trait(trait_)) => trait_,
+ let coerce_unsized_trait = match LangItem::CoerceUnsized.resolve_trait(self.db, krate) {
+ Some(trait_) => trait_,
_ => return Err(TypeError),
};
diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs
index 5468254ab9..8084b394d0 100644
--- a/crates/hir-ty/src/infer/expr.rs
+++ b/crates/hir-ty/src/infer/expr.rs
@@ -827,9 +827,9 @@ impl InferenceContext<'_> {
}
let assoc = self.resolve_ops_index_output();
self.resolve_associated_type_with_params(
- self_ty.clone(),
+ self_ty,
assoc,
- &[index_ty.clone().cast(Interner)],
+ &[index_ty.cast(Interner)],
)
} else {
self.err_ty()
diff --git a/crates/hir-ty/src/infer/mutability.rs b/crates/hir-ty/src/infer/mutability.rs
index cf0152ecd2..ac450c0b55 100644
--- a/crates/hir-ty/src/infer/mutability.rs
+++ b/crates/hir-ty/src/infer/mutability.rs
@@ -126,10 +126,8 @@ impl InferenceContext<'_> {
&Expr::Index { base, index } => {
if mutability == Mutability::Mut {
if let Some((f, _)) = self.result.method_resolutions.get_mut(&tgt_expr) {
- if let Some(index_trait) = self
- .db
- .lang_item(self.table.trait_env.krate, LangItem::IndexMut)
- .and_then(|l| l.as_trait())
+ if let Some(index_trait) =
+ LangItem::IndexMut.resolve_trait(self.db, self.table.trait_env.krate)
{
if let Some(index_fn) = self
.db
@@ -183,10 +181,8 @@ impl InferenceContext<'_> {
let mut mutability = mutability;
if let Some((f, _)) = self.result.method_resolutions.get_mut(&tgt_expr) {
if mutability == Mutability::Mut {
- if let Some(deref_trait) = self
- .db
- .lang_item(self.table.trait_env.krate, LangItem::DerefMut)
- .and_then(|l| l.as_trait())
+ if let Some(deref_trait) =
+ LangItem::DerefMut.resolve_trait(self.db, self.table.trait_env.krate)
{
let ty = self.result.type_of_expr.get(*expr);
let is_mut_ptr = ty.is_some_and(|ty| {
diff --git a/crates/hir-ty/src/infer/pat.rs b/crates/hir-ty/src/infer/pat.rs
index dc1de3b9e8..a9a3265858 100644
--- a/crates/hir-ty/src/infer/pat.rs
+++ b/crates/hir-ty/src/infer/pat.rs
@@ -435,7 +435,7 @@ impl InferenceContext<'_> {
decl: Option<DeclContext>,
) -> Ty {
let (expectation_type, expectation_lt) = match expected.as_reference() {
- Some((inner_ty, lifetime, _exp_mut)) => (inner_ty.clone(), lifetime.clone()),
+ Some((inner_ty, lifetime, _exp_mut)) => (inner_ty.clone(), lifetime),
None => {
let inner_ty = self.table.new_type_var();
let inner_lt = self.table.new_lifetime_var();
@@ -597,7 +597,7 @@ impl InferenceContext<'_> {
let size = consteval::usize_const(self.db, Some(len as u128), self.owner.krate(self.db));
let elem_ty = self.table.new_type_var();
- let array_ty = TyKind::Array(elem_ty.clone(), size).intern(Interner);
+ let array_ty = TyKind::Array(elem_ty, size).intern(Interner);
Some(array_ty)
}
diff --git a/crates/hir-ty/src/infer/unify.rs b/crates/hir-ty/src/infer/unify.rs
index 60aa9b5a17..631b571465 100644
--- a/crates/hir-ty/src/infer/unify.rs
+++ b/crates/hir-ty/src/infer/unify.rs
@@ -1024,16 +1024,12 @@ impl<'a> InferenceTable<'a> {
}
}
- let Some(sized) = self
- .db
- .lang_item(self.trait_env.krate, LangItem::Sized)
- .and_then(|sized| sized.as_trait())
- else {
+ let Some(sized) = LangItem::Sized.resolve_trait(self.db, self.trait_env.krate) else {
return false;
};
let sized_pred = WhereClause::Implemented(TraitRef {
trait_id: to_chalk_trait_id(sized),
- substitution: Substitution::from1(Interner, ty.clone()),
+ substitution: Substitution::from1(Interner, ty),
});
let goal = GoalData::DomainGoal(chalk_ir::DomainGoal::Holds(sized_pred)).intern(Interner);
matches!(self.try_obligation(goal), Some(Solution::Unique(_)))
diff --git a/crates/hir-ty/src/lower.rs b/crates/hir-ty/src/lower.rs
index e4688d044e..9def39d5f9 100644
--- a/crates/hir-ty/src/lower.rs
+++ b/crates/hir-ty/src/lower.rs
@@ -590,10 +590,7 @@ impl<'a> TyLoweringContext<'a> {
}
}
&TypeBound::Path(path, TraitBoundModifier::Maybe) => {
- let sized_trait = self
- .db
- .lang_item(self.resolver.krate(), LangItem::Sized)
- .and_then(|lang_item| lang_item.as_trait());
+ let sized_trait = LangItem::Sized.resolve_trait(self.db, self.resolver.krate());
// Don't lower associated type bindings as the only possible relaxed trait bound
// `?Sized` has no of them.
// If we got another trait here ignore the bound completely.
@@ -736,10 +733,8 @@ impl<'a> TyLoweringContext<'a> {
}
if !ctx.unsized_types.contains(&self_ty) {
- let sized_trait = ctx
- .db
- .lang_item(krate, LangItem::Sized)
- .and_then(|lang_item| lang_item.as_trait().map(to_chalk_trait_id));
+ let sized_trait =
+ LangItem::Sized.resolve_trait(ctx.db, krate).map(to_chalk_trait_id);
let sized_clause = sized_trait.map(|trait_id| {
let clause = WhereClause::Implemented(TraitRef {
trait_id,
@@ -1188,9 +1183,7 @@ fn implicitly_sized_clauses<'a, 'subst: 'a>(
substitution: &'subst Substitution,
resolver: &Resolver,
) -> Option<impl Iterator<Item = WhereClause> + Captures<'a> + Captures<'subst>> {
- let sized_trait = db
- .lang_item(resolver.krate(), LangItem::Sized)
- .and_then(|lang_item| lang_item.as_trait().map(to_chalk_trait_id))?;
+ let sized_trait = LangItem::Sized.resolve_trait(db, resolver.krate()).map(to_chalk_trait_id)?;
let trait_self_idx = trait_self_param_idx(db, def);
@@ -1475,7 +1468,7 @@ fn type_for_enum_variant_constructor(
}
}
-#[salsa::tracked(cycle_result = type_for_adt_cycle_result)]
+#[salsa_macros::tracked(cycle_result = type_for_adt_cycle_result)]
fn type_for_adt_tracked(db: &dyn HirDatabase, adt: AdtId) -> Binders<Ty> {
type_for_adt(db, adt)
}
@@ -1540,7 +1533,7 @@ pub enum TyDefId {
}
impl_from!(BuiltinType, AdtId(StructId, EnumId, UnionId), TypeAliasId for TyDefId);
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, salsa::Supertype)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, salsa_macros::Supertype)]
pub enum ValueTyDefId {
FunctionId(FunctionId),
StructId(StructId),
diff --git a/crates/hir-ty/src/method_resolution.rs b/crates/hir-ty/src/method_resolution.rs
index 8f8e26eca2..8e549ca0cb 100644
--- a/crates/hir-ty/src/method_resolution.rs
+++ b/crates/hir-ty/src/method_resolution.rs
@@ -515,9 +515,15 @@ impl From<Option<BlockId>> for VisibleFromModule {
}
}
+#[derive(Debug, Clone)]
+pub enum AutorefOrPtrAdjustment {
+ Autoref(Mutability),
+ ToConstPtr,
+}
+
#[derive(Debug, Clone, Default)]
pub struct ReceiverAdjustments {
- autoref: Option<Mutability>,
+ autoref: Option<AutorefOrPtrAdjustment>,
autoderefs: usize,
unsize_array: bool,
}
@@ -535,10 +541,15 @@ impl ReceiverAdjustments {
}
Some((kind, new_ty)) => {
ty = new_ty.clone();
+ let mutbl = match self.autoref {
+ Some(AutorefOrPtrAdjustment::Autoref(m)) => Some(m),
+ Some(AutorefOrPtrAdjustment::ToConstPtr) => Some(Mutability::Not),
+ // FIXME should we know the mutability here, when autoref is `None`?
+ None => None,
+ };
adjust.push(Adjustment {
kind: Adjust::Deref(match kind {
- // FIXME should we know the mutability here, when autoref is `None`?
- AutoderefKind::Overloaded => Some(OverloadedDeref(self.autoref)),
+ AutoderefKind::Overloaded => Some(OverloadedDeref(mutbl)),
AutoderefKind::Builtin => None,
}),
target: new_ty,
@@ -546,11 +557,27 @@ impl ReceiverAdjustments {
}
}
}
- if let Some(m) = self.autoref {
+ if let Some(autoref) = &self.autoref {
let lt = table.new_lifetime_var();
- let a = Adjustment::borrow(m, ty, lt);
- ty = a.target.clone();
- adjust.push(a);
+ match autoref {
+ AutorefOrPtrAdjustment::Autoref(m) => {
+ let a = Adjustment::borrow(*m, ty, lt);
+ ty = a.target.clone();
+ adjust.push(a);
+ }
+ AutorefOrPtrAdjustment::ToConstPtr => {
+ if let TyKind::Raw(Mutability::Mut, pointee) = ty.kind(Interner) {
+ let a = Adjustment {
+ kind: Adjust::Pointer(PointerCast::MutToConstPointer),
+ target: TyKind::Raw(Mutability::Not, pointee.clone()).intern(Interner),
+ };
+ ty = a.target.clone();
+ adjust.push(a);
+ } else {
+ never!("`ToConstPtr` target is not a raw mutable pointer");
+ }
+ }
+ };
}
if self.unsize_array {
ty = 'it: {
@@ -575,8 +602,8 @@ impl ReceiverAdjustments {
(ty, adjust)
}
- fn with_autoref(&self, m: Mutability) -> ReceiverAdjustments {
- Self { autoref: Some(m), ..*self }
+ fn with_autoref(&self, a: AutorefOrPtrAdjustment) -> ReceiverAdjustments {
+ Self { autoref: Some(a), ..*self }
}
}
@@ -1051,7 +1078,7 @@ fn iterate_method_candidates_with_autoref(
let mut maybe_reborrowed = first_adjustment.clone();
if let Some((_, _, m)) = receiver_ty.value.as_reference() {
// Prefer reborrow of references to move
- maybe_reborrowed.autoref = Some(m);
+ maybe_reborrowed.autoref = Some(AutorefOrPtrAdjustment::Autoref(m));
maybe_reborrowed.autoderefs += 1;
}
@@ -1063,15 +1090,34 @@ fn iterate_method_candidates_with_autoref(
binders: receiver_ty.binders.clone(),
};
- iterate_method_candidates_by_receiver(refed, first_adjustment.with_autoref(Mutability::Not))?;
+ iterate_method_candidates_by_receiver(
+ refed,
+ first_adjustment.with_autoref(AutorefOrPtrAdjustment::Autoref(Mutability::Not)),
+ )?;
let ref_muted = Canonical {
value: TyKind::Ref(Mutability::Mut, error_lifetime(), receiver_ty.value.clone())
.intern(Interner),
- binders: receiver_ty.binders,
+ binders: receiver_ty.binders.clone(),
};
- iterate_method_candidates_by_receiver(ref_muted, first_adjustment.with_autoref(Mutability::Mut))
+ iterate_method_candidates_by_receiver(
+ ref_muted,
+ first_adjustment.with_autoref(AutorefOrPtrAdjustment::Autoref(Mutability::Mut)),
+ )?;
+
+ if let Some((ty, Mutability::Mut)) = receiver_ty.value.as_raw_ptr() {
+ let const_ptr_ty = Canonical {
+ value: TyKind::Raw(Mutability::Not, ty.clone()).intern(Interner),
+ binders: receiver_ty.binders,
+ };
+ iterate_method_candidates_by_receiver(
+ const_ptr_ty,
+ first_adjustment.with_autoref(AutorefOrPtrAdjustment::ToConstPtr),
+ )?;
+ }
+
+ ControlFlow::Continue(())
}
pub trait MethodCandidateCallback {
diff --git a/crates/hir-ty/src/mir.rs b/crates/hir-ty/src/mir.rs
index 6dc20203e0..bf80ed7967 100644
--- a/crates/hir-ty/src/mir.rs
+++ b/crates/hir-ty/src/mir.rs
@@ -77,7 +77,14 @@ pub struct Local {
/// currently implements it, but it seems like this may be something to check against in the
/// validator.
#[derive(Debug, PartialEq, Eq, Clone)]
-pub enum Operand {
+pub struct Operand {
+ kind: OperandKind,
+ // FIXME : This should actually just be of type `MirSpan`.
+ span: Option<MirSpan>,
+}
+
+#[derive(Debug, PartialEq, Eq, Clone)]
+pub enum OperandKind {
/// Creates a value by loading the given place.
///
/// Before drop elaboration, the type of the place must be `Copy`. After drop elaboration there
@@ -101,7 +108,13 @@ pub enum Operand {
impl Operand {
fn from_concrete_const(data: Box<[u8]>, memory_map: MemoryMap, ty: Ty) -> Self {
- Operand::Constant(intern_const_scalar(ConstScalar::Bytes(data, memory_map), ty))
+ Operand {
+ kind: OperandKind::Constant(intern_const_scalar(
+ ConstScalar::Bytes(data, memory_map),
+ ty,
+ )),
+ span: None,
+ }
}
fn from_bytes(data: Box<[u8]>, ty: Ty) -> Self {
@@ -1076,11 +1089,11 @@ impl MirBody {
f: &mut impl FnMut(&mut Place, &mut ProjectionStore),
store: &mut ProjectionStore,
) {
- match op {
- Operand::Copy(p) | Operand::Move(p) => {
+ match &mut op.kind {
+ OperandKind::Copy(p) | OperandKind::Move(p) => {
f(p, store);
}
- Operand::Constant(_) | Operand::Static(_) => (),
+ OperandKind::Constant(_) | OperandKind::Static(_) => (),
}
}
for (_, block) in self.basic_blocks.iter_mut() {
diff --git a/crates/hir-ty/src/mir/borrowck.rs b/crates/hir-ty/src/mir/borrowck.rs
index eca6f4692a..fb0c0dee09 100644
--- a/crates/hir-ty/src/mir/borrowck.rs
+++ b/crates/hir-ty/src/mir/borrowck.rs
@@ -15,13 +15,13 @@ use crate::{
ClosureId, Interner, Substitution, Ty, TyExt, TypeFlags,
db::{HirDatabase, InternedClosure},
display::DisplayTarget,
- mir::Operand,
+ mir::OperandKind,
utils::ClosureSubst,
};
use super::{
- BasicBlockId, BorrowKind, LocalId, MirBody, MirLowerError, MirSpan, MutBorrowKind, Place,
- ProjectionElem, Rvalue, StatementKind, TerminatorKind,
+ BasicBlockId, BorrowKind, LocalId, MirBody, MirLowerError, MirSpan, MutBorrowKind, Operand,
+ Place, ProjectionElem, Rvalue, StatementKind, TerminatorKind,
};
#[derive(Debug, Clone, PartialEq, Eq)]
@@ -120,8 +120,8 @@ fn make_fetch_closure_field(
fn moved_out_of_ref(db: &dyn HirDatabase, body: &MirBody) -> Vec<MovedOutOfRef> {
let mut result = vec![];
- let mut for_operand = |op: &Operand, span: MirSpan| match op {
- Operand::Copy(p) | Operand::Move(p) => {
+ let mut for_operand = |op: &Operand, span: MirSpan| match op.kind {
+ OperandKind::Copy(p) | OperandKind::Move(p) => {
let mut ty: Ty = body.locals[p.local].ty.clone();
let mut is_dereference_of_ref = false;
for proj in p.projection.lookup(&body.projection_store) {
@@ -139,10 +139,10 @@ fn moved_out_of_ref(db: &dyn HirDatabase, body: &MirBody) -> Vec<MovedOutOfRef>
&& !ty.clone().is_copy(db, body.owner)
&& !ty.data(Interner).flags.intersects(TypeFlags::HAS_ERROR)
{
- result.push(MovedOutOfRef { span, ty });
+ result.push(MovedOutOfRef { span: op.span.unwrap_or(span), ty });
}
}
- Operand::Constant(_) | Operand::Static(_) => (),
+ OperandKind::Constant(_) | OperandKind::Static(_) => (),
};
for (_, block) in body.basic_blocks.iter() {
db.unwind_if_revision_cancelled();
@@ -215,8 +215,8 @@ fn moved_out_of_ref(db: &dyn HirDatabase, body: &MirBody) -> Vec<MovedOutOfRef>
fn partially_moved(db: &dyn HirDatabase, body: &MirBody) -> Vec<PartiallyMoved> {
let mut result = vec![];
- let mut for_operand = |op: &Operand, span: MirSpan| match op {
- Operand::Copy(p) | Operand::Move(p) => {
+ let mut for_operand = |op: &Operand, span: MirSpan| match op.kind {
+ OperandKind::Copy(p) | OperandKind::Move(p) => {
let mut ty: Ty = body.locals[p.local].ty.clone();
for proj in p.projection.lookup(&body.projection_store) {
ty = proj.projected_ty(
@@ -232,7 +232,7 @@ fn partially_moved(db: &dyn HirDatabase, body: &MirBody) -> Vec<PartiallyMoved>
result.push(PartiallyMoved { span, ty, local: p.local });
}
}
- Operand::Constant(_) | Operand::Static(_) => (),
+ OperandKind::Constant(_) | OperandKind::Static(_) => (),
};
for (_, block) in body.basic_blocks.iter() {
db.unwind_if_revision_cancelled();
@@ -492,7 +492,7 @@ fn record_usage(local: LocalId, result: &mut ArenaMap<LocalId, MutabilityReason>
}
fn record_usage_for_operand(arg: &Operand, result: &mut ArenaMap<LocalId, MutabilityReason>) {
- if let Operand::Copy(p) | Operand::Move(p) = arg {
+ if let OperandKind::Copy(p) | OperandKind::Move(p) = arg.kind {
record_usage(p.local, result);
}
}
diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs
index 386226b16d..21e5428520 100644
--- a/crates/hir-ty/src/mir/eval.rs
+++ b/crates/hir-ty/src/mir/eval.rs
@@ -47,7 +47,7 @@ use crate::{
use super::{
AggregateKind, BasicBlockId, BinOp, CastKind, LocalId, MirBody, MirLowerError, MirSpan,
- Operand, Place, PlaceElem, ProjectionElem, ProjectionStore, Rvalue, StatementKind,
+ Operand, OperandKind, Place, PlaceElem, ProjectionElem, ProjectionStore, Rvalue, StatementKind,
TerminatorKind, UnOp, return_slot,
};
@@ -655,22 +655,15 @@ impl Evaluator<'_> {
mir_or_dyn_index_cache: RefCell::new(Default::default()),
unused_locals_store: RefCell::new(Default::default()),
cached_ptr_size,
- cached_fn_trait_func: db
- .lang_item(crate_id, LangItem::Fn)
- .and_then(|x| x.as_trait())
+ cached_fn_trait_func: LangItem::Fn
+ .resolve_trait(db, crate_id)
.and_then(|x| db.trait_items(x).method_by_name(&Name::new_symbol_root(sym::call))),
- cached_fn_mut_trait_func: db
- .lang_item(crate_id, LangItem::FnMut)
- .and_then(|x| x.as_trait())
- .and_then(|x| {
- db.trait_items(x).method_by_name(&Name::new_symbol_root(sym::call_mut))
- }),
- cached_fn_once_trait_func: db
- .lang_item(crate_id, LangItem::FnOnce)
- .and_then(|x| x.as_trait())
- .and_then(|x| {
- db.trait_items(x).method_by_name(&Name::new_symbol_root(sym::call_once))
- }),
+ cached_fn_mut_trait_func: LangItem::FnMut.resolve_trait(db, crate_id).and_then(|x| {
+ db.trait_items(x).method_by_name(&Name::new_symbol_root(sym::call_mut))
+ }),
+ cached_fn_once_trait_func: LangItem::FnOnce.resolve_trait(db, crate_id).and_then(|x| {
+ db.trait_items(x).method_by_name(&Name::new_symbol_root(sym::call_once))
+ }),
})
}
@@ -863,10 +856,10 @@ impl Evaluator<'_> {
}
fn operand_ty(&self, o: &Operand, locals: &Locals) -> Result<Ty> {
- Ok(match o {
- Operand::Copy(p) | Operand::Move(p) => self.place_ty(p, locals)?,
- Operand::Constant(c) => c.data(Interner).ty.clone(),
- &Operand::Static(s) => {
+ Ok(match &o.kind {
+ OperandKind::Copy(p) | OperandKind::Move(p) => self.place_ty(p, locals)?,
+ OperandKind::Constant(c) => c.data(Interner).ty.clone(),
+ &OperandKind::Static(s) => {
let ty = self.db.infer(s.into())[self.db.body(s.into()).body_expr].clone();
TyKind::Ref(Mutability::Not, static_lifetime(), ty).intern(Interner)
}
@@ -1880,16 +1873,16 @@ impl Evaluator<'_> {
}
fn eval_operand(&mut self, it: &Operand, locals: &mut Locals) -> Result<Interval> {
- Ok(match it {
- Operand::Copy(p) | Operand::Move(p) => {
+ Ok(match &it.kind {
+ OperandKind::Copy(p) | OperandKind::Move(p) => {
locals.drop_flags.remove_place(p, &locals.body.projection_store);
self.eval_place(p, locals)?
}
- Operand::Static(st) => {
+ OperandKind::Static(st) => {
let addr = self.eval_static(*st, locals)?;
Interval::new(addr, self.ptr_size())
}
- Operand::Constant(konst) => self.allocate_const_in_heap(locals, konst)?,
+ OperandKind::Constant(konst) => self.allocate_const_in_heap(locals, konst)?,
})
}
@@ -2811,7 +2804,7 @@ impl Evaluator<'_> {
span: MirSpan,
) -> Result<()> {
let Some(drop_fn) = (|| {
- let drop_trait = self.db.lang_item(self.crate_id, LangItem::Drop)?.as_trait()?;
+ let drop_trait = LangItem::Drop.resolve_trait(self.db, self.crate_id)?;
self.db.trait_items(drop_trait).method_by_name(&Name::new_symbol_root(sym::drop))
})() else {
// in some tests we don't have drop trait in minicore, and
diff --git a/crates/hir-ty/src/mir/eval/shim.rs b/crates/hir-ty/src/mir/eval/shim.rs
index 4de44cfd02..26ef95d264 100644
--- a/crates/hir-ty/src/mir/eval/shim.rs
+++ b/crates/hir-ty/src/mir/eval/shim.rs
@@ -6,7 +6,6 @@ use std::cmp::{self, Ordering};
use chalk_ir::TyKind;
use hir_def::{
builtin_type::{BuiltinInt, BuiltinUint},
- lang_item::LangItemTarget,
resolver::HasResolver,
};
use hir_expand::name::Name;
@@ -156,8 +155,8 @@ impl Evaluator<'_> {
if let Some(LangItem::PanicFmt) = self.db.lang_attr(def.into()) {
let resolver = self.db.crate_def_map(self.crate_id).crate_root().resolver(self.db);
- let Some(hir_def::lang_item::LangItemTarget::Function(const_panic_fmt)) =
- self.db.lang_item(resolver.krate(), LangItem::ConstPanicFmt)
+ let Some(const_panic_fmt) =
+ LangItem::ConstPanicFmt.resolve_function(self.db, resolver.krate())
else {
not_supported!("const_panic_fmt lang item not found or not a function");
};
@@ -1257,12 +1256,12 @@ impl Evaluator<'_> {
let addr = tuple.interval.addr.offset(offset);
args.push(IntervalAndTy::new(addr, field, self, locals)?);
}
- if let Some(target) = self.db.lang_item(self.crate_id, LangItem::FnOnce) {
- if let Some(def) = target.as_trait().and_then(|it| {
- self.db
- .trait_items(it)
- .method_by_name(&Name::new_symbol_root(sym::call_once))
- }) {
+ if let Some(target) = LangItem::FnOnce.resolve_trait(self.db, self.crate_id) {
+ if let Some(def) = self
+ .db
+ .trait_items(target)
+ .method_by_name(&Name::new_symbol_root(sym::call_once))
+ {
self.exec_fn_trait(
def,
&args,
@@ -1376,9 +1375,7 @@ impl Evaluator<'_> {
}
}
}
- if let Some(LangItemTarget::EnumId(e)) =
- self.db.lang_item(self.crate_id, LangItem::Ordering)
- {
+ if let Some(e) = LangItem::Ordering.resolve_enum(self.db, self.crate_id) {
let ty = self.db.ty(e.into());
let r = self
.compute_discriminant(ty.skip_binders().clone(), &[result as i8 as u8])?;
diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs
index 557027756f..7b48b15d9e 100644
--- a/crates/hir-ty/src/mir/lower.rs
+++ b/crates/hir-ty/src/mir/lower.rs
@@ -13,7 +13,7 @@ use hir_def::{
Pat, PatId, RecordFieldPat, RecordLitField,
},
item_tree::FieldsShape,
- lang_item::{LangItem, LangItemTarget},
+ lang_item::{LangItem, LangItemTarget, lang_item},
resolver::{HasResolver, ResolveValueResult, Resolver, ValueNs},
};
use hir_expand::name::Name;
@@ -48,6 +48,8 @@ use crate::{
utils::ClosureSubst,
};
+use super::OperandKind;
+
mod as_place;
mod pattern_matching;
@@ -324,7 +326,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
let Some((p, current)) = self.lower_expr_as_place(current, expr_id, true)? else {
return Ok(None);
};
- Ok(Some((Operand::Copy(p), current)))
+ Ok(Some((Operand { kind: OperandKind::Copy(p), span: Some(expr_id.into()) }, current)))
}
fn lower_expr_to_place_with_adjust(
@@ -347,7 +349,12 @@ impl<'ctx> MirLowerCtx<'ctx> {
else {
return Ok(None);
};
- self.push_assignment(current, place, Operand::Copy(p).into(), expr_id.into());
+ self.push_assignment(
+ current,
+ place,
+ Operand { kind: OperandKind::Copy(p), span: None }.into(),
+ expr_id.into(),
+ );
Ok(Some(current))
}
Adjust::Borrow(AutoBorrow::Ref(_, m) | AutoBorrow::RawPtr(m)) => {
@@ -371,7 +378,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
place,
Rvalue::Cast(
CastKind::PointerCoercion(*cast),
- Operand::Copy(p),
+ Operand { kind: OperandKind::Copy(p), span: None },
last.target.clone(),
),
expr_id.into(),
@@ -476,7 +483,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
self.push_assignment(
current,
place,
- Operand::Copy(temp).into(),
+ Operand { kind: OperandKind::Copy(temp), span: None }.into(),
expr_id.into(),
);
Ok(Some(current))
@@ -517,21 +524,23 @@ impl<'ctx> MirLowerCtx<'ctx> {
self.push_assignment(
current,
place,
- Operand::Constant(
- ConstData {
- ty,
- value: chalk_ir::ConstValue::BoundVar(BoundVar::new(
- DebruijnIndex::INNERMOST,
- generics.type_or_const_param_idx(p.into()).ok_or(
- MirLowerError::TypeError(
- "fail to lower const generic param",
- ),
- )?,
- )),
- }
- .intern(Interner),
- )
- .into(),
+ Rvalue::from(Operand {
+ kind: OperandKind::Constant(
+ ConstData {
+ ty,
+ value: chalk_ir::ConstValue::BoundVar(BoundVar::new(
+ DebruijnIndex::INNERMOST,
+ generics.type_or_const_param_idx(p.into()).ok_or(
+ MirLowerError::TypeError(
+ "fail to lower const generic param",
+ ),
+ )?,
+ )),
+ }
+ .intern(Interner),
+ ),
+ span: None,
+ }),
expr_id.into(),
);
Ok(Some(current))
@@ -876,7 +885,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
})),
&mut self.result.projection_store,
);
- Operand::Copy(p)
+ Operand { kind: OperandKind::Copy(p), span: None }
}
})
.collect(),
@@ -979,7 +988,12 @@ impl<'ctx> MirLowerCtx<'ctx> {
else {
return Ok(None);
};
- self.push_assignment(current, place, Operand::Copy(p).into(), expr_id.into());
+ self.push_assignment(
+ current,
+ place,
+ Operand { kind: OperandKind::Copy(p), span: None }.into(),
+ expr_id.into(),
+ );
Ok(Some(current))
}
Expr::UnaryOp {
@@ -1056,8 +1070,11 @@ impl<'ctx> MirLowerCtx<'ctx> {
else {
return Ok(None);
};
- let r_value =
- Rvalue::CheckedBinaryOp(op.into(), Operand::Copy(lhs_place), rhs_op);
+ let r_value = Rvalue::CheckedBinaryOp(
+ op.into(),
+ Operand { kind: OperandKind::Copy(lhs_place), span: None },
+ rhs_op,
+ );
self.push_assignment(current, lhs_place, r_value, expr_id.into());
return Ok(Some(current));
}
@@ -1232,9 +1249,11 @@ impl<'ctx> MirLowerCtx<'ctx> {
Rvalue::Ref(*bk, p),
capture_spans[0],
);
- operands.push(Operand::Move(tmp));
+ operands.push(Operand { kind: OperandKind::Move(tmp), span: None });
+ }
+ CaptureKind::ByValue => {
+ operands.push(Operand { kind: OperandKind::Move(p), span: None })
}
- CaptureKind::ByValue => operands.push(Operand::Move(p)),
}
}
self.push_assignment(
@@ -1476,7 +1495,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
.const_eval(const_id, subst, None)
.map_err(|e| MirLowerError::ConstEvalError(name.into(), Box::new(e)))?
};
- Ok(Operand::Constant(c))
+ Ok(Operand { kind: OperandKind::Constant(c), span: None })
}
fn write_bytes_to_place(
@@ -1727,7 +1746,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
fn resolve_lang_item(&self, item: LangItem) -> Result<LangItemTarget> {
let crate_id = self.owner.module(self.db).krate();
- self.db.lang_item(crate_id, item).ok_or(MirLowerError::LangItemNotFound(item))
+ lang_item(self.db, crate_id, item).ok_or(MirLowerError::LangItemNotFound(item))
}
fn lower_block_to_place(
diff --git a/crates/hir-ty/src/mir/lower/as_place.rs b/crates/hir-ty/src/mir/lower/as_place.rs
index d3cd009924..c22bada7a9 100644
--- a/crates/hir-ty/src/mir/lower/as_place.rs
+++ b/crates/hir-ty/src/mir/lower/as_place.rs
@@ -1,6 +1,6 @@
//! MIR lowering for places
-use crate::mir::MutBorrowKind;
+use crate::mir::{MutBorrowKind, Operand, OperandKind};
use super::*;
use hir_def::FunctionId;
@@ -155,7 +155,7 @@ impl MirLowerCtx<'_> {
self.push_assignment(
current,
temp,
- Operand::Static(s).into(),
+ Operand { kind: OperandKind::Static(s), span: None }.into(),
expr_id.into(),
);
Ok(Some((
@@ -305,7 +305,7 @@ impl MirLowerCtx<'_> {
);
let Some(current) = self.lower_call(
index_fn_op,
- Box::new([Operand::Copy(place), index_operand]),
+ Box::new([Operand { kind: OperandKind::Copy(place), span: None }, index_operand]),
result,
current,
false,
@@ -365,7 +365,7 @@ impl MirLowerCtx<'_> {
let mut result: Place = self.temp(target_ty_ref, current, span)?.into();
let Some(current) = self.lower_call(
deref_fn_op,
- Box::new([Operand::Copy(ref_place)]),
+ Box::new([Operand { kind: OperandKind::Copy(ref_place), span: None }]),
result,
current,
false,
diff --git a/crates/hir-ty/src/mir/lower/pattern_matching.rs b/crates/hir-ty/src/mir/lower/pattern_matching.rs
index abfa7aee04..b3c1f6f387 100644
--- a/crates/hir-ty/src/mir/lower/pattern_matching.rs
+++ b/crates/hir-ty/src/mir/lower/pattern_matching.rs
@@ -5,10 +5,10 @@ use hir_def::{AssocItemId, hir::ExprId, signatures::VariantFields};
use crate::{
BindingMode,
mir::{
- LocalId, MutBorrowKind,
+ LocalId, MutBorrowKind, Operand, OperandKind,
lower::{
BasicBlockId, BinOp, BindingId, BorrowKind, Either, Expr, FieldId, Idx, Interner,
- MemoryMap, MirLowerCtx, MirLowerError, MirSpan, Mutability, Operand, Pat, PatId, Place,
+ MemoryMap, MirLowerCtx, MirLowerError, MirSpan, Mutability, Pat, PatId, Place,
PlaceElem, ProjectionElem, RecordFieldPat, ResolveValueResult, Result, Rvalue,
Substitution, SwitchTargets, TerminatorKind, TupleFieldId, TupleId, TyBuilder, TyKind,
ValueNs, VariantId,
@@ -217,10 +217,14 @@ impl MirLowerCtx<'_> {
self.push_assignment(
current,
discr,
- Rvalue::CheckedBinaryOp(binop, lv, Operand::Copy(cond_place)),
+ Rvalue::CheckedBinaryOp(
+ binop,
+ lv,
+ Operand { kind: OperandKind::Copy(cond_place), span: None },
+ ),
pattern.into(),
);
- let discr = Operand::Copy(discr);
+ let discr = Operand { kind: OperandKind::Copy(discr), span: None };
self.set_terminator(
current,
TerminatorKind::SwitchInt {
@@ -262,7 +266,10 @@ impl MirLowerCtx<'_> {
self.set_terminator(
current,
TerminatorKind::SwitchInt {
- discr: Operand::Copy(place_len),
+ discr: Operand {
+ kind: OperandKind::Copy(place_len),
+ span: None,
+ },
targets: SwitchTargets::static_if(
pattern_len as u128,
next,
@@ -282,10 +289,14 @@ impl MirLowerCtx<'_> {
self.push_assignment(
current,
discr,
- Rvalue::CheckedBinaryOp(BinOp::Le, c, Operand::Copy(place_len)),
+ Rvalue::CheckedBinaryOp(
+ BinOp::Le,
+ c,
+ Operand { kind: OperandKind::Copy(place_len), span: None },
+ ),
pattern.into(),
);
- let discr = Operand::Copy(discr);
+ let discr = Operand { kind: OperandKind::Copy(discr), span: None };
self.set_terminator(
current,
TerminatorKind::SwitchInt {
@@ -407,8 +418,8 @@ impl MirLowerCtx<'_> {
tmp2,
Rvalue::CheckedBinaryOp(
BinOp::Eq,
- Operand::Copy(tmp),
- Operand::Copy(cond_place),
+ Operand { kind: OperandKind::Copy(tmp), span: None },
+ Operand { kind: OperandKind::Copy(cond_place), span: None },
),
span,
);
@@ -417,7 +428,7 @@ impl MirLowerCtx<'_> {
self.set_terminator(
current,
TerminatorKind::SwitchInt {
- discr: Operand::Copy(tmp2),
+ discr: Operand { kind: OperandKind::Copy(tmp2), span: None },
targets: SwitchTargets::static_if(1, next, else_target),
},
span,
@@ -486,7 +497,7 @@ impl MirLowerCtx<'_> {
self.push_assignment(
current,
lhs_place,
- Operand::Copy(cond_place).into(),
+ Operand { kind: OperandKind::Copy(cond_place), span: None }.into(),
expr.into(),
);
(current, current_else)
@@ -523,7 +534,9 @@ impl MirLowerCtx<'_> {
current,
target_place.into(),
match mode {
- BindingMode::Move => Operand::Copy(cond_place).into(),
+ BindingMode::Move => {
+ Operand { kind: OperandKind::Copy(cond_place), span: None }.into()
+ }
BindingMode::Ref(Mutability::Not) => Rvalue::Ref(BorrowKind::Shared, cond_place),
BindingMode::Ref(Mutability::Mut) => {
Rvalue::Ref(BorrowKind::Mut { kind: MutBorrowKind::Default }, cond_place)
@@ -547,10 +560,14 @@ impl MirLowerCtx<'_> {
self.push_assignment(
current,
discr,
- Rvalue::CheckedBinaryOp(BinOp::Eq, c, Operand::Copy(cond_place)),
+ Rvalue::CheckedBinaryOp(
+ BinOp::Eq,
+ c,
+ Operand { kind: OperandKind::Copy(cond_place), span: None },
+ ),
pattern.into(),
);
- let discr = Operand::Copy(discr);
+ let discr = Operand { kind: OperandKind::Copy(discr), span: None };
self.set_terminator(
current,
TerminatorKind::SwitchInt {
@@ -583,7 +600,7 @@ impl MirLowerCtx<'_> {
self.set_terminator(
current,
TerminatorKind::SwitchInt {
- discr: Operand::Copy(tmp),
+ discr: Operand { kind: OperandKind::Copy(tmp), span: None },
targets: SwitchTargets::static_if(e, next, *else_target),
},
span,
diff --git a/crates/hir-ty/src/mir/monomorphization.rs b/crates/hir-ty/src/mir/monomorphization.rs
index d4f10c032c..d8f443145c 100644
--- a/crates/hir-ty/src/mir/monomorphization.rs
+++ b/crates/hir-ty/src/mir/monomorphization.rs
@@ -25,7 +25,7 @@ use crate::{
infer::normalize,
};
-use super::{MirBody, MirLowerError, Operand, Rvalue, StatementKind, TerminatorKind};
+use super::{MirBody, MirLowerError, Operand, OperandKind, Rvalue, StatementKind, TerminatorKind};
macro_rules! not_supported {
($it: expr) => {
@@ -170,8 +170,8 @@ impl Filler<'_> {
}
fn fill_operand(&mut self, op: &mut Operand) -> Result<(), MirLowerError> {
- match op {
- Operand::Constant(c) => {
+ match &mut op.kind {
+ OperandKind::Constant(c) => {
match &c.data(Interner).value {
chalk_ir::ConstValue::BoundVar(b) => {
let resolved = self
@@ -215,7 +215,7 @@ impl Filler<'_> {
}
self.fill_const(c)?;
}
- Operand::Copy(_) | Operand::Move(_) | Operand::Static(_) => (),
+ OperandKind::Copy(_) | OperandKind::Move(_) | OperandKind::Static(_) => (),
}
Ok(())
}
diff --git a/crates/hir-ty/src/mir/pretty.rs b/crates/hir-ty/src/mir/pretty.rs
index f71e297897..7ae6e907e7 100644
--- a/crates/hir-ty/src/mir/pretty.rs
+++ b/crates/hir-ty/src/mir/pretty.rs
@@ -18,8 +18,8 @@ use crate::{
};
use super::{
- AggregateKind, BasicBlockId, BorrowKind, LocalId, MirBody, MutBorrowKind, Operand, Place,
- Rvalue, UnOp,
+ AggregateKind, BasicBlockId, BorrowKind, LocalId, MirBody, MutBorrowKind, Operand, OperandKind,
+ Place, Rvalue, UnOp,
};
macro_rules! w {
@@ -374,14 +374,14 @@ impl<'a> MirPrettyCtx<'a> {
}
fn operand(&mut self, r: &Operand) {
- match r {
- Operand::Copy(p) | Operand::Move(p) => {
+ match &r.kind {
+ OperandKind::Copy(p) | OperandKind::Move(p) => {
// MIR at the time of writing doesn't have difference between move and copy, so we show them
// equally. Feel free to change it.
self.place(p);
}
- Operand::Constant(c) => w!(self, "Const({})", self.hir_display(c)),
- Operand::Static(s) => w!(self, "Static({:?})", s),
+ OperandKind::Constant(c) => w!(self, "Const({})", self.hir_display(c)),
+ OperandKind::Static(s) => w!(self, "Static({:?})", s),
}
}
diff --git a/crates/hir-ty/src/test_db.rs b/crates/hir-ty/src/test_db.rs
index d2bba120b6..bcd8aa6c4e 100644
--- a/crates/hir-ty/src/test_db.rs
+++ b/crates/hir-ty/src/test_db.rs
@@ -16,7 +16,7 @@ use syntax::TextRange;
use test_utils::extract_annotations;
use triomphe::Arc;
-#[salsa::db]
+#[salsa_macros::db]
#[derive(Clone)]
pub(crate) struct TestDB {
storage: salsa::Storage<Self>,
@@ -47,7 +47,7 @@ impl fmt::Debug for TestDB {
}
}
-#[salsa::db]
+#[salsa_macros::db]
impl SourceDatabase for TestDB {
fn file_text(&self, file_id: base_db::FileId) -> FileText {
self.files.file_text(file_id)
@@ -102,7 +102,7 @@ impl SourceDatabase for TestDB {
}
}
-#[salsa::db]
+#[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();
diff --git a/crates/hir-ty/src/tests/coercion.rs b/crates/hir-ty/src/tests/coercion.rs
index eeaacbf12e..ddc5b71519 100644
--- a/crates/hir-ty/src/tests/coercion.rs
+++ b/crates/hir-ty/src/tests/coercion.rs
@@ -561,7 +561,7 @@ trait Foo {}
fn test(f: impl Foo, g: &(impl Foo + ?Sized)) {
let _: &dyn Foo = &f;
let _: &dyn Foo = g;
- //^ expected &'? dyn Foo, got &'? impl Foo + ?Sized
+ //^ expected &'? (dyn Foo + 'static), got &'? impl Foo + ?Sized
}
"#,
);
@@ -827,11 +827,11 @@ struct V<T> { t: T }
fn main() {
let a: V<&dyn Tr>;
(a,) = V { t: &S };
- //^^^^expected V<&'? S>, got (V<&'? dyn Tr>,)
+ //^^^^expected V<&'? S>, got (V<&'? (dyn Tr + '?)>,)
let mut a: V<&dyn Tr> = V { t: &S };
(a,) = V { t: &S };
- //^^^^expected V<&'? S>, got (V<&'? dyn Tr>,)
+ //^^^^expected V<&'? S>, got (V<&'? (dyn Tr + '?)>,)
}
"#,
);
diff --git a/crates/hir-ty/src/tests/display_source_code.rs b/crates/hir-ty/src/tests/display_source_code.rs
index 60c03b5224..a986b54a7b 100644
--- a/crates/hir-ty/src/tests/display_source_code.rs
+++ b/crates/hir-ty/src/tests/display_source_code.rs
@@ -65,13 +65,13 @@ trait A {
}
trait B: A {}
-fn test(
+fn test<'a>(
_: &(dyn A<Assoc = ()> + Send),
- //^ &'_ (dyn A<Assoc = ()> + Send)
- _: &(dyn Send + A<Assoc = ()>),
- //^ &'_ (dyn A<Assoc = ()> + Send)
+ //^ &(dyn A<Assoc = ()> + Send + 'static)
+ _: &'a (dyn Send + A<Assoc = ()>),
+ //^ &'a (dyn A<Assoc = ()> + Send + 'static)
_: &dyn B<Assoc = ()>,
- //^ &'_ (dyn B<Assoc = ()>)
+ //^ &(dyn B<Assoc = ()> + 'static)
) {}
"#,
);
@@ -85,7 +85,7 @@ fn render_dyn_for_ty() {
trait Foo<'a> {}
fn foo(foo: &dyn for<'a> Foo<'a>) {}
- // ^^^ &'_ dyn Foo<'_>
+ // ^^^ &(dyn Foo<'?> + 'static)
"#,
);
}
@@ -111,11 +111,11 @@ fn test(
b;
//^ impl Foo
c;
- //^ &'_ impl Foo + ?Sized
+ //^ &impl Foo + ?Sized
d;
//^ S<impl Foo>
ref_any;
- //^^^^^^^ &'_ impl ?Sized
+ //^^^^^^^ &impl ?Sized
empty;
} //^^^^^ impl Sized
"#,
@@ -192,7 +192,7 @@ fn test(
b;
//^ fn(impl Foo) -> impl Foo
c;
-} //^ fn(&'_ impl Foo + ?Sized) -> &'_ impl Foo + ?Sized
+} //^ fn(&impl Foo + ?Sized) -> &impl Foo + ?Sized
"#,
);
}
diff --git a/crates/hir-ty/src/tests/method_resolution.rs b/crates/hir-ty/src/tests/method_resolution.rs
index 3a258ecad1..94826acca3 100644
--- a/crates/hir-ty/src/tests/method_resolution.rs
+++ b/crates/hir-ty/src/tests/method_resolution.rs
@@ -1153,9 +1153,9 @@ fn dyn_trait_super_trait_not_in_scope() {
51..55 'self': &'? Self
64..69 '{ 0 }': u32
66..67 '0': u32
- 176..177 'd': &'? dyn Trait
+ 176..177 'd': &'? (dyn Trait + 'static)
191..207 '{ ...o(); }': ()
- 197..198 'd': &'? dyn Trait
+ 197..198 'd': &'? (dyn Trait + 'static)
197..204 'd.foo()': u32
"#]],
);
@@ -2019,10 +2019,10 @@ impl dyn Error + Send {
/// Attempts to downcast the box to a concrete type.
pub fn downcast<T: Error + 'static>(self: Box<Self>) -> Result<Box<T>, Box<dyn Error + Send>> {
let err: Box<dyn Error> = self;
- // ^^^^ expected Box<dyn Error>, got Box<dyn Error + Send>
+ // ^^^^ expected Box<dyn Error + 'static>, got Box<dyn Error + Send + 'static>
// FIXME, type mismatch should not occur
<dyn Error>::downcast(err).map_err(|_| loop {})
- //^^^^^^^^^^^^^^^^^^^^^ type: fn downcast<{unknown}>(Box<dyn Error>) -> Result<Box<{unknown}>, Box<dyn Error>>
+ //^^^^^^^^^^^^^^^^^^^^^ type: fn downcast<{unknown}>(Box<dyn Error + 'static>) -> Result<Box<{unknown}>, Box<dyn Error + 'static>>
}
}
"#,
@@ -2170,3 +2170,26 @@ fn main() {
"#,
);
}
+
+#[test]
+fn mut_to_const_pointer() {
+ check(
+ r#"
+pub trait X {
+ fn perform(self) -> u64;
+}
+
+impl X for *const u8 {
+ fn perform(self) -> u64 {
+ 42
+ }
+}
+
+fn test(x: *mut u8) {
+ let _v = x.perform();
+ // ^ adjustments: Pointer(MutToConstPointer)
+ // ^^^^^^^^^^^ type: u64
+}
+"#,
+ );
+}
diff --git a/crates/hir-ty/src/tests/regression.rs b/crates/hir-ty/src/tests/regression.rs
index 638306054a..47c695c697 100644
--- a/crates/hir-ty/src/tests/regression.rs
+++ b/crates/hir-ty/src/tests/regression.rs
@@ -629,7 +629,7 @@ fn issue_4053_diesel_where_clauses() {
488..522 '{ ... }': ()
498..502 'self': SelectStatement<F, S, D, W, O, LOf, {unknown}, {unknown}>
498..508 'self.order': O
- 498..515 'self.o...into()': dyn QueryFragment<DB>
+ 498..515 'self.o...into()': dyn QueryFragment<DB> + 'static
"#]],
);
}
@@ -773,7 +773,7 @@ fn issue_4800() {
"#,
expect![[r#"
379..383 'self': &'? mut PeerSet<D>
- 401..424 '{ ... }': dyn Future<Output = ()>
+ 401..424 '{ ... }': dyn Future<Output = ()> + 'static
411..418 'loop {}': !
416..418 '{}': ()
575..579 'self': &'? mut Self
@@ -2278,3 +2278,26 @@ fn test(x: bool) {
"#]],
);
}
+
+#[test]
+fn issue_19730() {
+ check_infer(
+ r#"
+trait Trait<T = Self> {}
+
+trait Foo {
+ type Bar<A, B>: Trait;
+
+ fn foo<A, B>(bar: Self::Bar<A, B>) {
+ let _ = bar;
+ }
+}
+"#,
+ expect![[r#"
+ 83..86 'bar': Foo::Bar<Self, A, B>
+ 105..133 '{ ... }': ()
+ 119..120 '_': Foo::Bar<Self, A, B>
+ 123..126 'bar': Foo::Bar<Self, A, B>
+ "#]],
+ );
+}
diff --git a/crates/hir-ty/src/tests/simple.rs b/crates/hir-ty/src/tests/simple.rs
index 0f5e44151d..eeebe38f18 100644
--- a/crates/hir-ty/src/tests/simple.rs
+++ b/crates/hir-ty/src/tests/simple.rs
@@ -2741,11 +2741,11 @@ impl B for Astruct {}
715..744 '#[rust...1i32])': Box<[i32; 1], Global>
737..743 '[1i32]': [i32; 1]
738..742 '1i32': i32
- 755..756 'v': Vec<Box<dyn B, Global>, Global>
- 776..793 '<[_]> ...to_vec': fn into_vec<Box<dyn B, Global>, Global>(Box<[Box<dyn B, Global>], Global>) -> Vec<Box<dyn B, Global>, Global>
- 776..850 '<[_]> ...ct)]))': Vec<Box<dyn B, Global>, Global>
- 794..849 '#[rust...uct)])': Box<[Box<dyn B, Global>; 1], Global>
- 816..848 '[#[rus...ruct)]': [Box<dyn B, Global>; 1]
+ 755..756 'v': Vec<Box<dyn B + 'static, Global>, Global>
+ 776..793 '<[_]> ...to_vec': fn into_vec<Box<dyn B + 'static, Global>, Global>(Box<[Box<dyn B + 'static, Global>], Global>) -> Vec<Box<dyn B + 'static, Global>, Global>
+ 776..850 '<[_]> ...ct)]))': Vec<Box<dyn B + 'static, Global>, Global>
+ 794..849 '#[rust...uct)])': Box<[Box<dyn B + 'static, Global>; 1], Global>
+ 816..848 '[#[rus...ruct)]': [Box<dyn B + 'static, Global>; 1]
817..847 '#[rust...truct)': Box<Astruct, Global>
839..846 'Astruct': Astruct
"#]],
diff --git a/crates/hir-ty/src/tests/traits.rs b/crates/hir-ty/src/tests/traits.rs
index 2fb51acea8..14137605c9 100644
--- a/crates/hir-ty/src/tests/traits.rs
+++ b/crates/hir-ty/src/tests/traits.rs
@@ -1475,26 +1475,26 @@ fn test(x: Box<dyn Trait<u64>>, y: &dyn Trait<u64>) {
expect![[r#"
29..33 'self': &'? Self
54..58 'self': &'? Self
- 198..200 '{}': Box<dyn Trait<u64>>
- 210..211 'x': Box<dyn Trait<u64>>
- 234..235 'y': &'? dyn Trait<u64>
+ 198..200 '{}': Box<dyn Trait<u64> + 'static>
+ 210..211 'x': Box<dyn Trait<u64> + 'static>
+ 234..235 'y': &'? (dyn Trait<u64> + 'static)
254..371 '{ ...2(); }': ()
- 260..261 'x': Box<dyn Trait<u64>>
- 267..268 'y': &'? dyn Trait<u64>
- 278..279 'z': Box<dyn Trait<u64>>
- 282..285 'bar': fn bar() -> Box<dyn Trait<u64>>
- 282..287 'bar()': Box<dyn Trait<u64>>
- 293..294 'x': Box<dyn Trait<u64>>
+ 260..261 'x': Box<dyn Trait<u64> + 'static>
+ 267..268 'y': &'? (dyn Trait<u64> + 'static)
+ 278..279 'z': Box<dyn Trait<u64> + 'static>
+ 282..285 'bar': fn bar() -> Box<dyn Trait<u64> + 'static>
+ 282..287 'bar()': Box<dyn Trait<u64> + 'static>
+ 293..294 'x': Box<dyn Trait<u64> + 'static>
293..300 'x.foo()': u64
- 306..307 'y': &'? dyn Trait<u64>
+ 306..307 'y': &'? (dyn Trait<u64> + 'static)
306..313 'y.foo()': u64
- 319..320 'z': Box<dyn Trait<u64>>
+ 319..320 'z': Box<dyn Trait<u64> + 'static>
319..326 'z.foo()': u64
- 332..333 'x': Box<dyn Trait<u64>>
+ 332..333 'x': Box<dyn Trait<u64> + 'static>
332..340 'x.foo2()': i64
- 346..347 'y': &'? dyn Trait<u64>
+ 346..347 'y': &'? (dyn Trait<u64> + 'static)
346..354 'y.foo2()': i64
- 360..361 'z': Box<dyn Trait<u64>>
+ 360..361 'z': Box<dyn Trait<u64> + 'static>
360..368 'z.foo2()': i64
"#]],
);
@@ -1523,14 +1523,14 @@ fn test(s: S<u32, i32>) {
expect![[r#"
32..36 'self': &'? Self
102..106 'self': &'? S<T, U>
- 128..139 '{ loop {} }': &'? dyn Trait<T, U>
+ 128..139 '{ loop {} }': &'? (dyn Trait<T, U> + 'static)
130..137 'loop {}': !
135..137 '{}': ()
175..179 'self': &'? Self
251..252 's': S<u32, i32>
267..289 '{ ...z(); }': ()
273..274 's': S<u32, i32>
- 273..280 's.bar()': &'? dyn Trait<u32, i32>
+ 273..280 's.bar()': &'? (dyn Trait<u32, i32> + 'static)
273..286 's.bar().baz()': (u32, i32)
"#]],
);
@@ -1556,20 +1556,20 @@ fn test(x: Trait, y: &Trait) -> u64 {
}"#,
expect![[r#"
26..30 'self': &'? Self
- 60..62 '{}': dyn Trait
- 72..73 'x': dyn Trait
- 82..83 'y': &'? dyn Trait
+ 60..62 '{}': dyn Trait + 'static
+ 72..73 'x': dyn Trait + 'static
+ 82..83 'y': &'? (dyn Trait + 'static)
100..175 '{ ...o(); }': u64
- 106..107 'x': dyn Trait
- 113..114 'y': &'? dyn Trait
- 124..125 'z': dyn Trait
- 128..131 'bar': fn bar() -> dyn Trait
- 128..133 'bar()': dyn Trait
- 139..140 'x': dyn Trait
+ 106..107 'x': dyn Trait + 'static
+ 113..114 'y': &'? (dyn Trait + 'static)
+ 124..125 'z': dyn Trait + 'static
+ 128..131 'bar': fn bar() -> dyn Trait + 'static
+ 128..133 'bar()': dyn Trait + 'static
+ 139..140 'x': dyn Trait + 'static
139..146 'x.foo()': u64
- 152..153 'y': &'? dyn Trait
+ 152..153 'y': &'? (dyn Trait + 'static)
152..159 'y.foo()': u64
- 165..166 'z': dyn Trait
+ 165..166 'z': dyn Trait + 'static
165..172 'z.foo()': u64
"#]],
);
@@ -1589,10 +1589,10 @@ fn main() {
expect![[r#"
31..35 'self': &'? S
37..39 '{}': ()
- 47..48 '_': &'? dyn Fn(S)
+ 47..48 '_': &'? (dyn Fn(S) + 'static)
58..60 '{}': ()
71..105 '{ ...()); }': ()
- 77..78 'f': fn f(&'? dyn Fn(S))
+ 77..78 'f': fn f(&'? (dyn Fn(S) + 'static))
77..102 'f(&|nu...foo())': ()
79..101 '&|numb....foo()': &'? impl Fn(S)
80..101 '|numbe....foo()': impl Fn(S)
@@ -2927,13 +2927,13 @@ fn test(x: &dyn Foo) {
foo(x);
}"#,
expect![[r#"
- 21..22 'x': &'? dyn Foo
+ 21..22 'x': &'? (dyn Foo + 'static)
34..36 '{}': ()
- 46..47 'x': &'? dyn Foo
+ 46..47 'x': &'? (dyn Foo + 'static)
59..74 '{ foo(x); }': ()
- 65..68 'foo': fn foo(&'? dyn Foo)
+ 65..68 'foo': fn foo(&'? (dyn Foo + 'static))
65..71 'foo(x)': ()
- 69..70 'x': &'? dyn Foo
+ 69..70 'x': &'? (dyn Foo + 'static)
"#]],
);
}
@@ -3210,13 +3210,13 @@ fn foo() {
218..324 '{ ...&s); }': ()
228..229 's': Option<i32>
232..236 'None': Option<i32>
- 246..247 'f': Box<dyn FnOnce(&'? Option<i32>)>
- 281..310 'Box { ... {}) }': Box<dyn FnOnce(&'? Option<i32>)>
+ 246..247 'f': Box<dyn FnOnce(&'? Option<i32>) + 'static>
+ 281..310 'Box { ... {}) }': Box<dyn FnOnce(&'? Option<i32>) + 'static>
294..308 '&mut (|ps| {})': &'? mut impl FnOnce(&'? Option<i32>)
300..307 '|ps| {}': impl FnOnce(&'? Option<i32>)
301..303 'ps': &'? Option<i32>
305..307 '{}': ()
- 316..317 'f': Box<dyn FnOnce(&'? Option<i32>)>
+ 316..317 'f': Box<dyn FnOnce(&'? Option<i32>) + 'static>
316..321 'f(&s)': ()
318..320 '&s': &'? Option<i32>
319..320 's': Option<i32>
@@ -4252,9 +4252,9 @@ fn f<'a>(v: &dyn Trait<Assoc<i32> = &'a i32>) {
"#,
expect![[r#"
90..94 'self': &'? Self
- 127..128 'v': &'? (dyn Trait<Assoc<i32> = &'a i32>)
+ 127..128 'v': &'? (dyn Trait<Assoc<i32> = &'a i32> + 'static)
164..195 '{ ...f(); }': ()
- 170..171 'v': &'? (dyn Trait<Assoc<i32> = &'a i32>)
+ 170..171 'v': &'? (dyn Trait<Assoc<i32> = &'a i32> + 'static)
170..184 'v.get::<i32>()': &'? i32
170..192 'v.get:...eref()': &'? i32
"#]],
@@ -4735,7 +4735,7 @@ pub async fn foo_async<'a>() -> Box<dyn Trait + 'a> {
fn foo() {
foo_async();
- //^^^^^^^^^^^impl Future<Output = Box<dyn Trait>> + ?Sized
+ //^^^^^^^^^^^impl Future<Output = Box<dyn Trait + '?>> + ?Sized
}
"#,
)
diff --git a/crates/hir-ty/src/traits.rs b/crates/hir-ty/src/traits.rs
index a5c195d408..f9f8776cff 100644
--- a/crates/hir-ty/src/traits.rs
+++ b/crates/hir-ty/src/traits.rs
@@ -8,10 +8,7 @@ use chalk_recursive::Cache;
use chalk_solve::{Solver, logging_db::LoggingRustIrDatabase, rust_ir};
use base_db::Crate;
-use hir_def::{
- BlockId, TraitId,
- lang_item::{LangItem, LangItemTarget},
-};
+use hir_def::{BlockId, TraitId, lang_item::LangItem};
use hir_expand::name::Name;
use intern::sym;
use span::Edition;
@@ -292,10 +289,6 @@ impl FnTrait {
}
pub fn get_id(self, db: &dyn HirDatabase, krate: Crate) -> Option<TraitId> {
- let target = db.lang_item(krate, self.lang_item())?;
- match target {
- LangItemTarget::Trait(t) => Some(t),
- _ => None,
- }
+ self.lang_item().resolve_trait(db, krate)
}
}
diff --git a/crates/hir-ty/src/utils.rs b/crates/hir-ty/src/utils.rs
index 198f715a6d..1e0ff423de 100644
--- a/crates/hir-ty/src/utils.rs
+++ b/crates/hir-ty/src/utils.rs
@@ -36,8 +36,7 @@ use crate::{
pub(crate) fn fn_traits(db: &dyn DefDatabase, krate: Crate) -> impl Iterator<Item = TraitId> + '_ {
[LangItem::Fn, LangItem::FnMut, LangItem::FnOnce]
.into_iter()
- .filter_map(move |lang| db.lang_item(krate, lang))
- .flat_map(|it| it.as_trait())
+ .filter_map(move |lang| lang.resolve_trait(db, krate))
}
/// Returns an iterator over the direct super traits (including the trait itself).
diff --git a/crates/hir-ty/src/variance.rs b/crates/hir-ty/src/variance.rs
index 4e9aa5610a..6e1cd9a310 100644
--- a/crates/hir-ty/src/variance.rs
+++ b/crates/hir-ty/src/variance.rs
@@ -22,7 +22,6 @@ use crate::{
use chalk_ir::Mutability;
use hir_def::signatures::StructFlags;
use hir_def::{AdtId, GenericDefId, GenericParamId, VariantId};
-use salsa::CycleRecoveryAction;
use std::fmt;
use std::ops::Not;
use stdx::never;
@@ -55,14 +54,14 @@ pub(crate) fn variances_of(db: &dyn HirDatabase, def: GenericDefId) -> Option<Ar
variances.is_empty().not().then(|| Arc::from_iter(variances))
}
-pub(crate) fn variances_of_cycle_fn(
- _db: &dyn HirDatabase,
- _result: &Option<Arc<[Variance]>>,
- _count: u32,
- _def: GenericDefId,
-) -> CycleRecoveryAction<Option<Arc<[Variance]>>> {
- CycleRecoveryAction::Iterate
-}
+// pub(crate) fn variances_of_cycle_fn(
+// _db: &dyn HirDatabase,
+// _result: &Option<Arc<[Variance]>>,
+// _count: u32,
+// _def: GenericDefId,
+// ) -> salsa::CycleRecoveryAction<Option<Arc<[Variance]>>> {
+// salsa::CycleRecoveryAction::Iterate
+// }
pub(crate) fn variances_of_cycle_initial(
db: &dyn HirDatabase,
@@ -966,7 +965,7 @@ struct S3<T>(S<T, T>);
struct FixedPoint<T, U, V>(&'static FixedPoint<(), T, U>, V);
"#,
expect![[r#"
- FixedPoint[T: covariant, U: covariant, V: covariant]
+ FixedPoint[T: bivariant, U: bivariant, V: bivariant]
"#]],
);
}
diff --git a/crates/hir/src/display.rs b/crates/hir/src/display.rs
index 53817f37aa..124ab8e274 100644
--- a/crates/hir/src/display.rs
+++ b/crates/hir/src/display.rs
@@ -516,8 +516,7 @@ impl HirDisplay for TypeParam {
return Ok(());
}
- let sized_trait =
- f.db.lang_item(krate, LangItem::Sized).and_then(|lang_item| lang_item.as_trait());
+ let sized_trait = LangItem::Sized.resolve_trait(f.db, krate);
let has_only_sized_bound = predicates.iter().all(move |pred| match pred.skip_binders() {
WhereClause::Implemented(it) => Some(it.hir_trait_id()) == sized_trait,
_ => false,
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index 143c13069e..3f1d5bb01f 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -53,7 +53,6 @@ use hir_def::{
generics::{LifetimeParamData, TypeOrConstParamData, TypeParamProvenance},
},
item_tree::{AttrOwner, FieldParent, ImportAlias, ItemTreeFieldId, ItemTreeNode},
- lang_item::LangItemTarget,
layout::{self, ReprOptions, TargetDataLayout},
nameres::{self, diagnostics::DefDiagnostic},
per_ns::PerNs,
@@ -137,7 +136,6 @@ pub use {
HirFileRange, InFile, InFileWrapper, InMacroFile, InRealFile, MacroFilePosition,
MacroFileRange,
},
- hygiene::{SyntaxContextExt, marks_rev},
inert_attr_macro::AttributeTemplate,
mod_path::{ModPath, PathKind, tool_path},
name::Name,
@@ -781,7 +779,7 @@ impl Module {
let drop_maybe_dangle = (|| {
// FIXME: This can be simplified a lot by exposing hir-ty's utils.rs::Generics helper
let trait_ = trait_?;
- let drop_trait = db.lang_item(self.krate().into(), LangItem::Drop)?.as_trait()?;
+ let drop_trait = LangItem::Drop.resolve_trait(db, self.krate().into())?;
if drop_trait != trait_.into() {
return None;
}
@@ -2388,14 +2386,11 @@ impl Function {
}
let Some(impl_traits) = self.ret_type(db).as_impl_traits(db) else { return false };
- let Some(future_trait_id) =
- db.lang_item(self.ty(db).env.krate, LangItem::Future).and_then(|t| t.as_trait())
+ let Some(future_trait_id) = LangItem::Future.resolve_trait(db, self.ty(db).env.krate)
else {
return false;
};
- let Some(sized_trait_id) =
- db.lang_item(self.ty(db).env.krate, LangItem::Sized).and_then(|t| t.as_trait())
- else {
+ let Some(sized_trait_id) = LangItem::Sized.resolve_trait(db, self.ty(db).env.krate) else {
return false;
};
@@ -2861,9 +2856,7 @@ pub struct Trait {
impl Trait {
pub fn lang(db: &dyn HirDatabase, krate: Crate, name: &Name) -> Option<Trait> {
- db.lang_item(krate.into(), LangItem::from_name(name)?)
- .and_then(LangItemTarget::as_trait)
- .map(Into::into)
+ LangItem::from_name(name)?.resolve_trait(db, krate.into()).map(Into::into)
}
pub fn module(self, db: &dyn HirDatabase) -> Module {
@@ -3692,24 +3685,16 @@ impl GenericDef {
}
let source_map = match def {
- GenericDefId::AdtId(AdtId::EnumId(it)) => {
- db.enum_signature_with_source_map(it).1.clone()
- }
- GenericDefId::AdtId(AdtId::StructId(it)) => {
- db.struct_signature_with_source_map(it).1.clone()
- }
- GenericDefId::AdtId(AdtId::UnionId(it)) => {
- db.union_signature_with_source_map(it).1.clone()
- }
+ GenericDefId::AdtId(AdtId::EnumId(it)) => db.enum_signature_with_source_map(it).1,
+ GenericDefId::AdtId(AdtId::StructId(it)) => db.struct_signature_with_source_map(it).1,
+ GenericDefId::AdtId(AdtId::UnionId(it)) => db.union_signature_with_source_map(it).1,
GenericDefId::ConstId(_) => return,
- GenericDefId::FunctionId(it) => db.function_signature_with_source_map(it).1.clone(),
- GenericDefId::ImplId(it) => db.impl_signature_with_source_map(it).1.clone(),
+ GenericDefId::FunctionId(it) => db.function_signature_with_source_map(it).1,
+ GenericDefId::ImplId(it) => db.impl_signature_with_source_map(it).1,
GenericDefId::StaticId(_) => return,
- GenericDefId::TraitAliasId(it) => {
- db.trait_alias_signature_with_source_map(it).1.clone()
- }
- GenericDefId::TraitId(it) => db.trait_signature_with_source_map(it).1.clone(),
- GenericDefId::TypeAliasId(it) => db.type_alias_signature_with_source_map(it).1.clone(),
+ GenericDefId::TraitAliasId(it) => db.trait_alias_signature_with_source_map(it).1,
+ GenericDefId::TraitId(it) => db.trait_signature_with_source_map(it).1,
+ GenericDefId::TypeAliasId(it) => db.type_alias_signature_with_source_map(it).1,
};
expr_store_diagnostics(db, acc, &source_map);
@@ -3809,7 +3794,7 @@ impl GenericSubstitution {
container_params
.chain(self_params)
.filter_map(|(ty, name)| {
- Some((name?.symbol().clone(), Type { ty: ty.clone(), env: self.env.clone() }))
+ Some((name?.symbol().clone(), Type { ty, env: self.env.clone() }))
})
.collect()
}
@@ -4989,18 +4974,14 @@ impl Type {
/// `std::future::Future` and returns the `Output` associated type.
/// This function is used in `.await` syntax completion.
pub fn into_future_output(&self, db: &dyn HirDatabase) -> Option<Type> {
- let trait_ = db
- .lang_item(self.env.krate, LangItem::IntoFutureIntoFuture)
- .and_then(|it| {
- let into_future_fn = it.as_function()?;
+ let trait_ = LangItem::IntoFutureIntoFuture
+ .resolve_function(db, self.env.krate)
+ .and_then(|into_future_fn| {
let assoc_item = as_assoc_item(db, AssocItem::Function, into_future_fn)?;
let into_future_trait = assoc_item.container_or_implemented_trait(db)?;
Some(into_future_trait.id)
})
- .or_else(|| {
- let future_trait = db.lang_item(self.env.krate, LangItem::Future)?;
- future_trait.as_trait()
- })?;
+ .or_else(|| LangItem::Future.resolve_trait(db, self.env.krate))?;
let canonical_ty =
Canonical { value: self.ty.clone(), binders: CanonicalVarKinds::empty(Interner) };
@@ -5015,14 +4996,13 @@ impl Type {
/// This does **not** resolve `IntoFuture`, only `Future`.
pub fn future_output(self, db: &dyn HirDatabase) -> Option<Type> {
- let future_output =
- db.lang_item(self.env.krate, LangItem::FutureOutput)?.as_type_alias()?;
+ let future_output = LangItem::FutureOutput.resolve_type_alias(db, self.env.krate)?;
self.normalize_trait_assoc_type(db, &[], future_output.into())
}
/// This does **not** resolve `IntoIterator`, only `Iterator`.
pub fn iterator_item(self, db: &dyn HirDatabase) -> Option<Type> {
- let iterator_trait = db.lang_item(self.env.krate, LangItem::Iterator)?.as_trait()?;
+ let iterator_trait = LangItem::Iterator.resolve_trait(db, self.env.krate)?;
let iterator_item = db
.trait_items(iterator_trait)
.associated_type_by_name(&Name::new_symbol_root(sym::Item))?;
@@ -5030,9 +5010,7 @@ impl Type {
}
pub fn impls_iterator(self, db: &dyn HirDatabase) -> bool {
- let Some(iterator_trait) =
- db.lang_item(self.env.krate, LangItem::Iterator).and_then(|it| it.as_trait())
- else {
+ let Some(iterator_trait) = LangItem::Iterator.resolve_trait(db, self.env.krate) else {
return false;
};
let canonical_ty =
@@ -5042,12 +5020,13 @@ impl Type {
/// Resolves the projection `<Self as IntoIterator>::IntoIter` and returns the resulting type
pub fn into_iterator_iter(self, db: &dyn HirDatabase) -> Option<Type> {
- let trait_ = db.lang_item(self.env.krate, LangItem::IntoIterIntoIter).and_then(|it| {
- let into_iter_fn = it.as_function()?;
- let assoc_item = as_assoc_item(db, AssocItem::Function, into_iter_fn)?;
- let into_iter_trait = assoc_item.container_or_implemented_trait(db)?;
- Some(into_iter_trait.id)
- })?;
+ let trait_ = LangItem::IntoIterIntoIter.resolve_function(db, self.env.krate).and_then(
+ |into_iter_fn| {
+ let assoc_item = as_assoc_item(db, AssocItem::Function, into_iter_fn)?;
+ let into_iter_trait = assoc_item.container_or_implemented_trait(db)?;
+ Some(into_iter_trait.id)
+ },
+ )?;
let canonical_ty =
Canonical { value: self.ty.clone(), binders: CanonicalVarKinds::empty(Interner) };
@@ -5133,10 +5112,8 @@ impl Type {
}
pub fn is_copy(&self, db: &dyn HirDatabase) -> bool {
- let lang_item = db.lang_item(self.env.krate, LangItem::Copy);
- let copy_trait = match lang_item {
- Some(LangItemTarget::Trait(it)) => it,
- _ => return false,
+ let Some(copy_trait) = LangItem::Copy.resolve_trait(db, self.env.krate) else {
+ return false;
};
self.impls_trait(db, copy_trait.into(), &[])
}
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index f708f2e166..4d092c1f0b 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -25,7 +25,6 @@ use hir_expand::{
builtin::{BuiltinFnLikeExpander, EagerExpander},
db::ExpandDatabase,
files::{FileRangeWrapper, InRealFile},
- hygiene::SyntaxContextExt as _,
inert_attr_macro::find_builtin_attr_idx,
mod_path::{ModPath, PathKind},
name::AsName,
@@ -927,7 +926,7 @@ impl<'db> SemanticsImpl<'db> {
token: InRealFile<SyntaxToken>,
mut cb: impl FnMut(InFile<SyntaxToken>, SyntaxContext) -> ControlFlow<T>,
) -> Option<T> {
- self.descend_into_macros_impl(token.clone(), &mut cb)
+ self.descend_into_macros_impl(token, &mut cb)
}
/// Descends the token into expansions, returning the tokens that matches the input
@@ -959,17 +958,13 @@ impl<'db> SemanticsImpl<'db> {
let text = token.text();
let kind = token.kind();
if let Ok(token) = self.wrap_token_infile(token.clone()).into_real_file() {
- self.descend_into_macros_breakable(
- token.clone(),
- |InFile { value, file_id: _ }, _ctx| {
- let mapped_kind = value.kind();
- let any_ident_match =
- || kind.is_any_identifier() && value.kind().is_any_identifier();
- let matches =
- (kind == mapped_kind || any_ident_match()) && text == value.text();
- if matches { ControlFlow::Break(value) } else { ControlFlow::Continue(()) }
- },
- )
+ self.descend_into_macros_breakable(token, |InFile { value, file_id: _ }, _ctx| {
+ let mapped_kind = value.kind();
+ let any_ident_match =
+ || kind.is_any_identifier() && value.kind().is_any_identifier();
+ let matches = (kind == mapped_kind || any_ident_match()) && text == value.text();
+ if matches { ControlFlow::Break(value) } else { ControlFlow::Continue(()) }
+ })
} else {
None
}
diff --git a/crates/hir/src/semantics/source_to_def.rs b/crates/hir/src/semantics/source_to_def.rs
index 466bf7f6c8..587c51d8cc 100644
--- a/crates/hir/src/semantics/source_to_def.rs
+++ b/crates/hir/src/semantics/source_to_def.rs
@@ -559,7 +559,7 @@ impl SourceToDefCtx<'_, '_> {
let item = match ast::Item::cast(value.clone()) {
Some(it) => it,
None => {
- let variant = ast::Variant::cast(value.clone())?;
+ let variant = ast::Variant::cast(value)?;
return this
.enum_variant_to_def(InFile::new(file_id, &variant))
.map(Into::into);
diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs
index 666efe8ec6..c1a75ce7e5 100644
--- a/crates/hir/src/source_analyzer.rs
+++ b/crates/hir/src/source_analyzer.rs
@@ -556,8 +556,8 @@ impl SourceAnalyzer {
}
}
- let future_trait = db.lang_item(self.resolver.krate(), LangItem::Future)?.as_trait()?;
- let poll_fn = db.lang_item(self.resolver.krate(), LangItem::FuturePoll)?.as_function()?;
+ let future_trait = LangItem::Future.resolve_trait(db, self.resolver.krate())?;
+ let poll_fn = LangItem::FuturePoll.resolve_function(db, self.resolver.krate())?;
// HACK: subst for `poll()` coincides with that for `Future` because `poll()` itself
// doesn't have any generic parameters, so we skip building another subst for `poll()`.
let substs = hir_ty::TyBuilder::subst_for_def(db, future_trait, None).push(ty).build();
@@ -666,7 +666,7 @@ impl SourceAnalyzer {
) -> Option<FunctionId> {
let ty = self.ty_of_expr(try_expr.expr()?)?;
- let op_fn = db.lang_item(self.resolver.krate(), LangItem::TryTraitBranch)?.as_function()?;
+ let op_fn = LangItem::TryTraitBranch.resolve_function(db, self.resolver.krate())?;
let op_trait = match op_fn.lookup(db).container {
ItemContainerId::TraitId(id) => id,
_ => return None,
@@ -1425,13 +1425,13 @@ impl SourceAnalyzer {
lang_trait: LangItem,
method_name: &Name,
) -> Option<(TraitId, FunctionId)> {
- let trait_id = db.lang_item(self.resolver.krate(), lang_trait)?.as_trait()?;
+ let trait_id = lang_trait.resolve_trait(db, self.resolver.krate())?;
let fn_id = db.trait_items(trait_id).method_by_name(method_name)?;
Some((trait_id, fn_id))
}
fn ty_of_expr(&self, expr: ast::Expr) -> Option<&Ty> {
- self.infer()?.type_of_expr_or_pat(self.expr_id(expr.clone())?)
+ self.infer()?.type_of_expr_or_pat(self.expr_id(expr)?)
}
}
diff --git a/crates/hir/src/symbols.rs b/crates/hir/src/symbols.rs
index 41064d047a..e87ab87407 100644
--- a/crates/hir/src/symbols.rs
+++ b/crates/hir/src/symbols.rs
@@ -13,13 +13,13 @@ use hir_def::{
use hir_expand::{HirFileId, name::Name};
use hir_ty::{
db::HirDatabase,
- display::{DisplayTarget, HirDisplay, hir_display_with_store},
+ display::{HirDisplay, hir_display_with_store},
};
use intern::Symbol;
use rustc_hash::FxHashMap;
use syntax::{AstNode, AstPtr, SmolStr, SyntaxNode, SyntaxNodePtr, ToSmolStr, ast::HasName};
-use crate::{Module, ModuleDef, Semantics};
+use crate::{HasCrate, Module, ModuleDef, Semantics};
pub type FxIndexSet<T> = indexmap::IndexSet<T, std::hash::BuildHasherDefault<rustc_hash::FxHasher>>;
@@ -66,7 +66,6 @@ pub struct SymbolCollector<'a> {
symbols: FxIndexSet<FileSymbol>,
work: Vec<SymbolCollectorWork>,
current_container_name: Option<SmolStr>,
- display_target: DisplayTarget,
}
/// Given a [`ModuleId`] and a [`HirDatabase`], use the DefMap for the module's crate to collect
@@ -78,10 +77,6 @@ impl<'a> SymbolCollector<'a> {
symbols: Default::default(),
work: Default::default(),
current_container_name: None,
- display_target: DisplayTarget::from_crate(
- db,
- *db.all_crates().last().expect("no crate graph present"),
- ),
}
}
@@ -93,8 +88,7 @@ impl<'a> SymbolCollector<'a> {
pub fn collect(&mut self, module: Module) {
let _p = tracing::info_span!("SymbolCollector::collect", ?module).entered();
- tracing::info!(?module, "SymbolCollector::collect",);
- self.display_target = module.krate().to_display_target(self.db);
+ tracing::info!(?module, "SymbolCollector::collect");
// The initial work is the root module we're collecting, additional work will
// be populated as we traverse the module's definitions.
@@ -263,8 +257,9 @@ impl<'a> SymbolCollector<'a> {
for (name, Item { def, vis, import }) in scope.macros() {
if let Some(i) = import {
match i {
- ImportOrGlob::Import(i) => push_import(self, i, name, def.into(), vis),
- ImportOrGlob::Glob(_) => (),
+ ImportOrExternCrate::Import(i) => push_import(self, i, name, def.into(), vis),
+ ImportOrExternCrate::Glob(_) => (),
+ ImportOrExternCrate::ExternCrate(_) => (),
}
continue;
}
@@ -320,7 +315,10 @@ impl<'a> SymbolCollector<'a> {
let impl_data = self.db.impl_signature(impl_id);
let impl_name = Some(
hir_display_with_store(impl_data.self_ty, &impl_data.store)
- .display(self.db, self.display_target)
+ .display(
+ self.db,
+ crate::Impl::from(impl_id).krate(self.db).to_display_target(self.db),
+ )
.to_smolstr(),
);
self.with_container_name(impl_name, |s| {
diff --git a/crates/ide-assists/src/assist_config.rs b/crates/ide-assists/src/assist_config.rs
index 2de0013bb1..fb569f8cda 100644
--- a/crates/ide-assists/src/assist_config.rs
+++ b/crates/ide-assists/src/assist_config.rs
@@ -5,7 +5,7 @@
//! assists if we are allowed to.
use hir::ImportPathConfig;
-use ide_db::{SnippetCap, imports::insert_use::InsertUseConfig};
+use ide_db::{SnippetCap, assists::ExprFillDefaultMode, imports::insert_use::InsertUseConfig};
use crate::AssistKind;
@@ -21,6 +21,7 @@ pub struct AssistConfig {
pub term_search_fuel: u64,
pub term_search_borrowck: bool,
pub code_action_grouping: bool,
+ pub expr_fill_default: ExprFillDefaultMode,
}
impl AssistConfig {
diff --git a/crates/ide-assists/src/handlers/add_missing_impl_members.rs b/crates/ide-assists/src/handlers/add_missing_impl_members.rs
index 887ec5aeec..6a55f39e69 100644
--- a/crates/ide-assists/src/handlers/add_missing_impl_members.rs
+++ b/crates/ide-assists/src/handlers/add_missing_impl_members.rs
@@ -150,6 +150,7 @@ fn add_missing_impl_members_inner(
let new_impl_def = edit.make_mut(impl_def.clone());
let first_new_item = add_trait_assoc_items_to_impl(
&ctx.sema,
+ ctx.config,
&missing_items,
trait_,
&new_impl_def,
diff --git a/crates/ide-assists/src/handlers/add_missing_match_arms.rs b/crates/ide-assists/src/handlers/add_missing_match_arms.rs
index 8c1c83e3f7..858d436991 100644
--- a/crates/ide-assists/src/handlers/add_missing_match_arms.rs
+++ b/crates/ide-assists/src/handlers/add_missing_match_arms.rs
@@ -3,6 +3,7 @@ use std::iter::{self, Peekable};
use either::Either;
use hir::{Adt, Crate, HasAttrs, ImportPathConfig, ModuleDef, Semantics, sym};
use ide_db::RootDatabase;
+use ide_db::assists::ExprFillDefaultMode;
use ide_db::syntax_helpers::suggest_name;
use ide_db::{famous_defs::FamousDefs, helpers::mod_path_to_ast};
use itertools::Itertools;
@@ -216,7 +217,17 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>)
// filter out hidden patterns because they're handled by the catch-all arm
!hidden
})
- .map(|(pat, _)| make.match_arm(pat, None, make::ext::expr_todo()));
+ .map(|(pat, _)| {
+ make.match_arm(
+ pat,
+ None,
+ match ctx.config.expr_fill_default {
+ ExprFillDefaultMode::Todo => make::ext::expr_todo(),
+ ExprFillDefaultMode::Underscore => make::ext::expr_underscore(),
+ ExprFillDefaultMode::Default => make::ext::expr_todo(),
+ },
+ )
+ });
let mut arms: Vec<_> = match_arm_list
.arms()
@@ -246,7 +257,15 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>)
if needs_catch_all_arm && !has_catch_all_arm {
cov_mark::hit!(added_wildcard_pattern);
- let arm = make.match_arm(make.wildcard_pat().into(), None, make::ext::expr_todo());
+ let arm = make.match_arm(
+ make.wildcard_pat().into(),
+ None,
+ match ctx.config.expr_fill_default {
+ ExprFillDefaultMode::Todo => make::ext::expr_todo(),
+ ExprFillDefaultMode::Underscore => make::ext::expr_underscore(),
+ ExprFillDefaultMode::Default => make::ext::expr_todo(),
+ },
+ );
arms.push(arm);
}
@@ -474,8 +493,8 @@ fn build_pat(
hir::StructKind::Record => {
let fields = fields
.into_iter()
- .map(|f| make.name_ref(f.name(db).as_str()))
- .map(|name_ref| make.record_pat_field_shorthand(name_ref));
+ .map(|f| make.ident_pat(false, false, make.name(f.name(db).as_str())))
+ .map(|ident| make.record_pat_field_shorthand(ident.into()));
let fields = make.record_pat_field_list(fields, None);
make.record_pat_with_fields(path, fields).into()
}
diff --git a/crates/ide-assists/src/handlers/convert_bool_then.rs b/crates/ide-assists/src/handlers/convert_bool_then.rs
index cd23ad2237..bcd06c1ef7 100644
--- a/crates/ide-assists/src/handlers/convert_bool_then.rs
+++ b/crates/ide-assists/src/handlers/convert_bool_then.rs
@@ -196,7 +196,7 @@ pub(crate) fn convert_bool_then_to_if(acc: &mut Assists, ctx: &AssistContext<'_>
// Wrap all tails in `Some(...)`
let none_path = mapless_make.expr_path(mapless_make.ident_path("None"));
let some_path = mapless_make.expr_path(mapless_make.ident_path("Some"));
- for_each_tail_expr(&ast::Expr::BlockExpr(closure_body.clone()), &mut |e| {
+ for_each_tail_expr(&ast::Expr::BlockExpr(closure_body), &mut |e| {
let e = match e {
ast::Expr::BreakExpr(e) => e.expr(),
ast::Expr::ReturnExpr(e) => e.expr(),
diff --git a/crates/ide-assists/src/handlers/convert_closure_to_fn.rs b/crates/ide-assists/src/handlers/convert_closure_to_fn.rs
index 1d3a2db335..43515de71e 100644
--- a/crates/ide-assists/src/handlers/convert_closure_to_fn.rs
+++ b/crates/ide-assists/src/handlers/convert_closure_to_fn.rs
@@ -1066,7 +1066,7 @@ fn foo() {
r#"
fn foo() {
let (mut a, b) = (0.1, "abc");
- fn closure(p1: i32, p2: &mut bool, a: &mut f64, b: &&str) {
+ fn closure(p1: i32, p2: &mut bool, a: &mut f64, b: &&'static str) {
*a = 1.2;
let c = *b;
}
@@ -1098,7 +1098,7 @@ fn foo() {
r#"
fn foo() {
let (mut a, b) = (0.1, "abc");
- fn closure(p1: i32, p2: &mut bool, a: &mut f64, b: &&str) {
+ fn closure(p1: i32, p2: &mut bool, a: &mut f64, b: &&'static str) {
let _: &mut bool = p2;
*a = 1.2;
let c = *b;
@@ -1136,7 +1136,7 @@ fn foo() {
r#"
fn foo() {
let (mut a, b) = (0.1, "abc");
- fn closure(p1: i32, p2: &mut bool, a: &mut f64, b: &&str) {
+ fn closure(p1: i32, p2: &mut bool, a: &mut f64, b: &&'static str) {
let _: &mut bool = p2;
*a = 1.2;
let c = *b;
diff --git a/crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs b/crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs
index 24cc32d10d..db41927f1d 100644
--- a/crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs
+++ b/crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs
@@ -80,7 +80,7 @@ pub(crate) fn convert_from_to_tryfrom(acc: &mut Assists, ctx: &AssistContext<'_>
let from_fn_name = builder.make_mut(from_fn_name);
let tail_expr = builder.make_mut(tail_expr);
let return_exprs = return_exprs.map(|r| builder.make_mut(r)).collect_vec();
- let associated_items = builder.make_mut(associated_items).clone();
+ let associated_items = builder.make_mut(associated_items);
ted::replace(
trait_ty.syntax(),
diff --git a/crates/ide-assists/src/handlers/convert_let_else_to_match.rs b/crates/ide-assists/src/handlers/convert_let_else_to_match.rs
index df92b07cba..ebfed9f9ca 100644
--- a/crates/ide-assists/src/handlers/convert_let_else_to_match.rs
+++ b/crates/ide-assists/src/handlers/convert_let_else_to_match.rs
@@ -1,8 +1,9 @@
-use hir::Semantics;
-use ide_db::RootDatabase;
use syntax::T;
use syntax::ast::RangeItem;
-use syntax::ast::{AstNode, HasName, LetStmt, Name, Pat, edit::AstNodeEdit};
+use syntax::ast::edit::IndentLevel;
+use syntax::ast::edit_in_place::Indent;
+use syntax::ast::syntax_factory::SyntaxFactory;
+use syntax::ast::{self, AstNode, HasName, LetStmt, Pat};
use crate::{AssistContext, AssistId, Assists};
@@ -25,155 +26,205 @@ use crate::{AssistContext, AssistId, Assists};
// }
// ```
pub(crate) fn convert_let_else_to_match(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
- // should focus on else token to trigger
+ // Should focus on the `else` token to trigger
let let_stmt = ctx
.find_token_syntax_at_offset(T![else])
.and_then(|it| it.parent()?.parent())
.or_else(|| ctx.find_token_syntax_at_offset(T![let])?.parent())?;
let let_stmt = LetStmt::cast(let_stmt)?;
- let let_else_block = let_stmt.let_else()?.block_expr()?;
- let let_init = let_stmt.initializer()?;
+ let else_block = let_stmt.let_else()?.block_expr()?;
+ let else_expr = if else_block.statements().next().is_none() {
+ else_block.tail_expr()?
+ } else {
+ else_block.into()
+ };
+ let init = let_stmt.initializer()?;
+ // Ignore let stmt with type annotation
if let_stmt.ty().is_some() {
- // don't support let with type annotation
return None;
}
let pat = let_stmt.pat()?;
- let mut binders = Vec::new();
- binders_in_pat(&mut binders, &pat, &ctx.sema)?;
- let target = let_stmt.syntax().text_range();
+ let make = SyntaxFactory::with_mappings();
+ let mut idents = Vec::default();
+ let pat_without_mut = remove_mut_and_collect_idents(&make, &pat, &mut idents)?;
+ let bindings = idents
+ .into_iter()
+ .filter_map(|ref pat| {
+ // Identifiers which resolve to constants are not bindings
+ if ctx.sema.resolve_bind_pat_to_const(pat).is_none() {
+ Some((pat.name()?, pat.ref_token().is_none() && pat.mut_token().is_some()))
+ } else {
+ None
+ }
+ })
+ .collect::<Vec<_>>();
+
acc.add(
AssistId::refactor_rewrite("convert_let_else_to_match"),
- "Convert let-else to let and match",
- target,
- |edit| {
- let indent_level = let_stmt.indent_level().0 as usize;
- let indent = " ".repeat(indent_level);
- let indent1 = " ".repeat(indent_level + 1);
+ if bindings.is_empty() {
+ "Convert let-else to match"
+ } else {
+ "Convert let-else to let and match"
+ },
+ let_stmt.syntax().text_range(),
+ |builder| {
+ let mut editor = builder.make_editor(let_stmt.syntax());
- let binders_str = binders_to_str(&binders, false);
- let binders_str_mut = binders_to_str(&binders, true);
+ let binding_paths = bindings
+ .iter()
+ .map(|(name, _)| make.expr_path(make.ident_path(&name.to_string())))
+ .collect::<Vec<_>>();
- let init_expr = let_init.syntax().text();
- let mut pat_no_mut = pat.syntax().text().to_string();
- // remove the mut from the pattern
- for (b, ismut) in binders.iter() {
- if *ismut {
- pat_no_mut = pat_no_mut.replace(&format!("mut {b}"), &b.to_string());
- }
- }
+ let binding_arm = make.match_arm(
+ pat_without_mut,
+ None,
+ // There are three possible cases:
+ //
+ // - No bindings: `None => {}`
+ // - Single binding: `Some(it) => it`
+ // - Multiple bindings: `Foo::Bar { a, b, .. } => (a, b)`
+ match binding_paths.len() {
+ 0 => make.expr_empty_block().into(),
- let only_expr = let_else_block.statements().next().is_none();
- let branch2 = match &let_else_block.tail_expr() {
- Some(tail) if only_expr => format!("{tail},"),
- _ => let_else_block.syntax().text().to_string(),
- };
- let replace = if binders.is_empty() {
- format!(
- "match {init_expr} {{
-{indent1}{pat_no_mut} => {binders_str}
-{indent1}_ => {branch2}
-{indent}}}"
- )
+ 1 => binding_paths[0].clone(),
+ _ => make.expr_tuple(binding_paths).into(),
+ },
+ );
+ let else_arm = make.match_arm(make.wildcard_pat().into(), None, else_expr);
+ let match_ = make.expr_match(init, make.match_arm_list([binding_arm, else_arm]));
+ match_.reindent_to(IndentLevel::from_node(let_stmt.syntax()));
+
+ if bindings.is_empty() {
+ editor.replace(let_stmt.syntax(), match_.syntax());
} else {
- format!(
- "let {binders_str_mut} = match {init_expr} {{
-{indent1}{pat_no_mut} => {binders_str},
-{indent1}_ => {branch2}
-{indent}}};"
- )
- };
- edit.replace(target, replace);
+ let ident_pats = bindings
+ .into_iter()
+ .map(|(name, is_mut)| make.ident_pat(false, is_mut, name).into())
+ .collect::<Vec<Pat>>();
+ let new_let_stmt = make.let_stmt(
+ if ident_pats.len() == 1 {
+ ident_pats[0].clone()
+ } else {
+ make.tuple_pat(ident_pats).into()
+ },
+ None,
+ Some(match_.into()),
+ );
+ 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);
},
)
}
-/// Gets a list of binders in a pattern, and whether they are mut.
-fn binders_in_pat(
- acc: &mut Vec<(Name, bool)>,
- pat: &Pat,
- sem: &Semantics<'_, RootDatabase>,
-) -> Option<()> {
- use Pat::*;
- match pat {
- IdentPat(p) => {
- let ident = p.name()?;
- let ismut = p.ref_token().is_none() && p.mut_token().is_some();
- // check for const reference
- if sem.resolve_bind_pat_to_const(p).is_none() {
- acc.push((ident, ismut));
- }
+fn remove_mut_and_collect_idents(
+ make: &SyntaxFactory,
+ pat: &ast::Pat,
+ acc: &mut Vec<ast::IdentPat>,
+) -> Option<ast::Pat> {
+ Some(match pat {
+ ast::Pat::IdentPat(p) => {
+ acc.push(p.clone());
+ let non_mut_pat = make.ident_pat(
+ p.ref_token().is_some(),
+ p.ref_token().is_some() && p.mut_token().is_some(),
+ p.name()?,
+ );
if let Some(inner) = p.pat() {
- binders_in_pat(acc, &inner, sem)?;
- }
- Some(())
- }
- BoxPat(p) => p.pat().and_then(|p| binders_in_pat(acc, &p, sem)),
- RestPat(_) | LiteralPat(_) | PathPat(_) | WildcardPat(_) | ConstBlockPat(_) => Some(()),
- OrPat(p) => {
- for p in p.pats() {
- binders_in_pat(acc, &p, sem)?;
+ non_mut_pat.set_pat(remove_mut_and_collect_idents(make, &inner, acc));
}
- Some(())
+ non_mut_pat.into()
}
- ParenPat(p) => p.pat().and_then(|p| binders_in_pat(acc, &p, sem)),
- RangePat(p) => {
- if let Some(st) = p.start() {
- binders_in_pat(acc, &st, sem)?
- }
- if let Some(ed) = p.end() {
- binders_in_pat(acc, &ed, sem)?
- }
- Some(())
- }
- RecordPat(p) => {
- for f in p.record_pat_field_list()?.fields() {
- let pat = f.pat()?;
- binders_in_pat(acc, &pat, sem)?;
- }
- Some(())
+ ast::Pat::BoxPat(p) => {
+ make.box_pat(remove_mut_and_collect_idents(make, &p.pat()?, acc)?).into()
}
- RefPat(p) => p.pat().and_then(|p| binders_in_pat(acc, &p, sem)),
- SlicePat(p) => {
- for p in p.pats() {
- binders_in_pat(acc, &p, sem)?;
- }
- Some(())
- }
- TuplePat(p) => {
- for p in p.fields() {
- binders_in_pat(acc, &p, sem)?;
- }
- Some(())
+ ast::Pat::OrPat(p) => make
+ .or_pat(
+ p.pats()
+ .map(|pat| remove_mut_and_collect_idents(make, &pat, acc))
+ .collect::<Option<Vec<_>>>()?,
+ p.leading_pipe().is_some(),
+ )
+ .into(),
+ ast::Pat::ParenPat(p) => {
+ make.paren_pat(remove_mut_and_collect_idents(make, &p.pat()?, acc)?).into()
}
- TupleStructPat(p) => {
- for p in p.fields() {
- binders_in_pat(acc, &p, sem)?;
+ ast::Pat::RangePat(p) => make
+ .range_pat(
+ if let Some(start) = p.start() {
+ Some(remove_mut_and_collect_idents(make, &start, acc)?)
+ } else {
+ None
+ },
+ if let Some(end) = p.end() {
+ Some(remove_mut_and_collect_idents(make, &end, acc)?)
+ } else {
+ None
+ },
+ )
+ .into(),
+ ast::Pat::RecordPat(p) => make
+ .record_pat_with_fields(
+ p.path()?,
+ make.record_pat_field_list(
+ p.record_pat_field_list()?
+ .fields()
+ .map(|field| {
+ remove_mut_and_collect_idents(make, &field.pat()?, acc).map(|pat| {
+ if let Some(name_ref) = field.name_ref() {
+ make.record_pat_field(name_ref, pat)
+ } else {
+ make.record_pat_field_shorthand(pat)
+ }
+ })
+ })
+ .collect::<Option<Vec<_>>>()?,
+ p.record_pat_field_list()?.rest_pat(),
+ ),
+ )
+ .into(),
+ ast::Pat::RefPat(p) => {
+ let inner = p.pat()?;
+ if let ast::Pat::IdentPat(ident) = inner {
+ acc.push(ident);
+ p.clone_for_update().into()
+ } else {
+ make.ref_pat(remove_mut_and_collect_idents(make, &inner, acc)?).into()
}
- Some(())
}
+ ast::Pat::SlicePat(p) => make
+ .slice_pat(
+ p.pats()
+ .map(|pat| remove_mut_and_collect_idents(make, &pat, acc))
+ .collect::<Option<Vec<_>>>()?,
+ )
+ .into(),
+ ast::Pat::TuplePat(p) => make
+ .tuple_pat(
+ p.fields()
+ .map(|field| remove_mut_and_collect_idents(make, &field, acc))
+ .collect::<Option<Vec<_>>>()?,
+ )
+ .into(),
+ ast::Pat::TupleStructPat(p) => make
+ .tuple_struct_pat(
+ p.path()?,
+ p.fields()
+ .map(|field| remove_mut_and_collect_idents(make, &field, acc))
+ .collect::<Option<Vec<_>>>()?,
+ )
+ .into(),
+ ast::Pat::RestPat(_)
+ | ast::Pat::LiteralPat(_)
+ | ast::Pat::PathPat(_)
+ | ast::Pat::WildcardPat(_)
+ | ast::Pat::ConstBlockPat(_) => pat.clone(),
// don't support macro pat yet
- MacroPat(_) => None,
- }
-}
-
-fn binders_to_str(binders: &[(Name, bool)], addmut: bool) -> String {
- let vars = binders
- .iter()
- .map(
- |(ident, ismut)| {
- if *ismut && addmut { format!("mut {ident}") } else { ident.to_string() }
- },
- )
- .collect::<Vec<_>>()
- .join(", ");
- if binders.is_empty() {
- String::from("{}")
- } else if binders.len() == 1 {
- vars
- } else {
- format!("({vars})")
- }
+ ast::Pat::MacroPat(_) => return None,
+ })
}
#[cfg(test)]
diff --git a/crates/ide-assists/src/handlers/destructure_struct_binding.rs b/crates/ide-assists/src/handlers/destructure_struct_binding.rs
index 800ef89ac6..b8c647ac8b 100644
--- a/crates/ide-assists/src/handlers/destructure_struct_binding.rs
+++ b/crates/ide-assists/src/handlers/destructure_struct_binding.rs
@@ -196,7 +196,9 @@ fn destructure_pat(
let fields = field_names.iter().map(|(old_name, new_name)| {
// Use shorthand syntax if possible
if old_name == new_name && !is_mut {
- make.record_pat_field_shorthand(make.name_ref(old_name))
+ make.record_pat_field_shorthand(
+ make.ident_pat(false, false, make.name(old_name)).into(),
+ )
} else {
make.record_pat_field(
make.name_ref(old_name),
diff --git a/crates/ide-assists/src/handlers/destructure_tuple_binding.rs b/crates/ide-assists/src/handlers/destructure_tuple_binding.rs
index adf0f0997b..f09389f830 100644
--- a/crates/ide-assists/src/handlers/destructure_tuple_binding.rs
+++ b/crates/ide-assists/src/handlers/destructure_tuple_binding.rs
@@ -142,7 +142,7 @@ fn collect_data(ident_pat: IdentPat, ctx: &AssistContext<'_>) -> Option<TupleDat
.map(|(id, ty)| {
match name_generator.for_type(&ty, ctx.db(), ctx.edition()) {
Some(name) => name,
- None => name_generator.suggest_name(&format!("_{}", id)),
+ None => name_generator.suggest_name(&format!("_{id}")),
}
.to_string()
})
diff --git a/crates/ide-assists/src/handlers/expand_rest_pattern.rs b/crates/ide-assists/src/handlers/expand_rest_pattern.rs
index 4e487e2162..b71de5e00c 100644
--- a/crates/ide-assists/src/handlers/expand_rest_pattern.rs
+++ b/crates/ide-assists/src/handlers/expand_rest_pattern.rs
@@ -56,7 +56,12 @@ fn expand_record_rest_pattern(
let new_field_list = make.record_pat_field_list(old_field_list.fields(), None);
for (f, _) in missing_fields.iter() {
let field = make.record_pat_field_shorthand(
- make.name_ref(&f.name(ctx.sema.db).display_no_db(edition).to_smolstr()),
+ make.ident_pat(
+ false,
+ false,
+ make.name(&f.name(ctx.sema.db).display_no_db(edition).to_smolstr()),
+ )
+ .into(),
);
new_field_list.add_field(field);
}
diff --git a/crates/ide-assists/src/handlers/extract_function.rs b/crates/ide-assists/src/handlers/extract_function.rs
index 046af71a9d..e977798c4f 100644
--- a/crates/ide-assists/src/handlers/extract_function.rs
+++ b/crates/ide-assists/src/handlers/extract_function.rs
@@ -5033,7 +5033,7 @@ fn main() {
fun_name(bar);
}
-fn $0fun_name(bar: &str) {
+fn $0fun_name(bar: &'static str) {
m!(bar);
}
"#,
diff --git a/crates/ide-assists/src/handlers/extract_variable.rs b/crates/ide-assists/src/handlers/extract_variable.rs
index 3971b60f25..31e84e9adc 100644
--- a/crates/ide-assists/src/handlers/extract_variable.rs
+++ b/crates/ide-assists/src/handlers/extract_variable.rs
@@ -631,7 +631,7 @@ fn main() {
"#,
r#"
fn main() {
- const $0HELLO: &str = "hello";
+ const $0HELLO: &'static str = "hello";
}
"#,
"Extract into constant",
@@ -726,7 +726,7 @@ fn main() {
"#,
r#"
fn main() {
- static $0HELLO: &str = "hello";
+ static $0HELLO: &'static str = "hello";
}
"#,
"Extract into static",
@@ -2528,13 +2528,13 @@ fn foo() {
check_assist_by_label(
extract_variable,
r#"
-struct Entry(&str);
+struct Entry<'a>(&'a str);
fn foo() {
let entry = Entry($0"Hello"$0);
}
"#,
r#"
-struct Entry(&str);
+struct Entry<'a>(&'a str);
fn foo() {
let $0hello = "Hello";
let entry = Entry(hello);
@@ -2546,13 +2546,13 @@ fn foo() {
check_assist_by_label(
extract_variable,
r#"
-struct Entry(&str);
+struct Entry<'a>(&'a str);
fn foo() {
let entry = Entry($0"Hello"$0);
}
"#,
r#"
-struct Entry(&str);
+struct Entry<'a>(&'a str);
fn foo() {
const $0HELLO: &str = "Hello";
let entry = Entry(HELLO);
@@ -2564,13 +2564,13 @@ fn foo() {
check_assist_by_label(
extract_variable,
r#"
-struct Entry(&str);
+struct Entry<'a>(&'a str);
fn foo() {
let entry = Entry($0"Hello"$0);
}
"#,
r#"
-struct Entry(&str);
+struct Entry<'a>(&'a str);
fn foo() {
static $0HELLO: &str = "Hello";
let entry = Entry(HELLO);
@@ -2587,13 +2587,13 @@ fn foo() {
check_assist_by_label(
extract_variable,
r#"
-struct Entry { message: &str }
+struct Entry<'a> { message: &'a str }
fn foo() {
let entry = Entry { message: $0"Hello"$0 };
}
"#,
r#"
-struct Entry { message: &str }
+struct Entry<'a> { message: &'a str }
fn foo() {
let $0message = "Hello";
let entry = Entry { message };
@@ -2605,13 +2605,13 @@ fn foo() {
check_assist_by_label(
extract_variable,
r#"
-struct Entry { message: &str }
+struct Entry<'a> { message: &'a str }
fn foo() {
let entry = Entry { message: $0"Hello"$0 };
}
"#,
r#"
-struct Entry { message: &str }
+struct Entry<'a> { message: &'a str }
fn foo() {
const $0HELLO: &str = "Hello";
let entry = Entry { message: HELLO };
@@ -2623,13 +2623,13 @@ fn foo() {
check_assist_by_label(
extract_variable,
r#"
-struct Entry { message: &str }
+struct Entry<'a> { message: &'a str }
fn foo() {
let entry = Entry { message: $0"Hello"$0 };
}
"#,
r#"
-struct Entry { message: &str }
+struct Entry<'a> { message: &'a str }
fn foo() {
static $0HELLO: &str = "Hello";
let entry = Entry { message: HELLO };
diff --git a/crates/ide-assists/src/handlers/generate_function.rs b/crates/ide-assists/src/handlers/generate_function.rs
index 824380253a..30084d23d1 100644
--- a/crates/ide-assists/src/handlers/generate_function.rs
+++ b/crates/ide-assists/src/handlers/generate_function.rs
@@ -4,6 +4,7 @@ use hir::{
};
use ide_db::{
FileId, FxHashMap, FxHashSet, RootDatabase, SnippetCap,
+ assists::ExprFillDefaultMode,
defs::{Definition, NameRefClass},
famous_defs::FamousDefs,
helpers::is_editable_crate,
@@ -46,7 +47,7 @@ use crate::{
// bar("", baz());
// }
//
-// fn bar(arg: &str, baz: Baz) ${0:-> _} {
+// fn bar(arg: &'static str, baz: Baz) ${0:-> _} {
// todo!()
// }
//
@@ -276,7 +277,11 @@ impl FunctionBuilder {
target_module,
&mut necessary_generic_params,
);
- let placeholder_expr = make::ext::expr_todo();
+ let placeholder_expr = match ctx.config.expr_fill_default {
+ ExprFillDefaultMode::Todo => make::ext::expr_todo(),
+ ExprFillDefaultMode::Underscore => make::ext::expr_underscore(),
+ ExprFillDefaultMode::Default => make::ext::expr_todo(),
+ };
fn_body = make::block_expr(vec![], Some(placeholder_expr));
};
@@ -331,7 +336,11 @@ impl FunctionBuilder {
let (generic_param_list, where_clause) =
fn_generic_params(ctx, necessary_generic_params, &target)?;
- let placeholder_expr = make::ext::expr_todo();
+ let placeholder_expr = match ctx.config.expr_fill_default {
+ ExprFillDefaultMode::Todo => make::ext::expr_todo(),
+ ExprFillDefaultMode::Underscore => make::ext::expr_underscore(),
+ ExprFillDefaultMode::Default => make::ext::expr_todo(),
+ };
let fn_body = make::block_expr(vec![], Some(placeholder_expr));
Some(Self {
@@ -383,14 +392,14 @@ impl FunctionBuilder {
// Focus the return type if there is one
match ret_type {
Some(ret_type) => {
- edit.add_placeholder_snippet(cap, ret_type.clone());
+ edit.add_placeholder_snippet(cap, ret_type);
}
None => {
- edit.add_placeholder_snippet(cap, tail_expr.clone());
+ edit.add_placeholder_snippet(cap, tail_expr);
}
}
} else {
- edit.add_placeholder_snippet(cap, tail_expr.clone());
+ edit.add_placeholder_snippet(cap, tail_expr);
}
}
@@ -444,7 +453,11 @@ fn make_fn_body_as_new_function(
let adt_info = adt_info.as_ref()?;
let path_self = make::ext::ident_path("Self");
- let placeholder_expr = make::ext::expr_todo();
+ let placeholder_expr = match ctx.config.expr_fill_default {
+ ExprFillDefaultMode::Todo => make::ext::expr_todo(),
+ ExprFillDefaultMode::Underscore => make::ext::expr_underscore(),
+ ExprFillDefaultMode::Default => make::ext::expr_todo(),
+ };
let tail_expr = if let Some(strukt) = adt_info.adt.as_struct() {
match strukt.kind(ctx.db()) {
StructKind::Record => {
@@ -1505,7 +1518,7 @@ fn foo() {
bar("bar")
}
-fn bar(arg: &str) {
+fn bar(arg: &'static str) {
${0:todo!()}
}
"#,
@@ -2122,7 +2135,7 @@ fn foo() {
bar(baz(), baz(), "foo", "bar")
}
-fn bar(baz_1: Baz, baz_2: Baz, arg_1: &str, arg_2: &str) {
+fn bar(baz_1: Baz, baz_2: Baz, arg_1: &'static str, arg_2: &'static str) {
${0:todo!()}
}
"#,
@@ -3090,7 +3103,7 @@ pub struct Foo {
field_2: String,
}
impl Foo {
- fn new(baz_1: Baz, baz_2: Baz, arg_1: &str, arg_2: &str) -> Self {
+ fn new(baz_1: Baz, baz_2: Baz, arg_1: &'static str, arg_2: &'static str) -> Self {
${0:Self { field_1: todo!(), field_2: todo!() }}
}
}
diff --git a/crates/ide-assists/src/handlers/merge_imports.rs b/crates/ide-assists/src/handlers/merge_imports.rs
index b7f7cb9cb0..6bf7f58491 100644
--- a/crates/ide-assists/src/handlers/merge_imports.rs
+++ b/crates/ide-assists/src/handlers/merge_imports.rs
@@ -1,14 +1,14 @@
use either::Either;
use ide_db::imports::{
insert_use::{ImportGranularity, InsertUseConfig},
- merge_imports::{MergeBehavior, try_merge_imports, try_merge_trees, try_normalize_use_tree},
+ merge_imports::{MergeBehavior, try_merge_imports, try_merge_trees},
};
-use itertools::Itertools;
use syntax::{
AstNode, SyntaxElement, SyntaxNode,
algo::neighbor,
- ast::{self, edit_in_place::Removable},
- match_ast, ted,
+ ast::{self, syntax_factory::SyntaxFactory},
+ match_ast,
+ syntax_editor::Removable,
};
use crate::{
@@ -69,49 +69,32 @@ pub(crate) fn merge_imports(acc: &mut Assists, ctx: &AssistContext<'_>) -> Optio
(selection_range, edits?)
};
+ let parent_node = match ctx.covering_element() {
+ SyntaxElement::Node(n) => n,
+ SyntaxElement::Token(t) => t.parent()?,
+ };
+
acc.add(AssistId::refactor_rewrite("merge_imports"), "Merge imports", target, |builder| {
- let edits_mut: Vec<Edit> = edits
- .into_iter()
- .map(|it| match it {
- Remove(Either::Left(it)) => Remove(Either::Left(builder.make_mut(it))),
- Remove(Either::Right(it)) => Remove(Either::Right(builder.make_mut(it))),
- Replace(old, new) => Replace(builder.make_syntax_mut(old), new),
- })
- .collect();
- for edit in edits_mut {
+ let make = SyntaxFactory::with_mappings();
+ let mut editor = builder.make_editor(&parent_node);
+
+ for edit in edits {
match edit {
- Remove(it) => it.as_ref().either(Removable::remove, Removable::remove),
- Replace(old, new) => {
- ted::replace(old, &new);
-
- // If there's a selection and we're replacing a use tree in a tree list,
- // normalize the parent use tree if it only contains the merged subtree.
- if !ctx.has_empty_selection() {
- let normalized_use_tree = ast::UseTree::cast(new)
- .as_ref()
- .and_then(ast::UseTree::parent_use_tree_list)
- .and_then(|use_tree_list| {
- if use_tree_list.use_trees().collect_tuple::<(_,)>().is_some() {
- Some(use_tree_list.parent_use_tree())
- } else {
- None
- }
- })
- .and_then(|target_tree| {
- try_normalize_use_tree(
- &target_tree,
- ctx.config.insert_use.granularity.into(),
- )
- .map(|top_use_tree_flat| (target_tree, top_use_tree_flat))
- });
- if let Some((old_tree, new_tree)) = normalized_use_tree {
- cov_mark::hit!(replace_parent_with_normalized_use_tree);
- ted::replace(old_tree.syntax(), new_tree.syntax());
- }
+ Remove(it) => {
+ let node = it.as_ref();
+ if let Some(left) = node.left() {
+ left.remove(&mut editor);
+ } else if let Some(right) = node.right() {
+ right.remove(&mut editor);
}
}
+ Replace(old, new) => {
+ editor.replace(old, &new);
+ }
}
}
+ editor.add_mappings(make.finish_with_mappings());
+ builder.add_file_edits(ctx.vfs_file_id(), editor);
})
}
@@ -723,11 +706,10 @@ use std::{
);
cov_mark::check!(merge_with_selected_use_tree_neighbors);
- cov_mark::check!(replace_parent_with_normalized_use_tree);
check_assist(
merge_imports,
r"use std::$0{fmt::Display, fmt::Debug}$0;",
- r"use std::fmt::{Debug, Display};",
+ r"use std::{fmt::{Debug, Display}};",
);
}
diff --git a/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs b/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs
index 6dcdf5edbd..806c8fba9e 100644
--- a/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs
+++ b/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs
@@ -9,7 +9,7 @@ use syntax::{
};
use crate::{
- AssistId,
+ AssistConfig, AssistId,
assist_context::{AssistContext, Assists, SourceChangeBuilder},
utils::{
DefaultMethods, IgnoreAssocItems, add_trait_assoc_items_to_impl, filter_assoc_items,
@@ -128,8 +128,14 @@ fn add_assist(
acc.add(AssistId::refactor("replace_derive_with_manual_impl"), label, target, |builder| {
let insert_after = ted::Position::after(builder.make_mut(adt.clone()).syntax());
let impl_is_unsafe = trait_.map(|s| s.is_unsafe(ctx.db())).unwrap_or(false);
- let impl_def_with_items =
- impl_def_from_trait(&ctx.sema, adt, &annotated_name, trait_, replace_trait_path);
+ let impl_def_with_items = impl_def_from_trait(
+ &ctx.sema,
+ ctx.config,
+ adt,
+ &annotated_name,
+ trait_,
+ replace_trait_path,
+ );
update_attribute(builder, old_derives, old_tree, old_trait_path, attr);
let trait_path = make::ty_path(replace_trait_path.clone());
@@ -217,6 +223,7 @@ fn add_assist(
fn impl_def_from_trait(
sema: &hir::Semantics<'_, ide_db::RootDatabase>,
+ config: &AssistConfig,
adt: &ast::Adt,
annotated_name: &ast::Name,
trait_: Option<hir::Trait>,
@@ -241,7 +248,7 @@ fn impl_def_from_trait(
let impl_def = generate_trait_impl(adt, make::ty_path(trait_path.clone()));
let first_assoc_item =
- add_trait_assoc_items_to_impl(sema, &trait_items, trait_, &impl_def, &target_scope);
+ add_trait_assoc_items_to_impl(sema, config, &trait_items, trait_, &impl_def, &target_scope);
// Generate a default `impl` function body for the derived trait.
if let ast::AssocItem::Fn(ref func) = first_assoc_item {
diff --git a/crates/ide-assists/src/handlers/unmerge_use.rs b/crates/ide-assists/src/handlers/unmerge_imports.rs
index 805a734449..c066f41ca4 100644
--- a/crates/ide-assists/src/handlers/unmerge_use.rs
+++ b/crates/ide-assists/src/handlers/unmerge_imports.rs
@@ -1,7 +1,10 @@
use syntax::{
AstNode, SyntaxKind,
- ast::{self, HasVisibility, edit_in_place::Removable, make},
- ted::{self, Position},
+ ast::{
+ self, HasAttrs, HasVisibility, edit::IndentLevel, edit_in_place::AttrsOwnerEdit, make,
+ syntax_factory::SyntaxFactory,
+ },
+ syntax_editor::{Element, Position, Removable},
};
use crate::{
@@ -9,9 +12,9 @@ use crate::{
assist_context::{AssistContext, Assists},
};
-// Assist: unmerge_use
+// Assist: unmerge_imports
//
-// Extracts single use item from use list.
+// Extracts a use item from a use list into a standalone use list.
//
// ```
// use std::fmt::{Debug, Display$0};
@@ -21,21 +24,18 @@ use crate::{
// use std::fmt::{Debug};
// use std::fmt::Display;
// ```
-pub(crate) fn unmerge_use(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
- let tree: ast::UseTree = ctx.find_node_at_offset::<ast::UseTree>()?.clone_for_update();
+pub(crate) fn unmerge_imports(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+ let tree = ctx.find_node_at_offset::<ast::UseTree>()?;
let tree_list = tree.syntax().parent().and_then(ast::UseTreeList::cast)?;
if tree_list.use_trees().count() < 2 {
- cov_mark::hit!(skip_single_use_item);
+ cov_mark::hit!(skip_single_import);
return None;
}
- let use_: ast::Use = tree_list.syntax().ancestors().find_map(ast::Use::cast)?;
+ let use_ = tree_list.syntax().ancestors().find_map(ast::Use::cast)?;
let path = resolve_full_path(&tree)?;
- let old_parent_range = use_.syntax().parent()?.text_range();
- let new_parent = use_.syntax().parent()?;
-
// If possible, explain what is going to be done.
let label = match tree.path().and_then(|path| path.first_segment()) {
Some(name) => format!("Unmerge use of `{name}`"),
@@ -43,17 +43,31 @@ pub(crate) fn unmerge_use(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<
};
let target = tree.syntax().text_range();
- acc.add(AssistId::refactor_rewrite("unmerge_use"), label, target, |builder| {
- let new_use = make::use_(
+ acc.add(AssistId::refactor_rewrite("unmerge_imports"), label, target, |builder| {
+ let make = SyntaxFactory::with_mappings();
+ let new_use = make.use_(
use_.visibility(),
- make::use_tree(path, tree.use_tree_list(), tree.rename(), tree.star_token().is_some()),
- )
- .clone_for_update();
-
- tree.remove();
- ted::insert(Position::after(use_.syntax()), new_use.syntax());
+ make.use_tree(path, tree.use_tree_list(), tree.rename(), tree.star_token().is_some()),
+ );
+ // Add any attributes that are present on the use tree
+ use_.attrs().for_each(|attr| {
+ new_use.add_attr(attr.clone_for_update());
+ });
- builder.replace(old_parent_range, new_parent.to_string());
+ let mut editor = builder.make_editor(use_.syntax());
+ // Remove the use tree from the current use item
+ tree.remove(&mut editor);
+ // Insert a newline and indentation, followed by the new use item
+ editor.insert_all(
+ Position::after(use_.syntax()),
+ vec![
+ make.whitespace(&format!("\n{}", IndentLevel::from_node(use_.syntax())))
+ .syntax_element(),
+ new_use.syntax().syntax_element(),
+ ],
+ );
+ editor.add_mappings(make.finish_with_mappings());
+ builder.add_file_edits(ctx.vfs_file_id(), editor);
})
}
@@ -80,22 +94,22 @@ mod tests {
use super::*;
#[test]
- fn skip_single_use_item() {
- cov_mark::check!(skip_single_use_item);
+ fn skip_single_import() {
+ cov_mark::check!(skip_single_import);
check_assist_not_applicable(
- unmerge_use,
+ unmerge_imports,
r"
use std::fmt::Debug$0;
",
);
check_assist_not_applicable(
- unmerge_use,
+ unmerge_imports,
r"
use std::fmt::{Debug$0};
",
);
check_assist_not_applicable(
- unmerge_use,
+ unmerge_imports,
r"
use std::fmt::Debug as Dbg$0;
",
@@ -105,7 +119,7 @@ use std::fmt::Debug as Dbg$0;
#[test]
fn skip_single_glob_import() {
check_assist_not_applicable(
- unmerge_use,
+ unmerge_imports,
r"
use std::fmt::*$0;
",
@@ -113,9 +127,9 @@ use std::fmt::*$0;
}
#[test]
- fn unmerge_use_item() {
+ fn unmerge_import() {
check_assist(
- unmerge_use,
+ unmerge_imports,
r"
use std::fmt::{Debug, Display$0};
",
@@ -126,7 +140,7 @@ use std::fmt::Display;
);
check_assist(
- unmerge_use,
+ unmerge_imports,
r"
use std::fmt::{Debug, format$0, Display};
",
@@ -140,7 +154,7 @@ use std::fmt::format;
#[test]
fn unmerge_glob_import() {
check_assist(
- unmerge_use,
+ unmerge_imports,
r"
use std::fmt::{*$0, Display};
",
@@ -152,9 +166,9 @@ use std::fmt::*;
}
#[test]
- fn unmerge_renamed_use_item() {
+ fn unmerge_renamed_import() {
check_assist(
- unmerge_use,
+ unmerge_imports,
r"
use std::fmt::{Debug, Display as Disp$0};
",
@@ -166,9 +180,9 @@ use std::fmt::Display as Disp;
}
#[test]
- fn unmerge_indented_use_item() {
+ fn unmerge_indented_import() {
check_assist(
- unmerge_use,
+ unmerge_imports,
r"
mod format {
use std::fmt::{Debug, Display$0 as Disp, format};
@@ -184,9 +198,9 @@ mod format {
}
#[test]
- fn unmerge_nested_use_item() {
+ fn unmerge_nested_import() {
check_assist(
- unmerge_use,
+ unmerge_imports,
r"
use foo::bar::{baz::{qux$0, foobar}, barbaz};
",
@@ -196,7 +210,7 @@ use foo::bar::baz::qux;
",
);
check_assist(
- unmerge_use,
+ unmerge_imports,
r"
use foo::bar::{baz$0::{qux, foobar}, barbaz};
",
@@ -208,9 +222,9 @@ use foo::bar::baz::{qux, foobar};
}
#[test]
- fn unmerge_use_item_with_visibility() {
+ fn unmerge_import_with_visibility() {
check_assist(
- unmerge_use,
+ unmerge_imports,
r"
pub use std::fmt::{Debug, Display$0};
",
@@ -222,12 +236,27 @@ pub use std::fmt::Display;
}
#[test]
- fn unmerge_use_item_on_self() {
+ fn unmerge_import_on_self() {
check_assist(
- unmerge_use,
+ unmerge_imports,
r"use std::process::{Command, self$0};",
r"use std::process::{Command};
use std::process;",
);
}
+
+ #[test]
+ fn unmerge_import_with_attributes() {
+ check_assist(
+ unmerge_imports,
+ r"
+#[allow(deprecated)]
+use foo::{bar, baz$0};",
+ r"
+#[allow(deprecated)]
+use foo::{bar};
+#[allow(deprecated)]
+use foo::baz;",
+ );
+ }
}
diff --git a/crates/ide-assists/src/handlers/unmerge_match_arm.rs b/crates/ide-assists/src/handlers/unmerge_match_arm.rs
index 31ff47a054..5aedff5cc7 100644
--- a/crates/ide-assists/src/handlers/unmerge_match_arm.rs
+++ b/crates/ide-assists/src/handlers/unmerge_match_arm.rs
@@ -53,8 +53,14 @@ pub(crate) fn unmerge_match_arm(acc: &mut Assists, ctx: &AssistContext<'_>) -> O
|edit| {
let pats_after = pipe_token
.siblings_with_tokens(Direction::Next)
- .filter_map(|it| ast::Pat::cast(it.into_node()?));
- let new_pat = make::or_pat(pats_after, or_pat.leading_pipe().is_some());
+ .filter_map(|it| ast::Pat::cast(it.into_node()?))
+ .collect::<Vec<_>>();
+ // It is guaranteed that `pats_after` has at least one element
+ let new_pat = if pats_after.len() == 1 {
+ pats_after[0].clone()
+ } else {
+ make::or_pat(pats_after, or_pat.leading_pipe().is_some()).into()
+ };
let new_match_arm =
make::match_arm(new_pat, match_arm.guard(), match_arm_body).clone_for_update();
diff --git a/crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs b/crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs
index 1068d5d4cd..e1b94673e7 100644
--- a/crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs
+++ b/crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs
@@ -116,7 +116,7 @@ pub(crate) fn wrap_unwrap_cfg_attr(acc: &mut Assists, ctx: &AssistContext<'_>) -
(Some(attr), Some(ident))
if attr.simple_name().map(|v| v.eq("derive")).unwrap_or_default() =>
{
- Some(attempt_get_derive(attr.clone(), ident))
+ Some(attempt_get_derive(attr, ident))
}
(Some(attr), _) => Some(WrapUnwrapOption::WrapAttr(attr)),
@@ -128,7 +128,7 @@ pub(crate) fn wrap_unwrap_cfg_attr(acc: &mut Assists, ctx: &AssistContext<'_>) -
NodeOrToken::Node(node) => ast::Attr::cast(node).map(WrapUnwrapOption::WrapAttr),
NodeOrToken::Token(ident) if ident.kind() == syntax::T![ident] => {
let attr = ident.parent_ancestors().find_map(ast::Attr::cast)?;
- Some(attempt_get_derive(attr.clone(), ident))
+ Some(attempt_get_derive(attr, ident))
}
_ => None,
}
@@ -233,7 +233,7 @@ fn wrap_cfg_attr(acc: &mut Assists, ctx: &AssistContext<'_>, attr: ast::Attr) ->
if let Some(meta) = attr.meta() {
if let (Some(eq), Some(expr)) = (meta.eq_token(), meta.expr()) {
raw_tokens.push(NodeOrToken::Token(make::tokens::whitespace(" ")));
- raw_tokens.push(NodeOrToken::Token(eq.clone()));
+ raw_tokens.push(NodeOrToken::Token(eq));
raw_tokens.push(NodeOrToken::Token(make::tokens::whitespace(" ")));
expr.syntax().descendants_with_tokens().for_each(|it| {
diff --git a/crates/ide-assists/src/lib.rs b/crates/ide-assists/src/lib.rs
index a157483a44..627ed37b04 100644
--- a/crates/ide-assists/src/lib.rs
+++ b/crates/ide-assists/src/lib.rs
@@ -222,8 +222,8 @@ mod handlers {
mod toggle_async_sugar;
mod toggle_ignore;
mod toggle_macro_delimiter;
+ mod unmerge_imports;
mod unmerge_match_arm;
- mod unmerge_use;
mod unnecessary_async;
mod unqualify_method_call;
mod unwrap_block;
@@ -363,7 +363,7 @@ mod handlers {
toggle_ignore::toggle_ignore,
toggle_macro_delimiter::toggle_macro_delimiter,
unmerge_match_arm::unmerge_match_arm,
- unmerge_use::unmerge_use,
+ unmerge_imports::unmerge_imports,
unnecessary_async::unnecessary_async,
unqualify_method_call::unqualify_method_call,
unwrap_block::unwrap_block,
diff --git a/crates/ide-assists/src/tests.rs b/crates/ide-assists/src/tests.rs
index 0593e6930d..5e6889792d 100644
--- a/crates/ide-assists/src/tests.rs
+++ b/crates/ide-assists/src/tests.rs
@@ -4,6 +4,7 @@ use expect_test::expect;
use hir::Semantics;
use ide_db::{
EditionedFileId, FileRange, RootDatabase, SnippetCap,
+ assists::ExprFillDefaultMode,
base_db::SourceDatabase,
imports::insert_use::{ImportGranularity, InsertUseConfig},
source_change::FileSystemEdit,
@@ -35,6 +36,7 @@ pub(crate) const TEST_CONFIG: AssistConfig = AssistConfig {
term_search_fuel: 400,
term_search_borrowck: true,
code_action_grouping: true,
+ expr_fill_default: ExprFillDefaultMode::Todo,
};
pub(crate) const TEST_CONFIG_NO_GROUPING: AssistConfig = AssistConfig {
@@ -54,6 +56,7 @@ pub(crate) const TEST_CONFIG_NO_GROUPING: AssistConfig = AssistConfig {
term_search_fuel: 400,
term_search_borrowck: true,
code_action_grouping: false,
+ expr_fill_default: ExprFillDefaultMode::Todo,
};
pub(crate) const TEST_CONFIG_NO_SNIPPET_CAP: AssistConfig = AssistConfig {
@@ -73,6 +76,7 @@ pub(crate) const TEST_CONFIG_NO_SNIPPET_CAP: AssistConfig = AssistConfig {
term_search_fuel: 400,
term_search_borrowck: true,
code_action_grouping: true,
+ expr_fill_default: ExprFillDefaultMode::Todo,
};
pub(crate) const TEST_CONFIG_IMPORT_ONE: AssistConfig = AssistConfig {
@@ -92,6 +96,7 @@ pub(crate) const TEST_CONFIG_IMPORT_ONE: AssistConfig = AssistConfig {
term_search_fuel: 400,
term_search_borrowck: true,
code_action_grouping: true,
+ expr_fill_default: ExprFillDefaultMode::Todo,
};
pub(crate) fn with_single_file(text: &str) -> (RootDatabase, EditionedFileId) {
diff --git a/crates/ide-assists/src/tests/generated.rs b/crates/ide-assists/src/tests/generated.rs
index 00a9d35c31..01ab0be34b 100644
--- a/crates/ide-assists/src/tests/generated.rs
+++ b/crates/ide-assists/src/tests/generated.rs
@@ -1737,7 +1737,7 @@ fn foo() {
bar("", baz());
}
-fn bar(arg: &str, baz: Baz) ${0:-> _} {
+fn bar(arg: &'static str, baz: Baz) ${0:-> _} {
todo!()
}
@@ -3340,6 +3340,20 @@ sth!{ }
}
#[test]
+fn doctest_unmerge_imports() {
+ check_doc_test(
+ "unmerge_imports",
+ r#####"
+use std::fmt::{Debug, Display$0};
+"#####,
+ r#####"
+use std::fmt::{Debug};
+use std::fmt::Display;
+"#####,
+ )
+}
+
+#[test]
fn doctest_unmerge_match_arm() {
check_doc_test(
"unmerge_match_arm",
@@ -3366,20 +3380,6 @@ fn handle(action: Action) {
}
#[test]
-fn doctest_unmerge_use() {
- check_doc_test(
- "unmerge_use",
- r#####"
-use std::fmt::{Debug, Display$0};
-"#####,
- r#####"
-use std::fmt::{Debug};
-use std::fmt::Display;
-"#####,
- )
-}
-
-#[test]
fn doctest_unnecessary_async() {
check_doc_test(
"unnecessary_async",
diff --git a/crates/ide-assists/src/utils.rs b/crates/ide-assists/src/utils.rs
index 0471998f0b..ef6914fda1 100644
--- a/crates/ide-assists/src/utils.rs
+++ b/crates/ide-assists/src/utils.rs
@@ -8,6 +8,7 @@ use hir::{
};
use ide_db::{
RootDatabase,
+ assists::ExprFillDefaultMode,
famous_defs::FamousDefs,
path_transform::PathTransform,
syntax_helpers::{node_ext::preorder_expr, prettify_macro_expansion},
@@ -27,7 +28,10 @@ use syntax::{
ted,
};
-use crate::assist_context::{AssistContext, SourceChangeBuilder};
+use crate::{
+ AssistConfig,
+ assist_context::{AssistContext, SourceChangeBuilder},
+};
mod gen_trait_fn_body;
pub(crate) mod ref_field_expr;
@@ -174,6 +178,7 @@ pub fn filter_assoc_items(
/// inserted.
pub fn add_trait_assoc_items_to_impl(
sema: &Semantics<'_, RootDatabase>,
+ config: &AssistConfig,
original_items: &[InFile<ast::AssocItem>],
trait_: hir::Trait,
impl_: &ast::Impl,
@@ -219,7 +224,14 @@ pub fn add_trait_assoc_items_to_impl(
match &item {
ast::AssocItem::Fn(fn_) if fn_.body().is_none() => {
let body = AstNodeEdit::indent(
- &make::block_expr(None, Some(make::ext::expr_todo())),
+ &make::block_expr(
+ None,
+ Some(match config.expr_fill_default {
+ ExprFillDefaultMode::Todo => make::ext::expr_todo(),
+ ExprFillDefaultMode::Underscore => make::ext::expr_underscore(),
+ ExprFillDefaultMode::Default => make::ext::expr_todo(),
+ }),
+ ),
new_indent_level,
);
ted::replace(fn_.get_or_create_body().syntax(), body.clone_for_update().syntax())
diff --git a/crates/ide-completion/src/completions/expr.rs b/crates/ide-completion/src/completions/expr.rs
index ee1a21f9a1..7fbd1fbc1a 100644
--- a/crates/ide-completion/src/completions/expr.rs
+++ b/crates/ide-completion/src/completions/expr.rs
@@ -363,9 +363,14 @@ pub(crate) fn complete_expr_path(
add_keyword("true", "true");
add_keyword("false", "false");
- if in_condition || in_block_expr {
- add_keyword("letm", "let mut $0");
- add_keyword("let", "let $0");
+ if in_condition {
+ add_keyword("letm", "let mut $1 = $0");
+ add_keyword("let", "let $1 = $0");
+ }
+
+ if in_block_expr {
+ add_keyword("letm", "let mut $1 = $0;");
+ add_keyword("let", "let $1 = $0;");
}
if after_if_expr {
diff --git a/crates/ide-completion/src/completions/keyword.rs b/crates/ide-completion/src/completions/keyword.rs
index 039742463c..64bb1fce6b 100644
--- a/crates/ide-completion/src/completions/keyword.rs
+++ b/crates/ide-completion/src/completions/keyword.rs
@@ -336,7 +336,7 @@ fn main() {
}
#[test]
- fn completes_let_with_space() {
+ fn completes_let_in_block() {
check_edit(
"let",
r#"
@@ -346,7 +346,7 @@ fn main() {
"#,
r#"
fn main() {
- let $0
+ let $1 = $0;
}
"#,
);
@@ -359,7 +359,97 @@ fn main() {
"#,
r#"
fn main() {
- let mut $0
+ let mut $1 = $0;
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn completes_let_in_condition() {
+ check_edit(
+ "let",
+ r#"
+fn main() {
+ if $0 {}
+}
+"#,
+ r#"
+fn main() {
+ if let $1 = $0 {}
+}
+"#,
+ );
+ check_edit(
+ "letm",
+ r#"
+fn main() {
+ if $0 {}
+}
+"#,
+ r#"
+fn main() {
+ if let mut $1 = $0 {}
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn completes_let_in_no_empty_condition() {
+ check_edit(
+ "let",
+ r#"
+fn main() {
+ if $0x {}
+}
+"#,
+ r#"
+fn main() {
+ if let $1 = $0x {}
+}
+"#,
+ );
+ check_edit(
+ "letm",
+ r#"
+fn main() {
+ if $0x {}
+}
+"#,
+ r#"
+fn main() {
+ if let mut $1 = $0x {}
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn completes_let_in_condition_block() {
+ check_edit(
+ "let",
+ r#"
+fn main() {
+ if { $0 } {}
+}
+"#,
+ r#"
+fn main() {
+ if { let $1 = $0; } {}
+}
+"#,
+ );
+ check_edit(
+ "letm",
+ r#"
+fn main() {
+ if { $0 } {}
+}
+"#,
+ r#"
+fn main() {
+ if { let mut $1 = $0; } {}
}
"#,
);
diff --git a/crates/ide-completion/src/completions/lifetime.rs b/crates/ide-completion/src/completions/lifetime.rs
index b02f079b72..8902cd09ce 100644
--- a/crates/ide-completion/src/completions/lifetime.rs
+++ b/crates/ide-completion/src/completions/lifetime.rs
@@ -116,13 +116,13 @@ fn foo<'lifetime>(foo: &'a$0) {}
check(
r#"
struct Foo;
-impl<'impl> Foo {
+impl<'r#impl> Foo {
fn foo<'func>(&'a$0 self) {}
}
"#,
expect![[r#"
lt 'func
- lt 'impl
+ lt 'r#impl
lt 'static
"#]],
);
diff --git a/crates/ide-completion/src/context/analysis.rs b/crates/ide-completion/src/context/analysis.rs
index 5959973589..391e2379dc 100644
--- a/crates/ide-completion/src/context/analysis.rs
+++ b/crates/ide-completion/src/context/analysis.rs
@@ -387,11 +387,7 @@ fn expand(
match (
sema.expand_macro_call(&actual_macro_call),
- sema.speculative_expand_macro_call(
- &actual_macro_call,
- &speculative_args,
- fake_ident_token.clone(),
- ),
+ sema.speculative_expand_macro_call(&actual_macro_call, &speculative_args, fake_ident_token),
) {
// successful expansions
(Some(actual_expansion), Some((fake_expansion, fake_mapped_tokens))) => {
@@ -661,9 +657,8 @@ fn expected_type_and_name(
))
} else {
cov_mark::hit!(expected_type_struct_field_without_leading_char);
- let expr_field = token.prev_sibling_or_token()?
- .into_node()
- .and_then(ast::RecordExprField::cast)?;
+ cov_mark::hit!(expected_type_struct_field_followed_by_comma);
+ let expr_field = previous_non_trivia_token(token.clone())?.parent().and_then(ast::RecordExprField::cast)?;
let (_, _, ty) = sema.resolve_record_field(&expr_field)?;
Some((
Some(ty),
@@ -681,7 +676,6 @@ fn expected_type_and_name(
.or_else(|| sema.type_of_expr(&expr).map(TypeInfo::original));
(ty, field_name)
} else {
- cov_mark::hit!(expected_type_struct_field_followed_by_comma);
(field_ty, field_name)
}
},
diff --git a/crates/ide-completion/src/item.rs b/crates/ide-completion/src/item.rs
index e208b9fd41..19cdef30bd 100644
--- a/crates/ide-completion/src/item.rs
+++ b/crates/ide-completion/src/item.rs
@@ -135,7 +135,7 @@ impl fmt::Debug for CompletionItem {
},
CompletionItemRefMode::Dereference => "*",
};
- s.field("ref_match", &format!("{}@{offset:?}", prefix));
+ s.field("ref_match", &format!("{prefix}@{offset:?}"));
}
if self.trigger_call_info {
s.field("trigger_call_info", &true);
diff --git a/crates/ide-completion/src/tests/expression.rs b/crates/ide-completion/src/tests/expression.rs
index b30ac43bf8..d5137949d4 100644
--- a/crates/ide-completion/src/tests/expression.rs
+++ b/crates/ide-completion/src/tests/expression.rs
@@ -1517,7 +1517,7 @@ fn main() {
en Enum Enum
fn function() fn()
fn main() fn()
- lc variable &str
+ lc variable &'static str
ma helper!(…) macro_rules! helper
ma m!(…) macro_rules! m
ma makro!(…) macro_rules! makro
@@ -2110,3 +2110,19 @@ fn foo() {
"#]],
);
}
+
+#[test]
+fn escaped_label() {
+ check(
+ r#"
+fn main() {
+ 'r#break: {
+ break '$0;
+ }
+}
+ "#,
+ expect![[r#"
+ lb 'r#break
+ "#]],
+ );
+}
diff --git a/crates/ide-completion/src/tests/special.rs b/crates/ide-completion/src/tests/special.rs
index 15518e9837..148203107c 100644
--- a/crates/ide-completion/src/tests/special.rs
+++ b/crates/ide-completion/src/tests/special.rs
@@ -1358,7 +1358,7 @@ pub fn foo<'x, T>(x: &'x mut T) -> u8 where T: Clone, { 0u8 }
fn main() { fo$0 }
"#,
CompletionItemKind::SymbolKind(ide_db::SymbolKind::Function),
- expect!("fn(&mut T) -> u8"),
+ expect!("fn(&'x mut T) -> u8"),
expect!("pub fn foo<'x, T>(x: &'x mut T) -> u8 where T: Clone,"),
);
@@ -1391,7 +1391,7 @@ fn main() {
}
"#,
CompletionItemKind::SymbolKind(SymbolKind::Method),
- expect!("const fn(&'foo mut self, &Foo) -> !"),
+ expect!("const fn(&'foo mut self, &'foo Foo) -> !"),
expect!("pub const fn baz<'foo>(&'foo mut self, x: &'foo Foo) -> !"),
);
}
diff --git a/crates/ide-completion/src/tests/type_pos.rs b/crates/ide-completion/src/tests/type_pos.rs
index c7e2d05825..125e11e9e3 100644
--- a/crates/ide-completion/src/tests/type_pos.rs
+++ b/crates/ide-completion/src/tests/type_pos.rs
@@ -429,18 +429,18 @@ trait Tr<T> {
impl Tr<$0
"#,
expect![[r#"
- en Enum Enum
- ma makro!(…) macro_rules! makro
+ en Enum Enum
+ ma makro!(…) macro_rules! makro
md module
- sp Self dyn Tr<{unknown}>
- st Record Record
- st S S
- st Tuple Tuple
- st Unit Unit
+ sp Self dyn Tr<{unknown}> + 'static
+ st Record Record
+ st S S
+ st Tuple Tuple
+ st Unit Unit
tt Tr
tt Trait
- un Union Union
- bt u32 u32
+ un Union Union
+ bt u32 u32
kw crate::
kw self::
"#]],
diff --git a/crates/ide-db/Cargo.toml b/crates/ide-db/Cargo.toml
index f1d6b605b0..583318de26 100644
--- a/crates/ide-db/Cargo.toml
+++ b/crates/ide-db/Cargo.toml
@@ -24,6 +24,7 @@ arrayvec.workspace = true
indexmap.workspace = true
memchr = "2.7.4"
salsa.workspace = true
+salsa-macros.workspace = true
query-group.workspace = true
triomphe.workspace = true
nohash-hasher.workspace = true
diff --git a/crates/ide-db/src/assists.rs b/crates/ide-db/src/assists.rs
index 90ae4a3b5b..384eb57c0f 100644
--- a/crates/ide-db/src/assists.rs
+++ b/crates/ide-db/src/assists.rs
@@ -169,3 +169,15 @@ impl AssistResolveStrategy {
#[derive(Clone, Debug)]
pub struct GroupLabel(pub String);
+
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub enum ExprFillDefaultMode {
+ Todo,
+ Default,
+ Underscore,
+}
+impl Default for ExprFillDefaultMode {
+ fn default() -> Self {
+ Self::Todo
+ }
+}
diff --git a/crates/ide-db/src/items_locator.rs b/crates/ide-db/src/items_locator.rs
index e938525325..4b0a84a559 100644
--- a/crates/ide-db/src/items_locator.rs
+++ b/crates/ide-db/src/items_locator.rs
@@ -86,7 +86,7 @@ pub fn items_with_name_in_module<T>(
let local_query = match name {
NameToImport::Prefix(exact_name, case_sensitive)
| NameToImport::Exact(exact_name, case_sensitive) => {
- let mut local_query = symbol_index::Query::new(exact_name.clone());
+ let mut local_query = symbol_index::Query::new(exact_name);
local_query.assoc_search_mode(assoc_item_search);
if prefix {
local_query.prefix();
@@ -99,7 +99,7 @@ pub fn items_with_name_in_module<T>(
local_query
}
NameToImport::Fuzzy(fuzzy_search_string, case_sensitive) => {
- let mut local_query = symbol_index::Query::new(fuzzy_search_string.clone());
+ let mut local_query = symbol_index::Query::new(fuzzy_search_string);
local_query.fuzzy();
local_query.assoc_search_mode(assoc_item_search);
diff --git a/crates/ide-db/src/lib.rs b/crates/ide-db/src/lib.rs
index d3934e14ab..63cc7cde28 100644
--- a/crates/ide-db/src/lib.rs
+++ b/crates/ide-db/src/lib.rs
@@ -76,8 +76,10 @@ pub type FxIndexMap<K, V> =
pub type FilePosition = FilePositionWrapper<FileId>;
pub type FileRange = FileRangeWrapper<FileId>;
-#[salsa::db]
+#[salsa_macros::db]
pub struct RootDatabase {
+ // FIXME: Revisit this commit now that we migrated to the new salsa, given we store arcs in this
+ // db directly now
// We use `ManuallyDrop` here because every codegen unit that contains a
// `&RootDatabase -> &dyn OtherDatabase` cast will instantiate its drop glue in the vtable,
// which duplicates `Weak::drop` and `Arc::drop` tens of thousands of times, which makes
@@ -89,7 +91,7 @@ pub struct RootDatabase {
impl std::panic::RefUnwindSafe for RootDatabase {}
-#[salsa::db]
+#[salsa_macros::db]
impl salsa::Database for RootDatabase {
fn salsa_event(&self, _event: &dyn Fn() -> salsa::Event) {}
}
@@ -116,7 +118,7 @@ impl fmt::Debug for RootDatabase {
}
}
-#[salsa::db]
+#[salsa_macros::db]
impl SourceDatabase for RootDatabase {
fn file_text(&self, file_id: vfs::FileId) -> FileText {
self.files.file_text(file_id)
@@ -234,14 +236,6 @@ impl RootDatabase {
// );
// hir::db::BodyWithSourceMapQuery.in_db_mut(self).set_lru_capacity(2048);
}
-
- pub fn snapshot(&self) -> Self {
- Self {
- storage: self.storage.clone(),
- files: self.files.clone(),
- crates_map: self.crates_map.clone(),
- }
- }
}
#[query_group::query_group]
diff --git a/crates/ide-db/src/prime_caches.rs b/crates/ide-db/src/prime_caches.rs
index 17c3f75ce1..cbe31405ab 100644
--- a/crates/ide-db/src/prime_caches.rs
+++ b/crates/ide-db/src/prime_caches.rs
@@ -51,6 +51,7 @@ pub fn parallel_prime_caches(
enum ParallelPrimeCacheWorkerProgress {
BeginCrate { crate_id: Crate, crate_name: Symbol },
EndCrate { crate_id: Crate },
+ Cancelled(Cancelled),
}
// We split off def map computation from other work,
@@ -71,27 +72,35 @@ pub fn parallel_prime_caches(
progress_sender
.send(ParallelPrimeCacheWorkerProgress::BeginCrate { crate_id, crate_name })?;
- match kind {
+ let cancelled = Cancelled::catch(|| match kind {
PrimingPhase::DefMap => _ = db.crate_def_map(crate_id),
PrimingPhase::ImportMap => _ = db.import_map(crate_id),
PrimingPhase::CrateSymbols => _ = db.crate_symbols(crate_id.into()),
- }
+ });
- progress_sender.send(ParallelPrimeCacheWorkerProgress::EndCrate { crate_id })?;
+ match cancelled {
+ Ok(()) => progress_sender
+ .send(ParallelPrimeCacheWorkerProgress::EndCrate { crate_id })?,
+ Err(cancelled) => progress_sender
+ .send(ParallelPrimeCacheWorkerProgress::Cancelled(cancelled))?,
+ }
}
Ok::<_, crossbeam_channel::SendError<_>>(())
};
for id in 0..num_worker_threads {
- let worker = prime_caches_worker.clone();
- let db = db.snapshot();
-
- stdx::thread::Builder::new(stdx::thread::ThreadIntent::Worker)
- .allow_leak(true)
- .name(format!("PrimeCaches#{id}"))
- .spawn(move || Cancelled::catch(|| worker(db.snapshot())))
- .expect("failed to spawn thread");
+ stdx::thread::Builder::new(
+ stdx::thread::ThreadIntent::Worker,
+ format!("PrimeCaches#{id}"),
+ )
+ .allow_leak(true)
+ .spawn({
+ let worker = prime_caches_worker.clone();
+ let db = db.clone();
+ move || worker(db)
+ })
+ .expect("failed to spawn thread");
}
(work_sender, progress_receiver)
@@ -142,9 +151,14 @@ pub fn parallel_prime_caches(
continue;
}
Err(crossbeam_channel::RecvTimeoutError::Disconnected) => {
- // our workers may have died from a cancelled task, so we'll check and re-raise here.
- db.unwind_if_revision_cancelled();
- break;
+ // all our workers have exited, mark us as finished and exit
+ cb(ParallelPrimeCachesProgress {
+ crates_currently_indexing: vec![],
+ crates_done,
+ crates_total: crates_done,
+ work_type: "Indexing",
+ });
+ return;
}
};
match worker_progress {
@@ -156,6 +170,10 @@ pub fn parallel_prime_caches(
crates_to_prime.mark_done(crate_id);
crates_done += 1;
}
+ ParallelPrimeCacheWorkerProgress::Cancelled(cancelled) => {
+ // Cancelled::throw should probably be public
+ std::panic::resume_unwind(Box::new(cancelled));
+ }
};
let progress = ParallelPrimeCachesProgress {
@@ -186,9 +204,14 @@ pub fn parallel_prime_caches(
continue;
}
Err(crossbeam_channel::RecvTimeoutError::Disconnected) => {
- // our workers may have died from a cancelled task, so we'll check and re-raise here.
- db.unwind_if_revision_cancelled();
- break;
+ // all our workers have exited, mark us as finished and exit
+ cb(ParallelPrimeCachesProgress {
+ crates_currently_indexing: vec![],
+ crates_done,
+ crates_total: crates_done,
+ work_type: "Populating symbols",
+ });
+ return;
}
};
match worker_progress {
@@ -199,6 +222,10 @@ pub fn parallel_prime_caches(
crates_currently_indexing.swap_remove(&crate_id);
crates_done += 1;
}
+ ParallelPrimeCacheWorkerProgress::Cancelled(cancelled) => {
+ // Cancelled::throw should probably be public
+ std::panic::resume_unwind(Box::new(cancelled));
+ }
};
let progress = ParallelPrimeCachesProgress {
diff --git a/crates/ide-diagnostics/src/handlers/expected_function.rs b/crates/ide-diagnostics/src/handlers/expected_function.rs
index af25c2b2e3..a6da0fd9c5 100644
--- a/crates/ide-diagnostics/src/handlers/expected_function.rs
+++ b/crates/ide-diagnostics/src/handlers/expected_function.rs
@@ -31,7 +31,7 @@ fn foo() {
x();
// ^^^ error: expected function, found i32
""();
- // ^^^^ error: expected function, found &str
+ // ^^^^ error: expected function, found &'static str
foo();
}
"#,
diff --git a/crates/ide-diagnostics/src/handlers/invalid_cast.rs b/crates/ide-diagnostics/src/handlers/invalid_cast.rs
index b56255b1fd..d72b21099c 100644
--- a/crates/ide-diagnostics/src/handlers/invalid_cast.rs
+++ b/crates/ide-diagnostics/src/handlers/invalid_cast.rs
@@ -166,7 +166,7 @@ fn main() {
let _ = ptr as bool;
//^^^^^^^^^^^ error: cannot cast `*const ()` as `bool`
let v = "hello" as bool;
- //^^^^^^^^^^^^^^^ error: casting `&str` as `bool` is invalid: needs casting through a raw pointer first
+ //^^^^^^^^^^^^^^^ error: casting `&'static str` as `bool` is invalid: needs casting through a raw pointer first
}
"#,
);
@@ -956,7 +956,7 @@ fn main() {
fn main() {
let pointer: usize = &1_i32 as *const i32 as usize;
let _reference: &'static i32 = unsafe { pointer as *const i32 as &'static i32 };
- //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: non-primitive cast: `*const i32` as `&i32`
+ //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: non-primitive cast: `*const i32` as `&'static i32`
}
"#,
);
@@ -992,7 +992,7 @@ impl Deref for Foo {
fn main() {
let _ = "foo" as bool;
- //^^^^^^^^^^^^^ error: casting `&str` as `bool` is invalid: needs casting through a raw pointer first
+ //^^^^^^^^^^^^^ error: casting `&'static str` as `bool` is invalid: needs casting through a raw pointer first
let _ = Foo as bool;
//^^^^^^^^^^^ error: non-primitive cast: `Foo` as `bool`
diff --git a/crates/ide-diagnostics/src/handlers/missing_fields.rs b/crates/ide-diagnostics/src/handlers/missing_fields.rs
index 6b02111016..a354d123f5 100644
--- a/crates/ide-diagnostics/src/handlers/missing_fields.rs
+++ b/crates/ide-diagnostics/src/handlers/missing_fields.rs
@@ -5,9 +5,13 @@ use hir::{
sym,
};
use ide_db::{
- FxHashMap, assists::Assist, famous_defs::FamousDefs,
- imports::import_assets::item_for_path_search, source_change::SourceChange,
- syntax_helpers::tree_diff::diff, text_edit::TextEdit,
+ FxHashMap,
+ assists::{Assist, ExprFillDefaultMode},
+ famous_defs::FamousDefs,
+ imports::import_assets::item_for_path_search,
+ source_change::SourceChange,
+ syntax_helpers::tree_diff::diff,
+ text_edit::TextEdit,
use_trivial_constructor::use_trivial_constructor,
};
use stdx::format_to;
@@ -102,8 +106,9 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Option<Vec<Ass
});
let generate_fill_expr = |ty: &Type| match ctx.config.expr_fill_default {
- crate::ExprFillDefaultMode::Todo => make::ext::expr_todo(),
- crate::ExprFillDefaultMode::Default => {
+ ExprFillDefaultMode::Todo => make::ext::expr_todo(),
+ ExprFillDefaultMode::Underscore => make::ext::expr_underscore(),
+ ExprFillDefaultMode::Default => {
get_default_constructor(ctx, d, ty).unwrap_or_else(make::ext::expr_todo)
}
};
@@ -158,9 +163,14 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Option<Vec<Ass
let old_field_list = field_list_parent.record_pat_field_list()?;
let new_field_list = old_field_list.clone_for_update();
for (f, _) in missing_fields.iter() {
- let field = make::record_pat_field_shorthand(make::name_ref(
- &f.name(ctx.sema.db).display_no_db(ctx.edition).to_smolstr(),
- ));
+ let field = make::record_pat_field_shorthand(
+ make::ident_pat(
+ false,
+ false,
+ make::name(&f.name(ctx.sema.db).display_no_db(ctx.edition).to_smolstr()),
+ )
+ .into(),
+ );
new_field_list.add_field(field.clone_for_update());
}
build_text_edit(new_field_list.syntax(), old_field_list.syntax())
diff --git a/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs b/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs
index 7d0f10983d..780271361d 100644
--- a/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs
+++ b/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs
@@ -18,7 +18,22 @@ pub(crate) fn moved_out_of_ref(ctx: &DiagnosticsContext<'_>, d: &hir::MovedOutOf
mod tests {
use crate::tests::check_diagnostics;
- // FIXME: spans are broken
+ #[test]
+ fn operand_field_span_respected() {
+ check_diagnostics(
+ r#"
+struct NotCopy;
+struct S {
+ field: NotCopy,
+}
+
+fn f(s: &S) -> S {
+ S { field: s.field }
+ //^^^^^^^ error: cannot move `NotCopy` out of reference
+}
+ "#,
+ );
+ }
#[test]
fn move_by_explicit_deref() {
@@ -85,7 +100,7 @@ fn consume<T>(_: X<T>) {
fn main() {
let a = &X(Y);
consume(*a);
- //^^^^^^^^^^^ error: cannot move `X<Y>` out of reference
+ //^^ error: cannot move `X<Y>` out of reference
let a = &X(5);
consume(*a);
}
diff --git a/crates/ide-diagnostics/src/handlers/type_mismatch.rs b/crates/ide-diagnostics/src/handlers/type_mismatch.rs
index 8f6ed1a7bd..500c5de791 100644
--- a/crates/ide-diagnostics/src/handlers/type_mismatch.rs
+++ b/crates/ide-diagnostics/src/handlers/type_mismatch.rs
@@ -195,7 +195,7 @@ fn remove_unnecessary_wrapper(
let db = ctx.sema.db;
let root = db.parse_or_expand(expr_ptr.file_id);
let expr = expr_ptr.value.to_node(&root);
- let expr = ctx.sema.original_ast_node(expr.clone())?;
+ let expr = ctx.sema.original_ast_node(expr)?;
let Expr::CallExpr(call_expr) = expr else {
return None;
@@ -306,10 +306,9 @@ fn str_ref_to_owned(
acc: &mut Vec<Assist>,
) -> Option<()> {
let expected = d.expected.display(ctx.sema.db, ctx.display_target);
- let actual = d.actual.display(ctx.sema.db, ctx.display_target);
-
// FIXME do this properly
- if expected.to_string() != "String" || actual.to_string() != "&str" {
+ let is_applicable = d.actual.strip_reference().is_str() && expected.to_string() == "String";
+ if !is_applicable {
return None;
}
@@ -1176,7 +1175,7 @@ trait B {}
fn test(a: &dyn A) -> &dyn B {
a
- //^ error: expected &dyn B, found &dyn A
+ //^ error: expected &(dyn B + 'static), found &(dyn A + 'static)
}
"#,
);
diff --git a/crates/ide-diagnostics/src/handlers/typed_hole.rs b/crates/ide-diagnostics/src/handlers/typed_hole.rs
index 277aff2e08..a933f1b426 100644
--- a/crates/ide-diagnostics/src/handlers/typed_hole.rs
+++ b/crates/ide-diagnostics/src/handlers/typed_hole.rs
@@ -1,3 +1,5 @@
+use std::ops::Not;
+
use hir::{
ClosureStyle, HirDisplay, ImportPathConfig,
db::ExpandDatabase,
@@ -60,9 +62,13 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::TypedHole) -> Option<Vec<Assist>
let mut formatter = |_: &hir::Type| String::from("_");
- let assists: Vec<Assist> = paths
+ let assists: Vec<Assist> = d
+ .expected
+ .is_unknown()
+ .not()
+ .then(|| "todo!()".to_owned())
.into_iter()
- .filter_map(|path| {
+ .chain(paths.into_iter().filter_map(|path| {
path.gen_source_code(
&scope,
&mut formatter,
@@ -75,7 +81,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::TypedHole) -> Option<Vec<Assist>
ctx.display_target,
)
.ok()
- })
+ }))
.unique()
.map(|code| Assist {
id: AssistId::quick_fix("typed-hole"),
@@ -95,9 +101,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::TypedHole) -> Option<Vec<Assist>
#[cfg(test)]
mod tests {
- use crate::tests::{
- check_diagnostics, check_fixes_unordered, check_has_fix, check_has_single_fix,
- };
+ use crate::tests::{check_diagnostics, check_fixes_unordered, check_has_fix};
#[test]
fn unknown() {
@@ -119,9 +123,9 @@ fn main() {
if _ {}
//^ 💡 error: invalid `_` expression, expected type `bool`
let _: fn() -> i32 = _;
- //^ error: invalid `_` expression, expected type `fn() -> i32`
+ //^ 💡 error: invalid `_` expression, expected type `fn() -> i32`
let _: fn() -> () = _; // FIXME: This should trigger an assist because `main` matches via *coercion*
- //^ error: invalid `_` expression, expected type `fn()`
+ //^ 💡 error: invalid `_` expression, expected type `fn()`
}
"#,
);
@@ -147,7 +151,7 @@ fn main() {
fn main() {
let mut x = t();
x = _;
- //^ error: invalid `_` expression, expected type `&str`
+ //^ 💡 error: invalid `_` expression, expected type `&'static str`
x = "";
}
fn t<T>() -> T { loop {} }
@@ -308,7 +312,7 @@ fn main() {
#[test]
fn ignore_impl_func_with_incorrect_return() {
- check_has_single_fix(
+ check_fixes_unordered(
r#"
struct Bar {}
trait Foo {
@@ -323,7 +327,8 @@ fn main() {
let a: i32 = 1;
let c: Bar = _$0;
}"#,
- r#"
+ vec![
+ r#"
struct Bar {}
trait Foo {
type Res;
@@ -337,6 +342,21 @@ fn main() {
let a: i32 = 1;
let c: Bar = Bar { };
}"#,
+ r#"
+struct Bar {}
+trait Foo {
+ type Res;
+ fn foo(&self) -> Self::Res;
+}
+impl Foo for i32 {
+ type Res = Self;
+ fn foo(&self) -> Self::Res { 1 }
+}
+fn main() {
+ let a: i32 = 1;
+ let c: Bar = todo!();
+}"#,
+ ],
);
}
diff --git a/crates/ide-diagnostics/src/handlers/unresolved_method.rs b/crates/ide-diagnostics/src/handlers/unresolved_method.rs
index 4422d8f826..7f07009dc5 100644
--- a/crates/ide-diagnostics/src/handlers/unresolved_method.rs
+++ b/crates/ide-diagnostics/src/handlers/unresolved_method.rs
@@ -269,7 +269,7 @@ impl<T, U> A<T, U> {
}
fn main() {
let a = A {a: 0, b: ""};
- A::<i32, &str>::foo();
+ A::<i32, &'static str>::foo();
}
"#,
);
diff --git a/crates/ide-diagnostics/src/lib.rs b/crates/ide-diagnostics/src/lib.rs
index 11efedd8a5..607721d611 100644
--- a/crates/ide-diagnostics/src/lib.rs
+++ b/crates/ide-diagnostics/src/lib.rs
@@ -92,7 +92,7 @@ use hir::{
};
use ide_db::{
EditionedFileId, FileId, FileRange, FxHashMap, FxHashSet, RootDatabase, Severity, SnippetCap,
- assists::{Assist, AssistId, AssistResolveStrategy},
+ assists::{Assist, AssistId, AssistResolveStrategy, ExprFillDefaultMode},
base_db::{ReleaseChannel, RootQueryDb as _},
generated::lints::{CLIPPY_LINT_GROUPS, DEFAULT_LINT_GROUPS, DEFAULT_LINTS, Lint, LintGroup},
imports::insert_use::InsertUseConfig,
@@ -219,17 +219,6 @@ impl Diagnostic {
}
}
-#[derive(Clone, Debug, PartialEq, Eq)]
-pub enum ExprFillDefaultMode {
- Todo,
- Default,
-}
-impl Default for ExprFillDefaultMode {
- fn default() -> Self {
- Self::Todo
- }
-}
-
#[derive(Debug, Clone)]
pub struct DiagnosticsConfig {
/// Whether native diagnostics are enabled.
diff --git a/crates/ide-diagnostics/src/tests.rs b/crates/ide-diagnostics/src/tests.rs
index 13d08d46de..4e4bd47e1c 100644
--- a/crates/ide-diagnostics/src/tests.rs
+++ b/crates/ide-diagnostics/src/tests.rs
@@ -3,14 +3,16 @@
mod overly_long_real_world_cases;
use ide_db::{
- LineIndexDatabase, RootDatabase, assists::AssistResolveStrategy, base_db::SourceDatabase,
+ LineIndexDatabase, RootDatabase,
+ assists::{AssistResolveStrategy, ExprFillDefaultMode},
+ base_db::SourceDatabase,
};
use itertools::Itertools;
use stdx::trim_indent;
use test_fixture::WithFixture;
use test_utils::{MiniCore, assert_eq_text, extract_annotations};
-use crate::{DiagnosticsConfig, ExprFillDefaultMode, Severity};
+use crate::{DiagnosticsConfig, Severity};
/// Takes a multi-file input fixture with annotated cursor positions,
/// and checks that:
@@ -160,55 +162,6 @@ pub(crate) fn check_has_fix(
assert!(fix.is_some(), "no diagnostic with desired fix");
}
-#[track_caller]
-pub(crate) fn check_has_single_fix(
- #[rust_analyzer::rust_fixture] ra_fixture_before: &str,
- #[rust_analyzer::rust_fixture] ra_fixture_after: &str,
-) {
- let after = trim_indent(ra_fixture_after);
-
- let (db, file_position) = RootDatabase::with_position(ra_fixture_before);
- let mut conf = DiagnosticsConfig::test_sample();
- conf.expr_fill_default = ExprFillDefaultMode::Default;
- let mut n_fixes = 0;
- let fix = super::full_diagnostics(
- &db,
- &conf,
- &AssistResolveStrategy::All,
- file_position.file_id.file_id(&db),
- )
- .into_iter()
- .find(|d| {
- d.fixes
- .as_ref()
- .and_then(|fixes| {
- n_fixes += fixes.len();
- fixes.iter().find(|fix| {
- if !fix.target.contains_inclusive(file_position.offset) {
- return false;
- }
- let actual = {
- let source_change = fix.source_change.as_ref().unwrap();
- let file_id = *source_change.source_file_edits.keys().next().unwrap();
- let mut actual = db.file_text(file_id).text(&db).to_string();
-
- for (edit, snippet_edit) in source_change.source_file_edits.values() {
- edit.apply(&mut actual);
- if let Some(snippet_edit) = snippet_edit {
- snippet_edit.apply(&mut actual);
- }
- }
- actual
- };
- after == actual
- })
- })
- .is_some()
- });
- assert!(fix.is_some(), "no diagnostic with desired fix");
- assert!(n_fixes == 1, "Too many fixes suggested");
-}
-
/// Checks that there's a diagnostic *without* fix at `$0`.
pub(crate) fn check_no_fix(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
let (db, file_position) = RootDatabase::with_position(ra_fixture);
diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs
index ebbd68bcdf..f0247f32d7 100644
--- a/crates/ide/src/doc_links.rs
+++ b/crates/ide/src/doc_links.rs
@@ -599,7 +599,7 @@ fn filename_and_frag_for_def(
Some(name) => {
match m.attrs(db).by_key(sym::doc).find_string_value_in_tt(sym::keyword) {
Some(kw) => {
- format!("keyword.{}.html", kw)
+ format!("keyword.{kw}.html")
}
None => format!("{}/index.html", name.as_str()),
}
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs
index 2f2d2252f8..075afcec01 100644
--- a/crates/ide/src/hover.rs
+++ b/crates/ide/src/hover.rs
@@ -634,9 +634,7 @@ fn walk_and_push_ty(
} else if let Some(trait_) = t.as_associated_type_parent_trait(db) {
push_new_def(trait_.into());
} else if let Some(tp) = t.as_type_param(db) {
- let sized_trait = db
- .lang_item(t.krate(db).into(), LangItem::Sized)
- .and_then(|lang_item| lang_item.as_trait());
+ let sized_trait = LangItem::Sized.resolve_trait(db, t.krate(db).into());
tp.trait_bounds(db)
.into_iter()
.filter(|&it| Some(it.into()) != sized_trait)
diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs
index d469cd7c0c..7b7eef9d57 100644
--- a/crates/ide/src/hover/tests.rs
+++ b/crates/ide/src/hover/tests.rs
@@ -7085,9 +7085,9 @@ fn foo() {
}
"#,
expect![[r#"
- ```rust
- &str
- ```"#]],
+ ```rust
+ &'static str
+ ```"#]],
);
}
@@ -8228,7 +8228,7 @@ format_args!("{aaaaa$0}");
*aaaaa*
```rust
- let aaaaa: &str
+ let aaaaa: &'static str
```
"#]],
);
@@ -8248,7 +8248,7 @@ format_args!("{$0aaaaa}");
*aaaaa*
```rust
- let aaaaa: &str
+ let aaaaa: &'static str
```
"#]],
);
@@ -8268,7 +8268,7 @@ format_args!(r"{$0aaaaa}");
*aaaaa*
```rust
- let aaaaa: &str
+ let aaaaa: &'static str
```
"#]],
);
@@ -8293,7 +8293,7 @@ foo!(r"{$0aaaaa}");
*aaaaa*
```rust
- let aaaaa: &str
+ let aaaaa: &'static str
```
"#]],
);
@@ -8337,7 +8337,7 @@ fn main() {
expect![[r#"
*"🦀\u{1f980}\\\x41"*
```rust
- &str
+ &'static str
```
___
@@ -8353,7 +8353,7 @@ fn main() {
expect![[r#"
*r"🦀\u{1f980}\\\x41"*
```rust
- &str
+ &'static str
```
___
@@ -8375,7 +8375,7 @@ fsdghs";
fsdghs"*
```rust
- &str
+ &'static str
```
___
@@ -8395,7 +8395,7 @@ fn main() {
expect![[r#"
*c"🦀\u{1f980}\\\x41"*
```rust
- &{unknown}
+ &'static {unknown}
```
___
@@ -8414,7 +8414,7 @@ fn main() {
expect![[r#"
*r"`[^`]*`"*
```rust
- &str
+ &'static str
```
___
@@ -8429,7 +8429,7 @@ fn main() {
expect![[r#"
*r"`"*
```rust
- &str
+ &'static str
```
___
@@ -8444,7 +8444,7 @@ fn main() {
expect![[r#"
*r" "*
```rust
- &str
+ &'static str
```
___
@@ -8460,12 +8460,12 @@ fn main() {
expect![[r#"
*r" Hello World "*
```rust
- &str
+ &'static str
```
___
value of literal: ` Hello World `
-"#]],
+ "#]],
)
}
@@ -8480,7 +8480,7 @@ fn main() {
expect![[r#"
*b"\xF0\x9F\xA6\x80\\"*
```rust
- &[u8; 5]
+ &'static [u8; 5]
```
___
@@ -8496,7 +8496,7 @@ fn main() {
expect![[r#"
*br"\xF0\x9F\xA6\x80\\"*
```rust
- &[u8; 18]
+ &'static [u8; 18]
```
___
@@ -9070,7 +9070,7 @@ struct Pedro$0<'a> {
```rust
struct Pedro<'a> {
- hola: &str,
+ hola: &'a str,
}
```
@@ -9937,7 +9937,7 @@ fn baz() {
---
- `U` = `i32`, `T` = `&str`
+ `U` = `i32`, `T` = `&'static str`
"#]],
);
}
@@ -10030,7 +10030,7 @@ fn bar() {
---
- `T` = `i8`, `U` = `&str`
+ `T` = `i8`, `U` = `&'static str`
"#]],
);
}
diff --git a/crates/ide/src/inlay_hints/bind_pat.rs b/crates/ide/src/inlay_hints/bind_pat.rs
index 52ea2e5ec5..36fdd90e8a 100644
--- a/crates/ide/src/inlay_hints/bind_pat.rs
+++ b/crates/ide/src/inlay_hints/bind_pat.rs
@@ -380,9 +380,9 @@ fn main() {
let foo = foo3();
// ^^^ impl Fn(f64, f64) -> u32
let foo = foo4();
- // ^^^ &dyn Fn(f64, f64) -> u32
+ // ^^^ &'static (dyn Fn(f64, f64) -> u32 + 'static)
let foo = foo5();
- // ^^^ &dyn Fn(&dyn Fn(f64, f64) -> u32, f64) -> u32
+ // ^^^ &'static (dyn Fn(&(dyn Fn(f64, f64) -> u32 + 'static), f64) -> u32 + 'static)
let foo = foo6();
// ^^^ impl Fn(f64, f64) -> u32
let foo = foo7();
@@ -413,7 +413,7 @@ fn main() {
let foo = foo3();
// ^^^ impl Fn(f64, f64) -> u32
let foo = foo4();
- // ^^^ &dyn Fn(f64, f64) -> u32
+ // ^^^ &'static (dyn Fn(f64, f64) -> u32 + 'static)
let foo = foo5();
let foo = foo6();
let foo = foo7();
@@ -528,7 +528,7 @@ fn main() {
//^^^^ i32
let _ = 22;
let test = "test";
- //^^^^ &str
+ //^^^^ &'static str
let test = InnerStruct {};
//^^^^ InnerStruct
@@ -618,12 +618,12 @@ impl<T> Iterator for IntoIter<T> {
fn main() {
let mut data = Vec::new();
- //^^^^ Vec<&str>
+ //^^^^ Vec<&'static str>
data.push("foo");
for i in data {
- //^ &str
+ //^ &'static str
let z = i;
- //^ &str
+ //^ &'static str
}
}
"#,
@@ -651,8 +651,8 @@ fn main() {
//^^ Vec<Box<&(dyn Display + Sync)>>
let _v = { Vec::<Box<*const (dyn Display + Sync)>>::new() };
//^^ Vec<Box<*const (dyn Display + Sync)>>
- let _v = { Vec::<Box<dyn Display + Sync>>::new() };
- //^^ Vec<Box<dyn Display + Sync>>
+ let _v = { Vec::<Box<dyn Display + Sync + 'static>>::new() };
+ //^^ Vec<Box<dyn Display + Sync + 'static>>
}
"#,
);
@@ -1017,7 +1017,7 @@ fn test<T>(t: T) {
"#,
expect![[r#"
fn test<T>(t: T) {
- let f = |a: i32, b: &str, c: T| {};
+ let f = |a: i32, b: &'static str, c: T| {};
let result: () = f(42, "", t);
}
"#]],
diff --git a/crates/ide/src/inlay_hints/closing_brace.rs b/crates/ide/src/inlay_hints/closing_brace.rs
index de9ca8c000..2ec85da4a4 100644
--- a/crates/ide/src/inlay_hints/closing_brace.rs
+++ b/crates/ide/src/inlay_hints/closing_brace.rs
@@ -194,7 +194,7 @@ impl Tr for () {
//^ impl Tr for ()
impl dyn Tr {
}
-//^ impl dyn Tr
+//^ impl dyn Tr + 'static
static S0: () = 0;
static S1: () = {};
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index a13be6c492..aa525a8612 100644
--- a/crates/ide/src/lib.rs
+++ b/crates/ide/src/lib.rs
@@ -123,9 +123,9 @@ pub use ide_completion::{
CallableSnippets, CompletionConfig, CompletionFieldsToResolve, CompletionItem,
CompletionItemKind, CompletionItemRefMode, CompletionRelevance, Snippet, SnippetScope,
};
-pub use ide_db::text_edit::{Indel, TextEdit};
pub use ide_db::{
FileId, FilePosition, FileRange, RootDatabase, Severity, SymbolKind,
+ assists::ExprFillDefaultMode,
base_db::{Crate, CrateGraphBuilder, FileChange, SourceRoot, SourceRootId},
documentation::Documentation,
label::Label,
@@ -134,8 +134,9 @@ pub use ide_db::{
search::{ReferenceCategory, SearchScope},
source_change::{FileSystemEdit, SnippetEdit, SourceChange},
symbol_index::Query,
+ text_edit::{Indel, TextEdit},
};
-pub use ide_diagnostics::{Diagnostic, DiagnosticCode, DiagnosticsConfig, ExprFillDefaultMode};
+pub use ide_diagnostics::{Diagnostic, DiagnosticCode, DiagnosticsConfig};
pub use ide_ssr::SsrError;
pub use span::Edition;
pub use syntax::{TextRange, TextSize};
@@ -181,7 +182,7 @@ impl AnalysisHost {
/// Returns a snapshot of the current state, which you can query for
/// semantic information.
pub fn analysis(&self) -> Analysis {
- Analysis { db: self.db.snapshot() }
+ Analysis { db: self.db.clone() }
}
/// Applies changes to the current state of the world. If there are
@@ -863,7 +864,7 @@ impl Analysis {
where
F: FnOnce(&RootDatabase) -> T + std::panic::UnwindSafe,
{
- let snap = self.db.snapshot();
+ let snap = self.db.clone();
Cancelled::catch(|| f(&snap))
}
}
diff --git a/crates/ide/src/moniker.rs b/crates/ide/src/moniker.rs
index 4a06cd919f..795c1f2ca3 100644
--- a/crates/ide/src/moniker.rs
+++ b/crates/ide/src/moniker.rs
@@ -451,7 +451,7 @@ mod tests {
assert_eq!(x.len(), 1);
match x.into_iter().next().unwrap() {
MonikerResult::Local { enclosing_moniker } => {
- panic!("Unexpected local enclosed in {:?}", enclosing_moniker);
+ panic!("Unexpected local enclosed in {enclosing_moniker:?}");
}
MonikerResult::Moniker(x) => {
assert_eq!(identifier, x.identifier.to_string());
diff --git a/crates/ide/src/status.rs b/crates/ide/src/status.rs
index 55a0db2d82..cfcd76d2aa 100644
--- a/crates/ide/src/status.rs
+++ b/crates/ide/src/status.rs
@@ -51,8 +51,8 @@ pub(crate) fn status(db: &RootDatabase, file_id: Option<FileId>) -> String {
buf,
"Crate: {}\n",
match display_name {
- Some(it) => format!("{it}({:?})", crate_id),
- None => format!("{:?}", crate_id),
+ Some(it) => format!("{it}({crate_id:?})"),
+ None => format!("{crate_id:?}"),
}
);
format_to!(buf, " Root module file id: {}\n", root_file_id.index());
diff --git a/crates/ide/src/view_crate_graph.rs b/crates/ide/src/view_crate_graph.rs
index 4696fef320..7985279679 100644
--- a/crates/ide/src/view_crate_graph.rs
+++ b/crates/ide/src/view_crate_graph.rs
@@ -80,7 +80,7 @@ impl<'a> dot::Labeller<'a, Crate, Edge<'a>> for DotCrateGraph<'_> {
fn node_id(&'a self, n: &Crate) -> Id<'a> {
let id = n.as_id().as_u32();
- Id::new(format!("_{:?}", id)).unwrap()
+ Id::new(format!("_{id:?}")).unwrap()
}
fn node_shape(&'a self, _node: &Crate) -> Option<LabelText<'a>> {
diff --git a/crates/intern/src/symbol.rs b/crates/intern/src/symbol.rs
index 89c3be96fc..8b2d6e8717 100644
--- a/crates/intern/src/symbol.rs
+++ b/crates/intern/src/symbol.rs
@@ -163,28 +163,28 @@ impl Symbol {
pub fn integer(i: usize) -> Self {
match i {
- 0 => symbols::INTEGER_0.clone(),
- 1 => symbols::INTEGER_1.clone(),
- 2 => symbols::INTEGER_2.clone(),
- 3 => symbols::INTEGER_3.clone(),
- 4 => symbols::INTEGER_4.clone(),
- 5 => symbols::INTEGER_5.clone(),
- 6 => symbols::INTEGER_6.clone(),
- 7 => symbols::INTEGER_7.clone(),
- 8 => symbols::INTEGER_8.clone(),
- 9 => symbols::INTEGER_9.clone(),
- 10 => symbols::INTEGER_10.clone(),
- 11 => symbols::INTEGER_11.clone(),
- 12 => symbols::INTEGER_12.clone(),
- 13 => symbols::INTEGER_13.clone(),
- 14 => symbols::INTEGER_14.clone(),
- 15 => symbols::INTEGER_15.clone(),
+ 0 => symbols::INTEGER_0,
+ 1 => symbols::INTEGER_1,
+ 2 => symbols::INTEGER_2,
+ 3 => symbols::INTEGER_3,
+ 4 => symbols::INTEGER_4,
+ 5 => symbols::INTEGER_5,
+ 6 => symbols::INTEGER_6,
+ 7 => symbols::INTEGER_7,
+ 8 => symbols::INTEGER_8,
+ 9 => symbols::INTEGER_9,
+ 10 => symbols::INTEGER_10,
+ 11 => symbols::INTEGER_11,
+ 12 => symbols::INTEGER_12,
+ 13 => symbols::INTEGER_13,
+ 14 => symbols::INTEGER_14,
+ 15 => symbols::INTEGER_15,
i => Symbol::intern(&format!("{i}")),
}
}
pub fn empty() -> Self {
- symbols::__empty.clone()
+ symbols::__empty
}
#[inline]
diff --git a/crates/load-cargo/src/lib.rs b/crates/load-cargo/src/lib.rs
index 3e52dbaea6..2686a75c7c 100644
--- a/crates/load-cargo/src/lib.rs
+++ b/crates/load-cargo/src/lib.rs
@@ -292,7 +292,7 @@ impl ProjectFolders {
};
let file_set_roots = vec![VfsPath::from(ratoml_path.to_owned())];
- let entry = vfs::loader::Entry::Files(vec![ratoml_path.to_owned()]);
+ let entry = vfs::loader::Entry::Files(vec![ratoml_path]);
res.watch.push(res.load.len());
res.load.push(entry);
diff --git a/crates/parser/src/grammar/expressions.rs b/crates/parser/src/grammar/expressions.rs
index 34dcf2a182..0ac25da329 100644
--- a/crates/parser/src/grammar/expressions.rs
+++ b/crates/parser/src/grammar/expressions.rs
@@ -4,7 +4,7 @@ use crate::grammar::attributes::ATTRIBUTE_FIRST;
use super::*;
-pub(super) use atom::{LITERAL_FIRST, literal};
+pub(super) use atom::{EXPR_RECOVERY_SET, LITERAL_FIRST, literal};
pub(crate) use atom::{block_expr, match_arm_list};
#[derive(PartialEq, Eq)]
diff --git a/crates/parser/src/grammar/expressions/atom.rs b/crates/parser/src/grammar/expressions/atom.rs
index c66afed91c..5faf6fc275 100644
--- a/crates/parser/src/grammar/expressions/atom.rs
+++ b/crates/parser/src/grammar/expressions/atom.rs
@@ -46,7 +46,6 @@ pub(super) const ATOM_EXPR_FIRST: TokenSet =
T!['['],
T![|],
T![async],
- T![box],
T![break],
T![const],
T![continue],
@@ -68,7 +67,8 @@ pub(super) const ATOM_EXPR_FIRST: TokenSet =
LIFETIME_IDENT,
]));
-pub(super) const EXPR_RECOVERY_SET: TokenSet = TokenSet::new(&[T![')'], T![']']]);
+pub(in crate::grammar) const EXPR_RECOVERY_SET: TokenSet =
+ TokenSet::new(&[T!['}'], T![')'], T![']'], T![,]]);
pub(super) fn atom_expr(
p: &mut Parser<'_>,
diff --git a/crates/parser/src/grammar/items.rs b/crates/parser/src/grammar/items.rs
index f5f003be48..b9f4866574 100644
--- a/crates/parser/src/grammar/items.rs
+++ b/crates/parser/src/grammar/items.rs
@@ -32,6 +32,9 @@ pub(super) const ITEM_RECOVERY_SET: TokenSet = TokenSet::new(&[
T![impl],
T![trait],
T![const],
+ T![async],
+ T![unsafe],
+ T![extern],
T![static],
T![let],
T![mod],
diff --git a/crates/parser/src/grammar/paths.rs b/crates/parser/src/grammar/paths.rs
index 3410505cd4..770827c6b0 100644
--- a/crates/parser/src/grammar/paths.rs
+++ b/crates/parser/src/grammar/paths.rs
@@ -81,7 +81,7 @@ fn path_for_qualifier(
}
const EXPR_PATH_SEGMENT_RECOVERY_SET: TokenSet =
- items::ITEM_RECOVERY_SET.union(TokenSet::new(&[T![')'], T![,], T![let]]));
+ expressions::EXPR_RECOVERY_SET.union(items::ITEM_RECOVERY_SET);
const TYPE_PATH_SEGMENT_RECOVERY_SET: TokenSet = types::TYPE_RECOVERY_SET;
fn path_segment(p: &mut Parser<'_>, mode: Mode, first: bool) -> Option<CompletedMarker> {
diff --git a/crates/parser/src/grammar/patterns.rs b/crates/parser/src/grammar/patterns.rs
index 460051a0f4..4dd44c030f 100644
--- a/crates/parser/src/grammar/patterns.rs
+++ b/crates/parser/src/grammar/patterns.rs
@@ -199,8 +199,19 @@ fn pattern_single_r(p: &mut Parser<'_>, recovery_set: TokenSet) {
}
}
-const PAT_RECOVERY_SET: TokenSet =
- TokenSet::new(&[T![let], T![if], T![while], T![loop], T![match], T![')'], T![,], T![=]]);
+const PAT_RECOVERY_SET: TokenSet = TokenSet::new(&[
+ T![let],
+ T![if],
+ T![while],
+ T![loop],
+ T![match],
+ T![')'],
+ T![']'],
+ T!['}'],
+ T![,],
+ T![=],
+ T![&],
+]);
fn atom_pat(p: &mut Parser<'_>, recovery_set: TokenSet) -> Option<CompletedMarker> {
let m = match p.current() {
diff --git a/crates/parser/src/grammar/types.rs b/crates/parser/src/grammar/types.rs
index 0133b7d5d8..9d31e435cf 100644
--- a/crates/parser/src/grammar/types.rs
+++ b/crates/parser/src/grammar/types.rs
@@ -20,10 +20,15 @@ pub(super) const TYPE_FIRST: TokenSet = paths::PATH_FIRST.union(TokenSet::new(&[
pub(super) const TYPE_RECOVERY_SET: TokenSet = TokenSet::new(&[
T![')'],
+ // test_err type_in_array_recover
+ // const _: [&];
+ T![']'],
+ T!['}'],
T![>],
T![,],
// test_err struct_field_recover
// struct S { f pub g: () }
+ // struct S { f: pub g: () }
T![pub],
]);
diff --git a/crates/parser/src/lexed_str.rs b/crates/parser/src/lexed_str.rs
index 585e7ffb1a..0a5c16dc4c 100644
--- a/crates/parser/src/lexed_str.rs
+++ b/crates/parser/src/lexed_str.rs
@@ -179,6 +179,15 @@ impl<'a> Converter<'a> {
COMMENT
}
+ rustc_lexer::TokenKind::Frontmatter { has_invalid_preceding_whitespace, invalid_infostring } => {
+ if *has_invalid_preceding_whitespace {
+ err = "invalid preceding whitespace for frontmatter opening"
+ } else if *invalid_infostring {
+ err = "invalid infostring for frontmatter"
+ }
+ FRONTMATTER
+ }
+
rustc_lexer::TokenKind::Whitespace => WHITESPACE,
rustc_lexer::TokenKind::Ident if token_text == "_" => UNDERSCORE,
diff --git a/crates/parser/src/syntax_kind/generated.rs b/crates/parser/src/syntax_kind/generated.rs
index e6f93a1fbd..b1727509b1 100644
--- a/crates/parser/src/syntax_kind/generated.rs
+++ b/crates/parser/src/syntax_kind/generated.rs
@@ -150,6 +150,7 @@ pub enum SyntaxKind {
STRING,
COMMENT,
ERROR,
+ FRONTMATTER,
IDENT,
LIFETIME_IDENT,
NEWLINE,
@@ -483,6 +484,7 @@ impl SyntaxKind {
| YIELD_EXPR
| COMMENT
| ERROR
+ | FRONTMATTER
| IDENT
| LIFETIME_IDENT
| NEWLINE
@@ -994,7 +996,7 @@ impl SyntaxKind {
}
}
#[macro_export]
-macro_rules ! T_ { [$] => { $ crate :: SyntaxKind :: DOLLAR } ; [;] => { $ crate :: SyntaxKind :: SEMICOLON } ; [,] => { $ crate :: SyntaxKind :: COMMA } ; ['('] => { $ crate :: SyntaxKind :: L_PAREN } ; [')'] => { $ crate :: SyntaxKind :: R_PAREN } ; ['{'] => { $ crate :: SyntaxKind :: L_CURLY } ; ['}'] => { $ crate :: SyntaxKind :: R_CURLY } ; ['['] => { $ crate :: SyntaxKind :: L_BRACK } ; [']'] => { $ crate :: SyntaxKind :: R_BRACK } ; [<] => { $ crate :: SyntaxKind :: L_ANGLE } ; [>] => { $ crate :: SyntaxKind :: R_ANGLE } ; [@] => { $ crate :: SyntaxKind :: AT } ; [#] => { $ crate :: SyntaxKind :: POUND } ; [~] => { $ crate :: SyntaxKind :: TILDE } ; [?] => { $ crate :: SyntaxKind :: QUESTION } ; [&] => { $ crate :: SyntaxKind :: AMP } ; [|] => { $ crate :: SyntaxKind :: PIPE } ; [+] => { $ crate :: SyntaxKind :: PLUS } ; [*] => { $ crate :: SyntaxKind :: STAR } ; [/] => { $ crate :: SyntaxKind :: SLASH } ; [^] => { $ crate :: SyntaxKind :: CARET } ; [%] => { $ crate :: SyntaxKind :: PERCENT } ; [_] => { $ crate :: SyntaxKind :: UNDERSCORE } ; [.] => { $ crate :: SyntaxKind :: DOT } ; [..] => { $ crate :: SyntaxKind :: DOT2 } ; [...] => { $ crate :: SyntaxKind :: DOT3 } ; [..=] => { $ crate :: SyntaxKind :: DOT2EQ } ; [:] => { $ crate :: SyntaxKind :: COLON } ; [::] => { $ crate :: SyntaxKind :: COLON2 } ; [=] => { $ crate :: SyntaxKind :: EQ } ; [==] => { $ crate :: SyntaxKind :: EQ2 } ; [=>] => { $ crate :: SyntaxKind :: FAT_ARROW } ; [!] => { $ crate :: SyntaxKind :: BANG } ; [!=] => { $ crate :: SyntaxKind :: NEQ } ; [-] => { $ crate :: SyntaxKind :: MINUS } ; [->] => { $ crate :: SyntaxKind :: THIN_ARROW } ; [<=] => { $ crate :: SyntaxKind :: LTEQ } ; [>=] => { $ crate :: SyntaxKind :: GTEQ } ; [+=] => { $ crate :: SyntaxKind :: PLUSEQ } ; [-=] => { $ crate :: SyntaxKind :: MINUSEQ } ; [|=] => { $ crate :: SyntaxKind :: PIPEEQ } ; [&=] => { $ crate :: SyntaxKind :: AMPEQ } ; [^=] => { $ crate :: SyntaxKind :: CARETEQ } ; [/=] => { $ crate :: SyntaxKind :: SLASHEQ } ; [*=] => { $ crate :: SyntaxKind :: STAREQ } ; [%=] => { $ crate :: SyntaxKind :: PERCENTEQ } ; [&&] => { $ crate :: SyntaxKind :: AMP2 } ; [||] => { $ crate :: SyntaxKind :: PIPE2 } ; [<<] => { $ crate :: SyntaxKind :: SHL } ; [>>] => { $ crate :: SyntaxKind :: SHR } ; [<<=] => { $ crate :: SyntaxKind :: SHLEQ } ; [>>=] => { $ crate :: SyntaxKind :: SHREQ } ; [Self] => { $ crate :: SyntaxKind :: SELF_TYPE_KW } ; [abstract] => { $ crate :: SyntaxKind :: ABSTRACT_KW } ; [as] => { $ crate :: SyntaxKind :: AS_KW } ; [become] => { $ crate :: SyntaxKind :: BECOME_KW } ; [box] => { $ crate :: SyntaxKind :: BOX_KW } ; [break] => { $ crate :: SyntaxKind :: BREAK_KW } ; [const] => { $ crate :: SyntaxKind :: CONST_KW } ; [continue] => { $ crate :: SyntaxKind :: CONTINUE_KW } ; [crate] => { $ crate :: SyntaxKind :: CRATE_KW } ; [do] => { $ crate :: SyntaxKind :: DO_KW } ; [else] => { $ crate :: SyntaxKind :: ELSE_KW } ; [enum] => { $ crate :: SyntaxKind :: ENUM_KW } ; [extern] => { $ crate :: SyntaxKind :: EXTERN_KW } ; [false] => { $ crate :: SyntaxKind :: FALSE_KW } ; [final] => { $ crate :: SyntaxKind :: FINAL_KW } ; [fn] => { $ crate :: SyntaxKind :: FN_KW } ; [for] => { $ crate :: SyntaxKind :: FOR_KW } ; [if] => { $ crate :: SyntaxKind :: IF_KW } ; [impl] => { $ crate :: SyntaxKind :: IMPL_KW } ; [in] => { $ crate :: SyntaxKind :: IN_KW } ; [let] => { $ crate :: SyntaxKind :: LET_KW } ; [loop] => { $ crate :: SyntaxKind :: LOOP_KW } ; [macro] => { $ crate :: SyntaxKind :: MACRO_KW } ; [match] => { $ crate :: SyntaxKind :: MATCH_KW } ; [mod] => { $ crate :: SyntaxKind :: MOD_KW } ; [move] => { $ crate :: SyntaxKind :: MOVE_KW } ; [mut] => { $ crate :: SyntaxKind :: MUT_KW } ; [override] => { $ crate :: SyntaxKind :: OVERRIDE_KW } ; [priv] => { $ crate :: SyntaxKind :: PRIV_KW } ; [pub] => { $ crate :: SyntaxKind :: PUB_KW } ; [ref] => { $ crate :: SyntaxKind :: REF_KW } ; [return] => { $ crate :: SyntaxKind :: RETURN_KW } ; [self] => { $ crate :: SyntaxKind :: SELF_KW } ; [static] => { $ crate :: SyntaxKind :: STATIC_KW } ; [struct] => { $ crate :: SyntaxKind :: STRUCT_KW } ; [super] => { $ crate :: SyntaxKind :: SUPER_KW } ; [trait] => { $ crate :: SyntaxKind :: TRAIT_KW } ; [true] => { $ crate :: SyntaxKind :: TRUE_KW } ; [type] => { $ crate :: SyntaxKind :: TYPE_KW } ; [typeof] => { $ crate :: SyntaxKind :: TYPEOF_KW } ; [unsafe] => { $ crate :: SyntaxKind :: UNSAFE_KW } ; [unsized] => { $ crate :: SyntaxKind :: UNSIZED_KW } ; [use] => { $ crate :: SyntaxKind :: USE_KW } ; [virtual] => { $ crate :: SyntaxKind :: VIRTUAL_KW } ; [where] => { $ crate :: SyntaxKind :: WHERE_KW } ; [while] => { $ crate :: SyntaxKind :: WHILE_KW } ; [yield] => { $ crate :: SyntaxKind :: YIELD_KW } ; [asm] => { $ crate :: SyntaxKind :: ASM_KW } ; [att_syntax] => { $ crate :: SyntaxKind :: ATT_SYNTAX_KW } ; [auto] => { $ crate :: SyntaxKind :: AUTO_KW } ; [builtin] => { $ crate :: SyntaxKind :: BUILTIN_KW } ; [clobber_abi] => { $ crate :: SyntaxKind :: CLOBBER_ABI_KW } ; [default] => { $ crate :: SyntaxKind :: DEFAULT_KW } ; [dyn] => { $ crate :: SyntaxKind :: DYN_KW } ; [format_args] => { $ crate :: SyntaxKind :: FORMAT_ARGS_KW } ; [inlateout] => { $ crate :: SyntaxKind :: INLATEOUT_KW } ; [inout] => { $ crate :: SyntaxKind :: INOUT_KW } ; [label] => { $ crate :: SyntaxKind :: LABEL_KW } ; [lateout] => { $ crate :: SyntaxKind :: LATEOUT_KW } ; [macro_rules] => { $ crate :: SyntaxKind :: MACRO_RULES_KW } ; [may_unwind] => { $ crate :: SyntaxKind :: MAY_UNWIND_KW } ; [nomem] => { $ crate :: SyntaxKind :: NOMEM_KW } ; [noreturn] => { $ crate :: SyntaxKind :: NORETURN_KW } ; [nostack] => { $ crate :: SyntaxKind :: NOSTACK_KW } ; [offset_of] => { $ crate :: SyntaxKind :: OFFSET_OF_KW } ; [options] => { $ crate :: SyntaxKind :: OPTIONS_KW } ; [out] => { $ crate :: SyntaxKind :: OUT_KW } ; [preserves_flags] => { $ crate :: SyntaxKind :: PRESERVES_FLAGS_KW } ; [pure] => { $ crate :: SyntaxKind :: PURE_KW } ; [raw] => { $ crate :: SyntaxKind :: RAW_KW } ; [readonly] => { $ crate :: SyntaxKind :: READONLY_KW } ; [safe] => { $ crate :: SyntaxKind :: SAFE_KW } ; [sym] => { $ crate :: SyntaxKind :: SYM_KW } ; [union] => { $ crate :: SyntaxKind :: UNION_KW } ; [yeet] => { $ crate :: SyntaxKind :: YEET_KW } ; [async] => { $ crate :: SyntaxKind :: ASYNC_KW } ; [await] => { $ crate :: SyntaxKind :: AWAIT_KW } ; [dyn] => { $ crate :: SyntaxKind :: DYN_KW } ; [gen] => { $ crate :: SyntaxKind :: GEN_KW } ; [try] => { $ crate :: SyntaxKind :: TRY_KW } ; [lifetime_ident] => { $ crate :: SyntaxKind :: LIFETIME_IDENT } ; [int_number] => { $ crate :: SyntaxKind :: INT_NUMBER } ; [ident] => { $ crate :: SyntaxKind :: IDENT } ; [string] => { $ crate :: SyntaxKind :: STRING } ; [shebang] => { $ crate :: SyntaxKind :: SHEBANG } ; }
+macro_rules ! T_ { [$] => { $ crate :: SyntaxKind :: DOLLAR } ; [;] => { $ crate :: SyntaxKind :: SEMICOLON } ; [,] => { $ crate :: SyntaxKind :: COMMA } ; ['('] => { $ crate :: SyntaxKind :: L_PAREN } ; [')'] => { $ crate :: SyntaxKind :: R_PAREN } ; ['{'] => { $ crate :: SyntaxKind :: L_CURLY } ; ['}'] => { $ crate :: SyntaxKind :: R_CURLY } ; ['['] => { $ crate :: SyntaxKind :: L_BRACK } ; [']'] => { $ crate :: SyntaxKind :: R_BRACK } ; [<] => { $ crate :: SyntaxKind :: L_ANGLE } ; [>] => { $ crate :: SyntaxKind :: R_ANGLE } ; [@] => { $ crate :: SyntaxKind :: AT } ; [#] => { $ crate :: SyntaxKind :: POUND } ; [~] => { $ crate :: SyntaxKind :: TILDE } ; [?] => { $ crate :: SyntaxKind :: QUESTION } ; [&] => { $ crate :: SyntaxKind :: AMP } ; [|] => { $ crate :: SyntaxKind :: PIPE } ; [+] => { $ crate :: SyntaxKind :: PLUS } ; [*] => { $ crate :: SyntaxKind :: STAR } ; [/] => { $ crate :: SyntaxKind :: SLASH } ; [^] => { $ crate :: SyntaxKind :: CARET } ; [%] => { $ crate :: SyntaxKind :: PERCENT } ; [_] => { $ crate :: SyntaxKind :: UNDERSCORE } ; [.] => { $ crate :: SyntaxKind :: DOT } ; [..] => { $ crate :: SyntaxKind :: DOT2 } ; [...] => { $ crate :: SyntaxKind :: DOT3 } ; [..=] => { $ crate :: SyntaxKind :: DOT2EQ } ; [:] => { $ crate :: SyntaxKind :: COLON } ; [::] => { $ crate :: SyntaxKind :: COLON2 } ; [=] => { $ crate :: SyntaxKind :: EQ } ; [==] => { $ crate :: SyntaxKind :: EQ2 } ; [=>] => { $ crate :: SyntaxKind :: FAT_ARROW } ; [!] => { $ crate :: SyntaxKind :: BANG } ; [!=] => { $ crate :: SyntaxKind :: NEQ } ; [-] => { $ crate :: SyntaxKind :: MINUS } ; [->] => { $ crate :: SyntaxKind :: THIN_ARROW } ; [<=] => { $ crate :: SyntaxKind :: LTEQ } ; [>=] => { $ crate :: SyntaxKind :: GTEQ } ; [+=] => { $ crate :: SyntaxKind :: PLUSEQ } ; [-=] => { $ crate :: SyntaxKind :: MINUSEQ } ; [|=] => { $ crate :: SyntaxKind :: PIPEEQ } ; [&=] => { $ crate :: SyntaxKind :: AMPEQ } ; [^=] => { $ crate :: SyntaxKind :: CARETEQ } ; [/=] => { $ crate :: SyntaxKind :: SLASHEQ } ; [*=] => { $ crate :: SyntaxKind :: STAREQ } ; [%=] => { $ crate :: SyntaxKind :: PERCENTEQ } ; [&&] => { $ crate :: SyntaxKind :: AMP2 } ; [||] => { $ crate :: SyntaxKind :: PIPE2 } ; [<<] => { $ crate :: SyntaxKind :: SHL } ; [>>] => { $ crate :: SyntaxKind :: SHR } ; [<<=] => { $ crate :: SyntaxKind :: SHLEQ } ; [>>=] => { $ crate :: SyntaxKind :: SHREQ } ; [Self] => { $ crate :: SyntaxKind :: SELF_TYPE_KW } ; [abstract] => { $ crate :: SyntaxKind :: ABSTRACT_KW } ; [as] => { $ crate :: SyntaxKind :: AS_KW } ; [become] => { $ crate :: SyntaxKind :: BECOME_KW } ; [box] => { $ crate :: SyntaxKind :: BOX_KW } ; [break] => { $ crate :: SyntaxKind :: BREAK_KW } ; [const] => { $ crate :: SyntaxKind :: CONST_KW } ; [continue] => { $ crate :: SyntaxKind :: CONTINUE_KW } ; [crate] => { $ crate :: SyntaxKind :: CRATE_KW } ; [do] => { $ crate :: SyntaxKind :: DO_KW } ; [else] => { $ crate :: SyntaxKind :: ELSE_KW } ; [enum] => { $ crate :: SyntaxKind :: ENUM_KW } ; [extern] => { $ crate :: SyntaxKind :: EXTERN_KW } ; [false] => { $ crate :: SyntaxKind :: FALSE_KW } ; [final] => { $ crate :: SyntaxKind :: FINAL_KW } ; [fn] => { $ crate :: SyntaxKind :: FN_KW } ; [for] => { $ crate :: SyntaxKind :: FOR_KW } ; [if] => { $ crate :: SyntaxKind :: IF_KW } ; [impl] => { $ crate :: SyntaxKind :: IMPL_KW } ; [in] => { $ crate :: SyntaxKind :: IN_KW } ; [let] => { $ crate :: SyntaxKind :: LET_KW } ; [loop] => { $ crate :: SyntaxKind :: LOOP_KW } ; [macro] => { $ crate :: SyntaxKind :: MACRO_KW } ; [match] => { $ crate :: SyntaxKind :: MATCH_KW } ; [mod] => { $ crate :: SyntaxKind :: MOD_KW } ; [move] => { $ crate :: SyntaxKind :: MOVE_KW } ; [mut] => { $ crate :: SyntaxKind :: MUT_KW } ; [override] => { $ crate :: SyntaxKind :: OVERRIDE_KW } ; [priv] => { $ crate :: SyntaxKind :: PRIV_KW } ; [pub] => { $ crate :: SyntaxKind :: PUB_KW } ; [ref] => { $ crate :: SyntaxKind :: REF_KW } ; [return] => { $ crate :: SyntaxKind :: RETURN_KW } ; [self] => { $ crate :: SyntaxKind :: SELF_KW } ; [static] => { $ crate :: SyntaxKind :: STATIC_KW } ; [struct] => { $ crate :: SyntaxKind :: STRUCT_KW } ; [super] => { $ crate :: SyntaxKind :: SUPER_KW } ; [trait] => { $ crate :: SyntaxKind :: TRAIT_KW } ; [true] => { $ crate :: SyntaxKind :: TRUE_KW } ; [type] => { $ crate :: SyntaxKind :: TYPE_KW } ; [typeof] => { $ crate :: SyntaxKind :: TYPEOF_KW } ; [unsafe] => { $ crate :: SyntaxKind :: UNSAFE_KW } ; [unsized] => { $ crate :: SyntaxKind :: UNSIZED_KW } ; [use] => { $ crate :: SyntaxKind :: USE_KW } ; [virtual] => { $ crate :: SyntaxKind :: VIRTUAL_KW } ; [where] => { $ crate :: SyntaxKind :: WHERE_KW } ; [while] => { $ crate :: SyntaxKind :: WHILE_KW } ; [yield] => { $ crate :: SyntaxKind :: YIELD_KW } ; [asm] => { $ crate :: SyntaxKind :: ASM_KW } ; [att_syntax] => { $ crate :: SyntaxKind :: ATT_SYNTAX_KW } ; [auto] => { $ crate :: SyntaxKind :: AUTO_KW } ; [builtin] => { $ crate :: SyntaxKind :: BUILTIN_KW } ; [clobber_abi] => { $ crate :: SyntaxKind :: CLOBBER_ABI_KW } ; [default] => { $ crate :: SyntaxKind :: DEFAULT_KW } ; [dyn] => { $ crate :: SyntaxKind :: DYN_KW } ; [format_args] => { $ crate :: SyntaxKind :: FORMAT_ARGS_KW } ; [inlateout] => { $ crate :: SyntaxKind :: INLATEOUT_KW } ; [inout] => { $ crate :: SyntaxKind :: INOUT_KW } ; [label] => { $ crate :: SyntaxKind :: LABEL_KW } ; [lateout] => { $ crate :: SyntaxKind :: LATEOUT_KW } ; [macro_rules] => { $ crate :: SyntaxKind :: MACRO_RULES_KW } ; [may_unwind] => { $ crate :: SyntaxKind :: MAY_UNWIND_KW } ; [nomem] => { $ crate :: SyntaxKind :: NOMEM_KW } ; [noreturn] => { $ crate :: SyntaxKind :: NORETURN_KW } ; [nostack] => { $ crate :: SyntaxKind :: NOSTACK_KW } ; [offset_of] => { $ crate :: SyntaxKind :: OFFSET_OF_KW } ; [options] => { $ crate :: SyntaxKind :: OPTIONS_KW } ; [out] => { $ crate :: SyntaxKind :: OUT_KW } ; [preserves_flags] => { $ crate :: SyntaxKind :: PRESERVES_FLAGS_KW } ; [pure] => { $ crate :: SyntaxKind :: PURE_KW } ; [raw] => { $ crate :: SyntaxKind :: RAW_KW } ; [readonly] => { $ crate :: SyntaxKind :: READONLY_KW } ; [safe] => { $ crate :: SyntaxKind :: SAFE_KW } ; [sym] => { $ crate :: SyntaxKind :: SYM_KW } ; [union] => { $ crate :: SyntaxKind :: UNION_KW } ; [yeet] => { $ crate :: SyntaxKind :: YEET_KW } ; [async] => { $ crate :: SyntaxKind :: ASYNC_KW } ; [await] => { $ crate :: SyntaxKind :: AWAIT_KW } ; [dyn] => { $ crate :: SyntaxKind :: DYN_KW } ; [gen] => { $ crate :: SyntaxKind :: GEN_KW } ; [try] => { $ crate :: SyntaxKind :: TRY_KW } ; [lifetime_ident] => { $ crate :: SyntaxKind :: LIFETIME_IDENT } ; [int_number] => { $ crate :: SyntaxKind :: INT_NUMBER } ; [ident] => { $ crate :: SyntaxKind :: IDENT } ; [string] => { $ crate :: SyntaxKind :: STRING } ; [shebang] => { $ crate :: SyntaxKind :: SHEBANG } ; [frontmatter] => { $ crate :: SyntaxKind :: FRONTMATTER } ; }
impl ::core::marker::Copy for SyntaxKind {}
impl ::core::clone::Clone for SyntaxKind {
#[inline]
diff --git a/crates/parser/test_data/generated/runner.rs b/crates/parser/test_data/generated/runner.rs
index 6c9d02aaa8..24db9478ee 100644
--- a/crates/parser/test_data/generated/runner.rs
+++ b/crates/parser/test_data/generated/runner.rs
@@ -870,6 +870,10 @@ mod err {
run_and_expect_errors("test_data/parser/inline/err/tuple_pat_leading_comma.rs");
}
#[test]
+ fn type_in_array_recover() {
+ run_and_expect_errors("test_data/parser/inline/err/type_in_array_recover.rs");
+ }
+ #[test]
fn unsafe_block_in_mod() {
run_and_expect_errors("test_data/parser/inline/err/unsafe_block_in_mod.rs");
}
diff --git a/crates/parser/test_data/parser/err/0022_bad_exprs.rast b/crates/parser/test_data/parser/err/0022_bad_exprs.rast
index d97fc6c720..1a8e881dd9 100644
--- a/crates/parser/test_data/parser/err/0022_bad_exprs.rast
+++ b/crates/parser/test_data/parser/err/0022_bad_exprs.rast
@@ -35,8 +35,8 @@ SOURCE_FILE
WHITESPACE " "
LET_STMT
LET_KW "let"
- ERROR
- R_BRACK "]"
+ ERROR
+ R_BRACK "]"
WHITESPACE " "
R_CURLY "}"
WHITESPACE "\n"
@@ -149,7 +149,8 @@ error 17: expected expression, item or let statement
error 25: expected a name
error 26: expected `;`, `{`, or `(`
error 30: expected pattern
-error 31: expected SEMICOLON
+error 30: expected SEMICOLON
+error 30: expected expression, item or let statement
error 53: expected expression
error 54: expected R_PAREN
error 54: expected SEMICOLON
diff --git a/crates/parser/test_data/parser/inline/err/comma_after_default_values_syntax.rast b/crates/parser/test_data/parser/inline/err/comma_after_default_values_syntax.rast
index feb617e1aa..b57066f2fb 100644
--- a/crates/parser/test_data/parser/inline/err/comma_after_default_values_syntax.rast
+++ b/crates/parser/test_data/parser/inline/err/comma_after_default_values_syntax.rast
@@ -23,8 +23,7 @@ SOURCE_FILE
L_CURLY "{"
WHITESPACE " "
DOT2 ".."
- ERROR
- COMMA ","
+ COMMA ","
WHITESPACE " "
R_CURLY "}"
SEMICOLON ";"
@@ -39,8 +38,7 @@ SOURCE_FILE
L_CURLY "{"
WHITESPACE " "
DOT2 ".."
- ERROR
- COMMA ","
+ COMMA ","
WHITESPACE " "
RECORD_EXPR_FIELD
NAME_REF
@@ -55,5 +53,6 @@ SOURCE_FILE
R_CURLY "}"
WHITESPACE "\n"
error 21: expected expression
+error 21: cannot use a comma after the base struct
error 36: expected expression
-error 37: expected COMMA
+error 36: cannot use a comma after the base struct
diff --git a/crates/parser/test_data/parser/inline/err/struct_field_recover.rast b/crates/parser/test_data/parser/inline/err/struct_field_recover.rast
index 458d7f4e2f..5a12c21b64 100644
--- a/crates/parser/test_data/parser/inline/err/struct_field_recover.rast
+++ b/crates/parser/test_data/parser/inline/err/struct_field_recover.rast
@@ -26,6 +26,36 @@ SOURCE_FILE
WHITESPACE " "
R_CURLY "}"
WHITESPACE "\n"
+ STRUCT
+ STRUCT_KW "struct"
+ WHITESPACE " "
+ NAME
+ IDENT "S"
+ WHITESPACE " "
+ RECORD_FIELD_LIST
+ L_CURLY "{"
+ WHITESPACE " "
+ RECORD_FIELD
+ NAME
+ IDENT "f"
+ COLON ":"
+ WHITESPACE " "
+ RECORD_FIELD
+ VISIBILITY
+ PUB_KW "pub"
+ WHITESPACE " "
+ NAME
+ IDENT "g"
+ COLON ":"
+ WHITESPACE " "
+ TUPLE_TYPE
+ L_PAREN "("
+ R_PAREN ")"
+ WHITESPACE " "
+ R_CURLY "}"
+ WHITESPACE "\n"
error 12: expected COLON
error 12: expected type
error 12: expected COMMA
+error 38: expected type
+error 38: expected COMMA
diff --git a/crates/parser/test_data/parser/inline/err/struct_field_recover.rs b/crates/parser/test_data/parser/inline/err/struct_field_recover.rs
index da32227adc..5b1e5a5b8a 100644
--- a/crates/parser/test_data/parser/inline/err/struct_field_recover.rs
+++ b/crates/parser/test_data/parser/inline/err/struct_field_recover.rs
@@ -1 +1,2 @@
struct S { f pub g: () }
+struct S { f: pub g: () }
diff --git a/crates/parser/test_data/parser/inline/err/type_in_array_recover.rast b/crates/parser/test_data/parser/inline/err/type_in_array_recover.rast
new file mode 100644
index 0000000000..db76e8d7c8
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/err/type_in_array_recover.rast
@@ -0,0 +1,15 @@
+SOURCE_FILE
+ CONST
+ CONST_KW "const"
+ WHITESPACE " "
+ UNDERSCORE "_"
+ COLON ":"
+ WHITESPACE " "
+ SLICE_TYPE
+ L_BRACK "["
+ REF_TYPE
+ AMP "&"
+ R_BRACK "]"
+ SEMICOLON ";"
+ WHITESPACE "\n"
+error 11: expected type
diff --git a/crates/parser/test_data/parser/inline/err/type_in_array_recover.rs b/crates/parser/test_data/parser/inline/err/type_in_array_recover.rs
new file mode 100644
index 0000000000..039bf82997
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/err/type_in_array_recover.rs
@@ -0,0 +1 @@
+const _: [&];
diff --git a/crates/proc-macro-srv/build.rs b/crates/proc-macro-srv/build.rs
index 07a10aaae5..97c0c4bda7 100644
--- a/crates/proc-macro-srv/build.rs
+++ b/crates/proc-macro-srv/build.rs
@@ -12,5 +12,5 @@ fn main() {
let version_string = std::str::from_utf8(&output.stdout[..])
.expect("rustc --version output must be UTF-8")
.trim();
- println!("cargo::rustc-env=RUSTC_VERSION={}", version_string);
+ println!("cargo::rustc-env=RUSTC_VERSION={version_string}");
}
diff --git a/crates/project-model/src/env.rs b/crates/project-model/src/env.rs
index f2e5df171a..e7293b0b2e 100644
--- a/crates/project-model/src/env.rs
+++ b/crates/project-model/src/env.rs
@@ -25,7 +25,7 @@ pub(crate) fn inject_cargo_package_env(env: &mut Env, package: &PackageData) {
env.set("CARGO_PKG_VERSION_PATCH", package.version.patch.to_string());
env.set("CARGO_PKG_VERSION_PRE", package.version.pre.to_string());
- env.set("CARGO_PKG_AUTHORS", package.authors.join(":").clone());
+ env.set("CARGO_PKG_AUTHORS", package.authors.join(":"));
env.set("CARGO_PKG_NAME", package.name.clone());
env.set("CARGO_PKG_DESCRIPTION", package.description.as_deref().unwrap_or_default());
diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs
index c6e0cf36af..eec0077ea6 100644
--- a/crates/project-model/src/workspace.rs
+++ b/crates/project-model/src/workspace.rs
@@ -1370,7 +1370,7 @@ fn detached_file_to_crate_graph(
Edition::CURRENT,
display_name.clone(),
None,
- cfg_options.clone(),
+ cfg_options,
None,
Env::default(),
CrateOrigin::Local {
diff --git a/crates/query-group-macro/Cargo.toml b/crates/query-group-macro/Cargo.toml
index 8aeb262942..8b03d8f8cc 100644
--- a/crates/query-group-macro/Cargo.toml
+++ b/crates/query-group-macro/Cargo.toml
@@ -20,3 +20,4 @@ syn = { version = "2.0", features = ["full", "extra-traits", "visit-mut"] }
[dev-dependencies]
expect-test = "1.5.1"
salsa.workspace = true
+salsa-macros.workspace = true
diff --git a/crates/query-group-macro/src/lib.rs b/crates/query-group-macro/src/lib.rs
index f4f316c1ac..ec4b6b2a4a 100644
--- a/crates/query-group-macro/src/lib.rs
+++ b/crates/query-group-macro/src/lib.rs
@@ -178,7 +178,7 @@ pub(crate) fn query_group_impl(
let supertraits = &item_trait.supertraits;
let db_attr: Attribute = parse_quote! {
- #[salsa::db]
+ #[salsa_macros::db]
};
item_trait.attrs.push(db_attr);
@@ -210,7 +210,7 @@ pub(crate) fn query_group_impl(
.into_iter()
.filter(|fn_arg| matches!(fn_arg, FnArg::Typed(_)))
.map(|fn_arg| match fn_arg {
- FnArg::Typed(pat_type) => pat_type.clone(),
+ FnArg::Typed(pat_type) => pat_type,
FnArg::Receiver(_) => unreachable!("this should have been filtered out"),
})
.collect::<Vec<syn::PatType>>();
@@ -407,7 +407,7 @@ pub(crate) fn query_group_impl(
.collect::<Vec<proc_macro2::TokenStream>>();
let input_struct = quote! {
- #[salsa::input]
+ #[salsa_macros::input]
pub(crate) struct #input_struct_name {
#(#fields),*
}
@@ -418,7 +418,7 @@ pub(crate) fn query_group_impl(
let create_data_method = quote! {
#[allow(non_snake_case)]
- #[salsa::tracked]
+ #[salsa_macros::tracked]
fn #create_data_ident(db: &dyn #trait_name_ident) -> #input_struct_name {
#input_struct_name::new(db, #(#field_params),*)
}
@@ -443,7 +443,7 @@ pub(crate) fn query_group_impl(
item_trait.items.append(&mut lookup_signatures);
let trait_impl = quote! {
- #[salsa::db]
+ #[salsa_macros::db]
impl<DB> #trait_name_ident for DB
where
DB: #supertraits,
diff --git a/crates/query-group-macro/src/queries.rs b/crates/query-group-macro/src/queries.rs
index d4d40588bf..baac3e8bbf 100644
--- a/crates/query-group-macro/src/queries.rs
+++ b/crates/query-group-macro/src/queries.rs
@@ -49,7 +49,7 @@ impl ToTokens for TrackedQuery {
})
.into_iter()
.chain(self.lru.map(|lru| quote!(lru = #lru)));
- let annotation = quote!(#[salsa::tracked( #(#options),* )]);
+ let annotation = quote!(#[salsa_macros::tracked( #(#options),* )]);
let pat_and_tys = &self.pat_and_tys;
let params = self
diff --git a/crates/query-group-macro/tests/interned.rs b/crates/query-group-macro/tests/interned.rs
index 26ed316122..f738185b1f 100644
--- a/crates/query-group-macro/tests/interned.rs
+++ b/crates/query-group-macro/tests/interned.rs
@@ -6,7 +6,7 @@ use salsa::plumbing::AsId;
mod logger_db;
use logger_db::LoggerDb;
-#[salsa::interned(no_lifetime)]
+#[salsa_macros::interned(no_lifetime)]
pub struct InternedString {
data: String,
}
diff --git a/crates/query-group-macro/tests/logger_db.rs b/crates/query-group-macro/tests/logger_db.rs
index 5cf9be36f7..bade0c2cd6 100644
--- a/crates/query-group-macro/tests/logger_db.rs
+++ b/crates/query-group-macro/tests/logger_db.rs
@@ -1,6 +1,6 @@
use std::sync::{Arc, Mutex};
-#[salsa::db]
+#[salsa_macros::db]
#[derive(Default, Clone)]
pub(crate) struct LoggerDb {
storage: salsa::Storage<Self>,
@@ -12,7 +12,7 @@ struct Logger {
logs: Arc<Mutex<Vec<String>>>,
}
-#[salsa::db]
+#[salsa_macros::db]
impl salsa::Database for LoggerDb {
fn salsa_event(&self, event: &dyn Fn() -> salsa::Event) {
let event = event();
@@ -40,7 +40,7 @@ impl LoggerDb {
/// it is meant to be run from outside any tracked functions.
pub(crate) fn assert_logs(&self, expected: expect_test::Expect) {
let logs = std::mem::take(&mut *self.logger.logs.lock().unwrap());
- expected.assert_eq(&format!("{:#?}", logs));
+ expected.assert_eq(&format!("{logs:#?}"));
}
}
diff --git a/crates/query-group-macro/tests/old_and_new.rs b/crates/query-group-macro/tests/old_and_new.rs
index a18b23a7d8..cc57ba7845 100644
--- a/crates/query-group-macro/tests/old_and_new.rs
+++ b/crates/query-group-macro/tests/old_and_new.rs
@@ -4,7 +4,7 @@ mod logger_db;
use logger_db::LoggerDb;
use query_group_macro::query_group;
-#[salsa::input]
+#[salsa_macros::input]
struct Input {
str: String,
}
@@ -30,7 +30,7 @@ fn invoke_length_query_actual(db: &dyn PartialMigrationDatabase, input: Input) -
input.str(db).len()
}
-#[salsa::tracked]
+#[salsa_macros::tracked]
fn invoke_length_tracked_actual(db: &dyn PartialMigrationDatabase, input: Input) -> usize {
input.str(db).len()
}
@@ -87,12 +87,12 @@ fn invoke_tracked_query() {
fn new_salsa_baseline() {
let db = LoggerDb::default();
- #[salsa::input]
+ #[salsa_macros::input]
struct Input {
str: String,
}
- #[salsa::tracked]
+ #[salsa_macros::tracked]
fn new_salsa_length_query(db: &dyn PartialMigrationDatabase, input: Input) -> usize {
input.str(db).len()
}
diff --git a/crates/query-group-macro/tests/supertrait.rs b/crates/query-group-macro/tests/supertrait.rs
index 70073ac1de..ad8ada3ef1 100644
--- a/crates/query-group-macro/tests/supertrait.rs
+++ b/crates/query-group-macro/tests/supertrait.rs
@@ -1,6 +1,6 @@
use query_group_macro::query_group;
-#[salsa::db]
+#[salsa_macros::db]
pub trait SourceDb: salsa::Database {
/// Text of the file.
fn file_text(&self, id: usize) -> String;
diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs
index ea5a5eaa6a..4dba97c8ec 100644
--- a/crates/rust-analyzer/src/bin/main.rs
+++ b/crates/rust-analyzer/src/bin/main.rs
@@ -182,10 +182,8 @@ fn with_extra_thread(
thread_intent: stdx::thread::ThreadIntent,
f: impl FnOnce() -> anyhow::Result<()> + Send + 'static,
) -> anyhow::Result<()> {
- let handle = stdx::thread::Builder::new(thread_intent)
- .name(thread_name.into())
- .stack_size(STACK_SIZE)
- .spawn(f)?;
+ let handle =
+ stdx::thread::Builder::new(thread_intent, thread_name).stack_size(STACK_SIZE).spawn(f)?;
handle.join()?;
diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs
index a62005e3c0..a1e4adf084 100644
--- a/crates/rust-analyzer/src/cli/analysis_stats.rs
+++ b/crates/rust-analyzer/src/cli/analysis_stats.rs
@@ -175,7 +175,7 @@ impl flags::AnalysisStats {
UsizeWithUnderscore(dep_loc),
UsizeWithUnderscore(dep_item_trees),
);
- eprintln!(" dependency item stats: {}", dep_item_stats);
+ eprintln!(" dependency item stats: {dep_item_stats}");
// FIXME(salsa-transition): bring back stats for ParseQuery (file size)
// and ParseMacroExpansionQuery (macro expansion "file") size whenever we implement
@@ -295,7 +295,7 @@ impl flags::AnalysisStats {
UsizeWithUnderscore(workspace_loc),
UsizeWithUnderscore(workspace_item_trees),
);
- eprintln!(" usages: {}", workspace_item_stats);
+ eprintln!(" usages: {workspace_item_stats}");
eprintln!(" Dependencies:");
eprintln!(
@@ -303,7 +303,7 @@ impl flags::AnalysisStats {
UsizeWithUnderscore(dep_loc),
UsizeWithUnderscore(dep_item_trees),
);
- eprintln!(" declarations: {}", dep_item_stats);
+ eprintln!(" declarations: {dep_item_stats}");
let crate_def_map_time = crate_def_map_sw.elapsed();
eprintln!("{:<20} {}", "Item Collection:", crate_def_map_time);
@@ -701,10 +701,9 @@ impl flags::AnalysisStats {
if self.parallel {
let mut inference_sw = self.stop_watch();
- let snap = db.snapshot();
bodies
.par_iter()
- .map_with(snap, |snap, &body| {
+ .map_with(db.clone(), |snap, &body| {
snap.body(body.into());
snap.infer(body.into());
})
@@ -1294,7 +1293,7 @@ impl fmt::Display for UsizeWithUnderscore {
let num_str = self.0.to_string();
if num_str.len() <= 3 {
- return write!(f, "{}", num_str);
+ return write!(f, "{num_str}");
}
let mut result = String::new();
@@ -1307,7 +1306,7 @@ impl fmt::Display for UsizeWithUnderscore {
}
let result = result.chars().rev().collect::<String>();
- write!(f, "{}", result)
+ write!(f, "{result}")
}
}
diff --git a/crates/rust-analyzer/src/cli/diagnostics.rs b/crates/rust-analyzer/src/cli/diagnostics.rs
index 7c4eeebdfa..7b12cb1400 100644
--- a/crates/rust-analyzer/src/cli/diagnostics.rs
+++ b/crates/rust-analyzer/src/cli/diagnostics.rs
@@ -15,11 +15,13 @@ impl flags::Diagnostics {
pub fn run(self) -> anyhow::Result<()> {
const STACK_SIZE: usize = 1024 * 1024 * 8;
- let handle = stdx::thread::Builder::new(stdx::thread::ThreadIntent::LatencySensitive)
- .name("BIG_STACK_THREAD".into())
- .stack_size(STACK_SIZE)
- .spawn(|| self.run_())
- .unwrap();
+ let handle = stdx::thread::Builder::new(
+ stdx::thread::ThreadIntent::LatencySensitive,
+ "BIG_STACK_THREAD",
+ )
+ .stack_size(STACK_SIZE)
+ .spawn(|| self.run_())
+ .unwrap();
handle.join()
}
diff --git a/crates/rust-analyzer/src/cli/rustc_tests.rs b/crates/rust-analyzer/src/cli/rustc_tests.rs
index c042c26bd1..e3b372c914 100644
--- a/crates/rust-analyzer/src/cli/rustc_tests.rs
+++ b/crates/rust-analyzer/src/cli/rustc_tests.rs
@@ -7,6 +7,7 @@ use std::{cell::RefCell, fs::read_to_string, panic::AssertUnwindSafe, path::Path
use hir::{ChangeWithProcMacros, Crate};
use ide::{AnalysisHost, DiagnosticCode, DiagnosticsConfig};
+use ide_db::base_db;
use itertools::Either;
use paths::Utf8PathBuf;
use profile::StopWatch;
@@ -310,7 +311,7 @@ impl flags::RustcTests {
let tester = AssertUnwindSafe(&mut tester);
let p = p.clone();
move || {
- let _guard = stdx::panic_context::enter(p.display().to_string());
+ let _guard = base_db::DbPanicContext::enter(p.display().to_string());
{ tester }.0.test(p);
}
}) {
diff --git a/crates/rust-analyzer/src/cli/scip.rs b/crates/rust-analyzer/src/cli/scip.rs
index 2062294f80..d258c5d819 100644
--- a/crates/rust-analyzer/src/cli/scip.rs
+++ b/crates/rust-analyzer/src/cli/scip.rs
@@ -265,10 +265,10 @@ impl flags::Scip {
};
if !duplicate_symbol_errors.is_empty() {
- eprintln!("{}", DUPLICATE_SYMBOLS_MESSAGE);
+ eprintln!("{DUPLICATE_SYMBOLS_MESSAGE}");
for (source_location, symbol) in duplicate_symbol_errors {
- eprintln!("{}", source_location);
- eprintln!(" Duplicate symbol: {}", symbol);
+ eprintln!("{source_location}");
+ eprintln!(" Duplicate symbol: {symbol}");
eprintln!();
}
}
diff --git a/crates/rust-analyzer/src/cli/unresolved_references.rs b/crates/rust-analyzer/src/cli/unresolved_references.rs
index bca7c8a098..0362e13b88 100644
--- a/crates/rust-analyzer/src/cli/unresolved_references.rs
+++ b/crates/rust-analyzer/src/cli/unresolved_references.rs
@@ -15,11 +15,13 @@ impl flags::UnresolvedReferences {
pub fn run(self) -> anyhow::Result<()> {
const STACK_SIZE: usize = 1024 * 1024 * 8;
- let handle = stdx::thread::Builder::new(stdx::thread::ThreadIntent::LatencySensitive)
- .name("BIG_STACK_THREAD".into())
- .stack_size(STACK_SIZE)
- .spawn(|| self.run_())
- .unwrap();
+ let handle = stdx::thread::Builder::new(
+ stdx::thread::ThreadIntent::LatencySensitive,
+ "BIG_STACK_THREAD",
+ )
+ .stack_size(STACK_SIZE)
+ .spawn(|| self.run_())
+ .unwrap();
handle.join()
}
@@ -28,7 +30,7 @@ impl flags::UnresolvedReferences {
let root =
vfs::AbsPathBuf::assert_utf8(std::env::current_dir()?.join(&self.path)).normalize();
let config = crate::config::Config::new(
- root.clone(),
+ root,
lsp_types::ClientCapabilities::default(),
vec![],
None,
diff --git a/crates/rust-analyzer/src/command.rs b/crates/rust-analyzer/src/command.rs
index 0035d941e2..d6c80c399b 100644
--- a/crates/rust-analyzer/src/command.rs
+++ b/crates/rust-analyzer/src/command.rs
@@ -148,10 +148,10 @@ impl<T: Sized + Send + 'static> CommandHandle<T> {
let stderr = child.0.stderr().take().unwrap();
let actor = CargoActor::<T>::new(parser, sender, stdout, stderr);
- let thread = stdx::thread::Builder::new(stdx::thread::ThreadIntent::Worker)
- .name("CommandHandle".to_owned())
- .spawn(move || actor.run())
- .expect("failed to spawn thread");
+ let thread =
+ stdx::thread::Builder::new(stdx::thread::ThreadIntent::Worker, "CommandHandle")
+ .spawn(move || actor.run())
+ .expect("failed to spawn thread");
Ok(CommandHandle { program, arguments, current_dir, child, thread, _phantom: PhantomData })
}
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index dd827949a9..03e5b1f6f4 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -9,13 +9,14 @@ use cfg::{CfgAtom, CfgDiff};
use hir::Symbol;
use ide::{
AssistConfig, CallHierarchyConfig, CallableSnippets, CompletionConfig,
- CompletionFieldsToResolve, DiagnosticsConfig, ExprFillDefaultMode, GenericParameterHints,
- HighlightConfig, HighlightRelatedConfig, HoverConfig, HoverDocFormat, InlayFieldsToResolve,
- InlayHintsConfig, JoinLinesConfig, MemoryLayoutHoverConfig, MemoryLayoutHoverRenderKind,
- Snippet, SnippetScope, SourceRootId,
+ CompletionFieldsToResolve, DiagnosticsConfig, GenericParameterHints, HighlightConfig,
+ HighlightRelatedConfig, HoverConfig, HoverDocFormat, InlayFieldsToResolve, InlayHintsConfig,
+ JoinLinesConfig, MemoryLayoutHoverConfig, MemoryLayoutHoverRenderKind, Snippet, SnippetScope,
+ SourceRootId,
};
use ide_db::{
SnippetCap,
+ assists::ExprFillDefaultMode,
imports::insert_use::{ImportGranularity, InsertUseConfig, PrefixKind},
};
use itertools::{Either, Itertools};
@@ -1182,7 +1183,7 @@ impl ConfigChange {
source_root_map: Arc<FxHashMap<SourceRootId, SourceRootId>>,
) {
assert!(self.source_map_change.is_none());
- self.source_map_change = Some(source_root_map.clone());
+ self.source_map_change = Some(source_root_map);
}
}
@@ -1493,6 +1494,11 @@ impl Config {
term_search_fuel: self.assist_termSearch_fuel(source_root).to_owned() as u64,
term_search_borrowck: self.assist_termSearch_borrowcheck(source_root).to_owned(),
code_action_grouping: self.code_action_group(),
+ expr_fill_default: match self.assist_expressionFillDefault(source_root) {
+ ExprFillDefaultDef::Todo => ExprFillDefaultMode::Todo,
+ ExprFillDefaultDef::Default => ExprFillDefaultMode::Default,
+ ExprFillDefaultDef::Underscore => ExprFillDefaultMode::Underscore,
+ },
}
}
@@ -1577,6 +1583,7 @@ impl Config {
expr_fill_default: match self.assist_expressionFillDefault(source_root) {
ExprFillDefaultDef::Todo => ExprFillDefaultMode::Todo,
ExprFillDefaultDef::Default => ExprFillDefaultMode::Default,
+ ExprFillDefaultDef::Underscore => ExprFillDefaultMode::Underscore,
},
snippet_cap: self.snippet_cap(),
insert_use: self.insert_use_config(source_root),
@@ -2527,6 +2534,7 @@ where
enum ExprFillDefaultDef {
Todo,
Default,
+ Underscore,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
diff --git a/crates/rust-analyzer/src/diagnostics.rs b/crates/rust-analyzer/src/diagnostics.rs
index 9b1463b112..438a2a0ba1 100644
--- a/crates/rust-analyzer/src/diagnostics.rs
+++ b/crates/rust-analyzer/src/diagnostics.rs
@@ -5,7 +5,7 @@ use std::mem;
use cargo_metadata::PackageId;
use ide::FileId;
-use ide_db::FxHashMap;
+use ide_db::{FxHashMap, base_db::DbPanicContext};
use itertools::Itertools;
use rustc_hash::FxHashSet;
use stdx::iter_eq_by;
@@ -215,7 +215,7 @@ pub(crate) fn fetch_native_diagnostics(
kind: NativeDiagnosticsFetchKind,
) -> Vec<(FileId, Vec<lsp_types::Diagnostic>)> {
let _p = tracing::info_span!("fetch_native_diagnostics").entered();
- let _ctx = stdx::panic_context::enter("fetch_native_diagnostics".to_owned());
+ let _ctx = DbPanicContext::enter("fetch_native_diagnostics".to_owned());
// the diagnostics produced may point to different files not requested by the concrete request,
// put those into here and filter later
diff --git a/crates/rust-analyzer/src/discover.rs b/crates/rust-analyzer/src/discover.rs
index 67ddc41f3b..24c433610f 100644
--- a/crates/rust-analyzer/src/discover.rs
+++ b/crates/rust-analyzer/src/discover.rs
@@ -126,10 +126,8 @@ impl CargoParser<DiscoverProjectMessage> for DiscoverProjectParser {
Some(msg)
}
Err(err) => {
- let err = DiscoverProjectData::Error {
- error: format!("{:#?}\n{}", err, line),
- source: None,
- };
+ let err =
+ DiscoverProjectData::Error { error: format!("{err:#?}\n{line}"), source: None };
Some(DiscoverProjectMessage::new(err))
}
}
diff --git a/crates/rust-analyzer/src/flycheck.rs b/crates/rust-analyzer/src/flycheck.rs
index 2778b311e1..fc312439d5 100644
--- a/crates/rust-analyzer/src/flycheck.rs
+++ b/crates/rust-analyzer/src/flycheck.rs
@@ -133,10 +133,10 @@ impl FlycheckHandle {
let actor =
FlycheckActor::new(id, sender, config, sysroot_root, workspace_root, manifest_path);
let (sender, receiver) = unbounded::<StateChange>();
- let thread = stdx::thread::Builder::new(stdx::thread::ThreadIntent::Worker)
- .name("Flycheck".to_owned())
- .spawn(move || actor.run(receiver))
- .expect("failed to spawn thread");
+ let thread =
+ stdx::thread::Builder::new(stdx::thread::ThreadIntent::Worker, format!("Flycheck{id}"))
+ .spawn(move || actor.run(receiver))
+ .expect("failed to spawn thread");
FlycheckHandle { id, sender, _thread: thread }
}
diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs
index 820276e8ae..3b3b9c8797 100644
--- a/crates/rust-analyzer/src/global_state.rs
+++ b/crates/rust-analyzer/src/global_state.rs
@@ -511,7 +511,7 @@ impl GlobalState {
self.fetch_workspaces_queue.request_op(
format!("workspace vfs file change: {path}"),
- FetchWorkspaceRequest { path: Some(path.to_owned()), force_crate_graph_reload },
+ FetchWorkspaceRequest { path: Some(path), force_crate_graph_reload },
);
}
}
diff --git a/crates/rust-analyzer/src/handlers/dispatch.rs b/crates/rust-analyzer/src/handlers/dispatch.rs
index 3b76edf528..f04ada3889 100644
--- a/crates/rust-analyzer/src/handlers/dispatch.rs
+++ b/crates/rust-analyzer/src/handlers/dispatch.rs
@@ -4,7 +4,10 @@ use std::{
panic, thread,
};
-use ide_db::base_db::salsa::{self, Cancelled};
+use ide_db::base_db::{
+ DbPanicContext,
+ salsa::{self, Cancelled},
+};
use lsp_server::{ExtractError, Response, ResponseError};
use serde::{Serialize, de::DeserializeOwned};
use stdx::thread::ThreadIntent;
@@ -56,7 +59,7 @@ impl RequestDispatcher<'_> {
tracing::info_span!("request", method = ?req.method, "request_id" = ?req.id).entered();
tracing::debug!(?params);
let result = {
- let _pctx = stdx::panic_context::enter(panic_context);
+ let _pctx = DbPanicContext::enter(panic_context);
f(self.global_state, params)
};
if let Ok(response) = result_to_response::<R>(req.id, result) {
@@ -86,7 +89,7 @@ impl RequestDispatcher<'_> {
let global_state_snapshot = self.global_state.snapshot();
let result = panic::catch_unwind(move || {
- let _pctx = stdx::panic_context::enter(panic_context);
+ let _pctx = DbPanicContext::enter(panic_context);
f(global_state_snapshot, params)
});
@@ -257,7 +260,7 @@ impl RequestDispatcher<'_> {
}
.spawn(intent, move || {
let result = panic::catch_unwind(move || {
- let _pctx = stdx::panic_context::enter(panic_context);
+ let _pctx = DbPanicContext::enter(panic_context);
f(world, params)
});
match thread_result_to_response::<R>(req.id.clone(), result) {
@@ -421,11 +424,8 @@ impl NotificationDispatcher<'_> {
tracing::debug!(?params);
- let _pctx = stdx::panic_context::enter(format!(
- "\nversion: {}\nnotification: {}",
- version(),
- N::METHOD
- ));
+ let _pctx =
+ DbPanicContext::enter(format!("\nversion: {}\nnotification: {}", version(), N::METHOD));
if let Err(e) = f(self.global_state, params) {
tracing::error!(handler = %N::METHOD, error = %e, "notification handler failed");
}
diff --git a/crates/rust-analyzer/src/handlers/notification.rs b/crates/rust-analyzer/src/handlers/notification.rs
index a30e5d8ce2..b7373f274f 100644
--- a/crates/rust-analyzer/src/handlers/notification.rs
+++ b/crates/rust-analyzer/src/handlers/notification.rs
@@ -309,7 +309,7 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool {
let task = move || -> std::result::Result<(), Cancelled> {
if invocation_strategy_once {
let saved_file = vfs_path.as_path().map(|p| p.to_owned());
- world.flycheck[0].restart_workspace(saved_file.clone());
+ world.flycheck[0].restart_workspace(saved_file);
}
let target = TargetSpec::for_file(&world, file_id)?.and_then(|it| {
diff --git a/crates/rust-analyzer/src/handlers/request.rs b/crates/rust-analyzer/src/handlers/request.rs
index e08dd80973..69983a6762 100644
--- a/crates/rust-analyzer/src/handlers/request.rs
+++ b/crates/rust-analyzer/src/handlers/request.rs
@@ -2210,7 +2210,7 @@ fn runnable_action_links(
let label = update_test.label();
if let Some(r) = to_proto::make_update_runnable(&r, update_test) {
let update_command = to_proto::command::run_single(&r, label.unwrap().as_str());
- group.commands.push(to_command_link(update_command, r.label.clone()));
+ group.commands.push(to_command_link(update_command, r.label));
}
}
diff --git a/crates/rust-analyzer/tests/slow-tests/main.rs b/crates/rust-analyzer/tests/slow-tests/main.rs
index 96c2ceef6b..f6bcb5642c 100644
--- a/crates/rust-analyzer/tests/slow-tests/main.rs
+++ b/crates/rust-analyzer/tests/slow-tests/main.rs
@@ -1064,7 +1064,7 @@ fn main() {
),
work_done_progress_params: Default::default(),
});
- assert!(res.to_string().contains("&str"));
+ assert!(res.to_string().contains("&'static str"));
let res = server.send_request::<HoverRequest>(HoverParams {
text_document_position_params: TextDocumentPositionParams::new(
@@ -1073,7 +1073,7 @@ fn main() {
),
work_done_progress_params: Default::default(),
});
- assert!(res.to_string().contains("&str"));
+ assert!(res.to_string().contains("&'static str"));
server.request::<GotoTypeDefinition>(
GotoDefinitionParams {
diff --git a/crates/rust-analyzer/tests/slow-tests/ratoml.rs b/crates/rust-analyzer/tests/slow-tests/ratoml.rs
index 3f313b7e57..485f32281d 100644
--- a/crates/rust-analyzer/tests/slow-tests/ratoml.rs
+++ b/crates/rust-analyzer/tests/slow-tests/ratoml.rs
@@ -82,11 +82,8 @@ impl RatomlTest {
}
Url::parse(
- format!(
- "file://{}",
- path.into_string().to_owned().replace("C:\\", "/c:/").replace('\\', "/")
- )
- .as_str(),
+ format!("file://{}", path.into_string().replace("C:\\", "/c:/").replace('\\', "/"))
+ .as_str(),
)
.unwrap()
}
diff --git a/crates/rust-analyzer/tests/slow-tests/support.rs b/crates/rust-analyzer/tests/slow-tests/support.rs
index 3f97952365..2bebb0c1b9 100644
--- a/crates/rust-analyzer/tests/slow-tests/support.rs
+++ b/crates/rust-analyzer/tests/slow-tests/support.rs
@@ -202,7 +202,7 @@ impl Project<'_> {
}
let mut config = Config::new(
- tmp_dir_path.clone(),
+ tmp_dir_path,
lsp_types::ClientCapabilities {
workspace: Some(lsp_types::WorkspaceClientCapabilities {
did_change_watched_files: Some(
@@ -298,8 +298,7 @@ impl Server {
) -> Server {
let (connection, client) = Connection::memory();
- let _thread = stdx::thread::Builder::new(stdx::thread::ThreadIntent::Worker)
- .name("test server".to_owned())
+ let _thread = stdx::thread::Builder::new(stdx::thread::ThreadIntent::Worker, "test server")
.spawn(move || main_loop(config, connection).unwrap())
.expect("failed to spawn a thread");
diff --git a/crates/span/src/hygiene.rs b/crates/span/src/hygiene.rs
index b21102f2db..7bb88ac365 100644
--- a/crates/span/src/hygiene.rs
+++ b/crates/span/src/hygiene.rs
@@ -94,16 +94,11 @@ const _: () = {
}
}
impl zalsa_struct_::Configuration for SyntaxContext {
+ const LOCATION: salsa::plumbing::Location =
+ salsa::plumbing::Location { file: file!(), line: line!() };
const DEBUG_NAME: &'static str = "SyntaxContextData";
type Fields<'a> = SyntaxContextData;
type Struct<'a> = SyntaxContext;
- fn struct_from_id<'db>(id: salsa::Id) -> Self::Struct<'db> {
- SyntaxContext::from_salsa_id(id)
- }
- fn deref_struct(s: Self::Struct<'_>) -> salsa::Id {
- s.as_salsa_id()
- .expect("`SyntaxContext::deref_structs()` called on a root `SyntaxContext`")
- }
}
impl SyntaxContext {
pub fn ingredient<Db>(db: &Db) -> &zalsa_struct_::IngredientImpl<Self>
@@ -308,7 +303,7 @@ impl SyntaxContext {
}
#[cfg(feature = "salsa")]
-impl SyntaxContext {
+impl<'db> SyntaxContext {
const MAX_ID: u32 = salsa::Id::MAX_U32 - 1;
#[inline]
@@ -340,6 +335,60 @@ impl SyntaxContext {
// SAFETY: This comes from a Salsa ID.
unsafe { Self::from_u32(id.as_u32()) }
}
+
+ #[inline]
+ pub fn outer_mark(
+ self,
+ db: &'db dyn salsa::Database,
+ ) -> (Option<crate::MacroCallId>, Transparency) {
+ (self.outer_expn(db), self.outer_transparency(db))
+ }
+
+ #[inline]
+ pub fn normalize_to_macros_2_0(self, db: &'db dyn salsa::Database) -> SyntaxContext {
+ self.opaque(db)
+ }
+
+ #[inline]
+ pub fn normalize_to_macro_rules(self, db: &'db dyn salsa::Database) -> SyntaxContext {
+ self.opaque_and_semitransparent(db)
+ }
+
+ pub fn is_opaque(self, db: &'db dyn salsa::Database) -> bool {
+ !self.is_root() && self.outer_transparency(db).is_opaque()
+ }
+
+ pub fn remove_mark(
+ &mut self,
+ db: &'db dyn salsa::Database,
+ ) -> (Option<crate::MacroCallId>, Transparency) {
+ let data = *self;
+ *self = data.parent(db);
+ (data.outer_expn(db), data.outer_transparency(db))
+ }
+
+ pub fn marks(
+ self,
+ db: &'db dyn salsa::Database,
+ ) -> impl Iterator<Item = (crate::MacroCallId, Transparency)> {
+ let mut marks = self.marks_rev(db).collect::<Vec<_>>();
+ marks.reverse();
+ marks.into_iter()
+ }
+
+ pub fn marks_rev(
+ self,
+ db: &'db dyn salsa::Database,
+ ) -> impl Iterator<Item = (crate::MacroCallId, Transparency)> {
+ std::iter::successors(Some(self), move |&mark| Some(mark.parent(db)))
+ .take_while(|&it| !it.is_root())
+ .map(|ctx| {
+ let mark = ctx.outer_mark(db);
+ // We stop before taking the root expansion, as such we cannot encounter a `None` outer
+ // expansion, as only the ROOT has it.
+ (mark.0.unwrap(), mark.1)
+ })
+ }
}
#[cfg(not(feature = "salsa"))]
#[derive(Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
diff --git a/crates/stdx/src/thread.rs b/crates/stdx/src/thread.rs
index 6c742fecf1..a34e9e4a65 100644
--- a/crates/stdx/src/thread.rs
+++ b/crates/stdx/src/thread.rs
@@ -26,12 +26,12 @@ pub use pool::Pool;
/// # Panics
///
/// Panics if failed to spawn the thread.
-pub fn spawn<F, T>(intent: ThreadIntent, f: F) -> JoinHandle<T>
+pub fn spawn<F, T>(intent: ThreadIntent, name: String, f: F) -> JoinHandle<T>
where
F: (FnOnce() -> T) + Send + 'static,
T: Send + 'static,
{
- Builder::new(intent).spawn(f).expect("failed to spawn thread")
+ Builder::new(intent, name).spawn(f).expect("failed to spawn thread")
}
pub struct Builder {
@@ -42,13 +42,8 @@ pub struct Builder {
impl Builder {
#[must_use]
- pub fn new(intent: ThreadIntent) -> Self {
- Self { intent, inner: jod_thread::Builder::new(), allow_leak: false }
- }
-
- #[must_use]
- pub fn name(self, name: String) -> Self {
- Self { inner: self.inner.name(name), ..self }
+ pub fn new(intent: ThreadIntent, name: impl Into<String>) -> Self {
+ Self { intent, inner: jod_thread::Builder::new().name(name.into()), allow_leak: false }
}
#[must_use]
@@ -56,6 +51,8 @@ impl Builder {
Self { inner: self.inner.stack_size(size), ..self }
}
+ /// Whether dropping should detach the thread
+ /// instead of joining it.
#[must_use]
pub fn allow_leak(self, allow_leak: bool) -> Self {
Self { allow_leak, ..self }
diff --git a/crates/stdx/src/thread/pool.rs b/crates/stdx/src/thread/pool.rs
index 074cd747da..a8de4db624 100644
--- a/crates/stdx/src/thread/pool.rs
+++ b/crates/stdx/src/thread/pool.rs
@@ -50,10 +50,9 @@ impl Pool {
let extant_tasks = Arc::new(AtomicUsize::new(0));
let mut handles = Vec::with_capacity(threads);
- for _ in 0..threads {
- let handle = Builder::new(INITIAL_INTENT)
+ for idx in 0..threads {
+ let handle = Builder::new(INITIAL_INTENT, format!("Worker{idx}",))
.stack_size(STACK_SIZE)
- .name("Worker".into())
.allow_leak(true)
.spawn({
let extant_tasks = Arc::clone(&extant_tasks);
diff --git a/crates/syntax/Cargo.toml b/crates/syntax/Cargo.toml
index 510d44d009..4c7704803e 100644
--- a/crates/syntax/Cargo.toml
+++ b/crates/syntax/Cargo.toml
@@ -14,7 +14,7 @@ rust-version.workspace = true
[dependencies]
either.workspace = true
itertools.workspace = true
-rowan = "=0.15.15"
+rowan.workspace = true
rustc-hash.workspace = true
rustc-literal-escaper.workspace = true
smol_str.workspace = true
diff --git a/crates/syntax/rust.ungram b/crates/syntax/rust.ungram
index a0ae0d6858..10abca7d35 100644
--- a/crates/syntax/rust.ungram
+++ b/crates/syntax/rust.ungram
@@ -133,6 +133,7 @@ Meta =
SourceFile =
'#shebang'?
+ '#frontmatter'?
Attr*
Item*
diff --git a/crates/syntax/src/ast/generated/nodes.rs b/crates/syntax/src/ast/generated/nodes.rs
index 1243f6418f..cd9f4dba89 100644
--- a/crates/syntax/src/ast/generated/nodes.rs
+++ b/crates/syntax/src/ast/generated/nodes.rs
@@ -1525,6 +1525,10 @@ impl ast::HasDocComments for SourceFile {}
impl ast::HasModuleItem for SourceFile {}
impl SourceFile {
#[inline]
+ pub fn frontmatter_token(&self) -> Option<SyntaxToken> {
+ support::token(&self.syntax, T![frontmatter])
+ }
+ #[inline]
pub fn shebang_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![shebang]) }
}
pub struct Static {
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs
index d608a35eff..596f73e0b1 100644
--- a/crates/syntax/src/ast/make.rs
+++ b/crates/syntax/src/ast/make.rs
@@ -69,6 +69,9 @@ pub mod ext {
pub fn expr_todo() -> ast::Expr {
expr_from_text("todo!()")
}
+ pub fn expr_underscore() -> ast::Expr {
+ expr_from_text("_")
+ }
pub fn expr_ty_default(ty: &ast::Type) -> ast::Expr {
expr_from_text(&format!("{ty}::default()"))
}
@@ -706,7 +709,7 @@ pub fn wildcard_pat() -> ast::WildcardPat {
}
pub fn rest_pat() -> ast::RestPat {
- ast_from_text("fn f(..)")
+ ast_from_text("fn f() { let ..; }")
}
pub fn literal_pat(lit: &str) -> ast::LiteralPat {
@@ -785,8 +788,8 @@ pub fn record_pat_field(name_ref: ast::NameRef, pat: ast::Pat) -> ast::RecordPat
ast_from_text(&format!("fn f(S {{ {name_ref}: {pat} }}: ()))"))
}
-pub fn record_pat_field_shorthand(name_ref: ast::NameRef) -> ast::RecordPatField {
- ast_from_text(&format!("fn f(S {{ {name_ref} }}: ()))"))
+pub fn record_pat_field_shorthand(pat: ast::Pat) -> ast::RecordPatField {
+ ast_from_text(&format!("fn f(S {{ {pat} }}: ()))"))
}
/// Returns a `IdentPat` if the path has just one segment, a `PathPat` otherwise.
@@ -798,16 +801,38 @@ pub fn path_pat(path: ast::Path) -> ast::Pat {
}
/// Returns a `Pat` if the path has just one segment, an `OrPat` otherwise.
-pub fn or_pat(pats: impl IntoIterator<Item = ast::Pat>, leading_pipe: bool) -> ast::Pat {
+///
+/// Invariant: `pats` must be length > 1.
+pub fn or_pat(pats: impl IntoIterator<Item = ast::Pat>, leading_pipe: bool) -> ast::OrPat {
let leading_pipe = if leading_pipe { "| " } else { "" };
let pats = pats.into_iter().join(" | ");
return from_text(&format!("{leading_pipe}{pats}"));
- fn from_text(text: &str) -> ast::Pat {
+ fn from_text(text: &str) -> ast::OrPat {
ast_from_text(&format!("fn f({text}: ())"))
}
}
+pub fn box_pat(pat: ast::Pat) -> ast::BoxPat {
+ ast_from_text(&format!("fn f(box {pat}: ())"))
+}
+
+pub fn paren_pat(pat: ast::Pat) -> ast::ParenPat {
+ ast_from_text(&format!("fn f(({pat}): ())"))
+}
+
+pub fn range_pat(start: Option<ast::Pat>, end: Option<ast::Pat>) -> ast::RangePat {
+ ast_from_text(&format!(
+ "fn f({}..{}: ())",
+ start.map(|e| e.to_string()).unwrap_or_default(),
+ end.map(|e| e.to_string()).unwrap_or_default()
+ ))
+}
+
+pub fn ref_pat(pat: ast::Pat) -> ast::RefPat {
+ ast_from_text(&format!("fn f(&{pat}: ())"))
+}
+
pub fn match_arm(pat: ast::Pat, guard: Option<ast::MatchGuard>, expr: ast::Expr) -> ast::MatchArm {
return match guard {
Some(guard) => from_text(&format!("{pat} {guard} => {expr}")),
diff --git a/crates/syntax/src/ast/syntax_factory/constructors.rs b/crates/syntax/src/ast/syntax_factory/constructors.rs
index 1854000d3d..8dee3964d4 100644
--- a/crates/syntax/src/ast/syntax_factory/constructors.rs
+++ b/crates/syntax/src/ast/syntax_factory/constructors.rs
@@ -3,7 +3,7 @@ use crate::{
AstNode, NodeOrToken, SyntaxKind, SyntaxNode, SyntaxToken,
ast::{
self, HasArgList, HasGenericArgs, HasGenericParams, HasLoopBody, HasName, HasTypeBounds,
- HasVisibility, make,
+ HasVisibility, RangeItem, make,
},
syntax_editor::SyntaxMappingBuilder,
};
@@ -107,6 +107,20 @@ impl SyntaxFactory {
ast
}
+ pub fn use_(&self, visibility: Option<ast::Visibility>, use_tree: ast::UseTree) -> ast::Use {
+ make::use_(visibility, use_tree).clone_for_update()
+ }
+
+ pub fn use_tree(
+ &self,
+ path: ast::Path,
+ use_tree_list: Option<ast::UseTreeList>,
+ alias: Option<ast::Rename>,
+ add_star: bool,
+ ) -> ast::UseTree {
+ make::use_tree(path, use_tree_list, alias, add_star).clone_for_update()
+ }
+
pub fn path_unqualified(&self, segment: ast::PathSegment) -> ast::Path {
let ast = make::path_unqualified(segment.clone()).clone_for_update();
@@ -254,12 +268,12 @@ impl SyntaxFactory {
ast
}
- pub fn record_pat_field_shorthand(&self, name_ref: ast::NameRef) -> ast::RecordPatField {
- let ast = make::record_pat_field_shorthand(name_ref.clone()).clone_for_update();
+ pub fn record_pat_field_shorthand(&self, pat: ast::Pat) -> ast::RecordPatField {
+ let ast = make::record_pat_field_shorthand(pat.clone()).clone_for_update();
if let Some(mut mapping) = self.mappings() {
let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
- builder.map_node(name_ref.syntax().clone(), ast.pat().unwrap().syntax().clone());
+ builder.map_node(pat.syntax().clone(), ast.pat().unwrap().syntax().clone());
builder.finish(&mut mapping);
}
@@ -294,6 +308,76 @@ impl SyntaxFactory {
make::rest_pat().clone_for_update()
}
+ pub fn or_pat(
+ &self,
+ pats: impl IntoIterator<Item = ast::Pat>,
+ leading_pipe: bool,
+ ) -> ast::OrPat {
+ let (pats, input) = iterator_input(pats);
+ let ast = make::or_pat(pats, leading_pipe).clone_for_update();
+
+ if let Some(mut mapping) = self.mappings() {
+ let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+ builder.map_children(input, ast.pats().map(|it| it.syntax().clone()));
+ builder.finish(&mut mapping);
+ }
+
+ ast
+ }
+
+ pub fn box_pat(&self, pat: ast::Pat) -> ast::BoxPat {
+ let ast = make::box_pat(pat.clone()).clone_for_update();
+
+ if let Some(mut mapping) = self.mappings() {
+ let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+ builder.map_node(pat.syntax().clone(), ast.pat().unwrap().syntax().clone());
+ builder.finish(&mut mapping);
+ }
+
+ ast
+ }
+
+ pub fn paren_pat(&self, pat: ast::Pat) -> ast::ParenPat {
+ let ast = make::paren_pat(pat.clone()).clone_for_update();
+
+ if let Some(mut mapping) = self.mappings() {
+ let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+ builder.map_node(pat.syntax().clone(), ast.pat().unwrap().syntax().clone());
+ builder.finish(&mut mapping);
+ }
+
+ ast
+ }
+
+ pub fn range_pat(&self, start: Option<ast::Pat>, end: Option<ast::Pat>) -> ast::RangePat {
+ let ast = make::range_pat(start.clone(), end.clone()).clone_for_update();
+
+ if let Some(mut mapping) = self.mappings() {
+ let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+ if let Some(start) = start {
+ builder.map_node(start.syntax().clone(), ast.start().unwrap().syntax().clone());
+ }
+ if let Some(end) = end {
+ builder.map_node(end.syntax().clone(), ast.end().unwrap().syntax().clone());
+ }
+ builder.finish(&mut mapping);
+ }
+
+ ast
+ }
+
+ pub fn ref_pat(&self, pat: ast::Pat) -> ast::RefPat {
+ let ast = make::ref_pat(pat.clone()).clone_for_update();
+
+ if let Some(mut mapping) = self.mappings() {
+ let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+ builder.map_node(pat.syntax().clone(), ast.pat().unwrap().syntax().clone());
+ builder.finish(&mut mapping);
+ }
+
+ ast
+ }
+
pub fn block_expr(
&self,
statements: impl IntoIterator<Item = ast::Stmt>,
@@ -673,6 +757,38 @@ impl SyntaxFactory {
ast
}
+ pub fn let_else_stmt(
+ &self,
+ pattern: ast::Pat,
+ ty: Option<ast::Type>,
+ initializer: ast::Expr,
+ diverging: ast::BlockExpr,
+ ) -> ast::LetStmt {
+ let ast = make::let_else_stmt(
+ pattern.clone(),
+ ty.clone(),
+ initializer.clone(),
+ diverging.clone(),
+ )
+ .clone_for_update();
+
+ if let Some(mut mapping) = self.mappings() {
+ let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+ builder.map_node(pattern.syntax().clone(), ast.pat().unwrap().syntax().clone());
+ if let Some(input) = ty {
+ builder.map_node(input.syntax().clone(), ast.ty().unwrap().syntax().clone());
+ }
+ builder.map_node(
+ initializer.syntax().clone(),
+ ast.initializer().unwrap().syntax().clone(),
+ );
+ builder.map_node(diverging.syntax().clone(), ast.let_else().unwrap().syntax().clone());
+ builder.finish(&mut mapping);
+ }
+
+ ast
+ }
+
pub fn type_arg(&self, ty: ast::Type) -> ast::TypeArg {
let ast = make::type_arg(ty.clone()).clone_for_update();
diff --git a/crates/syntax/src/syntax_editor.rs b/crates/syntax/src/syntax_editor.rs
index 58200189c4..31caf618be 100644
--- a/crates/syntax/src/syntax_editor.rs
+++ b/crates/syntax/src/syntax_editor.rs
@@ -20,6 +20,7 @@ mod edit_algo;
mod edits;
mod mapping;
+pub use edits::Removable;
pub use mapping::{SyntaxMapping, SyntaxMappingBuilder};
#[derive(Debug)]
diff --git a/crates/syntax/src/syntax_editor/edit_algo.rs b/crates/syntax/src/syntax_editor/edit_algo.rs
index 6a9c88b55d..01c1f0d49b 100644
--- a/crates/syntax/src/syntax_editor/edit_algo.rs
+++ b/crates/syntax/src/syntax_editor/edit_algo.rs
@@ -391,7 +391,7 @@ fn report_intersecting_changes(
fn to_owning_node(element: &SyntaxElement) -> SyntaxNode {
match element {
SyntaxElement::Node(node) => node.clone(),
- SyntaxElement::Token(token) => token.parent().unwrap().clone(),
+ SyntaxElement::Token(token) => token.parent().unwrap(),
}
}
diff --git a/crates/syntax/src/syntax_editor/edits.rs b/crates/syntax/src/syntax_editor/edits.rs
index 350cb3e254..d66ea8aa28 100644
--- a/crates/syntax/src/syntax_editor/edits.rs
+++ b/crates/syntax/src/syntax_editor/edits.rs
@@ -1,7 +1,8 @@
//! Structural editing for ast using `SyntaxEditor`
use crate::{
- Direction, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken, T,
+ AstToken, Direction, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken, T,
+ algo::neighbor,
ast::{
self, AstNode, Fn, GenericParam, HasGenericParams, HasName, edit::IndentLevel, make,
syntax_factory::SyntaxFactory,
@@ -143,6 +144,53 @@ fn normalize_ws_between_braces(editor: &mut SyntaxEditor, node: &SyntaxNode) ->
Some(())
}
+pub trait Removable: AstNode {
+ fn remove(&self, editor: &mut SyntaxEditor);
+}
+
+impl Removable for ast::Use {
+ fn remove(&self, editor: &mut SyntaxEditor) {
+ let make = SyntaxFactory::without_mappings();
+
+ let next_ws = self
+ .syntax()
+ .next_sibling_or_token()
+ .and_then(|it| it.into_token())
+ .and_then(ast::Whitespace::cast);
+ if let Some(next_ws) = next_ws {
+ let ws_text = next_ws.syntax().text();
+ if let Some(rest) = ws_text.strip_prefix('\n') {
+ if rest.is_empty() {
+ editor.delete(next_ws.syntax());
+ } else {
+ editor.replace(next_ws.syntax(), make.whitespace(rest));
+ }
+ }
+ }
+
+ editor.delete(self.syntax());
+ }
+}
+
+impl Removable for ast::UseTree {
+ fn remove(&self, editor: &mut SyntaxEditor) {
+ for dir in [Direction::Next, Direction::Prev] {
+ if let Some(next_use_tree) = neighbor(self, dir) {
+ let separators = self
+ .syntax()
+ .siblings_with_tokens(dir)
+ .skip(1)
+ .take_while(|it| it.as_node() != Some(next_use_tree.syntax()));
+ for sep in separators {
+ editor.delete(sep);
+ }
+ break;
+ }
+ }
+ editor.delete(self.syntax());
+ }
+}
+
#[cfg(test)]
mod tests {
use parser::Edition;
diff --git a/crates/test-fixture/src/lib.rs b/crates/test-fixture/src/lib.rs
index 2f379d419e..f6ca5ab6c8 100644
--- a/crates/test-fixture/src/lib.rs
+++ b/crates/test-fixture/src/lib.rs
@@ -353,7 +353,7 @@ impl ChangeFixture {
)]),
CrateOrigin::Local { repo: None, name: None },
true,
- proc_macro_cwd.clone(),
+ proc_macro_cwd,
crate_ws_data,
);
proc_macros.insert(proc_macros_crate, Ok(proc_macro));
diff --git a/crates/tt/src/lib.rs b/crates/tt/src/lib.rs
index 36ccb67f3b..1dbc07c092 100644
--- a/crates/tt/src/lib.rs
+++ b/crates/tt/src/lib.rs
@@ -728,9 +728,9 @@ fn print_debug_subtree<S: fmt::Debug>(
};
write!(f, "{align}SUBTREE {delim} ",)?;
- write!(f, "{:#?}", open)?;
+ write!(f, "{open:#?}")?;
write!(f, " ")?;
- write!(f, "{:#?}", close)?;
+ write!(f, "{close:#?}")?;
for child in iter {
writeln!(f)?;
print_debug_token(f, level + 1, child)?;
@@ -855,7 +855,7 @@ impl<S> fmt::Display for Literal<S> {
}
}?;
if let Some(suffix) = &self.suffix {
- write!(f, "{}", suffix)?;
+ write!(f, "{suffix}")?;
}
Ok(())
}
diff --git a/crates/vfs-notify/src/lib.rs b/crates/vfs-notify/src/lib.rs
index e918fd0887..a03337dbc5 100644
--- a/crates/vfs-notify/src/lib.rs
+++ b/crates/vfs-notify/src/lib.rs
@@ -38,8 +38,7 @@ impl loader::Handle for NotifyHandle {
fn spawn(sender: loader::Sender) -> NotifyHandle {
let actor = NotifyActor::new(sender);
let (sender, receiver) = unbounded::<Message>();
- let thread = stdx::thread::Builder::new(stdx::thread::ThreadIntent::Worker)
- .name("VfsLoader".to_owned())
+ let thread = stdx::thread::Builder::new(stdx::thread::ThreadIntent::Worker, "VfsLoader")
.spawn(move || actor.run(receiver))
.expect("failed to spawn thread");
NotifyHandle { sender, _thread: thread }
diff --git a/rust-version b/rust-version
index 09c127f6bc..90e4d65bf9 100644
--- a/rust-version
+++ b/rust-version
@@ -1 +1 @@
-21079f53a359d9fc82668d4175d49dafdb600563
+6e23095adf9209614a45f7f75fea36dad7b92afb
diff --git a/xtask/src/codegen/grammar.rs b/xtask/src/codegen/grammar.rs
index 82df78c1a8..824b38fc87 100644
--- a/xtask/src/codegen/grammar.rs
+++ b/xtask/src/codegen/grammar.rs
@@ -414,7 +414,7 @@ fn generate_nodes(kinds: KindsSrc, grammar: &AstSrc) -> String {
.map(|kind| to_pascal_case(kind))
.filter(|name| !defined_nodes.iter().any(|&it| it == name))
{
- eprintln!("Warning: node {} not defined in AST source", node);
+ eprintln!("Warning: node {node} not defined in AST source");
drop(node);
}
@@ -670,6 +670,7 @@ fn generate_syntax_kinds(grammar: KindsSrc) -> String {
[ident] => { $crate::SyntaxKind::IDENT };
[string] => { $crate::SyntaxKind::STRING };
[shebang] => { $crate::SyntaxKind::SHEBANG };
+ [frontmatter] => { $crate::SyntaxKind::FRONTMATTER };
}
impl ::core::marker::Copy for SyntaxKind {}
diff --git a/xtask/src/codegen/parser_inline_tests.rs b/xtask/src/codegen/parser_inline_tests.rs
index f3b786b9d8..ae53771fe8 100644
--- a/xtask/src/codegen/parser_inline_tests.rs
+++ b/xtask/src/codegen/parser_inline_tests.rs
@@ -159,7 +159,7 @@ fn collect_tests(s: &str) -> Vec<Test> {
(name.to_owned(), Some(edition.to_owned()))
}
[name] => (name.to_owned(), None),
- _ => panic!("invalid test name: {:?}", name),
+ _ => panic!("invalid test name: {name:?}"),
};
let text: String = edition
.as_ref()
@@ -212,7 +212,7 @@ fn existing_tests(dir: &Path, ok: TestKind) -> Result<HashMap<String, (PathBuf,
text.lines().next().and_then(|it| it.strip_prefix("// ")).map(ToOwned::to_owned);
let test = Test { name: name.clone(), text, kind: ok, edition };
if let Some(old) = res.insert(name, (path, test)) {
- println!("Duplicate test: {:?}", old);
+ println!("Duplicate test: {old:?}");
}
}
}
diff --git a/xtask/src/dist.rs b/xtask/src/dist.rs
index b3d6f06b07..dbfecdbe11 100644
--- a/xtask/src/dist.rs
+++ b/xtask/src/dist.rs
@@ -1,9 +1,6 @@
use anyhow::Context;
use flate2::{Compression, write::GzEncoder};
-use std::env::consts::EXE_EXTENSION;
-use std::ffi::OsStr;
use std::{
- env,
fs::File,
io::{self, BufWriter},
path::{Path, PathBuf},
@@ -12,11 +9,11 @@ use time::OffsetDateTime;
use xshell::{Cmd, Shell, cmd};
use zip::{DateTime, ZipWriter, write::SimpleFileOptions};
-use crate::flags::PgoTrainingCrate;
use crate::{
date_iso,
- flags::{self, Malloc},
+ flags::{self, Malloc, PgoTrainingCrate},
project_root,
+ util::detect_target,
};
const VERSION_STABLE: &str = "0.3";
@@ -28,7 +25,7 @@ impl flags::Dist {
let stable = sh.var("GITHUB_REF").unwrap_or_default().as_str() == "refs/heads/release";
let project_root = project_root();
- let target = Target::get(&project_root);
+ let target = Target::get(&project_root, sh);
let allocator = self.allocator();
let dist = project_root.join("dist");
sh.remove_path(&dist)?;
@@ -113,9 +110,9 @@ fn dist_server(
let command = if linux_target && zig { "zigbuild" } else { "build" };
let pgo_profile = if let Some(train_crate) = pgo {
- Some(gather_pgo_profile(
+ Some(crate::pgo::gather_pgo_profile(
sh,
- build_command(sh, command, &target_name, features),
+ crate::pgo::build_command(sh, command, &target_name, features),
&target_name,
train_crate,
)?)
@@ -151,85 +148,6 @@ fn build_command<'a>(
)
}
-/// Decorates `ra_build_cmd` to add PGO instrumentation, and then runs the PGO instrumented
-/// Rust Analyzer on itself to gather a PGO profile.
-fn gather_pgo_profile<'a>(
- sh: &'a Shell,
- ra_build_cmd: Cmd<'a>,
- target: &str,
- train_crate: PgoTrainingCrate,
-) -> anyhow::Result<PathBuf> {
- let pgo_dir = std::path::absolute("rust-analyzer-pgo")?;
- // Clear out any stale profiles
- if pgo_dir.is_dir() {
- std::fs::remove_dir_all(&pgo_dir)?;
- }
- std::fs::create_dir_all(&pgo_dir)?;
-
- // Figure out a path to `llvm-profdata`
- let target_libdir = cmd!(sh, "rustc --print=target-libdir")
- .read()
- .context("cannot resolve target-libdir from rustc")?;
- let target_bindir = PathBuf::from(target_libdir).parent().unwrap().join("bin");
- let llvm_profdata = target_bindir.join("llvm-profdata").with_extension(EXE_EXTENSION);
-
- // Build RA with PGO instrumentation
- let cmd_gather =
- ra_build_cmd.env("RUSTFLAGS", format!("-Cprofile-generate={}", pgo_dir.to_str().unwrap()));
- cmd_gather.run().context("cannot build rust-analyzer with PGO instrumentation")?;
-
- let (train_path, label) = match &train_crate {
- PgoTrainingCrate::RustAnalyzer => (PathBuf::from("."), "itself"),
- PgoTrainingCrate::GitHub(repo) => {
- (download_crate_for_training(sh, &pgo_dir, repo)?, repo.as_str())
- }
- };
-
- // Run RA either on itself or on a downloaded crate
- eprintln!("Training RA on {label}...");
- cmd!(
- sh,
- "target/{target}/release/rust-analyzer analysis-stats -q --run-all-ide-things {train_path}"
- )
- .run()
- .context("cannot generate PGO profiles")?;
-
- // Merge profiles into a single file
- let merged_profile = pgo_dir.join("merged.profdata");
- let profile_files = std::fs::read_dir(pgo_dir)?.filter_map(|entry| {
- let entry = entry.ok()?;
- if entry.path().extension() == Some(OsStr::new("profraw")) {
- Some(entry.path().to_str().unwrap().to_owned())
- } else {
- None
- }
- });
- cmd!(sh, "{llvm_profdata} merge {profile_files...} -o {merged_profile}").run().context(
- "cannot merge PGO profiles. Do you have the rustup `llvm-tools` component installed?",
- )?;
-
- Ok(merged_profile)
-}
-
-/// Downloads a crate from GitHub, stores it into `pgo_dir` and returns a path to it.
-fn download_crate_for_training(sh: &Shell, pgo_dir: &Path, repo: &str) -> anyhow::Result<PathBuf> {
- let mut it = repo.splitn(2, '@');
- let repo = it.next().unwrap();
- let revision = it.next();
-
- // FIXME: switch to `--revision` here around 2035 or so
- let revision =
- if let Some(revision) = revision { &["--branch", revision] as &[&str] } else { &[] };
-
- let normalized_path = repo.replace("/", "-");
- let target_path = pgo_dir.join(normalized_path);
- cmd!(sh, "git clone --depth 1 https://github.com/{repo} {revision...} {target_path}")
- .run()
- .with_context(|| "cannot download PGO training crate from {repo}")?;
-
- Ok(target_path)
-}
-
fn gzip(src_path: &Path, dest_path: &Path) -> anyhow::Result<()> {
let mut encoder = GzEncoder::new(File::create(dest_path)?, Compression::best());
let mut input = io::BufReader::new(File::open(src_path)?);
@@ -283,21 +201,8 @@ struct Target {
}
impl Target {
- fn get(project_root: &Path) -> Self {
- let name = match env::var("RA_TARGET") {
- Ok(target) => target,
- _ => {
- if cfg!(target_os = "linux") {
- "x86_64-unknown-linux-gnu".to_owned()
- } else if cfg!(target_os = "windows") {
- "x86_64-pc-windows-msvc".to_owned()
- } else if cfg!(target_os = "macos") {
- "x86_64-apple-darwin".to_owned()
- } else {
- panic!("Unsupported OS, maybe try setting RA_TARGET")
- }
- }
- };
+ fn get(project_root: &Path, sh: &Shell) -> Self {
+ let name = detect_target(sh);
let (name, libc_suffix) = match name.split_once('.') {
Some((l, r)) => (l.to_owned(), Some(r.to_owned())),
None => (name, None),
diff --git a/xtask/src/flags.rs b/xtask/src/flags.rs
index 700806d178..2fd471b35c 100644
--- a/xtask/src/flags.rs
+++ b/xtask/src/flags.rs
@@ -4,6 +4,25 @@ use std::{fmt, str::FromStr};
use crate::install::{ClientOpt, ProcMacroServerOpt, ServerOpt};
+#[derive(Debug, Clone)]
+pub enum PgoTrainingCrate {
+ // Use RA's own sources for PGO training
+ RustAnalyzer,
+ // Download a Rust crate from `https://github.com/{0}` and use it for PGO training.
+ GitHub(String),
+}
+
+impl FromStr for PgoTrainingCrate {
+ type Err = String;
+
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ match s {
+ "rust-analyzer" => Ok(Self::RustAnalyzer),
+ url => Ok(Self::GitHub(url.to_owned())),
+ }
+ }
+}
+
xflags::xflags! {
src "./src/flags.rs"
@@ -29,6 +48,9 @@ xflags::xflags! {
/// build in release with debug info set to 2.
optional --dev-rel
+
+ /// Apply PGO optimizations
+ optional --pgo pgo: PgoTrainingCrate
}
cmd fuzz-tests {}
@@ -110,17 +132,15 @@ pub enum XtaskCmd {
}
#[derive(Debug)]
-pub struct Tidy {}
-
-#[derive(Debug)]
pub struct Install {
pub client: bool,
pub code_bin: Option<String>,
pub server: bool,
- pub proc_macro_server: bool,
pub mimalloc: bool,
pub jemalloc: bool,
+ pub proc_macro_server: bool,
pub dev_rel: bool,
+ pub pgo: Option<PgoTrainingCrate>,
}
#[derive(Debug)]
@@ -144,25 +164,6 @@ pub struct RustcPush {
}
#[derive(Debug)]
-pub enum PgoTrainingCrate {
- // Use RA's own sources for PGO training
- RustAnalyzer,
- // Download a Rust crate from `https://github.com/{0}` and use it for PGO training.
- GitHub(String),
-}
-
-impl FromStr for PgoTrainingCrate {
- type Err = String;
-
- fn from_str(s: &str) -> Result<Self, Self::Err> {
- match s {
- "rust-analyzer" => Ok(Self::RustAnalyzer),
- url => Ok(Self::GitHub(url.to_owned())),
- }
- }
-}
-
-#[derive(Debug)]
pub struct Dist {
pub mimalloc: bool,
pub jemalloc: bool,
@@ -195,6 +196,9 @@ pub struct Codegen {
pub check: bool,
}
+#[derive(Debug)]
+pub struct Tidy;
+
impl Xtask {
#[allow(dead_code)]
pub fn from_env_or_exit() -> Self {
@@ -324,7 +328,7 @@ impl Install {
} else {
Malloc::System
};
- Some(ServerOpt { malloc, dev_rel: self.dev_rel })
+ Some(ServerOpt { malloc, dev_rel: self.dev_rel, pgo: self.pgo.clone() })
}
pub(crate) fn proc_macro_server(&self) -> Option<ProcMacroServerOpt> {
if !self.proc_macro_server {
diff --git a/xtask/src/install.rs b/xtask/src/install.rs
index 4e2093f069..f0cc445dfa 100644
--- a/xtask/src/install.rs
+++ b/xtask/src/install.rs
@@ -5,7 +5,10 @@ use std::{env, path::PathBuf, str};
use anyhow::{Context, bail, format_err};
use xshell::{Shell, cmd};
-use crate::flags::{self, Malloc};
+use crate::{
+ flags::{self, Malloc, PgoTrainingCrate},
+ util::detect_target,
+};
impl flags::Install {
pub(crate) fn run(self, sh: &Shell) -> anyhow::Result<()> {
@@ -35,6 +38,7 @@ const VS_CODES: &[&str] = &["code", "code-exploration", "code-insiders", "codium
pub(crate) struct ServerOpt {
pub(crate) malloc: Malloc,
pub(crate) dev_rel: bool,
+ pub(crate) pgo: Option<PgoTrainingCrate>,
}
pub(crate) struct ProcMacroServerOpt {
@@ -135,21 +139,33 @@ fn install_server(sh: &Shell, opts: ServerOpt) -> anyhow::Result<()> {
let features = opts.malloc.to_features();
let profile = if opts.dev_rel { "dev-rel" } else { "release" };
- let cmd = cmd!(
+ let mut install_cmd = cmd!(
sh,
"cargo install --path crates/rust-analyzer --profile={profile} --locked --force --features force-always-assert {features...}"
);
- cmd.run()?;
+
+ if let Some(train_crate) = opts.pgo {
+ let build_cmd = cmd!(
+ sh,
+ "cargo build --manifest-path ./crates/rust-analyzer/Cargo.toml --bin rust-analyzer --profile={profile} --locked --features force-always-assert {features...}"
+ );
+
+ let target = detect_target(sh);
+ let profile = crate::pgo::gather_pgo_profile(sh, build_cmd, &target, train_crate)?;
+ install_cmd = crate::pgo::apply_pgo_to_cmd(install_cmd, &profile);
+ }
+
+ install_cmd.run()?;
Ok(())
}
fn install_proc_macro_server(sh: &Shell, opts: ProcMacroServerOpt) -> anyhow::Result<()> {
let profile = if opts.dev_rel { "dev-rel" } else { "release" };
- let cmd = cmd!(
+ cmd!(
sh,
"cargo +nightly install --path crates/proc-macro-srv-cli --profile={profile} --locked --force --features sysroot-abi"
- );
- cmd.run()?;
+ ).run()?;
+
Ok(())
}
diff --git a/xtask/src/main.rs b/xtask/src/main.rs
index 52ea896c73..aaa8d0e1d4 100644
--- a/xtask/src/main.rs
+++ b/xtask/src/main.rs
@@ -22,6 +22,7 @@ mod codegen;
mod dist;
mod install;
mod metrics;
+mod pgo;
mod publish;
mod release;
mod tidy;
diff --git a/xtask/src/pgo.rs b/xtask/src/pgo.rs
new file mode 100644
index 0000000000..7f7b3311d9
--- /dev/null
+++ b/xtask/src/pgo.rs
@@ -0,0 +1,105 @@
+//! PGO (Profile-Guided Optimization) utilities.
+
+use anyhow::Context;
+use std::env::consts::EXE_EXTENSION;
+use std::ffi::OsStr;
+use std::path::{Path, PathBuf};
+use xshell::{Cmd, Shell, cmd};
+
+use crate::flags::PgoTrainingCrate;
+
+/// Decorates `ra_build_cmd` to add PGO instrumentation, and then runs the PGO instrumented
+/// Rust Analyzer on itself to gather a PGO profile.
+pub(crate) fn gather_pgo_profile<'a>(
+ sh: &'a Shell,
+ ra_build_cmd: Cmd<'a>,
+ target: &str,
+ train_crate: PgoTrainingCrate,
+) -> anyhow::Result<PathBuf> {
+ let pgo_dir = std::path::absolute("rust-analyzer-pgo")?;
+ // Clear out any stale profiles
+ if pgo_dir.is_dir() {
+ std::fs::remove_dir_all(&pgo_dir)?;
+ }
+ std::fs::create_dir_all(&pgo_dir)?;
+
+ // Figure out a path to `llvm-profdata`
+ let target_libdir = cmd!(sh, "rustc --print=target-libdir")
+ .read()
+ .context("cannot resolve target-libdir from rustc")?;
+ let target_bindir = PathBuf::from(target_libdir).parent().unwrap().join("bin");
+ let llvm_profdata = target_bindir.join("llvm-profdata").with_extension(EXE_EXTENSION);
+
+ // Build RA with PGO instrumentation
+ let cmd_gather =
+ ra_build_cmd.env("RUSTFLAGS", format!("-Cprofile-generate={}", pgo_dir.to_str().unwrap()));
+ cmd_gather.run().context("cannot build rust-analyzer with PGO instrumentation")?;
+
+ let (train_path, label) = match &train_crate {
+ PgoTrainingCrate::RustAnalyzer => (PathBuf::from("."), "itself"),
+ PgoTrainingCrate::GitHub(repo) => {
+ (download_crate_for_training(sh, &pgo_dir, repo)?, repo.as_str())
+ }
+ };
+
+ // Run RA either on itself or on a downloaded crate
+ eprintln!("Training RA on {label}...");
+ cmd!(
+ sh,
+ "target/{target}/release/rust-analyzer analysis-stats -q --run-all-ide-things {train_path}"
+ )
+ .run()
+ .context("cannot generate PGO profiles")?;
+
+ // Merge profiles into a single file
+ let merged_profile = pgo_dir.join("merged.profdata");
+ let profile_files = std::fs::read_dir(pgo_dir)?.filter_map(|entry| {
+ let entry = entry.ok()?;
+ if entry.path().extension() == Some(OsStr::new("profraw")) {
+ Some(entry.path().to_str().unwrap().to_owned())
+ } else {
+ None
+ }
+ });
+ cmd!(sh, "{llvm_profdata} merge {profile_files...} -o {merged_profile}").run().context(
+ "cannot merge PGO profiles. Do you have the rustup `llvm-tools` component installed?",
+ )?;
+
+ Ok(merged_profile)
+}
+
+/// Downloads a crate from GitHub, stores it into `pgo_dir` and returns a path to it.
+fn download_crate_for_training(sh: &Shell, pgo_dir: &Path, repo: &str) -> anyhow::Result<PathBuf> {
+ let mut it = repo.splitn(2, '@');
+ let repo = it.next().unwrap();
+ let revision = it.next();
+
+ // FIXME: switch to `--revision` here around 2035 or so
+ let revision =
+ if let Some(revision) = revision { &["--branch", revision] as &[&str] } else { &[] };
+
+ let normalized_path = repo.replace("/", "-");
+ let target_path = pgo_dir.join(normalized_path);
+ cmd!(sh, "git clone --depth 1 https://github.com/{repo} {revision...} {target_path}")
+ .run()
+ .with_context(|| "cannot download PGO training crate from {repo}")?;
+
+ Ok(target_path)
+}
+
+/// Helper function to create a build command for rust-analyzer
+pub(crate) fn build_command<'a>(
+ sh: &'a Shell,
+ command: &str,
+ target_name: &str,
+ features: &[&str],
+) -> Cmd<'a> {
+ cmd!(
+ sh,
+ "cargo {command} --manifest-path ./crates/rust-analyzer/Cargo.toml --bin rust-analyzer --target {target_name} {features...} --release"
+ )
+}
+
+pub(crate) fn apply_pgo_to_cmd<'a>(cmd: Cmd<'a>, profile_path: &Path) -> Cmd<'a> {
+ cmd.env("RUSTFLAGS", format!("-Cprofile-use={}", profile_path.to_str().unwrap()))
+}
diff --git a/xtask/src/util.rs b/xtask/src/util.rs
index 39f52938c8..e5404d5717 100644
--- a/xtask/src/util.rs
+++ b/xtask/src/util.rs
@@ -1,5 +1,7 @@
use std::path::{Path, PathBuf};
+use xshell::{Shell, cmd};
+
pub(crate) fn list_rust_files(dir: &Path) -> Vec<PathBuf> {
let mut res = list_files(dir);
res.retain(|it| {
@@ -29,3 +31,13 @@ pub(crate) fn list_files(dir: &Path) -> Vec<PathBuf> {
}
res
}
+
+pub(crate) fn detect_target(sh: &Shell) -> String {
+ match std::env::var("RA_TARGET") {
+ Ok(target) => target,
+ _ => match cmd!(sh, "rustc --print=host-tuple").read() {
+ Ok(target) => target,
+ Err(e) => panic!("Failed to detect target: {e}\nPlease set RA_TARGET explicitly"),
+ },
+ }
+}