From f6f92a22ba451e42313c90fb8214f5d26ec71dfd Mon Sep 17 00:00:00 2001 From: gohome001 <3156514693@qq.com> Date: Fri, 25 Apr 2025 15:59:11 +0800 Subject: [PATCH 01/51] feat: highlight unsafe operations --- .../crates/ide/src/highlight_related.rs | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs b/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs index 80624eeae80c..750ad2302629 100644 --- a/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs +++ b/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs @@ -89,6 +89,9 @@ pub(crate) fn highlight_related( T![break] | T![loop] | T![while] | T![continue] if config.break_points => { highlight_break_points(sema, token).remove(&file_id) } + T![unsafe] if token.parent_ancestors().find_map(ast::BlockExpr::cast).is_some() => { + highlight_unsafe_points(sema, token).remove(&file_id) + } T![|] if config.closure_captures => { highlight_closure_captures(sema, token, file_id, span_file_id.file_id()) } @@ -706,6 +709,60 @@ fn is_async_const_block_or_closure(expr: &ast::Expr) -> bool { } } +pub(crate) fn highlight_unsafe_points( + sema: &Semantics<'_, RootDatabase>, + token: SyntaxToken, +) -> FxHashMap> { + fn hl( + sema: &Semantics<'_, RootDatabase>, + unsafe_token: Option, + block_expr: Option, + ) -> Option>> { + let mut highlights: FxHashMap> = FxHashMap::default(); + + let mut push_to_highlights = |file_id, range| { + if let Some(FileRange { file_id, range }) = original_frange(sema.db, file_id, range) { + let hrange = HighlightedRange { category: ReferenceCategory::empty(), range }; + highlights.entry(file_id).or_default().push(hrange); + } + }; + + // highlight unsafe keyword itself + let unsafe_token = unsafe_token?; + let unsafe_token_file_id = sema.hir_file_for(&unsafe_token.parent()?); + push_to_highlights(unsafe_token_file_id, Some(unsafe_token.text_range())); + + if let Some(block) = block_expr { + if let Some(node) = block.syntax().ancestors().find(|n| ast::Fn::can_cast(n.kind())) { + if let Some(function) = ast::Fn::cast(node) { + // highlight unsafe keyword of the function + if let Some(unsafe_token) = function.unsafe_token() { + push_to_highlights(unsafe_token_file_id, Some(unsafe_token.text_range())); + } + // highlight unsafe operations + if let Some(f) = sema.to_def(&function) { + let unsafe_ops = sema.get_unsafe_ops(f.into()); + for unsafe_op in unsafe_ops { + push_to_highlights( + unsafe_op.file_id, + Some(unsafe_op.value.text_range()), + ); + } + } + } + } + } + + Some(highlights) + } + + let Some(block_expr) = token.parent().and_then(ast::BlockExpr::cast) else { + return FxHashMap::default(); + }; + + hl(sema, Some(token), Some(block_expr)).unwrap_or_default() +} + #[cfg(test)] mod tests { use itertools::Itertools; From 9031c86009c4d09cc315670bb3e03b70ef2d5edf Mon Sep 17 00:00:00 2001 From: gohome001 <3156514693@qq.com> Date: Fri, 25 Apr 2025 16:01:20 +0800 Subject: [PATCH 02/51] test: add test case for highlight unsafe operations --- .../crates/ide/src/highlight_related.rs | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs b/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs index 750ad2302629..2943d60682ad 100644 --- a/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs +++ b/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs @@ -811,6 +811,34 @@ fn check_with_config( assert_eq!(expected, actual); } + #[test] + fn test_hl_unsafe_block() { + check( + r#" +fn foo() { + unsafe fn this_is_unsafe_function() { + } + + + unsa$0fe { + //^^^^^^ + let raw_ptr = &42 as *const i32; + let val = *raw_ptr; + //^^^^^^^^ + + let mut_ptr = &mut 5 as *mut i32; + *mut_ptr = 10; + //^^^^^^^^ + + this_is_unsafe_function(); + //^^^^^^^^^^^^^^^^^^^^^^^^^ + } + +} +"#, + ); + } + #[test] fn test_hl_tuple_fields() { check( From 2ec065062a2328de40ff29282cbd56f66276a748 Mon Sep 17 00:00:00 2001 From: gohome001 <3156514693@qq.com> Date: Fri, 25 Apr 2025 17:02:08 +0800 Subject: [PATCH 03/51] minor: format --- .../rust-analyzer/crates/ide/src/highlight_related.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs b/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs index 2943d60682ad..90a7c62d71bb 100644 --- a/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs +++ b/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs @@ -816,24 +816,22 @@ fn test_hl_unsafe_block() { check( r#" fn foo() { - unsafe fn this_is_unsafe_function() { - } + unsafe fn this_is_unsafe_function() {} - unsa$0fe { //^^^^^^ let raw_ptr = &42 as *const i32; let val = *raw_ptr; //^^^^^^^^ - + let mut_ptr = &mut 5 as *mut i32; *mut_ptr = 10; //^^^^^^^^ - + this_is_unsafe_function(); //^^^^^^^^^^^^^^^^^^^^^^^^^ } - + } "#, ); From bad7d122f4d64560b7dc43fbae797a9bd37a4dda Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 5 May 2025 08:29:34 +0200 Subject: [PATCH 04/51] Fix incorrect handling of unresolved non-module imports in name resolution --- .../rust-analyzer/crates/hir-def/src/lib.rs | 10 ++ .../crates/hir-def/src/nameres/assoc.rs | 9 ++ .../crates/hir-def/src/nameres/collector.rs | 76 ++++++--- .../hir-def/src/nameres/path_resolution.rs | 65 +++++++- .../src/nameres/tests/mod_resolution.rs | 146 ++++++++++++++++++ .../crates/hir-ty/src/tests/traits.rs | 19 +++ 6 files changed, 303 insertions(+), 22 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs index 28011bda7c54..85761347a7fc 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs @@ -701,6 +701,16 @@ pub enum AssocItemId { // casting them, and somehow making the constructors private, which would be annoying. impl_from!(FunctionId, ConstId, TypeAliasId for AssocItemId); +impl From for ModuleDefId { + fn from(item: AssocItemId) -> Self { + match item { + AssocItemId::FunctionId(f) => f.into(), + AssocItemId::ConstId(c) => c.into(), + AssocItemId::TypeAliasId(t) => t.into(), + } + } +} + #[derive(Debug, PartialOrd, Ord, Clone, Copy, PartialEq, Eq, Hash, salsa_macros::Supertype)] pub enum GenericDefId { AdtId(AdtId), diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/assoc.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/assoc.rs index 448b908936a2..dcb46fdd36ba 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/assoc.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/assoc.rs @@ -66,6 +66,15 @@ pub fn method_by_name(&self, name: &Name) -> Option { }) } + pub fn assoc_item_by_name(&self, name: &Name) -> Option { + self.items.iter().find_map(|&(ref item_name, item)| match item { + AssocItemId::FunctionId(_) if item_name == name => Some(item), + AssocItemId::TypeAliasId(_) if item_name == name => Some(item), + AssocItemId::ConstId(_) if item_name == name => Some(item), + _ => None, + }) + } + pub fn attribute_calls(&self) -> impl Iterator, MacroCallId)> + '_ { self.macro_calls.iter().flat_map(|it| it.iter()).copied() } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs index 8df0f092cd0b..b783be1b3348 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs @@ -26,7 +26,7 @@ use triomphe::Arc; use crate::{ - AdtId, AstId, AstIdWithPath, ConstLoc, CrateRootModuleId, EnumLoc, ExternBlockLoc, + AdtId, AssocItemId, AstId, AstIdWithPath, ConstLoc, CrateRootModuleId, EnumLoc, ExternBlockLoc, ExternCrateId, ExternCrateLoc, FunctionId, FunctionLoc, ImplLoc, Intern, ItemContainerId, LocalModuleId, Lookup, Macro2Id, Macro2Loc, MacroExpander, MacroId, MacroRulesId, MacroRulesLoc, MacroRulesLocFlags, ModuleDefId, ModuleId, ProcMacroId, ProcMacroLoc, StaticLoc, @@ -45,7 +45,7 @@ attr_resolution::{attr_macro_as_call_id, derive_macro_as_call_id}, diagnostics::DefDiagnostic, mod_resolution::ModDir, - path_resolution::ReachedFixedPoint, + path_resolution::{ReachedFixedPoint, ResolvePathResult}, proc_macro::{ProcMacroDef, ProcMacroKind, parse_macro_name_and_helper_attrs}, sub_namespace_match, }, @@ -811,32 +811,35 @@ fn resolve_import(&self, module_id: LocalModuleId, import: &Import) -> PartialRe let _p = tracing::info_span!("resolve_import", import_path = %import.path.display(self.db, Edition::LATEST)) .entered(); tracing::debug!("resolving import: {:?} ({:?})", import, self.def_map.data.edition); - let res = self.def_map.resolve_path_fp_with_macro( - self.crate_local_def_map.as_deref().unwrap_or(&self.local_def_map), - self.db, - ResolveMode::Import, - module_id, - &import.path, - BuiltinShadowMode::Module, - None, // An import may resolve to any kind of macro. - ); + let ResolvePathResult { resolved_def, segment_index, reached_fixedpoint, prefix_info } = + self.def_map.resolve_path_fp_with_macro( + self.crate_local_def_map.as_deref().unwrap_or(&self.local_def_map), + self.db, + ResolveMode::Import, + module_id, + &import.path, + BuiltinShadowMode::Module, + None, // An import may resolve to any kind of macro. + ); - let def = res.resolved_def; - if res.reached_fixedpoint == ReachedFixedPoint::No || def.is_none() { + if reached_fixedpoint == ReachedFixedPoint::No + || resolved_def.is_none() + || segment_index.is_some() + { return PartialResolvedImport::Unresolved; } - if res.prefix_info.differing_crate { + if prefix_info.differing_crate { return PartialResolvedImport::Resolved( - def.filter_visibility(|v| matches!(v, Visibility::Public)), + resolved_def.filter_visibility(|v| matches!(v, Visibility::Public)), ); } // Check whether all namespaces are resolved. - if def.is_full() { - PartialResolvedImport::Resolved(def) + if resolved_def.is_full() { + PartialResolvedImport::Resolved(resolved_def) } else { - PartialResolvedImport::Indeterminate(def) + PartialResolvedImport::Indeterminate(resolved_def) } } @@ -986,6 +989,43 @@ fn record_resolved_import(&mut self, directive: &ImportDirective) { Some(ImportOrExternCrate::Glob(glob)), ); } + Some(ModuleDefId::TraitId(it)) => { + // FIXME: Implement this correctly + // We can't actually call `trait_items`, the reason being that if macro calls + // occur, they will call back into the def map which we might be computing right + // now resulting in a cycle. + // To properly implement this, trait item collection needs to be done in def map + // collection... + let resolutions = if true { + vec![] + } else { + self.db + .trait_items(it) + .items + .iter() + .map(|&(ref name, variant)| { + let res = match variant { + AssocItemId::FunctionId(it) => { + PerNs::values(it.into(), vis, None) + } + AssocItemId::ConstId(it) => { + PerNs::values(it.into(), vis, None) + } + AssocItemId::TypeAliasId(it) => { + PerNs::types(it.into(), vis, None) + } + }; + (Some(name.clone()), res) + }) + .collect::>() + }; + self.update( + module_id, + &resolutions, + vis, + Some(ImportOrExternCrate::Glob(glob)), + ); + } Some(d) => { tracing::debug!("glob import {:?} from non-module/enum {:?}", import, d); } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs index a49155d878ca..708ae9c8f02e 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs @@ -17,6 +17,7 @@ name::Name, }; use span::Edition; +use stdx::TupleExt; use triomphe::Arc; use crate::{ @@ -44,6 +45,7 @@ pub(super) enum ReachedFixedPoint { #[derive(Debug, Clone)] pub(super) struct ResolvePathResult { pub(super) resolved_def: PerNs, + /// The index of the last resolved segment, or `None` if the full path has been resolved. pub(super) segment_index: Option, pub(super) reached_fixedpoint: ReachedFixedPoint, pub(super) prefix_info: ResolvePathResultPrefixInfo, @@ -364,7 +366,15 @@ pub(super) fn resolve_path_fp_with_macro_single( }, }; - self.resolve_remaining_segments(segments, curr_per_ns, path, db, shadow, original_module) + self.resolve_remaining_segments( + db, + mode, + segments, + curr_per_ns, + path, + shadow, + original_module, + ) } /// Resolves a path only in the preludes, without accounting for item scopes. @@ -413,7 +423,15 @@ pub(super) fn resolve_path_fp_in_all_preludes( } }; - self.resolve_remaining_segments(segments, curr_per_ns, path, db, shadow, original_module) + self.resolve_remaining_segments( + db, + mode, + segments, + curr_per_ns, + path, + shadow, + original_module, + ) } /// 2018-style absolute path -- only extern prelude @@ -441,10 +459,11 @@ fn resolve_path_abs<'a>( fn resolve_remaining_segments<'a>( &self, + db: &dyn DefDatabase, + mode: ResolveMode, mut segments: impl Iterator, mut curr_per_ns: PerNs, path: &ModPath, - db: &dyn DefDatabase, shadow: BuiltinShadowMode, original_module: LocalModuleId, ) -> ResolvePathResult { @@ -478,7 +497,7 @@ fn resolve_remaining_segments<'a>( let resolution = defp_map.resolve_path_fp_with_macro( LocalDefMap::EMPTY, db, - ResolveMode::Other, + mode, module.local_id, &path, shadow, @@ -553,6 +572,44 @@ fn resolve_remaining_segments<'a>( ), }; } + def @ ModuleDefId::TraitId(t) if mode == ResolveMode::Import => { + // FIXME: Implement this correctly + // We can't actually call `trait_items`, the reason being that if macro calls + // occur, they will call back into the def map which we might be computing right + // now resulting in a cycle. + // To properly implement this, trait item collection needs to be done in def map + // collection... + let item = + if true { None } else { db.trait_items(t).assoc_item_by_name(segment) }; + return match item { + Some(item) => ResolvePathResult::new( + match item { + crate::AssocItemId::FunctionId(function_id) => PerNs::values( + function_id.into(), + curr.vis, + curr.import.and_then(|it| it.import_or_glob()), + ), + crate::AssocItemId::ConstId(const_id) => PerNs::values( + const_id.into(), + curr.vis, + curr.import.and_then(|it| it.import_or_glob()), + ), + crate::AssocItemId::TypeAliasId(type_alias_id) => { + PerNs::types(type_alias_id.into(), curr.vis, curr.import) + } + }, + ReachedFixedPoint::Yes, + segments.next().map(TupleExt::head), + ResolvePathResultPrefixInfo::default(), + ), + None => ResolvePathResult::new( + PerNs::types(def, curr.vis, curr.import), + ReachedFixedPoint::Yes, + Some(i), + ResolvePathResultPrefixInfo::default(), + ), + }; + } s => { // could be an inherent method call in UFCS form // (`Struct::method`), or some other kind of associated item diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/mod_resolution.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/mod_resolution.rs index 071b55c83d8d..f5bacc5327c7 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/mod_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/mod_resolution.rs @@ -894,3 +894,149 @@ fn cfg_in_module_file() { "#]], ) } + +#[test] +fn invalid_imports() { + check( + r#" +//- /main.rs +mod module; + +use self::module::S::new; +use self::module::unresolved; +use self::module::C::const_based; +use self::module::Enum::Variant::NoAssoc; + +//- /module.rs +pub struct S; +impl S { + pub fn new() {} +} +pub const C: () = (); +pub enum Enum { + Variant, +} + "#, + expect![[r#" + crate + NoAssoc: _ + const_based: _ + module: t + new: _ + unresolved: _ + + crate::module + C: v + Enum: t + S: t v + "#]], + ); +} + +#[test] +fn trait_item_imports_same_crate() { + check( + r#" +//- /main.rs +mod module; + +use self::module::Trait::{AssocType, ASSOC_CONST, MACRO_CONST, method}; + +//- /module.rs +macro_rules! m { + ($name:ident) => { const $name: () = (); }; +} +pub trait Trait { + type AssocType; + const ASSOC_CONST: (); + fn method(&self); + m!(MACRO_CONST); +} + "#, + expect![[r#" + crate + ASSOC_CONST: _ + AssocType: _ + MACRO_CONST: _ + method: _ + module: t + + crate::module + Trait: t + "#]], + ); + check( + r#" +//- /main.rs +mod module; + +use self::module::Trait::*; + +//- /module.rs +macro_rules! m { + ($name:ident) => { const $name: () = (); }; +} +pub trait Trait { + type AssocType; + const ASSOC_CONST: (); + fn method(&self); + m!(MACRO_CONST); +} + "#, + expect![[r#" + crate + module: t + + crate::module + Trait: t + "#]], + ); +} + +#[test] +fn trait_item_imports_differing_crate() { + check( + r#" +//- /main.rs deps:lib crate:main +use lib::Trait::{AssocType, ASSOC_CONST, MACRO_CONST, method}; + +//- /lib.rs crate:lib +macro_rules! m { + ($name:ident) => { const $name: () = (); }; +} +pub trait Trait { + type AssocType; + const ASSOC_CONST: (); + fn method(&self); + m!(MACRO_CONST); +} + "#, + expect![[r#" + crate + ASSOC_CONST: _ + AssocType: _ + MACRO_CONST: _ + method: _ + "#]], + ); + check( + r#" +//- /main.rs deps:lib crate:main +use lib::Trait::*; + +//- /lib.rs crate:lib +macro_rules! m { + ($name:ident) => { const $name: () = (); }; +} +pub trait Trait { + type AssocType; + const ASSOC_CONST: (); + fn method(&self); + m!(MACRO_CONST); +} + "#, + expect![[r#" + crate + "#]], + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs index 14137605c9f2..2b527a4ae12e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs @@ -4884,3 +4884,22 @@ async fn baz i32>(c: T) { "#]], ); } + +#[test] +fn import_trait_items() { + check_infer( + r#" +//- minicore: default +use core::default::Default::default; +fn main() { + let a: i32 = default(); +} + "#, + expect![[r#" + 47..78 '{ ...t(); }': () + 57..58 'a': i32 + 66..73 'default': {unknown} + 66..75 'default()': i32 + "#]], + ); +} From 79f0e2990b3c1de7909ccc06510fcef655d9afcb Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 28 Apr 2025 13:30:54 +0200 Subject: [PATCH 05/51] refactor: De-arc defmap queries --- .../rust-analyzer/crates/hir-def/src/db.rs | 14 +- .../crates/hir-def/src/expr_store.rs | 7 +- .../crates/hir-def/src/expr_store/lower.rs | 16 +- .../src/expr_store/lower/path/tests.rs | 5 +- .../crates/hir-def/src/expr_store/scope.rs | 6 +- .../hir-def/src/expr_store/tests/body.rs | 5 +- .../src/expr_store/tests/body/block.rs | 4 +- .../src/expr_store/tests/signatures.rs | 3 +- .../crates/hir-def/src/find_path.rs | 4 +- .../crates/hir-def/src/import_map.rs | 4 +- .../crates/hir-def/src/lang_item.rs | 5 +- .../rust-analyzer/crates/hir-def/src/lib.rs | 34 ++-- .../hir-def/src/macro_expansion_tests/mod.rs | 10 +- .../crates/hir-def/src/nameres.rs | 172 ++++++++++-------- .../crates/hir-def/src/nameres/assoc.rs | 8 +- .../crates/hir-def/src/nameres/collector.rs | 35 ++-- .../hir-def/src/nameres/path_resolution.rs | 35 ++-- .../crates/hir-def/src/nameres/tests.rs | 15 +- .../hir-def/src/nameres/tests/incremental.rs | 37 ++-- .../hir-def/src/nameres/tests/macros.rs | 20 +- .../src/nameres/tests/mod_resolution.rs | 2 + .../crates/hir-def/src/resolver.rs | 152 ++++++++-------- .../crates/hir-def/src/test_db.rs | 16 +- .../crates/hir-def/src/visibility.rs | 8 +- .../crates/hir-ty/src/consteval.rs | 2 +- .../crates/hir-ty/src/diagnostics/expr.rs | 2 +- .../diagnostics/match_check/pat_analysis.rs | 2 +- .../hir-ty/src/diagnostics/unsafe_check.rs | 22 +-- .../crates/hir-ty/src/dyn_compatibility.rs | 6 +- .../rust-analyzer/crates/hir-ty/src/infer.rs | 18 +- .../crates/hir-ty/src/infer/diagnostics.rs | 2 +- .../rust-analyzer/crates/hir-ty/src/lower.rs | 25 ++- .../crates/hir-ty/src/method_resolution.rs | 20 +- .../crates/hir-ty/src/mir/eval/shim.rs | 3 +- .../crates/hir-ty/src/mir/lower.rs | 10 +- .../crates/hir-ty/src/test_db.rs | 6 +- .../rust-analyzer/crates/hir-ty/src/tests.rs | 10 +- .../hir-ty/src/tests/closure_captures.rs | 2 +- .../crates/hir-ty/src/tests/incremental.rs | 8 +- .../crates/hir-ty/src/variance.rs | 2 +- .../rust-analyzer/crates/hir/src/attrs.rs | 4 +- src/tools/rust-analyzer/crates/hir/src/lib.rs | 27 ++- .../rust-analyzer/crates/hir/src/semantics.rs | 24 +-- .../crates/hir/src/semantics/source_to_def.rs | 5 +- .../crates/hir/src/source_analyzer.rs | 118 ++++++------ .../crates/ide-db/src/prime_caches.rs | 2 +- .../src/handlers/unlinked_file.rs | 8 +- src/tools/rust-analyzer/crates/ide/src/lib.rs | 4 +- .../crates/ide/src/parent_module.rs | 4 +- 49 files changed, 498 insertions(+), 455 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/db.rs b/src/tools/rust-analyzer/crates/hir-def/src/db.rs index 2cbdbe16f9bb..4a9a3b12cfab 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/db.rs @@ -24,8 +24,8 @@ item_tree::{AttrOwner, ItemTree}, lang_item::{self, LangItem}, nameres::{ - DefMap, LocalDefMap, assoc::{ImplItems, TraitItems}, + crate_def_map, diagnostics::DefDiagnostics, }, signatures::{ @@ -111,16 +111,6 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + SourceDatabase { #[salsa::invoke(ItemTree::block_item_tree_query)] fn block_item_tree(&self, block_id: BlockId) -> Arc; - #[salsa::invoke(DefMap::crate_local_def_map_query)] - fn crate_local_def_map(&self, krate: Crate) -> (Arc, Arc); - - #[salsa::invoke(DefMap::crate_def_map_query)] - fn crate_def_map(&self, krate: Crate) -> Arc; - - /// Computes the block-level `DefMap`. - #[salsa::invoke(DefMap::block_def_map_query)] - fn block_def_map(&self, block: BlockId) -> Arc; - /// Turns a MacroId into a MacroDefId, describing the macro's definition post name resolution. #[salsa::invoke(macro_def)] fn macro_def(&self, m: MacroId) -> MacroDefId; @@ -363,7 +353,7 @@ fn include_macro_invoc( db: &dyn DefDatabase, krate: Crate, ) -> Arc<[(MacroCallId, EditionedFileId)]> { - db.crate_def_map(krate) + crate_def_map(db, krate) .modules .values() .flat_map(|m| m.scope.iter_macro_invoc()) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs index e3775c4931ae..09ee286f5cfe 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs @@ -19,7 +19,6 @@ use smallvec::SmallVec; use span::{Edition, SyntaxContext}; use syntax::{AstPtr, SyntaxNodePtr, ast}; -use triomphe::Arc; use tt::TextRange; use crate::{ @@ -30,7 +29,7 @@ Array, AsmOperand, Binding, BindingId, Expr, ExprId, ExprOrPatId, Label, LabelId, Pat, PatId, RecordFieldPat, Statement, }, - nameres::DefMap, + nameres::{DefMap, block_def_map}, type_ref::{LifetimeRef, LifetimeRefId, PathId, TypeRef, TypeRefId}, }; @@ -225,8 +224,8 @@ impl ExpressionStore { pub fn blocks<'a>( &'a self, db: &'a dyn DefDatabase, - ) -> impl Iterator)> + 'a { - self.block_scopes.iter().map(move |&block| (block, db.block_def_map(block))) + ) -> impl Iterator + 'a { + self.block_scopes.iter().map(move |&block| (block, block_def_map(db, block))) } pub fn walk_bindings_in_pat(&self, pat_id: PatId, mut f: impl FnMut(BindingId)) { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs index 50505d54ba2f..29871f5e04db 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs @@ -56,7 +56,7 @@ item_scope::BuiltinShadowMode, item_tree::FieldsShape, lang_item::LangItem, - nameres::{DefMap, LocalDefMap, MacroSubNs}, + nameres::{DefMap, LocalDefMap, MacroSubNs, block_def_map}, type_ref::{ ArrayType, ConstRef, FnType, LifetimeRef, LifetimeRefId, Mutability, PathId, Rawness, RefType, TraitBoundModifier, TraitRef, TypeBound, TypeRef, TypeRefId, UseArgRef, @@ -436,8 +436,8 @@ pub struct ExprCollector<'db> { db: &'db dyn DefDatabase, cfg_options: &'db CfgOptions, expander: Expander, - def_map: Arc, - local_def_map: Arc, + def_map: &'db DefMap, + local_def_map: &'db LocalDefMap, module: ModuleId, pub store: ExpressionStoreBuilder, pub(crate) source_map: ExpressionStoreSourceMap, @@ -544,7 +544,7 @@ pub fn new( current_file_id: HirFileId, ) -> ExprCollector<'_> { let (def_map, local_def_map) = module.local_def_map(db); - let expander = Expander::new(db, current_file_id, &def_map); + let expander = Expander::new(db, current_file_id, def_map); ExprCollector { db, cfg_options: module.krate().cfg_options(db), @@ -1947,7 +1947,7 @@ fn collect_macro_call( let resolver = |path: &_| { self.def_map .resolve_path( - &self.local_def_map, + self.local_def_map, self.db, module, path, @@ -2163,12 +2163,12 @@ fn collect_block_( }; let (module, def_map) = - match block_id.map(|block_id| (self.db.block_def_map(block_id), block_id)) { + match block_id.map(|block_id| (block_def_map(self.db, block_id), block_id)) { Some((def_map, block_id)) => { self.store.block_scopes.push(block_id); (def_map.module_id(DefMap::ROOT), def_map) } - None => (self.module, self.def_map.clone()), + None => (self.module, self.def_map), }; let prev_def_map = mem::replace(&mut self.def_map, def_map); let prev_local_module = mem::replace(&mut self.module, module); @@ -2247,7 +2247,7 @@ fn collect_pat(&mut self, pat: ast::Pat, binding_list: &mut BindingList) -> PatI // This could also be a single-segment path pattern. To // decide that, we need to try resolving the name. let (resolved, _) = self.def_map.resolve_path( - &self.local_def_map, + self.local_def_map, self.db, self.module.local_id, &name.clone().into(), diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/path/tests.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/path/tests.rs index 337cb103bde2..8fd81c7b3dff 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/path/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/path/tests.rs @@ -4,7 +4,6 @@ use test_fixture::WithFixture; use crate::{ - db::DefDatabase, expr_store::{ ExpressionStore, lower::{ @@ -14,13 +13,15 @@ path::Path, pretty, }, + nameres::crate_def_map, test_db::TestDB, }; fn lower_path(path: ast::Path) -> (TestDB, ExpressionStore, Option) { let (db, file_id) = TestDB::with_single_file(""); let krate = db.fetch_test_crate(); - let mut ctx = ExprCollector::new(&db, db.crate_def_map(krate).root_module_id(), file_id.into()); + let mut ctx = + ExprCollector::new(&db, crate_def_map(&db, krate).root_module_id(), file_id.into()); let lowered_path = ctx.lower_path(path, &mut ExprCollector::impl_trait_allocator); let store = ctx.store.finish(); (db, store, lowered_path) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/scope.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/scope.rs index 431ea9eb1d46..a46711c67e87 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/scope.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/scope.rs @@ -324,11 +324,13 @@ mod tests { use test_fixture::WithFixture; use test_utils::{assert_eq_text, extract_offset}; - use crate::{FunctionId, ModuleDefId, db::DefDatabase, test_db::TestDB}; + use crate::{ + FunctionId, ModuleDefId, db::DefDatabase, nameres::crate_def_map, test_db::TestDB, + }; fn find_function(db: &TestDB, file_id: FileId) -> FunctionId { let krate = db.test_crate(); - let crate_def_map = db.crate_def_map(krate); + let crate_def_map = crate_def_map(db, krate); let module = crate_def_map.modules_for_file(db, file_id).next().unwrap(); let (_, def) = crate_def_map[module].scope.entries().next().unwrap(); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body.rs index d6645dc1d1d3..29e249b07a72 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body.rs @@ -1,9 +1,10 @@ mod block; -use crate::{DefWithBodyId, ModuleDefId, hir::MatchArm, test_db::TestDB}; +use crate::{DefWithBodyId, ModuleDefId, hir::MatchArm, nameres::crate_def_map, test_db::TestDB}; use expect_test::{Expect, expect}; use la_arena::RawIdx; use test_fixture::WithFixture; +use triomphe::Arc; use super::super::*; @@ -11,7 +12,7 @@ fn lower(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> (TestDB, Arc, let db = TestDB::with_files(ra_fixture); let krate = db.fetch_test_crate(); - let def_map = db.crate_def_map(krate); + let def_map = crate_def_map(&db, krate); let mut fn_def = None; 'outer: for (_, module) in def_map.modules() { for decl in module.scope.declarations() { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body/block.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body/block.rs index da3b65d4203d..5f7b510bba4b 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body/block.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body/block.rs @@ -189,8 +189,8 @@ fn f() { } "#, expect![[r#" - BlockId(3801) in BlockRelativeModuleId { block: Some(BlockId(3800)), local_id: Idx::(1) } - BlockId(3800) in BlockRelativeModuleId { block: None, local_id: Idx::(0) } + BlockId(3c01) in BlockRelativeModuleId { block: Some(BlockId(3c00)), local_id: Idx::(1) } + BlockId(3c00) in BlockRelativeModuleId { block: None, local_id: Idx::(0) } crate scope "#]], ); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/signatures.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/signatures.rs index 80561d647083..efb558a77581 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/signatures.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/signatures.rs @@ -1,6 +1,7 @@ use crate::{ GenericDefId, ModuleDefId, expr_store::pretty::{print_function, print_struct}, + nameres::crate_def_map, test_db::TestDB, }; use expect_test::{Expect, expect}; @@ -12,7 +13,7 @@ fn lower_and_print(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expe let db = TestDB::with_files(ra_fixture); let krate = db.fetch_test_crate(); - let def_map = db.crate_def_map(krate); + let def_map = crate_def_map(&db, krate); let mut defs = vec![]; for (_, module) in def_map.modules() { for decl in module.scope.declarations() { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs b/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs index 9d62d9ce6526..bb75621c7e07 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs @@ -52,7 +52,7 @@ pub fn find_path( ignore_local_imports, is_std_item: item_module.krate().data(db).origin.is_lang(), from, - from_def_map: &from.def_map(db), + from_def_map: from.def_map(db), fuel: Cell::new(FIND_PATH_FUEL), }, item, @@ -691,7 +691,7 @@ fn check_found_path_( let (def_map, local_def_map) = module.local_def_map(&db); let resolved = def_map .resolve_path( - &local_def_map, + local_def_map, &db, module.local_id, &mod_path, diff --git a/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs b/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs index db571f045d74..a6138fb6821d 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs @@ -16,7 +16,7 @@ AssocItemId, AttrDefId, Complete, FxIndexMap, ModuleDefId, ModuleId, TraitId, db::DefDatabase, item_scope::{ImportOrExternCrate, ItemInNs}, - nameres::DefMap, + nameres::{DefMap, crate_def_map}, visibility::Visibility, }; @@ -129,7 +129,7 @@ pub fn import_info_for(&self, item: ItemInNs) -> Option<&[ImportInfo]> { fn collect_import_map(db: &dyn DefDatabase, krate: Crate) -> ImportMapIndex { let _p = tracing::info_span!("collect_import_map").entered(); - let def_map = db.crate_def_map(krate); + let def_map = crate_def_map(db, krate); let mut map = FxIndexMap::default(); // We look only into modules that are public(ly reexported), starting with the crate root. diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs index 51a833b5f150..59344641f47a 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs @@ -10,6 +10,7 @@ use crate::{ AdtId, AssocItemId, AttrDefId, Crate, EnumId, EnumVariantId, FunctionId, ImplId, ModuleDefId, StaticId, StructId, TraitId, TypeAliasId, UnionId, db::DefDatabase, expr_store::path::Path, + nameres::crate_def_map, }; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -90,7 +91,7 @@ pub fn crate_lang_items(db: &dyn DefDatabase, krate: Crate) -> Option Option let mut traits = Vec::new(); - let crate_def_map = db.crate_def_map(krate); + let crate_def_map = crate_def_map(db, krate); for (_, module_data) in crate_def_map.modules() { for def in module_data.scope.declarations() { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs index 85761347a7fc..b41ff026bcaa 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs @@ -92,7 +92,7 @@ Const, Enum, ExternCrate, Function, Impl, ItemTreeId, ItemTreeNode, Macro2, MacroRules, Static, Struct, Trait, TraitAlias, TypeAlias, Union, Use, Variant, }, - nameres::LocalDefMap, + nameres::{LocalDefMap, block_def_map, crate_def_map, crate_local_def_map}, signatures::VariantFields, }; @@ -324,12 +324,13 @@ pub struct CrateRootModuleId { } impl CrateRootModuleId { - pub fn def_map(&self, db: &dyn DefDatabase) -> Arc { - db.crate_def_map(self.krate) + pub fn def_map(self, db: &dyn DefDatabase) -> &DefMap { + crate_def_map(db, self.krate) } - pub(crate) fn local_def_map(&self, db: &dyn DefDatabase) -> (Arc, Arc) { - db.crate_local_def_map(self.krate) + pub(crate) fn local_def_map(self, db: &dyn DefDatabase) -> (&DefMap, &LocalDefMap) { + let def_map = crate_local_def_map(db, self.krate); + (def_map.def_map(db), def_map.local(db)) } pub fn krate(self) -> Crate { @@ -390,26 +391,29 @@ pub struct ModuleId { } impl ModuleId { - pub fn def_map(self, db: &dyn DefDatabase) -> Arc { + pub fn def_map(self, db: &dyn DefDatabase) -> &DefMap { match self.block { - Some(block) => db.block_def_map(block), - None => db.crate_def_map(self.krate), + Some(block) => block_def_map(db, block), + None => crate_def_map(db, self.krate), } } - pub(crate) fn local_def_map(self, db: &dyn DefDatabase) -> (Arc, Arc) { + pub(crate) fn local_def_map(self, db: &dyn DefDatabase) -> (&DefMap, &LocalDefMap) { match self.block { - Some(block) => (db.block_def_map(block), self.only_local_def_map(db)), - None => db.crate_local_def_map(self.krate), + Some(block) => (block_def_map(db, block), self.only_local_def_map(db)), + None => { + let def_map = crate_local_def_map(db, self.krate); + (def_map.def_map(db), def_map.local(db)) + } } } - pub(crate) fn only_local_def_map(self, db: &dyn DefDatabase) -> Arc { - db.crate_local_def_map(self.krate).1 + pub(crate) fn only_local_def_map(self, db: &dyn DefDatabase) -> &LocalDefMap { + crate_local_def_map(db, self.krate).local(db) } - pub fn crate_def_map(self, db: &dyn DefDatabase) -> Arc { - db.crate_def_map(self.krate) + pub fn crate_def_map(self, db: &dyn DefDatabase) -> &DefMap { + crate_def_map(db, self.krate) } pub fn krate(self) -> Crate { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs index 800c96ebdae0..fcb4684c9300 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs @@ -39,7 +39,7 @@ use crate::{ AdtId, Lookup, ModuleDefId, db::DefDatabase, - nameres::{DefMap, ModuleSource}, + nameres::{DefMap, ModuleSource, crate_def_map}, src::HasSource, test_db::TestDB, tt::TopSubtree, @@ -49,7 +49,7 @@ fn check_errors(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) { let db = TestDB::with_files(ra_fixture); let krate = db.fetch_test_crate(); - let def_map = db.crate_def_map(krate); + let def_map = crate_def_map(&db, krate); let errors = def_map .modules() .flat_map(|module| module.1.scope.all_macro_calls()) @@ -113,7 +113,7 @@ fn resolve( 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)) + body.blocks(db).find_map(|block| resolve(db, block.1, ast_id, ast_ptr)) { return Some(it); } @@ -127,7 +127,7 @@ fn resolve( 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 def_map = crate_def_map(&db, krate); let local_id = DefMap::ROOT; let source = def_map[local_id].definition_source(&db); let source_file = match source.value { @@ -142,7 +142,7 @@ fn resolve( let ast_id = db.ast_id_map(source.file_id).ast_id(¯o_call_node); let ast_id = InFile::new(source.file_id, ast_id); let ptr = InFile::new(source.file_id, AstPtr::new(¯o_call_node)); - let macro_call_id = resolve(&db, &def_map, ast_id, ptr) + 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)); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs index fc66d8e28d8c..d4b30a1d3e68 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs @@ -112,6 +112,18 @@ pub struct LocalDefMap { extern_prelude: FxIndexMap)>, } +impl std::hash::Hash for LocalDefMap { + fn hash(&self, state: &mut H) { + let LocalDefMap { extern_prelude } = self; + extern_prelude.len().hash(state); + for (name, (crate_root, extern_crate)) in extern_prelude { + name.hash(state); + crate_root.hash(state); + extern_crate.hash(state); + } + } +} + impl LocalDefMap { pub(crate) const EMPTY: &Self = &Self { extern_prelude: FxIndexMap::with_hasher(rustc_hash::FxBuildHasher) }; @@ -250,7 +262,7 @@ struct BlockRelativeModuleId { } impl BlockRelativeModuleId { - fn def_map(self, db: &dyn DefDatabase, krate: Crate) -> Arc { + fn def_map(self, db: &dyn DefDatabase, krate: Crate) -> &DefMap { self.into_module(krate).def_map(db) } @@ -358,6 +370,87 @@ pub struct ModuleData { pub scope: ItemScope, } +#[inline] +pub fn crate_def_map(db: &dyn DefDatabase, crate_id: Crate) -> &DefMap { + crate_local_def_map(db, crate_id).def_map(db) +} + +#[allow(unused_lifetimes)] +mod __ { + use super::*; + #[salsa_macros::tracked] + pub(crate) struct DefMapPair<'db> { + #[tracked] + #[return_ref] + pub(crate) def_map: DefMap, + #[return_ref] + pub(crate) local: LocalDefMap, + } +} +pub(crate) use __::DefMapPair; + +#[salsa_macros::tracked(return_ref)] +pub(crate) fn crate_local_def_map(db: &dyn DefDatabase, crate_id: Crate) -> DefMapPair<'_> { + let krate = crate_id.data(db); + let _p = tracing::info_span!( + "crate_def_map_query", + name=?crate_id + .extra_data(db) + .display_name + .as_ref() + .map(|it| it.crate_name().to_smolstr()) + .unwrap_or_default() + ) + .entered(); + + let module_data = ModuleData::new( + ModuleOrigin::CrateRoot { definition: krate.root_file_id(db) }, + Visibility::Public, + ); + + let def_map = + DefMap::empty(crate_id, Arc::new(DefMapCrateData::new(krate.edition)), module_data, None); + let (def_map, local_def_map) = collector::collect_defs( + db, + def_map, + TreeId::new(krate.root_file_id(db).into(), None), + None, + ); + + DefMapPair::new(db, def_map, local_def_map) +} + +#[salsa_macros::tracked(return_ref)] +pub fn block_def_map(db: &dyn DefDatabase, block_id: BlockId) -> DefMap { + let BlockLoc { ast_id, module } = block_id.lookup(db); + + let visibility = Visibility::Module( + ModuleId { krate: module.krate, local_id: DefMap::ROOT, block: module.block }, + VisibilityExplicitness::Implicit, + ); + let module_data = + ModuleData::new(ModuleOrigin::BlockExpr { block: ast_id, id: block_id }, visibility); + + let local_def_map = crate_local_def_map(db, module.krate); + let def_map = DefMap::empty( + module.krate, + local_def_map.def_map(db).data.clone(), + module_data, + Some(BlockInfo { + block: block_id, + parent: BlockRelativeModuleId { block: module.block, local_id: module.local_id }, + }), + ); + + let (def_map, _) = collector::collect_defs( + db, + def_map, + TreeId::new(ast_id.file_id, Some(block_id)), + Some(local_def_map.local(db)), + ); + def_map +} + impl DefMap { /// The module id of a crate or block root. pub const ROOT: LocalModuleId = LocalModuleId::from_raw(la_arena::RawIdx::from_u32(0)); @@ -366,77 +459,6 @@ pub fn edition(&self) -> Edition { self.data.edition } - pub(crate) fn crate_def_map_query(db: &dyn DefDatabase, crate_id: Crate) -> Arc { - db.crate_local_def_map(crate_id).0 - } - - pub(crate) fn crate_local_def_map_query( - db: &dyn DefDatabase, - crate_id: Crate, - ) -> (Arc, Arc) { - let krate = crate_id.data(db); - let _p = tracing::info_span!( - "crate_def_map_query", - name=?crate_id - .extra_data(db) - .display_name - .as_ref() - .map(|it| it.crate_name().to_smolstr()) - .unwrap_or_default() - ) - .entered(); - - let module_data = ModuleData::new( - ModuleOrigin::CrateRoot { definition: krate.root_file_id(db) }, - Visibility::Public, - ); - - let def_map = DefMap::empty( - crate_id, - Arc::new(DefMapCrateData::new(krate.edition)), - module_data, - None, - ); - let (def_map, local_def_map) = collector::collect_defs( - db, - def_map, - TreeId::new(krate.root_file_id(db).into(), None), - None, - ); - - (Arc::new(def_map), Arc::new(local_def_map)) - } - - pub(crate) fn block_def_map_query(db: &dyn DefDatabase, block_id: BlockId) -> Arc { - let BlockLoc { ast_id, module } = block_id.lookup(db); - - let visibility = Visibility::Module( - ModuleId { krate: module.krate, local_id: Self::ROOT, block: module.block }, - VisibilityExplicitness::Implicit, - ); - let module_data = - ModuleData::new(ModuleOrigin::BlockExpr { block: ast_id, id: block_id }, visibility); - - let (crate_map, crate_local_map) = db.crate_local_def_map(module.krate); - let def_map = DefMap::empty( - module.krate, - crate_map.data.clone(), - module_data, - Some(BlockInfo { - block: block_id, - parent: BlockRelativeModuleId { block: module.block, local_id: module.local_id }, - }), - ); - - let (def_map, _) = collector::collect_defs( - db, - def_map, - TreeId::new(ast_id.file_id, Some(block_id)), - Some(crate_local_map), - ); - Arc::new(def_map) - } - fn empty( krate: Crate, crate_data: Arc, @@ -595,7 +617,7 @@ pub fn dump(&self, db: &dyn DefDatabase) -> String { go(&mut buf, db, current_map, "block scope", Self::ROOT); buf.push('\n'); arc = block.parent.def_map(db, self.krate); - current_map = &arc; + current_map = arc; } go(&mut buf, db, current_map, "crate", Self::ROOT); return buf; @@ -628,7 +650,7 @@ pub fn dump_block_scopes(&self, db: &dyn DefDatabase) -> String { while let Some(block) = current_map.block { format_to!(buf, "{:?} in {:?}\n", block.block, block.parent); arc = block.parent.def_map(db, self.krate); - current_map = &arc; + current_map = arc; } format_to!(buf, "crate scope\n"); @@ -708,7 +730,7 @@ pub(crate) fn with_ancestor_maps( let mut block = self.block; while let Some(block_info) = block { let parent = block_info.parent.def_map(db, self.krate); - if let Some(it) = f(&parent, block_info.parent.local_id) { + if let Some(it) = f(parent, block_info.parent.local_id) { return Some(it); } block = parent.block; diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/assoc.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/assoc.rs index dcb46fdd36ba..d45709b8b903 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/assoc.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/assoc.rs @@ -117,8 +117,8 @@ pub fn attribute_calls(&self) -> impl Iterator, MacroCa struct AssocItemCollector<'a> { db: &'a dyn DefDatabase, module_id: ModuleId, - def_map: Arc, - local_def_map: Arc, + def_map: &'a DefMap, + local_def_map: &'a LocalDefMap, diagnostics: Vec, container: ItemContainerId, @@ -183,7 +183,7 @@ fn collect_item(&mut self, item_tree: &ItemTree, tree_id: TreeId, item: AssocIte let ast_id_with_path = AstIdWithPath { path: attr.path.clone(), ast_id }; match self.def_map.resolve_attr_macro( - &self.local_def_map, + self.local_def_map, self.db, self.module_id.local_id, ast_id_with_path, @@ -255,7 +255,7 @@ fn record_item(&mut self, item_tree: &ItemTree, tree_id: TreeId, item: AssocItem let resolver = |path: &_| { self.def_map .resolve_path( - &self.local_def_map, + self.local_def_map, self.db, self.module_id.local_id, path, diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs index b783be1b3348..350c97c39825 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs @@ -43,6 +43,7 @@ nameres::{ BuiltinShadowMode, DefMap, LocalDefMap, MacroSubNs, ModuleData, ModuleOrigin, ResolveMode, attr_resolution::{attr_macro_as_call_id, derive_macro_as_call_id}, + crate_def_map, diagnostics::DefDiagnostic, mod_resolution::ModDir, path_resolution::{ReachedFixedPoint, ResolvePathResult}, @@ -61,7 +62,7 @@ pub(super) fn collect_defs( db: &dyn DefDatabase, def_map: DefMap, tree_id: TreeId, - crate_local_def_map: Option>, + crate_local_def_map: Option<&LocalDefMap>, ) -> (DefMap, LocalDefMap) { let krate = &def_map.krate.data(db); let cfg_options = def_map.krate.cfg_options(db); @@ -216,7 +217,7 @@ struct DefCollector<'a> { def_map: DefMap, local_def_map: LocalDefMap, /// Set only in case of blocks. - crate_local_def_map: Option>, + crate_local_def_map: Option<&'a LocalDefMap>, // The dependencies of the current crate, including optional deps like `test`. deps: FxHashMap, glob_imports: FxHashMap>, @@ -533,7 +534,7 @@ fn inject_prelude(&mut self) { ); let (per_ns, _) = self.def_map.resolve_path( - self.crate_local_def_map.as_deref().unwrap_or(&self.local_def_map), + self.crate_local_def_map.unwrap_or(&self.local_def_map), self.db, DefMap::ROOT, &path, @@ -556,7 +557,7 @@ fn inject_prelude(&mut self) { } fn local_def_map(&mut self) -> &LocalDefMap { - self.crate_local_def_map.as_deref().unwrap_or(&self.local_def_map) + self.crate_local_def_map.unwrap_or(&self.local_def_map) } /// Adds a definition of procedural macro `name` to the root module. @@ -688,7 +689,7 @@ fn define_macro_def( let vis = self .def_map .resolve_visibility( - self.crate_local_def_map.as_deref().unwrap_or(&self.local_def_map), + self.crate_local_def_map.unwrap_or(&self.local_def_map), self.db, module_id, vis, @@ -731,7 +732,7 @@ fn import_macros_from_extern_crate( names: Option>, extern_crate: Option, ) { - let def_map = self.db.crate_def_map(krate); + let def_map = crate_def_map(self.db, krate); // `#[macro_use]` brings macros into macro_use prelude. Yes, even non-`macro_rules!` // macros. let root_scope = &def_map[DefMap::ROOT].scope; @@ -813,7 +814,7 @@ fn resolve_import(&self, module_id: LocalModuleId, import: &Import) -> PartialRe tracing::debug!("resolving import: {:?} ({:?})", import, self.def_map.data.edition); let ResolvePathResult { resolved_def, segment_index, reached_fixedpoint, prefix_info } = self.def_map.resolve_path_fp_with_macro( - self.crate_local_def_map.as_deref().unwrap_or(&self.local_def_map), + self.crate_local_def_map.unwrap_or(&self.local_def_map), self.db, ResolveMode::Import, module_id, @@ -852,7 +853,7 @@ fn record_resolved_import(&mut self, directive: &ImportDirective) { let vis = self .def_map .resolve_visibility( - self.crate_local_def_map.as_deref().unwrap_or(&self.local_def_map), + self.crate_local_def_map.unwrap_or(&self.local_def_map), self.db, module_id, &directive.import.visibility, @@ -1280,7 +1281,7 @@ enum Resolved { }; let resolver = |path: &_| { let resolved_res = self.def_map.resolve_path_fp_with_macro( - self.crate_local_def_map.as_deref().unwrap_or(&self.local_def_map), + self.crate_local_def_map.unwrap_or(&self.local_def_map), self.db, ResolveMode::Other, directive.module_id, @@ -1347,7 +1348,7 @@ enum Resolved { ); // Record its helper attributes. if def_id.krate != self.def_map.krate { - let def_map = self.db.crate_def_map(def_id.krate); + let def_map = crate_def_map(self.db, def_id.krate); if let Some(helpers) = def_map.data.exported_derives.get(&def_id) { self.def_map .derive_helpers_in_scope @@ -1593,7 +1594,7 @@ fn finish(mut self) -> (DefMap, LocalDefMap) { self.def_map.krate, |path| { let resolved_res = self.def_map.resolve_path_fp_with_macro( - self.crate_local_def_map.as_deref().unwrap_or(&self.local_def_map), + self.crate_local_def_map.unwrap_or(&self.local_def_map), self.db, ResolveMode::Other, directive.module_id, @@ -1742,11 +1743,8 @@ fn collect(&mut self, items: &[ModItem], container: ItemContainerId) { let module = self.def_collector.def_map.module_id(module_id); let def_map = &mut self.def_collector.def_map; - let local_def_map = self - .def_collector - .crate_local_def_map - .as_deref() - .unwrap_or(&self.def_collector.local_def_map); + let local_def_map = + self.def_collector.crate_local_def_map.unwrap_or(&self.def_collector.local_def_map); match item { ModItem::Mod(m) => self.collect_module(m, &attrs), @@ -2173,10 +2171,7 @@ fn push_child_module( let def_map = &mut self.def_collector.def_map; let vis = def_map .resolve_visibility( - self.def_collector - .crate_local_def_map - .as_deref() - .unwrap_or(&self.def_collector.local_def_map), + self.def_collector.crate_local_def_map.unwrap_or(&self.def_collector.local_def_map), self.def_collector.db, self.module_id, visibility, diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs index 708ae9c8f02e..2ad5e9eca08f 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs @@ -18,14 +18,16 @@ }; use span::Edition; use stdx::TupleExt; -use triomphe::Arc; use crate::{ AdtId, LocalModuleId, ModuleDefId, db::DefDatabase, item_scope::{BUILTIN_SCOPE, ImportOrExternCrate}, item_tree::FieldsShape, - nameres::{BlockInfo, BuiltinShadowMode, DefMap, LocalDefMap, MacroSubNs, sub_namespace_match}, + nameres::{ + BlockInfo, BuiltinShadowMode, DefMap, LocalDefMap, MacroSubNs, crate_def_map, + sub_namespace_match, + }, per_ns::PerNs, visibility::{RawVisibility, Visibility}, }; @@ -175,7 +177,6 @@ pub(super) fn resolve_path_fp_with_macro( return result; } - let mut arc; let mut current_map = self; let mut merge = |new: ResolvePathResult| { @@ -197,8 +198,7 @@ pub(super) fn resolve_path_fp_with_macro( Some(block) if original_module == Self::ROOT => { // Block modules "inherit" names from its parent module. original_module = block.parent.local_id; - arc = block.parent.def_map(db, current_map.krate); - current_map = &arc; + current_map = block.parent.def_map(db, current_map.krate); } // Proper (non-block) modules, including those in block `DefMap`s, don't. _ => { @@ -206,8 +206,7 @@ pub(super) fn resolve_path_fp_with_macro( // A module inside a block. Do not resolve items declared in upper blocks, but we do need to get // the prelude items (which are not inserted into blocks because they can be overridden there). original_module = Self::ROOT; - arc = db.crate_def_map(self.krate); - current_map = &arc; + current_map = crate_def_map(db, self.krate); let new = current_map.resolve_path_fp_in_all_preludes( local_def_map, @@ -255,7 +254,7 @@ pub(super) fn resolve_path_fp_with_macro_single( cov_mark::hit!(macro_dollar_crate_self); PerNs::types(self.crate_root().into(), Visibility::Public, None) } else { - let def_map = db.crate_def_map(krate); + let def_map = crate_def_map(db, krate); let module = def_map.module_id(Self::ROOT); cov_mark::hit!(macro_dollar_crate_other); PerNs::types(module.into(), Visibility::Public, None) @@ -314,7 +313,7 @@ pub(super) fn resolve_path_fp_with_macro_single( // Adjust `local_id` to `self`, i.e. the nearest non-block module. if def_map.module_id(local_id).is_block_module() { (ext, local_id) = adjust_to_nearest_non_block_module(db, def_map, local_id); - def_map = &ext; + def_map = ext; } // Go up the module tree but skip block modules as `super` always refers to the @@ -327,7 +326,7 @@ pub(super) fn resolve_path_fp_with_macro_single( if def_map.module_id(local_id).is_block_module() { (ext, local_id) = adjust_to_nearest_non_block_module(db, def_map, local_id); - def_map = &ext; + def_map = ext; } } else { stdx::always!(def_map.block.is_none()); @@ -772,7 +771,7 @@ fn resolve_in_prelude(&self, db: &dyn DefDatabase, name: &Name) -> PerNs { } else { // Extend lifetime keep = prelude.def_map(db); - &keep + keep }; def_map[prelude.local_id].scope.get(name) } else { @@ -782,25 +781,23 @@ fn resolve_in_prelude(&self, db: &dyn DefDatabase, name: &Name) -> PerNs { } /// Given a block module, returns its nearest non-block module and the `DefMap` it belongs to. -fn adjust_to_nearest_non_block_module( - db: &dyn DefDatabase, - def_map: &DefMap, +fn adjust_to_nearest_non_block_module<'db>( + db: &'db dyn DefDatabase, + def_map: &'db DefMap, mut local_id: LocalModuleId, -) -> (Arc, LocalModuleId) { +) -> (&'db DefMap, LocalModuleId) { // INVARIANT: `local_id` in `def_map` must be a block module. stdx::always!(def_map.module_id(local_id).is_block_module()); - let mut ext; // This needs to be a local variable due to our mighty lifetime. let mut def_map = def_map; loop { let BlockInfo { parent, .. } = def_map.block.expect("block module without parent module"); - ext = parent.def_map(db, def_map.krate); - def_map = &ext; + def_map = parent.def_map(db, def_map.krate); local_id = parent.local_id; if !parent.is_block_module() { - return (ext, local_id); + return (def_map, local_id); } } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs index 3fd095a9a98a..4a7974c4fa15 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs @@ -7,20 +7,25 @@ use base_db::RootQueryDb; use expect_test::{Expect, expect}; use test_fixture::WithFixture; -use triomphe::Arc; -use crate::{db::DefDatabase, nameres::DefMap, test_db::TestDB}; +use crate::{ + nameres::{DefMap, crate_def_map}, + test_db::TestDB, +}; -fn compute_crate_def_map(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> Arc { +fn compute_crate_def_map( + #[rust_analyzer::rust_fixture] ra_fixture: &str, + cb: impl FnOnce(&DefMap), +) { let db = TestDB::with_files(ra_fixture); let krate = db.fetch_test_crate(); - db.crate_def_map(krate) + cb(crate_def_map(&db, krate)); } fn render_crate_def_map(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> String { let db = TestDB::with_files(ra_fixture); let krate = db.fetch_test_crate(); - db.crate_def_map(krate).dump(&db) + crate_def_map(&db, krate).dump(&db) } fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs index 179a9c8fec21..948e8bed66de 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs @@ -7,24 +7,37 @@ use test_fixture::WithFixture; use triomphe::Arc; -use crate::{AdtId, ModuleDefId, db::DefDatabase, nameres::tests::TestDB}; +use crate::{ + AdtId, ModuleDefId, + db::DefDatabase, + nameres::{crate_def_map, tests::TestDB}, +}; -fn check_def_map_is_not_recomputed(ra_fixture_initial: &str, ra_fixture_change: &str) { +fn check_def_map_is_not_recomputed( + #[rust_analyzer::rust_fixture] ra_fixture_initial: &str, + #[rust_analyzer::rust_fixture] ra_fixture_change: &str, +) { let (mut db, pos) = TestDB::with_position(ra_fixture_initial); let krate = db.fetch_test_crate(); { let events = db.log_executed(|| { - db.crate_def_map(krate); + crate_def_map(&db, krate); }); - assert!(format!("{events:?}").contains("crate_def_map"), "{events:#?}") + assert!( + format!("{events:?}").contains("crate_local_def_map"), + "no crate def map computed:\n{events:#?}", + ) } db.set_file_text(pos.file_id.file_id(&db), ra_fixture_change); { let events = db.log_executed(|| { - db.crate_def_map(krate); + crate_def_map(&db, krate); }); - assert!(!format!("{events:?}").contains("crate_def_map"), "{events:#?}") + assert!( + !format!("{events:?}").contains("crate_local_def_map"), + "crate def map invalidated:\n{events:#?}", + ) } } @@ -44,7 +57,7 @@ pub fn foo() {} ); for &krate in db.all_crates().iter() { - db.crate_def_map(krate); + crate_def_map(&db, krate); } let all_crates_before = db.all_crates(); @@ -94,11 +107,11 @@ pub fn foo() {} let events = db.log_executed(|| { for &krate in db.all_crates().iter() { - db.crate_def_map(krate); + crate_def_map(&db, krate); } }); let invalidated_def_maps = - events.iter().filter(|event| event.contains("crate_def_map")).count(); + events.iter().filter(|event| event.contains("crate_local_def_map")).count(); assert_eq!(invalidated_def_maps, 1, "{events:#?}") } @@ -330,7 +343,7 @@ fn quux() { 1$0 } let krate = db.test_crate(); { let events = db.log_executed(|| { - let crate_def_map = db.crate_def_map(krate); + let crate_def_map = crate_def_map(&db, krate); let (_, module_data) = crate_def_map.modules.iter().last().unwrap(); assert_eq!(module_data.scope.resolutions().count(), 4); }); @@ -352,7 +365,7 @@ fn quux() { 92 } { let events = db.log_executed(|| { - let crate_def_map = db.crate_def_map(krate); + let crate_def_map = crate_def_map(&db, krate); let (_, module_data) = crate_def_map.modules.iter().last().unwrap(); assert_eq!(module_data.scope.resolutions().count(), 4); }); @@ -403,7 +416,7 @@ impl Tr for () {} { let events = db.log_executed(|| { - let crate_def_map = db.crate_def_map(krate); + let crate_def_map = crate_def_map(&db, krate); let (_, module_data) = crate_def_map.modules.iter().last().unwrap(); assert_eq!(module_data.scope.resolutions().count(), 8); assert_eq!(module_data.scope.impls().count(), 1); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/macros.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/macros.rs index 5f8a01523d82..3cba88ec2f17 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/macros.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/macros.rs @@ -736,7 +736,7 @@ macro_rules! foo { #[test] fn macro_dollar_crate_is_correct_in_derive_meta() { - let map = compute_crate_def_map( + compute_crate_def_map( r#" //- minicore: derive, clone //- /main.rs crate:main deps:lib @@ -753,13 +753,13 @@ macro_rules! foo { pub use core::clone::Clone; "#, + |map| assert_eq!(map.modules[DefMap::ROOT].scope.impls().len(), 1), ); - assert_eq!(map.modules[DefMap::ROOT].scope.impls().len(), 1); } #[test] fn expand_derive() { - let map = compute_crate_def_map( + compute_crate_def_map( r#" //- /main.rs crate:main deps:core use core::Copy; @@ -775,8 +775,8 @@ fn expand_derive() { #[rustc_builtin_macro] pub macro Clone {} "#, + |map| assert_eq!(map.modules[DefMap::ROOT].scope.impls().len(), 2), ); - assert_eq!(map.modules[DefMap::ROOT].scope.impls().len(), 2); } #[test] @@ -803,7 +803,7 @@ pub trait Clone {} fn builtin_derive_with_unresolved_attributes_fall_back() { // Tests that we still resolve derives after ignoring an unresolved attribute. cov_mark::check!(unresolved_attribute_fallback); - let map = compute_crate_def_map( + compute_crate_def_map( r#" //- /main.rs crate:main deps:core use core::{Clone, derive}; @@ -818,8 +818,8 @@ fn builtin_derive_with_unresolved_attributes_fall_back() { #[rustc_builtin_macro] pub macro Clone {} "#, + |map| assert_eq!(map.modules[DefMap::ROOT].scope.impls().len(), 1), ); - assert_eq!(map.modules[DefMap::ROOT].scope.impls().len(), 1); } #[test] @@ -1096,7 +1096,7 @@ pub fn derive_macro_2(_item: TokenStream) -> TokenStream { "#, ); let krate = *db.all_crates().last().expect("no crate graph present"); - let def_map = db.crate_def_map(krate); + let def_map = crate_def_map(&db, krate); assert_eq!(def_map.data.exported_derives.len(), 1); match def_map.data.exported_derives.values().next() { @@ -1446,7 +1446,7 @@ fn proc_attr(a: TokenStream, b: TokenStream) -> TokenStream { a } "#, ); let krate = *db.all_crates().last().expect("no crate graph present"); - let def_map = db.crate_def_map(krate); + let def_map = crate_def_map(&db, krate); let root_module = &def_map[DefMap::ROOT].scope; assert!( @@ -1544,7 +1544,7 @@ macro_rules! foo { #[test] fn macro_sub_namespace() { - let map = compute_crate_def_map( + compute_crate_def_map( r#" //- minicore: derive, clone macro_rules! Clone { () => {} } @@ -1553,8 +1553,8 @@ macro_rules! derive { () => {} } #[derive(Clone)] struct S; "#, + |map| assert_eq!(map.modules[DefMap::ROOT].scope.impls().len(), 1), ); - assert_eq!(map.modules[DefMap::ROOT].scope.impls().len(), 1); } #[test] diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/mod_resolution.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/mod_resolution.rs index f5bacc5327c7..9c97e42f4fd3 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/mod_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/mod_resolution.rs @@ -839,6 +839,7 @@ fn circular_mods() { #[path = "./foo.rs"] mod foo; "#, + |_| (), ); compute_crate_def_map( @@ -852,6 +853,7 @@ fn circular_mods() { #[path = "./foo.rs"] mod foo; "#, + |_| (), ); } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs index 8a8d17018c1b..16988ddf04b2 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs @@ -34,30 +34,30 @@ item_scope::{BUILTIN_SCOPE, BuiltinShadowMode, ImportOrExternCrate, ImportOrGlob, ItemScope}, item_tree::ImportAlias, lang_item::LangItemTarget, - nameres::{DefMap, LocalDefMap, MacroSubNs, ResolvePathResultPrefixInfo}, + nameres::{DefMap, LocalDefMap, MacroSubNs, ResolvePathResultPrefixInfo, block_def_map}, per_ns::PerNs, type_ref::LifetimeRef, visibility::{RawVisibility, Visibility}, }; #[derive(Debug, Clone)] -pub struct Resolver { +pub struct Resolver<'db> { /// The stack of scopes, where the inner-most scope is the last item. /// /// When using, you generally want to process the scopes in reverse order, /// there's `scopes` *method* for that. - scopes: Vec, - module_scope: ModuleItemMap, + scopes: Vec>, + module_scope: ModuleItemMap<'db>, } #[derive(Clone)] -struct ModuleItemMap { - def_map: Arc, - local_def_map: Arc, +struct ModuleItemMap<'db> { + def_map: &'db DefMap, + local_def_map: &'db LocalDefMap, module_id: LocalModuleId, } -impl fmt::Debug for ModuleItemMap { +impl fmt::Debug for ModuleItemMap<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("ModuleItemMap").field("module_id", &self.module_id).finish() } @@ -80,9 +80,9 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { } #[derive(Debug, Clone)] -enum Scope { +enum Scope<'db> { /// All the items and imported names of a module - BlockScope(ModuleItemMap), + BlockScope(ModuleItemMap<'db>), /// Brings the generic parameters of an item into scope as well as the `Self` type alias / /// generic for ADTs and impls. GenericParams { def: GenericDefId, params: Arc }, @@ -133,7 +133,7 @@ pub enum LifetimeNs { LifetimeParam(LifetimeParamId), } -impl Resolver { +impl<'db> Resolver<'db> { /// Resolve known trait from std, like `std::futures::Future` pub fn resolve_known_trait(&self, db: &dyn DefDatabase, path: &ModPath) -> Option { let res = self.resolve_module_path(db, path, BuiltinShadowMode::Other).take_types()?; @@ -580,7 +580,7 @@ pub fn names_in_scope( for scope in self.scopes() { scope.process_names(&mut res, db); } - let ModuleItemMap { ref def_map, module_id, ref local_def_map } = self.module_scope; + let ModuleItemMap { def_map, module_id, local_def_map } = self.module_scope; // FIXME: should we provide `self` here? // f( // Name::self_param(), @@ -842,14 +842,14 @@ pub fn rename_will_conflict_with_renamed( #[must_use] pub fn update_to_inner_scope( &mut self, - db: &dyn DefDatabase, + db: &'db dyn DefDatabase, owner: DefWithBodyId, expr_id: ExprId, ) -> UpdateGuard { #[inline(always)] - fn append_expr_scope( - db: &dyn DefDatabase, - resolver: &mut Resolver, + fn append_expr_scope<'db>( + db: &'db dyn DefDatabase, + resolver: &mut Resolver<'db>, owner: DefWithBodyId, expr_scopes: &Arc, scope_id: ScopeId, @@ -863,7 +863,7 @@ fn append_expr_scope( scope_id, })); if let Some(block) = expr_scopes.block(scope_id) { - let def_map = db.block_def_map(block); + let def_map = block_def_map(db, block); let local_def_map = block.lookup(db).module.only_local_def_map(db); resolver.scopes.push(Scope::BlockScope(ModuleItemMap { def_map, @@ -945,8 +945,8 @@ fn hygiene_info( pub struct UpdateGuard(usize); -impl Resolver { - fn scopes(&self) -> impl Iterator { +impl<'db> Resolver<'db> { + fn scopes(&self) -> impl Iterator> { self.scopes.iter().rev() } @@ -970,12 +970,12 @@ fn resolve_module_path( fn item_scope_(&self) -> (&DefMap, &LocalDefMap, LocalModuleId) { self.scopes() .find_map(|scope| match scope { - Scope::BlockScope(m) => Some((&*m.def_map, &*m.local_def_map, m.module_id)), + Scope::BlockScope(m) => Some((m.def_map, m.local_def_map, m.module_id)), _ => None, }) .unwrap_or(( - &self.module_scope.def_map, - &self.module_scope.local_def_map, + self.module_scope.def_map, + self.module_scope.local_def_map, self.module_scope.module_id, )) } @@ -992,8 +992,8 @@ pub enum ScopeDef { Label(LabelId), } -impl Scope { - fn process_names(&self, acc: &mut ScopeNames, db: &dyn DefDatabase) { +impl<'db> Scope<'db> { + fn process_names(&self, acc: &mut ScopeNames, db: &'db dyn DefDatabase) { match self { Scope::BlockScope(m) => { m.def_map[m.module_id].scope.entries().for_each(|(name, def)| { @@ -1047,7 +1047,11 @@ fn process_names(&self, acc: &mut ScopeNames, db: &dyn DefDatabase) { } } -pub fn resolver_for_expr(db: &dyn DefDatabase, owner: DefWithBodyId, expr_id: ExprId) -> Resolver { +pub fn resolver_for_expr( + db: &dyn DefDatabase, + owner: DefWithBodyId, + expr_id: ExprId, +) -> Resolver<'_> { let r = owner.resolver(db); let scopes = db.expr_scopes(owner); let scope_id = scopes.scope_for(expr_id); @@ -1058,25 +1062,25 @@ pub fn resolver_for_scope( db: &dyn DefDatabase, owner: DefWithBodyId, scope_id: Option, -) -> Resolver { +) -> Resolver<'_> { let r = owner.resolver(db); let scopes = db.expr_scopes(owner); resolver_for_scope_(db, scopes, scope_id, r, owner) } -fn resolver_for_scope_( - db: &dyn DefDatabase, +fn resolver_for_scope_<'db>( + db: &'db dyn DefDatabase, scopes: Arc, scope_id: Option, - mut r: Resolver, + mut r: Resolver<'db>, owner: DefWithBodyId, -) -> Resolver { +) -> Resolver<'db> { let scope_chain = scopes.scope_chain(scope_id).collect::>(); r.scopes.reserve(scope_chain.len()); for scope in scope_chain.into_iter().rev() { if let Some(block) = scopes.block(scope) { - let def_map = db.block_def_map(block); + let def_map = block_def_map(db, block); let local_def_map = block.lookup(db).module.only_local_def_map(db); r = r.push_block_scope(def_map, local_def_map); // FIXME: This adds as many module scopes as there are blocks, but resolving in each @@ -1092,18 +1096,26 @@ fn resolver_for_scope_( r } -impl Resolver { - fn push_scope(mut self, scope: Scope) -> Resolver { +impl<'db> Resolver<'db> { + fn push_scope(mut self, scope: Scope<'db>) -> Resolver<'db> { self.scopes.push(scope); self } - fn push_generic_params_scope(self, db: &dyn DefDatabase, def: GenericDefId) -> Resolver { + fn push_generic_params_scope( + self, + db: &'db dyn DefDatabase, + def: GenericDefId, + ) -> Resolver<'db> { let params = db.generic_params(def); self.push_scope(Scope::GenericParams { def, params }) } - fn push_block_scope(self, def_map: Arc, local_def_map: Arc) -> Resolver { + fn push_block_scope( + self, + def_map: &'db DefMap, + local_def_map: &'db LocalDefMap, + ) -> Resolver<'db> { self.push_scope(Scope::BlockScope(ModuleItemMap { def_map, local_def_map, @@ -1116,19 +1128,19 @@ fn push_expr_scope( owner: DefWithBodyId, expr_scopes: Arc, scope_id: ScopeId, - ) -> Resolver { + ) -> Resolver<'db> { self.push_scope(Scope::ExprScope(ExprScope { owner, expr_scopes, scope_id })) } } -impl ModuleItemMap { +impl<'db> ModuleItemMap<'db> { fn resolve_path_in_value_ns( &self, - db: &dyn DefDatabase, + db: &'db dyn DefDatabase, path: &ModPath, ) -> Option<(ResolveValueResult, ResolvePathResultPrefixInfo)> { let (module_def, unresolved_idx, prefix_info) = self.def_map.resolve_path_locally( - &self.local_def_map, + self.local_def_map, db, self.module_id, path, @@ -1167,7 +1179,7 @@ fn resolve_path_in_type_ns( ) -> Option<(TypeNs, Option, Option, ResolvePathResultPrefixInfo)> { let (module_def, idx, prefix_info) = self.def_map.resolve_path_locally( - &self.local_def_map, + self.local_def_map, db, self.module_id, path, @@ -1263,11 +1275,11 @@ fn add_local(&mut self, name: &Name, binding: BindingId) { pub trait HasResolver: Copy { /// Builds a resolver for type references inside this def. - fn resolver(self, db: &dyn DefDatabase) -> Resolver; + fn resolver(self, db: &dyn DefDatabase) -> Resolver<'_>; } impl HasResolver for ModuleId { - fn resolver(self, db: &dyn DefDatabase) -> Resolver { + fn resolver(self, db: &dyn DefDatabase) -> Resolver<'_> { let (mut def_map, local_def_map) = self.local_def_map(db); let mut module_id = self.local_id; @@ -1289,21 +1301,17 @@ fn resolver(self, db: &dyn DefDatabase) -> Resolver { } let mut resolver = Resolver { scopes: Vec::with_capacity(modules.len()), - module_scope: ModuleItemMap { - def_map, - local_def_map: local_def_map.clone(), - module_id, - }, + module_scope: ModuleItemMap { def_map, local_def_map, module_id }, }; for def_map in modules.into_iter().rev() { - resolver = resolver.push_block_scope(def_map, local_def_map.clone()); + resolver = resolver.push_block_scope(def_map, local_def_map); } resolver } } impl HasResolver for CrateRootModuleId { - fn resolver(self, db: &dyn DefDatabase) -> Resolver { + fn resolver(self, db: &dyn DefDatabase) -> Resolver<'_> { let (def_map, local_def_map) = self.local_def_map(db); Resolver { scopes: vec![], @@ -1313,75 +1321,75 @@ fn resolver(self, db: &dyn DefDatabase) -> Resolver { } impl HasResolver for TraitId { - fn resolver(self, db: &dyn DefDatabase) -> Resolver { + fn resolver(self, db: &dyn DefDatabase) -> Resolver<'_> { lookup_resolver(db, self).push_generic_params_scope(db, self.into()) } } impl HasResolver for TraitAliasId { - fn resolver(self, db: &dyn DefDatabase) -> Resolver { + fn resolver(self, db: &dyn DefDatabase) -> Resolver<'_> { lookup_resolver(db, self).push_generic_params_scope(db, self.into()) } } impl + Copy> HasResolver for T { - fn resolver(self, db: &dyn DefDatabase) -> Resolver { + fn resolver(self, db: &dyn DefDatabase) -> Resolver<'_> { let def = self.into(); def.module(db).resolver(db).push_generic_params_scope(db, def.into()) } } impl HasResolver for FunctionId { - fn resolver(self, db: &dyn DefDatabase) -> Resolver { + fn resolver(self, db: &dyn DefDatabase) -> Resolver<'_> { lookup_resolver(db, self).push_generic_params_scope(db, self.into()) } } impl HasResolver for ConstId { - fn resolver(self, db: &dyn DefDatabase) -> Resolver { + fn resolver(self, db: &dyn DefDatabase) -> Resolver<'_> { lookup_resolver(db, self) } } impl HasResolver for StaticId { - fn resolver(self, db: &dyn DefDatabase) -> Resolver { + fn resolver(self, db: &dyn DefDatabase) -> Resolver<'_> { lookup_resolver(db, self) } } impl HasResolver for TypeAliasId { - fn resolver(self, db: &dyn DefDatabase) -> Resolver { + fn resolver(self, db: &dyn DefDatabase) -> Resolver<'_> { lookup_resolver(db, self).push_generic_params_scope(db, self.into()) } } impl HasResolver for ImplId { - fn resolver(self, db: &dyn DefDatabase) -> Resolver { + fn resolver(self, db: &dyn DefDatabase) -> Resolver<'_> { self.lookup(db).container.resolver(db).push_generic_params_scope(db, self.into()) } } impl HasResolver for ExternBlockId { - fn resolver(self, db: &dyn DefDatabase) -> Resolver { + fn resolver(self, db: &dyn DefDatabase) -> Resolver<'_> { // Same as parent's lookup_resolver(db, self) } } impl HasResolver for ExternCrateId { - fn resolver(self, db: &dyn DefDatabase) -> Resolver { + fn resolver(self, db: &dyn DefDatabase) -> Resolver<'_> { lookup_resolver(db, self) } } impl HasResolver for UseId { - fn resolver(self, db: &dyn DefDatabase) -> Resolver { + fn resolver(self, db: &dyn DefDatabase) -> Resolver<'_> { lookup_resolver(db, self) } } impl HasResolver for DefWithBodyId { - fn resolver(self, db: &dyn DefDatabase) -> Resolver { + fn resolver(self, db: &dyn DefDatabase) -> Resolver<'_> { match self { DefWithBodyId::ConstId(c) => c.resolver(db), DefWithBodyId::FunctionId(f) => f.resolver(db), @@ -1392,7 +1400,7 @@ fn resolver(self, db: &dyn DefDatabase) -> Resolver { } impl HasResolver for ItemContainerId { - fn resolver(self, db: &dyn DefDatabase) -> Resolver { + fn resolver(self, db: &dyn DefDatabase) -> Resolver<'_> { match self { ItemContainerId::ModuleId(it) => it.resolver(db), ItemContainerId::TraitId(it) => it.resolver(db), @@ -1403,7 +1411,7 @@ fn resolver(self, db: &dyn DefDatabase) -> Resolver { } impl HasResolver for GenericDefId { - fn resolver(self, db: &dyn DefDatabase) -> Resolver { + fn resolver(self, db: &dyn DefDatabase) -> Resolver<'_> { match self { GenericDefId::FunctionId(inner) => inner.resolver(db), GenericDefId::AdtId(adt) => adt.resolver(db), @@ -1418,13 +1426,13 @@ fn resolver(self, db: &dyn DefDatabase) -> Resolver { } impl HasResolver for EnumVariantId { - fn resolver(self, db: &dyn DefDatabase) -> Resolver { + fn resolver(self, db: &dyn DefDatabase) -> Resolver<'_> { self.lookup(db).parent.resolver(db) } } impl HasResolver for VariantId { - fn resolver(self, db: &dyn DefDatabase) -> Resolver { + fn resolver(self, db: &dyn DefDatabase) -> Resolver<'_> { match self { VariantId::EnumVariantId(it) => it.resolver(db), VariantId::StructId(it) => it.resolver(db), @@ -1434,7 +1442,7 @@ fn resolver(self, db: &dyn DefDatabase) -> Resolver { } impl HasResolver for MacroId { - fn resolver(self, db: &dyn DefDatabase) -> Resolver { + fn resolver(self, db: &dyn DefDatabase) -> Resolver<'_> { match self { MacroId::Macro2Id(it) => it.resolver(db), MacroId::MacroRulesId(it) => it.resolver(db), @@ -1444,29 +1452,29 @@ fn resolver(self, db: &dyn DefDatabase) -> Resolver { } impl HasResolver for Macro2Id { - fn resolver(self, db: &dyn DefDatabase) -> Resolver { + fn resolver(self, db: &dyn DefDatabase) -> Resolver<'_> { lookup_resolver(db, self) } } impl HasResolver for ProcMacroId { - fn resolver(self, db: &dyn DefDatabase) -> Resolver { + fn resolver(self, db: &dyn DefDatabase) -> Resolver<'_> { lookup_resolver(db, self) } } impl HasResolver for MacroRulesId { - fn resolver(self, db: &dyn DefDatabase) -> Resolver { + fn resolver(self, db: &dyn DefDatabase) -> Resolver<'_> { lookup_resolver(db, self) } } -fn lookup_resolver<'db>( - db: &(dyn DefDatabase + 'db), +fn lookup_resolver( + db: &dyn DefDatabase, lookup: impl Lookup< Database = dyn DefDatabase, Data = impl ItemTreeLoc, >, -) -> Resolver { +) -> Resolver<'_> { lookup.lookup(db).container().resolver(db) } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/test_db.rs b/src/tools/rust-analyzer/crates/hir-def/src/test_db.rs index 470975482951..6c995ab6c23f 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/test_db.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/test_db.rs @@ -15,7 +15,7 @@ use crate::{ LocalModuleId, Lookup, ModuleDefId, ModuleId, db::DefDatabase, - nameres::{DefMap, ModuleSource}, + nameres::{DefMap, ModuleSource, block_def_map, crate_def_map}, src::HasSource, }; @@ -133,7 +133,7 @@ pub(crate) fn fetch_test_crate(&self) -> Crate { pub(crate) fn module_for_file(&self, file_id: FileId) -> ModuleId { for &krate in self.relevant_crates(file_id).iter() { - let crate_def_map = self.crate_def_map(krate); + let crate_def_map = crate_def_map(self, krate); for (local_id, data) in crate_def_map.modules() { if data.origin.file_id().map(|file_id| file_id.file_id(self)) == Some(file_id) { return crate_def_map.module_id(local_id); @@ -146,16 +146,16 @@ pub(crate) fn module_for_file(&self, file_id: FileId) -> ModuleId { pub(crate) fn module_at_position(&self, position: FilePosition) -> ModuleId { let file_module = self.module_for_file(position.file_id.file_id(self)); let mut def_map = file_module.def_map(self); - let module = self.mod_at_position(&def_map, position); + let module = self.mod_at_position(def_map, position); - def_map = match self.block_at_position(&def_map, position) { + def_map = match self.block_at_position(def_map, position) { Some(it) => it, None => return def_map.module_id(module), }; loop { - let new_map = self.block_at_position(&def_map, position); + let new_map = self.block_at_position(def_map, position); match new_map { - Some(new_block) if !Arc::ptr_eq(&new_block, &def_map) => { + Some(new_block) if !std::ptr::eq(&new_block, &def_map) => { def_map = new_block; } _ => { @@ -206,7 +206,7 @@ fn mod_at_position(&self, def_map: &DefMap, position: FilePosition) -> LocalModu res } - fn block_at_position(&self, def_map: &DefMap, position: FilePosition) -> Option> { + fn block_at_position(&self, def_map: &DefMap, position: FilePosition) -> Option<&DefMap> { // Find the smallest (innermost) function in `def_map` containing the cursor. let mut size = None; let mut fn_def = None; @@ -263,7 +263,7 @@ fn block_at_position(&self, def_map: &DefMap, position: FilePosition) -> Option< let mut containing_blocks = scopes.scope_chain(Some(scope)).filter_map(|scope| scopes.block(scope)); - if let Some(block) = containing_blocks.next().map(|block| self.block_def_map(block)) { + if let Some(block) = containing_blocks.next().map(|block| block_def_map(self, block)) { return Some(block); } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/visibility.rs b/src/tools/rust-analyzer/crates/hir-def/src/visibility.rs index b42c8d383d4a..3c67ee9fe5b3 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/visibility.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/visibility.rs @@ -28,7 +28,7 @@ pub enum Visibility { impl Visibility { pub fn resolve( db: &dyn DefDatabase, - resolver: &crate::resolver::Resolver, + resolver: &crate::resolver::Resolver<'_>, raw_vis: &RawVisibility, ) -> Self { // we fall back to public visibility (i.e. fail open) if the path can't be resolved @@ -50,7 +50,7 @@ pub fn is_visible_from(self, db: &dyn DefDatabase, from_module: ModuleId) -> boo return false; } let def_map = from_module.def_map(db); - Self::is_visible_from_def_map_(db, &def_map, to_module, from_module.local_id) + Self::is_visible_from_def_map_(db, def_map, to_module, from_module.local_id) } pub(crate) fn is_visible_from_def_map( @@ -116,7 +116,7 @@ fn is_visible_from_def_map_( match def_map.parent() { Some(module) => { parent_arc = module.def_map(db); - def_map = &*parent_arc; + def_map = parent_arc; from_module = module.local_id; } // Reached the root module, nothing left to check. @@ -257,7 +257,7 @@ pub(crate) fn type_alias_visibility_query(db: &dyn DefDatabase, def: TypeAliasId } #[inline] -fn trait_vis(db: &dyn DefDatabase, resolver: &Resolver, trait_id: TraitId) -> Visibility { +fn trait_vis(db: &dyn DefDatabase, resolver: &Resolver<'_>, trait_id: TraitId) -> Visibility { let ItemLoc { id: tree_id, .. } = trait_id.lookup(db); let item_tree = tree_id.item_tree(db); let tr_def = &item_tree[tree_id.value]; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs index d1a1e135ffff..f903b06d65e7 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs @@ -91,7 +91,7 @@ fn from(value: MirEvalError) -> Self { pub(crate) fn path_to_const<'g>( db: &dyn HirDatabase, - resolver: &Resolver, + resolver: &Resolver<'_>, path: &Path, mode: ParamLoweringMode, args: impl FnOnce() -> &'g Generics, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs index 57106412765a..e4a23cbbacff 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs @@ -480,7 +480,7 @@ struct FilterMapNextChecker { } impl FilterMapNextChecker { - fn new(resolver: &hir_def::resolver::Resolver, db: &dyn HirDatabase) -> Self { + 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 LangItem::IteratorNext .resolve_function(db, resolver.krate()) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs index 6323d8b71b72..9a3c331ef320 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs @@ -73,7 +73,7 @@ pub(crate) struct MatchCheckCtx<'db> { impl<'db> MatchCheckCtx<'db> { pub(crate) fn new(module: ModuleId, body: DefWithBodyId, db: &'db dyn HirDatabase) -> Self { - let def_map = db.crate_def_map(module.krate()); + let def_map = module.crate_def_map(db); let exhaustive_patterns = def_map.is_unstable_feature_enabled(&sym::exhaustive_patterns); Self { module, body, db, exhaustive_patterns } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs index 73b99db72684..6d5aaa34725d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs @@ -131,28 +131,28 @@ pub fn unsafe_operations( visitor.walk_expr(current); } -struct UnsafeVisitor<'a> { - db: &'a dyn HirDatabase, - infer: &'a InferenceResult, - body: &'a Body, - resolver: Resolver, +struct UnsafeVisitor<'db> { + db: &'db dyn HirDatabase, + infer: &'db InferenceResult, + body: &'db Body, + resolver: Resolver<'db>, def: DefWithBodyId, inside_unsafe_block: InsideUnsafeBlock, inside_assignment: bool, inside_union_destructure: bool, - callback: &'a mut dyn FnMut(UnsafeDiagnostic), + callback: &'db mut dyn FnMut(UnsafeDiagnostic), def_target_features: TargetFeatures, // FIXME: This needs to be the edition of the span of each call. edition: Edition, } -impl<'a> UnsafeVisitor<'a> { +impl<'db> UnsafeVisitor<'db> { fn new( - db: &'a dyn HirDatabase, - infer: &'a InferenceResult, - body: &'a Body, + db: &'db dyn HirDatabase, + infer: &'db InferenceResult, + body: &'db Body, def: DefWithBodyId, - unsafe_expr_cb: &'a mut dyn FnMut(UnsafeDiagnostic), + unsafe_expr_cb: &'db mut dyn FnMut(UnsafeDiagnostic), ) -> Self { let resolver = def.resolver(db); let def_target_features = match def { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs index 106b996b13ef..ed8d8dc26240 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs @@ -9,8 +9,8 @@ }; use chalk_solve::rust_ir::InlineBound; use hir_def::{ - AssocItemId, ConstId, FunctionId, GenericDefId, HasModule, TraitId, TypeAliasId, - lang_item::LangItem, signatures::TraitFlags, + AssocItemId, ConstId, CrateRootModuleId, FunctionId, GenericDefId, HasModule, TraitId, + TypeAliasId, lang_item::LangItem, signatures::TraitFlags, }; use rustc_hash::FxHashSet; use smallvec::SmallVec; @@ -343,7 +343,7 @@ fn dyn_compatibility_violation_for_assoc_item( }) } AssocItemId::TypeAliasId(it) => { - let def_map = db.crate_def_map(trait_.krate(db)); + let def_map = CrateRootModuleId::from(trait_.krate(db)).def_map(db); if def_map.is_unstable_feature_enabled(&intern::sym::generic_associated_type_extended) { ControlFlow::Continue(()) } else { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs index f0ec31db8bb9..e698fb201cb8 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -594,16 +594,16 @@ fn index(&self, b: BindingId) -> &Ty { /// The inference context contains all information needed during type inference. #[derive(Clone, Debug)] -pub(crate) struct InferenceContext<'a> { - pub(crate) db: &'a dyn HirDatabase, +pub(crate) struct InferenceContext<'db> { + pub(crate) db: &'db dyn HirDatabase, pub(crate) owner: DefWithBodyId, - pub(crate) body: &'a Body, + pub(crate) body: &'db Body, /// Generally you should not resolve things via this resolver. Instead create a TyLoweringContext /// and resolve the path via its methods. This will ensure proper error reporting. - pub(crate) resolver: Resolver, + pub(crate) resolver: Resolver<'db>, generic_def: GenericDefId, generics: OnceCell, - table: unify::InferenceTable<'a>, + table: unify::InferenceTable<'db>, /// The traits in scope, disregarding block modules. This is used for caching purposes. traits_in_scope: FxHashSet, pub(crate) result: InferenceResult, @@ -695,12 +695,12 @@ enum ImplTraitReplacingMode { TypeAlias, } -impl<'a> InferenceContext<'a> { +impl<'db> InferenceContext<'db> { fn new( - db: &'a dyn HirDatabase, + db: &'db dyn HirDatabase, owner: DefWithBodyId, - body: &'a Body, - resolver: Resolver, + body: &'db Body, + resolver: Resolver<'db>, ) -> Self { let trait_env = db.trait_environment_for_body(owner); InferenceContext { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/diagnostics.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/diagnostics.rs index e3c4f5562d5c..003364d4336b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/diagnostics.rs @@ -61,7 +61,7 @@ impl<'a> InferenceTyLoweringContext<'a> { #[inline] pub(super) fn new( db: &'a dyn HirDatabase, - resolver: &'a Resolver, + resolver: &'a Resolver<'_>, store: &'a ExpressionStore, diagnostics: &'a Diagnostics, source: InferenceTyDiagnosticSource, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs index 9def39d5f979..ea8e7cc2be90 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -42,7 +42,6 @@ use hir_expand::name::Name; use la_arena::{Arena, ArenaMap}; use rustc_hash::FxHashSet; -use rustc_pattern_analysis::Captures; use stdx::{impl_from, never}; use triomphe::{Arc, ThinArc}; @@ -151,10 +150,10 @@ pub(crate) fn for_fn_ret() -> LifetimeElisionKind { } #[derive(Debug)] -pub struct TyLoweringContext<'a> { - pub db: &'a dyn HirDatabase, - resolver: &'a Resolver, - store: &'a ExpressionStore, +pub struct TyLoweringContext<'db> { + pub db: &'db dyn HirDatabase, + resolver: &'db Resolver<'db>, + store: &'db ExpressionStore, def: GenericDefId, generics: OnceCell, in_binders: DebruijnIndex, @@ -170,11 +169,11 @@ pub struct TyLoweringContext<'a> { lifetime_elision: LifetimeElisionKind, } -impl<'a> TyLoweringContext<'a> { +impl<'db> TyLoweringContext<'db> { pub fn new( - db: &'a dyn HirDatabase, - resolver: &'a Resolver, - store: &'a ExpressionStore, + db: &'db dyn HirDatabase, + resolver: &'db Resolver<'db>, + store: &'db ExpressionStore, def: GenericDefId, lifetime_elision: LifetimeElisionKind, ) -> Self { @@ -1176,13 +1175,13 @@ fn generic_predicates_filtered_by( /// Generate implicit `: Sized` predicates for all generics that has no `?Sized` bound. /// Exception is Self of a trait def. -fn implicitly_sized_clauses<'a, 'subst: 'a>( - db: &dyn HirDatabase, +fn implicitly_sized_clauses<'db, 'a, 'subst: 'a>( + db: &'db dyn HirDatabase, def: GenericDefId, explicitly_unsized_tys: &'a FxHashSet, substitution: &'subst Substitution, - resolver: &Resolver, -) -> Option + Captures<'a> + Captures<'subst>> { + resolver: &Resolver<'db>, +) -> Option> { 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); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs index 8e549ca0cbd5..3b295d41e6e3 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs @@ -10,7 +10,7 @@ use hir_def::{ AssocItemId, BlockId, ConstId, FunctionId, HasModule, ImplId, ItemContainerId, Lookup, ModuleId, TraitId, - nameres::{DefMap, assoc::ImplItems}, + nameres::{DefMap, assoc::ImplItems, block_def_map, crate_def_map}, signatures::{ConstFlags, EnumFlags, FnFlags, StructFlags, TraitFlags, TypeAliasFlags}, }; use hir_expand::name::Name; @@ -152,7 +152,7 @@ pub(crate) fn trait_impls_in_crate_query(db: &dyn HirDatabase, krate: Crate) -> let _p = tracing::info_span!("trait_impls_in_crate_query", ?krate).entered(); let mut impls = FxHashMap::default(); - Self::collect_def_map(db, &mut impls, &db.crate_def_map(krate)); + Self::collect_def_map(db, &mut impls, crate_def_map(db, krate)); Arc::new(Self::finish(impls)) } @@ -164,7 +164,7 @@ pub(crate) fn trait_impls_in_block_query( let _p = tracing::info_span!("trait_impls_in_block_query").entered(); let mut impls = FxHashMap::default(); - Self::collect_def_map(db, &mut impls, &db.block_def_map(block)); + Self::collect_def_map(db, &mut impls, block_def_map(db, block)); if impls.is_empty() { None } else { Some(Arc::new(Self::finish(impls))) } } @@ -214,7 +214,7 @@ fn collect_def_map(db: &dyn HirDatabase, map: &mut TraitFpMapCollector, def_map: for konst in module_data.scope.unnamed_consts() { let body = db.body(konst.into()); for (_, block_def_map) in body.blocks(db) { - Self::collect_def_map(db, map, &block_def_map); + Self::collect_def_map(db, map, block_def_map); } } } @@ -280,8 +280,8 @@ pub(crate) fn inherent_impls_in_crate_query(db: &dyn HirDatabase, krate: Crate) let _p = tracing::info_span!("inherent_impls_in_crate_query", ?krate).entered(); let mut impls = Self { map: FxHashMap::default(), invalid_impls: Vec::default() }; - let crate_def_map = db.crate_def_map(krate); - impls.collect_def_map(db, &crate_def_map); + let crate_def_map = crate_def_map(db, krate); + impls.collect_def_map(db, crate_def_map); impls.shrink_to_fit(); Arc::new(impls) @@ -294,8 +294,8 @@ pub(crate) fn inherent_impls_in_block_query( let _p = tracing::info_span!("inherent_impls_in_block_query").entered(); let mut impls = Self { map: FxHashMap::default(), invalid_impls: Vec::default() }; - let block_def_map = db.block_def_map(block); - impls.collect_def_map(db, &block_def_map); + let block_def_map = block_def_map(db, block); + impls.collect_def_map(db, block_def_map); impls.shrink_to_fit(); if impls.map.is_empty() && impls.invalid_impls.is_empty() { @@ -337,7 +337,7 @@ fn collect_def_map(&mut self, db: &dyn HirDatabase, def_map: &DefMap) { for konst in module_data.scope.unnamed_consts() { let body = db.body(konst.into()); for (_, block_def_map) in body.blocks(db) { - self.collect_def_map(db, &block_def_map); + self.collect_def_map(db, block_def_map); } } } @@ -1399,7 +1399,7 @@ fn iterate_inherent_methods( )?; } - block = db.block_def_map(block_id).parent().and_then(|module| module.containing_block()); + block = block_def_map(db, block_id).parent().and_then(|module| module.containing_block()); } for krate in def_crates { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs index 26ef95d264be..7cf948b178e4 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs @@ -5,6 +5,7 @@ use chalk_ir::TyKind; use hir_def::{ + CrateRootModuleId, builtin_type::{BuiltinInt, BuiltinUint}, resolver::HasResolver, }; @@ -153,7 +154,7 @@ pub(super) fn detect_and_redirect_special_function( ) -> Result> { // `PanicFmt` is redirected to `ConstPanicFmt` 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 resolver = CrateRootModuleId::from(self.crate_id).resolver(self.db); let Some(const_panic_fmt) = LangItem::ConstPanicFmt.resolve_function(self.db, resolver.krate()) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs index 7b48b15d9ea9..7fcc89e5183a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs @@ -68,16 +68,16 @@ struct DropScope { locals: Vec, } -struct MirLowerCtx<'a> { +struct MirLowerCtx<'db> { result: MirBody, owner: DefWithBodyId, current_loop_blocks: Option, labeled_loop_blocks: FxHashMap, discr_temp: Option, - db: &'a dyn HirDatabase, - body: &'a Body, - infer: &'a InferenceResult, - resolver: Resolver, + db: &'db dyn HirDatabase, + body: &'db Body, + infer: &'db InferenceResult, + resolver: Resolver<'db>, drop_scopes: Vec, } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs index bcd8aa6c4e95..8f0d17c9dc4e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs @@ -7,7 +7,7 @@ SourceRoot, SourceRootId, SourceRootInput, }; -use hir_def::{ModuleId, db::DefDatabase}; +use hir_def::{ModuleId, db::DefDatabase, nameres::crate_def_map}; use hir_expand::EditionedFileId; use rustc_hash::FxHashMap; use salsa::{AsDynDatabase, Durability}; @@ -118,7 +118,7 @@ impl TestDB { pub(crate) fn module_for_file_opt(&self, file_id: impl Into) -> Option { let file_id = file_id.into(); for &krate in self.relevant_crates(file_id).iter() { - let crate_def_map = self.crate_def_map(krate); + let crate_def_map = crate_def_map(self, krate); for (local_id, data) in crate_def_map.modules() { if data.origin.file_id().map(|file_id| file_id.file_id(self)) == Some(file_id) { return Some(crate_def_map.module_id(local_id)); @@ -137,7 +137,7 @@ pub(crate) fn extract_annotations( ) -> FxHashMap> { let mut files = Vec::new(); for &krate in self.all_crates().iter() { - let crate_def_map = self.crate_def_map(krate); + let crate_def_map = crate_def_map(self, krate); for (module_id, _) in crate_def_map.modules() { let file_id = crate_def_map[module_id].origin.file_id(); files.extend(file_id) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs index cc37f65c26c2..2b75bd6f1604 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs @@ -132,7 +132,7 @@ fn check_impl( None => continue, }; let def_map = module.def_map(&db); - visit_module(&db, &def_map, module.local_id, &mut |it| { + visit_module(&db, def_map, module.local_id, &mut |it| { let def = match it { ModuleDefId::FunctionId(it) => it.into(), ModuleDefId::EnumVariantId(it) => it.into(), @@ -391,7 +391,7 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String { let def_map = module.def_map(&db); let mut defs: Vec<(DefWithBodyId, Crate)> = Vec::new(); - visit_module(&db, &def_map, module.local_id, &mut |it| { + visit_module(&db, def_map, module.local_id, &mut |it| { let def = match it { ModuleDefId::FunctionId(it) => it.into(), ModuleDefId::EnumVariantId(it) => it.into(), @@ -504,7 +504,7 @@ fn visit_scope( fn visit_body(db: &TestDB, body: &Body, cb: &mut dyn FnMut(ModuleDefId)) { for (_, def_map) in body.blocks(db) { for (mod_id, _) in def_map.modules() { - visit_module(db, &def_map, mod_id, cb); + visit_module(db, def_map, mod_id, cb); } } } @@ -570,7 +570,7 @@ fn main() { let module = db.module_for_file(pos.file_id.file_id(&db)); let crate_def_map = module.def_map(&db); - visit_module(&db, &crate_def_map, module.local_id, &mut |def| { + visit_module(&db, crate_def_map, module.local_id, &mut |def| { db.infer(match def { ModuleDefId::FunctionId(it) => it.into(), ModuleDefId::EnumVariantId(it) => it.into(), @@ -609,7 +609,7 @@ fn main() { let module = db.module_for_file(pos.file_id.file_id(&db)); let crate_def_map = module.def_map(&db); - visit_module(&db, &crate_def_map, module.local_id, &mut |def| { + visit_module(&db, crate_def_map, module.local_id, &mut |def| { db.infer(match def { ModuleDefId::FunctionId(it) => it.into(), ModuleDefId::EnumVariantId(it) => it.into(), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs index 73f1ae56457d..88d21be81ea6 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs @@ -20,7 +20,7 @@ fn check_closure_captures(#[rust_analyzer::rust_fixture] ra_fixture: &str, expec let def_map = module.def_map(&db); let mut defs = Vec::new(); - visit_module(&db, &def_map, module.local_id, &mut |it| defs.push(it)); + visit_module(&db, def_map, module.local_id, &mut |it| defs.push(it)); let mut captures_info = Vec::new(); for def in defs { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs index 0542be0ba896..48474d2d26de 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs @@ -19,7 +19,7 @@ fn foo() -> i32 { let events = db.log_executed(|| { let module = db.module_for_file(pos.file_id.file_id(&db)); let crate_def_map = module.def_map(&db); - visit_module(&db, &crate_def_map, module.local_id, &mut |def| { + visit_module(&db, crate_def_map, module.local_id, &mut |def| { if let ModuleDefId::FunctionId(it) = def { db.infer(it.into()); } @@ -41,7 +41,7 @@ fn foo() -> i32 { let events = db.log_executed(|| { let module = db.module_for_file(pos.file_id.file_id(&db)); let crate_def_map = module.def_map(&db); - visit_module(&db, &crate_def_map, module.local_id, &mut |def| { + visit_module(&db, crate_def_map, module.local_id, &mut |def| { if let ModuleDefId::FunctionId(it) = def { db.infer(it.into()); } @@ -70,7 +70,7 @@ fn baz() -> i32 { let events = db.log_executed(|| { let module = db.module_for_file(pos.file_id.file_id(&db)); let crate_def_map = module.def_map(&db); - visit_module(&db, &crate_def_map, module.local_id, &mut |def| { + visit_module(&db, crate_def_map, module.local_id, &mut |def| { if let ModuleDefId::FunctionId(it) = def { db.infer(it.into()); } @@ -97,7 +97,7 @@ fn baz() -> i32 { let events = db.log_executed(|| { let module = db.module_for_file(pos.file_id.file_id(&db)); let crate_def_map = module.def_map(&db); - visit_module(&db, &crate_def_map, module.local_id, &mut |def| { + visit_module(&db, crate_def_map, module.local_id, &mut |def| { if let ModuleDefId::FunctionId(it) = def { db.infer(it.into()); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs b/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs index 6e1cd9a310f1..d6b43aeed4d0 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs @@ -984,7 +984,7 @@ fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str, expected: Expect) { let mut defs: Vec = Vec::new(); let module = db.module_for_file_opt(file_id.file_id(&db)).unwrap(); let def_map = module.def_map(&db); - crate::tests::visit_module(&db, &def_map, module.local_id, &mut |it| { + crate::tests::visit_module(&db, def_map, module.local_id, &mut |it| { defs.push(match it { ModuleDefId::FunctionId(it) => it.into(), ModuleDefId::AdtId(it) => it.into(), diff --git a/src/tools/rust-analyzer/crates/hir/src/attrs.rs b/src/tools/rust-analyzer/crates/hir/src/attrs.rs index b1c478d1bf40..2d1727a6e904 100644 --- a/src/tools/rust-analyzer/crates/hir/src/attrs.rs +++ b/src/tools/rust-analyzer/crates/hir/src/attrs.rs @@ -160,7 +160,7 @@ fn resolve_doc_path_on_( fn resolve_assoc_or_field( db: &dyn HirDatabase, - resolver: Resolver, + resolver: Resolver<'_>, path: ModPath, name: Name, ns: Option, @@ -248,7 +248,7 @@ fn resolve_assoc_item( fn resolve_impl_trait_item( db: &dyn HirDatabase, - resolver: Resolver, + resolver: Resolver<'_>, ty: &Type, name: &Name, ns: Option, diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 3f1d5bb01f2a..c5d37cafd989 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -119,7 +119,7 @@ find_path::PrefixKind, import_map, lang_item::LangItem, - nameres::{DefMap, ModuleSource}, + nameres::{DefMap, ModuleSource, crate_def_map}, per_ns::Namespace, type_ref::{Mutability, TypeRef}, visibility::Visibility, @@ -227,7 +227,7 @@ pub fn root_module(self) -> Module { } pub fn modules(self, db: &dyn HirDatabase) -> Vec { - let def_map = db.crate_def_map(self.id); + let def_map = crate_def_map(db, self.id); def_map.modules().map(|(id, _)| def_map.module_id(id).into()).collect() } @@ -528,7 +528,7 @@ pub fn krate(self) -> Crate { /// might be missing `krate`. This can happen if a module's file is not included /// in the module tree of any target in `Cargo.toml`. pub fn crate_root(self, db: &dyn HirDatabase) -> Module { - let def_map = db.crate_def_map(self.id.krate()); + let def_map = crate_def_map(db, self.id.krate()); Module { id: def_map.crate_root().into() } } @@ -2468,7 +2468,7 @@ pub fn as_proc_macro(self, db: &dyn HirDatabase) -> Option { { return None; } - let def_map = db.crate_def_map(HasModule::krate(&self.id, db)); + let def_map = crate_def_map(db, HasModule::krate(&self.id, db)); def_map.fn_as_proc_macro(self.id).map(|id| Macro { id: id.into() }) } @@ -4015,8 +4015,7 @@ pub(crate) fn by_name(db: &dyn HirDatabase, krate: Crate, name: &str) -> Option< if let builtin @ Some(_) = Self::builtin(name) { return builtin; } - let idx = db - .crate_def_map(krate.id) + let idx = crate_def_map(db, krate.id) .registered_attrs() .iter() .position(|it| it.as_str() == name)? as u32; @@ -4031,7 +4030,7 @@ fn builtin(name: &str) -> Option { pub fn name(&self, db: &dyn HirDatabase) -> Name { match self.krate { Some(krate) => Name::new_symbol_root( - db.crate_def_map(krate).registered_attrs()[self.idx as usize].clone(), + crate_def_map(db, krate).registered_attrs()[self.idx as usize].clone(), ), None => Name::new_symbol_root(Symbol::intern( hir_expand::inert_attr_macro::INERT_ATTRIBUTES[self.idx as usize].name, @@ -4059,14 +4058,14 @@ impl ToolModule { pub(crate) fn by_name(db: &dyn HirDatabase, krate: Crate, name: &str) -> Option { let krate = krate.id; let idx = - db.crate_def_map(krate).registered_tools().iter().position(|it| it.as_str() == name)? + crate_def_map(db, krate).registered_tools().iter().position(|it| it.as_str() == name)? as u32; Some(ToolModule { krate, idx }) } pub fn name(&self, db: &dyn HirDatabase) -> Name { Name::new_symbol_root( - db.crate_def_map(self.krate).registered_tools()[self.idx as usize].clone(), + crate_def_map(db, self.krate).registered_tools()[self.idx as usize].clone(), ) } @@ -4488,7 +4487,7 @@ pub fn as_builtin_derive_path(self, db: &dyn HirDatabase) -> Option { let module_id = self.id.lookup(db).container; ( - db.crate_def_map(module_id.krate())[module_id.local_id] + crate_def_map(db, module_id.krate())[module_id.local_id] .scope .derive_macro_invoc(ast_id, derive_attr_index)?, derive_index, @@ -4530,7 +4529,7 @@ pub struct TraitRef { impl TraitRef { pub(crate) fn new_with_resolver( db: &dyn HirDatabase, - resolver: &Resolver, + resolver: &Resolver<'_>, trait_ref: hir_ty::TraitRef, ) -> TraitRef { let env = resolver @@ -4752,13 +4751,13 @@ pub struct Type { } impl Type { - pub(crate) fn new_with_resolver(db: &dyn HirDatabase, resolver: &Resolver, ty: Ty) -> Type { + pub(crate) fn new_with_resolver(db: &dyn HirDatabase, resolver: &Resolver<'_>, ty: Ty) -> Type { Type::new_with_resolver_inner(db, resolver, ty) } pub(crate) fn new_with_resolver_inner( db: &dyn HirDatabase, - resolver: &Resolver, + resolver: &Resolver<'_>, ty: Ty, ) -> Type { let environment = resolver @@ -6400,7 +6399,7 @@ pub fn resolve_absolute_path<'a, I: Iterator + Clone + 'a>( }) .filter_map(|&krate| { let segments = segments.clone(); - let mut def_map = db.crate_def_map(krate); + let mut def_map = crate_def_map(db, krate); let mut module = &def_map[DefMap::ROOT]; let mut segments = segments.with_position().peekable(); while let Some((_, segment)) = segments.next_if(|&(position, _)| { diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index 4d092c1f0bb0..327fe8a75839 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -15,7 +15,7 @@ DefWithBodyId, FunctionId, MacroId, StructId, TraitId, VariantId, expr_store::{Body, ExprOrPatSource, path::Path}, hir::{BindingId, Expr, ExprId, ExprOrPatId, Pat}, - nameres::ModuleOrigin, + nameres::{ModuleOrigin, crate_def_map}, resolver::{self, HasResolver, Resolver, TypeNs}, type_ref::Mutability, }; @@ -341,7 +341,7 @@ pub fn find_parent_file(&self, file_id: HirFileId) -> Option> match file_id { HirFileId::FileId(file_id) => { let module = self.file_to_module_defs(file_id.file_id(self.db)).next()?; - let def_map = self.db.crate_def_map(module.krate().id); + let def_map = crate_def_map(self.db, module.krate().id); match def_map[module.id.local_id].origin { ModuleOrigin::CrateRoot { .. } => None, ModuleOrigin::File { declaration, declaration_tree_id, .. } => { @@ -1711,13 +1711,13 @@ pub fn body_for(&self, node: InFile<&SyntaxNode>) -> Option { } /// Returns none if the file of the node is not part of a crate. - fn analyze(&self, node: &SyntaxNode) -> Option { + fn analyze(&self, node: &SyntaxNode) -> Option> { let node = self.find_file(node); self.analyze_impl(node, None, true) } /// Returns none if the file of the node is not part of a crate. - fn analyze_no_infer(&self, node: &SyntaxNode) -> Option { + fn analyze_no_infer(&self, node: &SyntaxNode) -> Option> { let node = self.find_file(node); self.analyze_impl(node, None, false) } @@ -1726,7 +1726,7 @@ fn analyze_with_offset_no_infer( &self, node: &SyntaxNode, offset: TextSize, - ) -> Option { + ) -> Option> { let node = self.find_file(node); self.analyze_impl(node, Some(offset), false) } @@ -1737,7 +1737,7 @@ fn analyze_impl( offset: Option, // replace this, just make the inference result a `LazyCell` infer_body: bool, - ) -> Option { + ) -> Option> { let _p = tracing::info_span!("SemanticsImpl::analyze_impl").entered(); let container = self.with_ctx(|ctx| ctx.find_container(node))?; @@ -1984,13 +1984,13 @@ fn find_root(node: &SyntaxNode) -> SyntaxNode { /// Note that if you are wondering "what does this specific existing name mean?", /// you'd better use the `resolve_` family of methods. #[derive(Debug)] -pub struct SemanticsScope<'a> { - pub db: &'a dyn HirDatabase, +pub struct SemanticsScope<'db> { + pub db: &'db dyn HirDatabase, file_id: HirFileId, - resolver: Resolver, + resolver: Resolver<'db>, } -impl SemanticsScope<'_> { +impl<'db> SemanticsScope<'db> { pub fn module(&self) -> Module { Module { id: self.resolver.module() } } @@ -2006,7 +2006,7 @@ pub fn containing_function(&self) -> Option { }) } - pub(crate) fn resolver(&self) -> &Resolver { + pub(crate) fn resolver(&self) -> &Resolver<'db> { &self.resolver } @@ -2133,7 +2133,7 @@ fn deref(&self) -> &Self::Target { struct RenameConflictsVisitor<'a> { db: &'a dyn HirDatabase, owner: DefWithBodyId, - resolver: Resolver, + resolver: Resolver<'a>, body: &'a Body, to_be_renamed: BindingId, new_name: Symbol, diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs b/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs index 587c51d8cc99..172af456d921 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs @@ -96,6 +96,7 @@ keys::{self, Key}, }, hir::{BindingId, Expr, LabelId}, + nameres::{block_def_map, crate_def_map}, }; use hir_expand::{ EditionedFileId, ExpansionInfo, HirFileId, InMacroFile, MacroCallId, attrs::AttrId, @@ -180,7 +181,7 @@ impl SourceToDefCtx<'_, '_> { for &crate_id in self.db.relevant_crates(file).iter() { // Note: `mod` declarations in block modules cannot be supported here - let crate_def_map = self.db.crate_def_map(crate_id); + let crate_def_map = crate_def_map(self.db, crate_id); let n_mods = mods.len(); let modules = |file| { crate_def_map @@ -226,7 +227,7 @@ pub(super) fn module_to_def(&mut self, src: InFile<&ast::Module>) -> Option self .block_to_def(parent_block.as_ref()) - .map(|block| self.db.block_def_map(block).root_module_id()), + .map(|block| block_def_map(self.db, block).root_module_id()), Some(Either::Left(parent_declaration)) => { self.module_to_def(parent_declaration.as_ref()) } diff --git a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs index c1a75ce7e574..42da7b942c73 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -24,7 +24,7 @@ }, hir::{BindingId, Expr, ExprId, ExprOrPatId, Pat}, lang_item::LangItem, - nameres::MacroSubNs, + nameres::{MacroSubNs, crate_def_map}, resolver::{HasResolver, Resolver, TypeNs, ValueNs, resolver_for_scope}, type_ref::{Mutability, TypeRefId}, }; @@ -57,9 +57,9 @@ /// `SourceAnalyzer` is a convenience wrapper which exposes HIR API in terms of /// original source files. It should not be used inside the HIR itself. #[derive(Debug)] -pub(crate) struct SourceAnalyzer { +pub(crate) struct SourceAnalyzer<'db> { pub(crate) file_id: HirFileId, - pub(crate) resolver: Resolver, + pub(crate) resolver: Resolver<'db>, pub(crate) body_or_sig: Option, } @@ -85,32 +85,32 @@ pub(crate) enum BodyOrSig { }, } -impl SourceAnalyzer { +impl<'db> SourceAnalyzer<'db> { pub(crate) fn new_for_body( - db: &dyn HirDatabase, + db: &'db dyn HirDatabase, def: DefWithBodyId, node: InFile<&SyntaxNode>, offset: Option, - ) -> SourceAnalyzer { + ) -> SourceAnalyzer<'db> { Self::new_for_body_(db, def, node, offset, Some(db.infer(def))) } pub(crate) fn new_for_body_no_infer( - db: &dyn HirDatabase, + db: &'db dyn HirDatabase, def: DefWithBodyId, node: InFile<&SyntaxNode>, offset: Option, - ) -> SourceAnalyzer { + ) -> SourceAnalyzer<'db> { Self::new_for_body_(db, def, node, offset, None) } pub(crate) fn new_for_body_( - db: &dyn HirDatabase, + db: &'db dyn HirDatabase, def: DefWithBodyId, node @ InFile { file_id, .. }: InFile<&SyntaxNode>, offset: Option, infer: Option>, - ) -> SourceAnalyzer { + ) -> SourceAnalyzer<'db> { let (body, source_map) = db.body_with_source_map(def); let scopes = db.expr_scopes(def); let scope = match offset { @@ -134,11 +134,11 @@ pub(crate) fn new_for_body_( } pub(crate) fn new_generic_def( - db: &dyn HirDatabase, + db: &'db dyn HirDatabase, def: GenericDefId, InFile { file_id, .. }: InFile<&SyntaxNode>, _offset: Option, - ) -> SourceAnalyzer { + ) -> SourceAnalyzer<'db> { let (_params, store, source_map) = db.generic_params_and_store_and_source_map(def); let resolver = def.resolver(db); SourceAnalyzer { @@ -149,11 +149,11 @@ pub(crate) fn new_generic_def( } pub(crate) fn new_variant_body( - db: &dyn HirDatabase, + db: &'db dyn HirDatabase, def: VariantId, InFile { file_id, .. }: InFile<&SyntaxNode>, _offset: Option, - ) -> SourceAnalyzer { + ) -> SourceAnalyzer<'db> { let (fields, source_map) = db.variant_fields_with_source_map(def); let resolver = def.resolver(db); SourceAnalyzer { @@ -168,9 +168,9 @@ pub(crate) fn new_variant_body( } pub(crate) fn new_for_resolver( - resolver: Resolver, + resolver: Resolver<'db>, node: InFile<&SyntaxNode>, - ) -> SourceAnalyzer { + ) -> SourceAnalyzer<'db> { SourceAnalyzer { resolver, body_or_sig: None, file_id: node.file_id } } @@ -220,7 +220,7 @@ pub(crate) fn expansion(&self, node: InFile<&ast::MacroCall>) -> Option Arc { + fn trait_environment(&self, db: &'db dyn HirDatabase) -> Arc { self.body_().map(|(def, ..)| def).map_or_else( || TraitEnvironment::empty(self.resolver.krate()), |def| db.trait_environment_for_body(def), @@ -259,7 +259,7 @@ pub(crate) fn expr_adjustments(&self, expr: &ast::Expr) -> Option<&[Adjustment]> infer.expr_adjustments.get(&expr_id).map(|v| &**v) } - pub(crate) fn type_of_type(&self, db: &dyn HirDatabase, ty: &ast::Type) -> Option { + pub(crate) fn type_of_type(&self, db: &'db dyn HirDatabase, ty: &ast::Type) -> Option { let type_ref = self.type_id(ty)?; let ty = TyLoweringContext::new( db, @@ -277,7 +277,7 @@ pub(crate) fn type_of_type(&self, db: &dyn HirDatabase, ty: &ast::Type) -> Optio pub(crate) fn type_of_expr( &self, - db: &dyn HirDatabase, + db: &'db dyn HirDatabase, expr: &ast::Expr, ) -> Option<(Type, Option)> { let expr_id = self.expr_id(expr.clone())?; @@ -293,7 +293,7 @@ pub(crate) fn type_of_expr( pub(crate) fn type_of_pat( &self, - db: &dyn HirDatabase, + db: &'db dyn HirDatabase, pat: &ast::Pat, ) -> Option<(Type, Option)> { let expr_or_pat_id = self.pat_id(pat)?; @@ -316,7 +316,7 @@ pub(crate) fn type_of_pat( pub(crate) fn type_of_binding_in_pat( &self, - db: &dyn HirDatabase, + db: &'db dyn HirDatabase, pat: &ast::IdentPat, ) -> Option { let binding_id = self.binding_id_of_pat(pat)?; @@ -328,7 +328,7 @@ pub(crate) fn type_of_binding_in_pat( pub(crate) fn type_of_self( &self, - db: &dyn HirDatabase, + db: &'db dyn HirDatabase, _param: &ast::SelfParam, ) -> Option { let binding = self.body()?.self_param?; @@ -338,7 +338,7 @@ pub(crate) fn type_of_self( pub(crate) fn binding_mode_of_pat( &self, - _db: &dyn HirDatabase, + _db: &'db dyn HirDatabase, pat: &ast::IdentPat, ) -> Option { let id = self.pat_id(&pat.clone().into())?; @@ -353,7 +353,7 @@ pub(crate) fn binding_mode_of_pat( } pub(crate) fn pattern_adjustments( &self, - db: &dyn HirDatabase, + db: &'db dyn HirDatabase, pat: &ast::Pat, ) -> Option> { let pat_id = self.pat_id(pat)?; @@ -370,7 +370,7 @@ pub(crate) fn pattern_adjustments( pub(crate) fn resolve_method_call_as_callable( &self, - db: &dyn HirDatabase, + db: &'db dyn HirDatabase, call: &ast::MethodCallExpr, ) -> Option { let expr_id = self.expr_id(call.clone().into())?.as_expr()?; @@ -384,7 +384,7 @@ pub(crate) fn resolve_method_call_as_callable( pub(crate) fn resolve_method_call( &self, - db: &dyn HirDatabase, + db: &'db dyn HirDatabase, call: &ast::MethodCallExpr, ) -> Option { let expr_id = self.expr_id(call.clone().into())?.as_expr()?; @@ -395,7 +395,7 @@ pub(crate) fn resolve_method_call( pub(crate) fn resolve_method_call_fallback( &self, - db: &dyn HirDatabase, + db: &'db dyn HirDatabase, call: &ast::MethodCallExpr, ) -> Option<(Either, Option)> { let expr_id = self.expr_id(call.clone().into())?.as_expr()?; @@ -419,7 +419,7 @@ pub(crate) fn resolve_method_call_fallback( pub(crate) fn resolve_expr_as_callable( &self, - db: &dyn HirDatabase, + db: &'db dyn HirDatabase, call: &ast::Expr, ) -> Option { let (orig, adjusted) = self.type_of_expr(db, &call.clone())?; @@ -441,7 +441,7 @@ fn field_subst( &self, field_expr: ExprId, infer: &InferenceResult, - db: &dyn HirDatabase, + db: &'db dyn HirDatabase, ) -> Option { let body = self.store()?; if let Expr::Field { expr: object_expr, name: _ } = body[field_expr] { @@ -457,7 +457,7 @@ fn field_subst( pub(crate) fn resolve_field_fallback( &self, - db: &dyn HirDatabase, + db: &'db dyn HirDatabase, field: &ast::FieldExpr, ) -> Option<(Either, Function>, Option)> { let (def, ..) = self.body_()?; @@ -490,7 +490,7 @@ pub(crate) fn resolve_field_fallback( pub(crate) fn resolve_range_pat( &self, - db: &dyn HirDatabase, + db: &'db dyn HirDatabase, range_pat: &ast::RangePat, ) -> Option { let path: ModPath = match (range_pat.op_kind()?, range_pat.start(), range_pat.end()) { @@ -509,7 +509,7 @@ pub(crate) fn resolve_range_pat( pub(crate) fn resolve_range_expr( &self, - db: &dyn HirDatabase, + db: &'db dyn HirDatabase, range_expr: &ast::RangeExpr, ) -> Option { let path: ModPath = match (range_expr.op_kind()?, range_expr.start(), range_expr.end()) { @@ -529,7 +529,7 @@ pub(crate) fn resolve_range_expr( pub(crate) fn resolve_await_to_poll( &self, - db: &dyn HirDatabase, + db: &'db dyn HirDatabase, await_expr: &ast::AwaitExpr, ) -> Option { let mut ty = self.ty_of_expr(await_expr.expr()?)?.clone(); @@ -566,7 +566,7 @@ pub(crate) fn resolve_await_to_poll( pub(crate) fn resolve_prefix_expr( &self, - db: &dyn HirDatabase, + db: &'db dyn HirDatabase, prefix_expr: &ast::PrefixExpr, ) -> Option { let (op_trait, op_fn) = match prefix_expr.op_kind()? { @@ -608,7 +608,7 @@ pub(crate) fn resolve_prefix_expr( pub(crate) fn resolve_index_expr( &self, - db: &dyn HirDatabase, + db: &'db dyn HirDatabase, index_expr: &ast::IndexExpr, ) -> Option { let base_ty = self.ty_of_expr(index_expr.base()?)?; @@ -640,7 +640,7 @@ pub(crate) fn resolve_index_expr( pub(crate) fn resolve_bin_expr( &self, - db: &dyn HirDatabase, + db: &'db dyn HirDatabase, binop_expr: &ast::BinExpr, ) -> Option { let op = binop_expr.op_kind()?; @@ -661,7 +661,7 @@ pub(crate) fn resolve_bin_expr( pub(crate) fn resolve_try_expr( &self, - db: &dyn HirDatabase, + db: &'db dyn HirDatabase, try_expr: &ast::TryExpr, ) -> Option { let ty = self.ty_of_expr(try_expr.expr()?)?; @@ -680,7 +680,7 @@ pub(crate) fn resolve_try_expr( pub(crate) fn resolve_record_field( &self, - db: &dyn HirDatabase, + db: &'db dyn HirDatabase, field: &ast::RecordExprField, ) -> Option<(Field, Option, Type, GenericSubstitution)> { let record_expr = ast::RecordExpr::cast(field.syntax().parent().and_then(|p| p.parent())?)?; @@ -724,7 +724,7 @@ pub(crate) fn resolve_record_field( pub(crate) fn resolve_record_pat_field( &self, - db: &dyn HirDatabase, + db: &'db dyn HirDatabase, field: &ast::RecordPatField, ) -> Option<(Field, Type, GenericSubstitution)> { let field_name = field.field_name()?.as_name(); @@ -745,14 +745,14 @@ pub(crate) fn resolve_record_pat_field( pub(crate) fn resolve_macro_call( &self, - db: &dyn HirDatabase, + db: &'db dyn HirDatabase, macro_call: InFile<&ast::MacroCall>, ) -> Option { let bs = self.store_sm()?; bs.expansion(macro_call).and_then(|it| { // FIXME: Block def maps let def = it.lookup(db).def; - db.crate_def_map(def.krate) + crate_def_map(db, def.krate) .macro_def_to_macro_id .get(&def.kind.erased_ast_id()) .map(|it| (*it).into()) @@ -761,7 +761,7 @@ pub(crate) fn resolve_macro_call( pub(crate) fn resolve_bind_pat_to_const( &self, - db: &dyn HirDatabase, + db: &'db dyn HirDatabase, pat: &ast::IdentPat, ) -> Option { let expr_or_pat_id = self.pat_id(&pat.clone().into())?; @@ -795,7 +795,7 @@ pub(crate) fn resolve_use_type_arg(&self, name: &ast::NameRef) -> Option Option<(Either, GenericSubstitution)> { let offset_of_expr = ast::OffsetOfExpr::cast(name_ref.syntax().parent()?)?; @@ -867,7 +867,7 @@ pub(crate) fn resolve_offset_of_field( pub(crate) fn resolve_path( &self, - db: &dyn HirDatabase, + db: &'db dyn HirDatabase, path: &ast::Path, ) -> Option<(PathResolution, Option)> { let parent = path.syntax().parent(); @@ -1211,7 +1211,7 @@ pub(crate) fn resolve_path( pub(crate) fn record_literal_missing_fields( &self, - db: &dyn HirDatabase, + db: &'db dyn HirDatabase, literal: &ast::RecordExpr, ) -> Option> { let body = self.store()?; @@ -1234,7 +1234,7 @@ pub(crate) fn record_literal_missing_fields( pub(crate) fn record_pattern_missing_fields( &self, - db: &dyn HirDatabase, + db: &'db dyn HirDatabase, pattern: &ast::RecordPat, ) -> Option> { let body = self.store()?; @@ -1251,7 +1251,7 @@ pub(crate) fn record_pattern_missing_fields( fn missing_fields( &self, - db: &dyn HirDatabase, + db: &'db dyn HirDatabase, substs: &Substitution, variant: VariantId, missing_fields: Vec, @@ -1270,7 +1270,7 @@ fn missing_fields( pub(crate) fn expand( &self, - db: &dyn HirDatabase, + db: &'db dyn HirDatabase, macro_call: InFile<&ast::MacroCall>, ) -> Option { self.store_sm().and_then(|bs| bs.expansion(macro_call)).or_else(|| { @@ -1288,7 +1288,7 @@ pub(crate) fn resolve_variant(&self, record_lit: ast::RecordExpr) -> Option, ) -> bool { if let Some((def, body, sm, Some(infer))) = self.body_() { @@ -1313,7 +1313,7 @@ pub(crate) fn is_unsafe_macro_call_expr( pub(crate) fn resolve_offset_in_format_args( &self, - db: &dyn HirDatabase, + db: &'db dyn HirDatabase, format_args: InFile<&ast::FormatArgsExpr>, offset: TextSize, ) -> Option<(TextRange, Option)> { @@ -1384,7 +1384,7 @@ pub(crate) fn as_asm_parts( fn resolve_impl_method_or_trait_def( &self, - db: &dyn HirDatabase, + db: &'db dyn HirDatabase, func: FunctionId, substs: Substitution, ) -> FunctionId { @@ -1393,7 +1393,7 @@ fn resolve_impl_method_or_trait_def( fn resolve_impl_method_or_trait_def_with_subst( &self, - db: &dyn HirDatabase, + db: &'db dyn HirDatabase, func: FunctionId, substs: Substitution, ) -> (FunctionId, Substitution) { @@ -1407,7 +1407,7 @@ fn resolve_impl_method_or_trait_def_with_subst( fn resolve_impl_const_or_trait_def_with_subst( &self, - db: &dyn HirDatabase, + db: &'db dyn HirDatabase, const_id: ConstId, subs: Substitution, ) -> (ConstId, Substitution) { @@ -1421,7 +1421,7 @@ fn resolve_impl_const_or_trait_def_with_subst( fn lang_trait_fn( &self, - db: &dyn HirDatabase, + db: &'db dyn HirDatabase, lang_trait: LangItem, method_name: &Name, ) -> Option<(TraitId, FunctionId)> { @@ -1527,7 +1527,7 @@ fn adjust( #[inline] pub(crate) fn resolve_hir_path( db: &dyn HirDatabase, - resolver: &Resolver, + resolver: &Resolver<'_>, path: &Path, hygiene: HygieneId, store: Option<&ExpressionStore>, @@ -1538,7 +1538,7 @@ pub(crate) fn resolve_hir_path( #[inline] pub(crate) fn resolve_hir_path_as_attr_macro( db: &dyn HirDatabase, - resolver: &Resolver, + resolver: &Resolver<'_>, path: &Path, ) -> Option { resolver @@ -1549,7 +1549,7 @@ pub(crate) fn resolve_hir_path_as_attr_macro( fn resolve_hir_path_( db: &dyn HirDatabase, - resolver: &Resolver, + resolver: &Resolver<'_>, path: &Path, prefer_value_ns: bool, hygiene: HygieneId, @@ -1642,7 +1642,7 @@ fn resolve_hir_path_( fn resolve_hir_value_path( db: &dyn HirDatabase, - resolver: &Resolver, + resolver: &Resolver<'_>, body_owner: Option, path: &Path, hygiene: HygieneId, @@ -1680,7 +1680,7 @@ fn resolve_hir_value_path( /// then we know that `foo` in `my::foo::Bar` refers to the module, not the function. fn resolve_hir_path_qualifier( db: &dyn HirDatabase, - resolver: &Resolver, + resolver: &Resolver<'_>, path: &Path, store: &ExpressionStore, ) -> Option { diff --git a/src/tools/rust-analyzer/crates/ide-db/src/prime_caches.rs b/src/tools/rust-analyzer/crates/ide-db/src/prime_caches.rs index cbe31405ab78..764b583fb84d 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/prime_caches.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/prime_caches.rs @@ -73,7 +73,7 @@ enum PrimingPhase { .send(ParallelPrimeCacheWorkerProgress::BeginCrate { crate_id, crate_name })?; let cancelled = Cancelled::catch(|| match kind { - PrimingPhase::DefMap => _ = db.crate_def_map(crate_id), + PrimingPhase::DefMap => _ = hir::crate_def_map(&db, crate_id), PrimingPhase::ImportMap => _ = db.import_map(crate_id), PrimingPhase::CrateSymbols => _ = db.crate_symbols(crate_id.into()), }); diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unlinked_file.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unlinked_file.rs index 47fa30593626..af9126c89331 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unlinked_file.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unlinked_file.rs @@ -2,7 +2,8 @@ use std::iter; -use hir::{DefMap, InFile, ModuleSource, db::DefDatabase}; +use hir::crate_def_map; +use hir::{DefMap, InFile, ModuleSource}; use ide_db::base_db::RootQueryDb; use ide_db::text_edit::TextEdit; use ide_db::{ @@ -101,7 +102,8 @@ fn fixes( // check crate roots, i.e. main.rs, lib.rs, ... let relevant_crates = db.relevant_crates(file_id); 'crates: for &krate in &*relevant_crates { - let crate_def_map = ctx.sema.db.crate_def_map(krate); + // FIXME: This shouldnt need to access the crate def map directly + let crate_def_map = crate_def_map(ctx.sema.db, krate); let root_module = &crate_def_map[DefMap::ROOT]; let Some(root_file_id) = root_module.origin.file_id() else { continue }; @@ -156,7 +158,7 @@ fn fixes( stack.pop(); let relevant_crates = db.relevant_crates(parent_id); 'crates: for &krate in relevant_crates.iter() { - let crate_def_map = ctx.sema.db.crate_def_map(krate); + let crate_def_map = crate_def_map(ctx.sema.db, krate); let Some((_, module)) = crate_def_map.modules().find(|(_, module)| { module.origin.file_id().map(|file_id| file_id.file_id(ctx.sema.db)) == Some(parent_id) && !module.origin.is_inline() diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs index aa525a86123d..d649dffbd90c 100644 --- a/src/tools/rust-analyzer/crates/ide/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs @@ -62,7 +62,7 @@ use cfg::CfgOptions; use fetch_crates::CrateInfo; -use hir::{ChangeWithProcMacros, EditionedFileId, sym}; +use hir::{ChangeWithProcMacros, EditionedFileId, crate_def_map, sym}; use ide_db::{ FxHashMap, FxIndexSet, LineIndexDatabase, base_db::{ @@ -627,7 +627,7 @@ pub fn is_proc_macro_crate(&self, crate_id: Crate) -> Cancellable { /// Returns true if this crate has `no_std` or `no_core` specified. pub fn is_crate_no_std(&self, crate_id: Crate) -> Cancellable { - self.with_db(|db| hir::db::DefDatabase::crate_def_map(db, crate_id).is_no_std()) + self.with_db(|db| crate_def_map(db, crate_id).is_no_std()) } /// Returns the root file of the given crate. diff --git a/src/tools/rust-analyzer/crates/ide/src/parent_module.rs b/src/tools/rust-analyzer/crates/ide/src/parent_module.rs index 6dc01c450633..50219cee57db 100644 --- a/src/tools/rust-analyzer/crates/ide/src/parent_module.rs +++ b/src/tools/rust-analyzer/crates/ide/src/parent_module.rs @@ -1,4 +1,4 @@ -use hir::{Semantics, db::DefDatabase}; +use hir::{Semantics, crate_def_map}; use ide_db::{ FileId, FilePosition, RootDatabase, base_db::{Crate, RootQueryDb}, @@ -58,7 +58,7 @@ pub(crate) fn crates_for(db: &RootDatabase, file_id: FileId) -> Vec { .iter() .copied() .filter(|&crate_id| { - db.crate_def_map(crate_id).modules_for_file(db, file_id).next().is_some() + crate_def_map(db, crate_id).modules_for_file(db, file_id).next().is_some() }) .sorted() .collect() From 77f7a9afb84ad729973e78f9fb692c10c6deefda Mon Sep 17 00:00:00 2001 From: Vishruth-Thimmaiah Date: Sun, 4 May 2025 23:39:00 +0530 Subject: [PATCH 06/51] feat: add an assist to unwrap a type with a generic arg This assist unwraps a type into its generic type argument, ignoring const and lifetime arguments --- .../handlers/unwrap_type_to_generic_arg.rs | 156 ++++++++++++++++++ .../crates/ide-assists/src/lib.rs | 2 + .../crates/ide-assists/src/tests/generated.rs | 17 ++ 3 files changed, 175 insertions(+) create mode 100644 src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_type_to_generic_arg.rs diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_type_to_generic_arg.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_type_to_generic_arg.rs new file mode 100644 index 000000000000..7b5adc1858b4 --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_type_to_generic_arg.rs @@ -0,0 +1,156 @@ +use ide_db::assists::AssistId; +use syntax::{ + AstNode, + ast::{self, GenericArg, HasGenericArgs}, +}; + +use crate::{AssistContext, Assists}; + +// Assist: unwrap_type_to_generic_arg +// +// This assist unwraps a type into its generic type argument. +// +// ``` +// fn foo() -> $0Option { +// todo!() +// } +// ``` +// -> +// ``` +// fn foo() -> i32 { +// todo!() +// } +// ``` +pub(crate) fn unwrap_type_to_generic_arg(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { + let path_type = ctx.find_node_at_offset::()?; + let path = path_type.path()?; + let segment = path.segment()?; + let args_list = segment.generic_arg_list()?; + + let mut generic_arg = None; + + for arg in args_list.generic_args() { + match arg { + GenericArg::ConstArg(_) | GenericArg::LifetimeArg(_) => (), + GenericArg::TypeArg(arg) if generic_arg.is_none() => { + generic_arg = Some(arg); + } + _ => return None, + } + } + + let generic_arg = generic_arg?; + + acc.add( + AssistId::refactor_extract("unwrap_type_to_generic_arg"), + format!("Unwrap type to type argument {generic_arg}"), + path_type.syntax().text_range(), + |builder| { + let mut editor = builder.make_editor(path_type.syntax()); + editor.replace(path_type.syntax(), generic_arg.syntax()); + + builder.add_file_edits(ctx.vfs_file_id(), editor); + }, + ) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::tests::{check_assist, check_assist_not_applicable}; + + #[test] + fn test_unwrap_type_to_generic_arg() { + check_assist( + unwrap_type_to_generic_arg, + r#" +//- minicore: option +fn foo() -> $0Option { + todo!() +} +"#, + r#" +fn foo() -> i32 { + todo!() +} +"#, + ); + } + + #[test] + fn unwrap_type_to_generic_arg_not_applicable_for_non_generic_arg_list() { + check_assist_not_applicable( + unwrap_type_to_generic_arg, + r#" +fn foo() -> $0i32 {} +"#, + ); + } + + #[test] + fn unwrap_type_to_generic_arg_not_applicable_for_multiple_generic_args() { + check_assist_not_applicable( + unwrap_type_to_generic_arg, + r#" +//- minicore: result +fn foo() -> $0Result { + todo!() +} +"#, + ); + } + + #[test] + fn unwrap_type_to_generic_arg_with_lifetime_and_const() { + check_assist( + unwrap_type_to_generic_arg, + r#" +enum Foo<'a, T, const N: usize> { + Bar(T), + Baz(&'a [T; N]), +} + +fn test<'a>() -> $0Foo<'a, i32, 3> { + todo!() +} +"#, + r#" +enum Foo<'a, T, const N: usize> { + Bar(T), + Baz(&'a [T; N]), +} + +fn test<'a>() -> i32 { + todo!() +} +"#, + ); + } + + #[test] + fn unwrap_type_to_generic_arg_in_let_stmt() { + check_assist( + unwrap_type_to_generic_arg, + r#" +enum Foo { + Bar(T), + Baz, +} + +fn test() { + let foo: $0Foo = todo!(); +} +"#, + r#" +enum Foo { + Bar(T), + Baz, +} + +fn test() { + let foo: i32 = todo!(); +} +"#, + ); + } +} diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs b/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs index 627ed37b04e5..2395091b6f2e 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs @@ -229,6 +229,7 @@ mod handlers { mod unwrap_block; mod unwrap_return_type; mod unwrap_tuple; + mod unwrap_type_to_generic_arg; mod wrap_return_type; mod wrap_unwrap_cfg_attr; @@ -369,6 +370,7 @@ pub(crate) fn all() -> &'static [Handler] { unwrap_block::unwrap_block, unwrap_return_type::unwrap_return_type, unwrap_tuple::unwrap_tuple, + unwrap_type_to_generic_arg::unwrap_type_to_generic_arg, wrap_return_type::wrap_return_type, wrap_unwrap_cfg_attr::wrap_unwrap_cfg_attr, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs index 01ab0be34b28..76134acb36eb 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs @@ -3481,6 +3481,23 @@ fn main() { ) } +#[test] +fn doctest_unwrap_type_to_generic_arg() { + check_doc_test( + "unwrap_type_to_generic_arg", + r#####" +fn foo() -> $0Option { + todo!() +} +"#####, + r#####" +fn foo() -> i32 { + todo!() +} +"#####, + ) +} + #[test] fn doctest_wrap_return_type_in_option() { check_doc_test( From c1b7b968e86c939a60a7c5398d52597ca07d8494 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 5 May 2025 12:09:49 +0200 Subject: [PATCH 07/51] minor: Add a mbe test for parsing negative literals --- .../hir-def/src/nameres/path_resolution.rs | 1 + .../rust-analyzer/crates/mbe/src/tests.rs | 117 ++++++++++++++++++ 2 files changed, 118 insertions(+) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs index 2ad5e9eca08f..74ce33a6419e 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs @@ -483,6 +483,7 @@ fn resolve_remaining_segments<'a>( curr_per_ns = match curr.def { ModuleDefId::ModuleId(module) => { if module.krate != self.krate { + // FIXME: Inefficient let path = ModPath::from_segments( PathKind::SELF, path.segments()[i..].iter().cloned(), diff --git a/src/tools/rust-analyzer/crates/mbe/src/tests.rs b/src/tools/rust-analyzer/crates/mbe/src/tests.rs index a5672e4e0504..3369dfff2816 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/tests.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/tests.rs @@ -356,3 +356,120 @@ fn expr_2021() { ;"#]], ); } + +#[test] +fn minus_belongs_to_literal() { + let decl = r#" +(-1) => {-1}; +(- 2) => {- 2}; +(- 3.0) => {- 3.0}; +(@$lit:literal) => {$lit} +"#; + let check = |args, expect| check(Edition::CURRENT, Edition::CURRENT, decl, args, expect); + check( + "-1", + expect![[r#" + SUBTREE $$ 1:0@0..2#ROOT2024 1:0@0..2#ROOT2024 + PUNCH - [alone] 0:0@10..11#ROOT2024 + LITERAL Integer 1 0:0@11..12#ROOT2024 + + -1"#]], + ); + check( + "- 1", + expect![[r#" + SUBTREE $$ 1:0@0..3#ROOT2024 1:0@0..3#ROOT2024 + PUNCH - [alone] 0:0@10..11#ROOT2024 + LITERAL Integer 1 0:0@11..12#ROOT2024 + + -1"#]], + ); + check( + "-2", + expect![[r#" + SUBTREE $$ 1:0@0..2#ROOT2024 1:0@0..2#ROOT2024 + PUNCH - [alone] 0:0@25..26#ROOT2024 + LITERAL Integer 2 0:0@27..28#ROOT2024 + + -2"#]], + ); + check( + "- 2", + expect![[r#" + SUBTREE $$ 1:0@0..3#ROOT2024 1:0@0..3#ROOT2024 + PUNCH - [alone] 0:0@25..26#ROOT2024 + LITERAL Integer 2 0:0@27..28#ROOT2024 + + -2"#]], + ); + check( + "-3.0", + expect![[r#" + SUBTREE $$ 1:0@0..4#ROOT2024 1:0@0..4#ROOT2024 + PUNCH - [alone] 0:0@43..44#ROOT2024 + LITERAL Float 3.0 0:0@45..48#ROOT2024 + + -3.0"#]], + ); + check( + "- 3.0", + expect![[r#" + SUBTREE $$ 1:0@0..5#ROOT2024 1:0@0..5#ROOT2024 + PUNCH - [alone] 0:0@43..44#ROOT2024 + LITERAL Float 3.0 0:0@45..48#ROOT2024 + + -3.0"#]], + ); + check( + "@1", + expect![[r#" + SUBTREE $$ 1:0@0..2#ROOT2024 1:0@0..2#ROOT2024 + LITERAL Integer 1 1:0@1..2#ROOT2024 + + 1"#]], + ); + check( + "@-1", + expect![[r#" + SUBTREE $$ 1:0@0..3#ROOT2024 1:0@0..3#ROOT2024 + PUNCH - [alone] 1:0@1..2#ROOT2024 + LITERAL Integer 1 1:0@2..3#ROOT2024 + + -1"#]], + ); + check( + "@1.0", + expect![[r#" + SUBTREE $$ 1:0@0..4#ROOT2024 1:0@0..4#ROOT2024 + LITERAL Float 1.0 1:0@1..4#ROOT2024 + + 1.0"#]], + ); + check( + "@-1.0", + expect![[r#" + SUBTREE $$ 1:0@0..5#ROOT2024 1:0@0..5#ROOT2024 + PUNCH - [alone] 1:0@1..2#ROOT2024 + LITERAL Float 1.0 1:0@2..5#ROOT2024 + + -1.0"#]], + ); + check( + "@--1.0", + expect![[r#" + ExpandError { + inner: ( + 1:0@1..2#ROOT2024, + BindingError( + "expected literal", + ), + ), + } + + SUBTREE $$ 1:0@0..6#ROOT2024 1:0@0..6#ROOT2024 + PUNCH - [joint] 1:0@1..2#ROOT2024 + PUNCH - [alone] 1:0@2..3#ROOT2024 + + --"#]], + ); +} From 53881efacb7af06677c1632523d43c5c173dd2dd Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 5 May 2025 13:39:38 +0200 Subject: [PATCH 08/51] minor: Add more proc-macro tests for parsing negative literals --- .../proc-macro-test/imp/src/lib.rs | 5 + .../crates/proc-macro-srv/src/tests/mod.rs | 267 +++++++++++++++++- .../crates/proc-macro-srv/src/tests/utils.rs | 43 ++- src/tools/rust-analyzer/crates/tt/src/lib.rs | 52 ++++ 4 files changed, 341 insertions(+), 26 deletions(-) diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/imp/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/imp/src/lib.rs index dfdbb4c95fca..6820e4b33539 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/imp/src/lib.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/imp/src/lib.rs @@ -31,12 +31,17 @@ pub fn fn_like_mk_literals(_args: TokenStream) -> TokenStream { TokenTree::from(Literal::byte_string(b"byte_string")), TokenTree::from(Literal::character('c')), TokenTree::from(Literal::string("string")), + TokenTree::from(Literal::c_string(c"cstring")), // as of 2022-07-21, there's no method on `Literal` to build a raw // string or a raw byte string TokenTree::from(Literal::f64_suffixed(3.14)), + TokenTree::from(Literal::f64_suffixed(-3.14)), TokenTree::from(Literal::f64_unsuffixed(3.14)), + TokenTree::from(Literal::f64_unsuffixed(-3.14)), TokenTree::from(Literal::i64_suffixed(123)), + TokenTree::from(Literal::i64_suffixed(-123)), TokenTree::from(Literal::i64_unsuffixed(123)), + TokenTree::from(Literal::i64_unsuffixed(-123)), ]; TokenStream::from_iter(trees) } diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs index 011221459657..7a0ac01c50da 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs @@ -11,8 +11,24 @@ fn test_derive_empty() { assert_expand( "DeriveEmpty", r#"struct S;"#, - expect!["SUBTREE $$ 1 1"], - expect!["SUBTREE $$ 42:2@0..100#ROOT2024 42:2@0..100#ROOT2024"], + expect![[r#" + SUBTREE $$ 1 1 + IDENT struct 1 + IDENT S 1 + PUNCH ; [alone] 1 + + + + SUBTREE $$ 1 1"#]], + expect![[r#" + SUBTREE $$ 42:2@0..100#ROOT2024 42:2@0..100#ROOT2024 + IDENT struct 42:2@0..6#ROOT2024 + IDENT S 42:2@7..8#ROOT2024 + PUNCH ; [alone] 42:2@8..9#ROOT2024 + + + + SUBTREE $$ 42:2@0..100#ROOT2024 42:2@0..100#ROOT2024"#]], ); } @@ -22,6 +38,13 @@ fn test_derive_error() { "DeriveError", r#"struct S;"#, expect![[r#" + SUBTREE $$ 1 1 + IDENT struct 1 + IDENT S 1 + PUNCH ; [alone] 1 + + + SUBTREE $$ 1 1 IDENT compile_error 1 PUNCH ! [alone] 1 @@ -29,6 +52,13 @@ fn test_derive_error() { LITERAL Str #[derive(DeriveError)] struct S ; 1 PUNCH ; [alone] 1"#]], expect![[r#" + SUBTREE $$ 42:2@0..100#ROOT2024 42:2@0..100#ROOT2024 + IDENT struct 42:2@0..6#ROOT2024 + IDENT S 42:2@7..8#ROOT2024 + PUNCH ; [alone] 42:2@8..9#ROOT2024 + + + SUBTREE $$ 42:2@0..100#ROOT2024 42:2@0..100#ROOT2024 IDENT compile_error 42:2@0..100#ROOT2024 PUNCH ! [alone] 42:2@0..100#ROOT2024 @@ -44,6 +74,17 @@ fn test_fn_like_macro_noop() { "fn_like_noop", r#"ident, 0, 1, []"#, expect![[r#" + SUBTREE $$ 1 1 + IDENT ident 1 + PUNCH , [alone] 1 + LITERAL Integer 0 1 + PUNCH , [alone] 1 + LITERAL Integer 1 1 + PUNCH , [alone] 1 + SUBTREE [] 1 1 + + + SUBTREE $$ 1 1 IDENT ident 1 PUNCH , [alone] 1 @@ -53,6 +94,17 @@ fn test_fn_like_macro_noop() { PUNCH , [alone] 1 SUBTREE [] 1 1"#]], expect![[r#" + SUBTREE $$ 42:2@0..100#ROOT2024 42:2@0..100#ROOT2024 + IDENT ident 42:2@0..5#ROOT2024 + PUNCH , [alone] 42:2@5..6#ROOT2024 + LITERAL Integer 0 42:2@7..8#ROOT2024 + PUNCH , [alone] 42:2@8..9#ROOT2024 + LITERAL Integer 1 42:2@10..11#ROOT2024 + PUNCH , [alone] 42:2@11..12#ROOT2024 + SUBTREE [] 42:2@13..14#ROOT2024 42:2@14..15#ROOT2024 + + + SUBTREE $$ 42:2@0..100#ROOT2024 42:2@0..100#ROOT2024 IDENT ident 42:2@0..5#ROOT2024 PUNCH , [alone] 42:2@5..6#ROOT2024 @@ -70,11 +122,25 @@ fn test_fn_like_macro_clone_ident_subtree() { "fn_like_clone_tokens", r#"ident, []"#, expect![[r#" + SUBTREE $$ 1 1 + IDENT ident 1 + PUNCH , [alone] 1 + SUBTREE [] 1 1 + + + SUBTREE $$ 1 1 IDENT ident 1 PUNCH , [alone] 1 SUBTREE [] 1 1"#]], expect![[r#" + SUBTREE $$ 42:2@0..100#ROOT2024 42:2@0..100#ROOT2024 + IDENT ident 42:2@0..5#ROOT2024 + PUNCH , [alone] 42:2@5..6#ROOT2024 + SUBTREE [] 42:2@7..8#ROOT2024 42:2@8..9#ROOT2024 + + + SUBTREE $$ 42:2@0..100#ROOT2024 42:2@0..100#ROOT2024 IDENT ident 42:2@0..5#ROOT2024 PUNCH , [alone] 42:2@5..6#ROOT2024 @@ -88,9 +154,19 @@ fn test_fn_like_macro_clone_raw_ident() { "fn_like_clone_tokens", "r#async", expect![[r#" + SUBTREE $$ 1 1 + IDENT r#async 1 + + + SUBTREE $$ 1 1 IDENT r#async 1"#]], expect![[r#" + SUBTREE $$ 42:2@0..100#ROOT2024 42:2@0..100#ROOT2024 + IDENT r#async 42:2@0..7#ROOT2024 + + + SUBTREE $$ 42:2@0..100#ROOT2024 42:2@0..100#ROOT2024 IDENT r#async 42:2@0..7#ROOT2024"#]], ); @@ -103,9 +179,21 @@ fn test_fn_like_fn_like_span_join() { "fn_like_span_join", "foo bar", expect![[r#" + SUBTREE $$ 1 1 + IDENT foo 1 + IDENT bar 1 + + + SUBTREE $$ 1 1 IDENT r#joined 1"#]], expect![[r#" + SUBTREE $$ 42:2@0..100#ROOT2024 42:2@0..100#ROOT2024 + IDENT foo 42:2@0..3#ROOT2024 + IDENT bar 42:2@8..11#ROOT2024 + + + SUBTREE $$ 42:2@0..100#ROOT2024 42:2@0..100#ROOT2024 IDENT r#joined 42:2@0..11#ROOT2024"#]], ); @@ -118,11 +206,25 @@ fn test_fn_like_fn_like_span_ops() { "fn_like_span_ops", "set_def_site resolved_at_def_site start_span", expect![[r#" + SUBTREE $$ 1 1 + IDENT set_def_site 1 + IDENT resolved_at_def_site 1 + IDENT start_span 1 + + + SUBTREE $$ 1 1 IDENT set_def_site 0 IDENT resolved_at_def_site 1 IDENT start_span 1"#]], expect![[r#" + SUBTREE $$ 42:2@0..100#ROOT2024 42:2@0..100#ROOT2024 + IDENT set_def_site 42:2@0..12#ROOT2024 + IDENT resolved_at_def_site 42:2@13..33#ROOT2024 + IDENT start_span 42:2@34..44#ROOT2024 + + + SUBTREE $$ 42:2@0..100#ROOT2024 42:2@0..100#ROOT2024 IDENT set_def_site 41:1@0..150#ROOT2024 IDENT resolved_at_def_site 42:2@13..33#ROOT2024 @@ -136,23 +238,41 @@ fn test_fn_like_mk_literals() { "fn_like_mk_literals", r#""#, expect![[r#" + SUBTREE $$ 1 1 + + + SUBTREE $$ 1 1 LITERAL ByteStr byte_string 1 LITERAL Char c 1 LITERAL Str string 1 + LITERAL CStr cstring 1 LITERAL Float 3.14f64 1 + LITERAL Float -3.14f64 1 LITERAL Float 3.14 1 + LITERAL Float -3.14 1 LITERAL Integer 123i64 1 - LITERAL Integer 123 1"#]], + LITERAL Integer -123i64 1 + LITERAL Integer 123 1 + LITERAL Integer -123 1"#]], expect![[r#" + SUBTREE $$ 42:2@0..100#ROOT2024 42:2@0..100#ROOT2024 + + + SUBTREE $$ 42:2@0..100#ROOT2024 42:2@0..100#ROOT2024 LITERAL ByteStr byte_string 42:2@0..100#ROOT2024 LITERAL Char c 42:2@0..100#ROOT2024 LITERAL Str string 42:2@0..100#ROOT2024 + LITERAL CStr cstring 42:2@0..100#ROOT2024 LITERAL Float 3.14f64 42:2@0..100#ROOT2024 + LITERAL Float -3.14f64 42:2@0..100#ROOT2024 LITERAL Float 3.14 42:2@0..100#ROOT2024 + LITERAL Float -3.14 42:2@0..100#ROOT2024 LITERAL Integer 123i64 42:2@0..100#ROOT2024 - LITERAL Integer 123 42:2@0..100#ROOT2024"#]], + LITERAL Integer -123i64 42:2@0..100#ROOT2024 + LITERAL Integer 123 42:2@0..100#ROOT2024 + LITERAL Integer -123 42:2@0..100#ROOT2024"#]], ); } @@ -162,10 +282,18 @@ fn test_fn_like_mk_idents() { "fn_like_mk_idents", r#""#, expect![[r#" + SUBTREE $$ 1 1 + + + SUBTREE $$ 1 1 IDENT standard 1 IDENT r#raw 1"#]], expect![[r#" + SUBTREE $$ 42:2@0..100#ROOT2024 42:2@0..100#ROOT2024 + + + SUBTREE $$ 42:2@0..100#ROOT2024 42:2@0..100#ROOT2024 IDENT standard 42:2@0..100#ROOT2024 IDENT r#raw 42:2@0..100#ROOT2024"#]], @@ -178,6 +306,30 @@ fn test_fn_like_macro_clone_literals() { "fn_like_clone_tokens", r###"1u16, 2_u32, -4i64, 3.14f32, "hello bridge", "suffixed"suffix, r##"raw"##, 'a', b'b', c"null""###, expect![[r#" + SUBTREE $$ 1 1 + LITERAL Integer 1u16 1 + PUNCH , [alone] 1 + LITERAL Integer 2_u32 1 + PUNCH , [alone] 1 + PUNCH - [alone] 1 + LITERAL Integer 4i64 1 + PUNCH , [alone] 1 + LITERAL Float 3.14f32 1 + PUNCH , [alone] 1 + LITERAL Str hello bridge 1 + PUNCH , [alone] 1 + LITERAL Str suffixedsuffix 1 + PUNCH , [alone] 1 + LITERAL StrRaw(2) raw 1 + PUNCH , [alone] 1 + LITERAL Char a 1 + PUNCH , [alone] 1 + LITERAL Byte b 1 + PUNCH , [alone] 1 + LITERAL CStr null 1 + + + SUBTREE $$ 1 1 LITERAL Integer 1u16 1 PUNCH , [alone] 1 @@ -200,6 +352,30 @@ fn test_fn_like_macro_clone_literals() { PUNCH , [alone] 1 LITERAL CStr null 1"#]], expect![[r#" + SUBTREE $$ 42:2@0..100#ROOT2024 42:2@0..100#ROOT2024 + LITERAL Integer 1u16 42:2@0..4#ROOT2024 + PUNCH , [alone] 42:2@4..5#ROOT2024 + LITERAL Integer 2_u32 42:2@6..11#ROOT2024 + PUNCH , [alone] 42:2@11..12#ROOT2024 + PUNCH - [alone] 42:2@13..14#ROOT2024 + LITERAL Integer 4i64 42:2@14..18#ROOT2024 + PUNCH , [alone] 42:2@18..19#ROOT2024 + LITERAL Float 3.14f32 42:2@20..27#ROOT2024 + PUNCH , [alone] 42:2@27..28#ROOT2024 + LITERAL Str hello bridge 42:2@29..43#ROOT2024 + PUNCH , [alone] 42:2@43..44#ROOT2024 + LITERAL Str suffixedsuffix 42:2@45..61#ROOT2024 + PUNCH , [alone] 42:2@61..62#ROOT2024 + LITERAL StrRaw(2) raw 42:2@63..73#ROOT2024 + PUNCH , [alone] 42:2@73..74#ROOT2024 + LITERAL Char a 42:2@75..78#ROOT2024 + PUNCH , [alone] 42:2@78..79#ROOT2024 + LITERAL Byte b 42:2@80..84#ROOT2024 + PUNCH , [alone] 42:2@84..85#ROOT2024 + LITERAL CStr null 42:2@86..93#ROOT2024 + + + SUBTREE $$ 42:2@0..100#ROOT2024 42:2@0..100#ROOT2024 LITERAL Integer 1u16 42:2@0..4#ROOT2024 PUNCH , [alone] 42:2@4..5#ROOT2024 @@ -224,6 +400,71 @@ fn test_fn_like_macro_clone_literals() { ); } + +#[test] +fn test_fn_like_macro_negative_literals() { + assert_expand( + "fn_like_clone_tokens", + r###"-1u16, - 2_u32, -3.14f32, - 2.7"###, + expect![[r#" + SUBTREE $$ 1 1 + PUNCH - [alone] 1 + LITERAL Integer 1u16 1 + PUNCH , [alone] 1 + PUNCH - [alone] 1 + LITERAL Integer 2_u32 1 + PUNCH , [alone] 1 + PUNCH - [alone] 1 + LITERAL Float 3.14f32 1 + PUNCH , [alone] 1 + PUNCH - [alone] 1 + LITERAL Float 2.7 1 + + + + SUBTREE $$ 1 1 + PUNCH - [alone] 1 + LITERAL Integer 1u16 1 + PUNCH , [alone] 1 + PUNCH - [alone] 1 + LITERAL Integer 2_u32 1 + PUNCH , [alone] 1 + PUNCH - [alone] 1 + LITERAL Float 3.14f32 1 + PUNCH , [alone] 1 + PUNCH - [alone] 1 + LITERAL Float 2.7 1"#]], + expect![[r#" + SUBTREE $$ 42:2@0..100#ROOT2024 42:2@0..100#ROOT2024 + PUNCH - [alone] 42:2@0..1#ROOT2024 + LITERAL Integer 1u16 42:2@1..5#ROOT2024 + PUNCH , [alone] 42:2@5..6#ROOT2024 + PUNCH - [alone] 42:2@7..8#ROOT2024 + LITERAL Integer 2_u32 42:2@9..14#ROOT2024 + PUNCH , [alone] 42:2@14..15#ROOT2024 + PUNCH - [alone] 42:2@16..17#ROOT2024 + LITERAL Float 3.14f32 42:2@17..24#ROOT2024 + PUNCH , [alone] 42:2@24..25#ROOT2024 + PUNCH - [alone] 42:2@26..27#ROOT2024 + LITERAL Float 2.7 42:2@28..31#ROOT2024 + + + + SUBTREE $$ 42:2@0..100#ROOT2024 42:2@0..100#ROOT2024 + PUNCH - [alone] 42:2@0..1#ROOT2024 + LITERAL Integer 1u16 42:2@1..5#ROOT2024 + PUNCH , [alone] 42:2@5..6#ROOT2024 + PUNCH - [alone] 42:2@7..8#ROOT2024 + LITERAL Integer 2_u32 42:2@9..14#ROOT2024 + PUNCH , [alone] 42:2@14..15#ROOT2024 + PUNCH - [alone] 42:2@16..17#ROOT2024 + LITERAL Float 3.14f32 42:2@17..24#ROOT2024 + PUNCH , [alone] 42:2@24..25#ROOT2024 + PUNCH - [alone] 42:2@26..27#ROOT2024 + LITERAL Float 2.7 42:2@28..31#ROOT2024"#]], + ); +} + #[test] fn test_attr_macro() { // Corresponds to @@ -234,6 +475,15 @@ fn test_attr_macro() { r#"mod m {}"#, r#"some arguments"#, expect![[r#" + SUBTREE $$ 1 1 + IDENT mod 1 + IDENT m 1 + SUBTREE {} 1 1 + + SUBTREE $$ 1 1 + IDENT some 1 + IDENT arguments 1 + SUBTREE $$ 1 1 IDENT compile_error 1 PUNCH ! [alone] 1 @@ -241,6 +491,15 @@ fn test_attr_macro() { LITERAL Str #[attr_error(some arguments)] mod m {} 1 PUNCH ; [alone] 1"#]], expect![[r#" + SUBTREE $$ 42:2@0..100#ROOT2024 42:2@0..100#ROOT2024 + IDENT mod 42:2@0..3#ROOT2024 + IDENT m 42:2@4..5#ROOT2024 + SUBTREE {} 42:2@6..7#ROOT2024 42:2@7..8#ROOT2024 + + SUBTREE $$ 42:2@0..100#ROOT2024 42:2@0..100#ROOT2024 + IDENT some 42:2@0..4#ROOT2024 + IDENT arguments 42:2@5..14#ROOT2024 + SUBTREE $$ 42:2@0..100#ROOT2024 42:2@0..100#ROOT2024 IDENT compile_error 42:2@0..100#ROOT2024 PUNCH ! [alone] 42:2@0..100#ROOT2024 diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs index a476a70a7409..a0a45b269e4a 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs @@ -32,9 +32,9 @@ pub fn assert_expand( macro_name: &str, #[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect, - expect_s: Expect, + expect_spanned: Expect, ) { - assert_expand_impl(macro_name, ra_fixture, None, expect, expect_s); + assert_expand_impl(macro_name, ra_fixture, None, expect, expect_spanned); } pub fn assert_expand_attr( @@ -42,9 +42,9 @@ pub fn assert_expand_attr( #[rust_analyzer::rust_fixture] ra_fixture: &str, attr_args: &str, expect: Expect, - expect_s: Expect, + expect_spanned: Expect, ) { - assert_expand_impl(macro_name, ra_fixture, Some(attr_args), expect, expect_s); + assert_expand_impl(macro_name, ra_fixture, Some(attr_args), expect, expect_spanned); } fn assert_expand_impl( @@ -52,7 +52,7 @@ fn assert_expand_impl( input: &str, attr: Option<&str>, expect: Expect, - expect_s: Expect, + expect_spanned: Expect, ) { let path = proc_macro_test_dylib_path(); let expander = dylib::Expander::new(&path).unwrap(); @@ -60,20 +60,17 @@ fn assert_expand_impl( let def_site = TokenId(0); let call_site = TokenId(1); let mixed_site = TokenId(2); - let input_ts = parse_string(call_site, input); + let input_ts = parse_string(call_site, input).into_subtree(call_site); let attr_ts = attr.map(|attr| parse_string(call_site, attr).into_subtree(call_site)); + let input_ts_string = format!("{input_ts:?}"); + let attr_ts_string = attr_ts.as_ref().map(|it| format!("{it:?}")); - let res = expander - .expand( - macro_name, - input_ts.into_subtree(call_site), - attr_ts, - def_site, - call_site, - mixed_site, - ) - .unwrap(); - expect.assert_eq(&format!("{res:?}")); + let res = + expander.expand(macro_name, input_ts, attr_ts, def_site, call_site, mixed_site).unwrap(); + expect.assert_eq(&format!( + "{input_ts_string}\n\n{}\n\n{res:?}", + attr_ts_string.unwrap_or_default() + )); let def_site = Span { range: TextRange::new(0.into(), 150.into()), @@ -93,15 +90,17 @@ fn assert_expand_impl( }; let mixed_site = call_site; - let fixture = parse_string_spanned(call_site.anchor, call_site.ctx, input); + let fixture = + parse_string_spanned(call_site.anchor, call_site.ctx, input).into_subtree(call_site); let attr = attr.map(|attr| { parse_string_spanned(call_site.anchor, call_site.ctx, attr).into_subtree(call_site) }); + let fixture_string = format!("{fixture:?}"); + let attr_string = attr.as_ref().map(|it| format!("{it:?}")); - let res = expander - .expand(macro_name, fixture.into_subtree(call_site), attr, def_site, call_site, mixed_site) - .unwrap(); - expect_s.assert_eq(&format!("{res:#?}")); + let res = expander.expand(macro_name, fixture, attr, def_site, call_site, mixed_site).unwrap(); + expect_spanned + .assert_eq(&format!("{fixture_string}\n\n{}\n\n{res:#?}", attr_string.unwrap_or_default())); } pub(crate) fn list() -> Vec { diff --git a/src/tools/rust-analyzer/crates/tt/src/lib.rs b/src/tools/rust-analyzer/crates/tt/src/lib.rs index 1dbc07c0929c..a511bb0039cb 100644 --- a/src/tools/rust-analyzer/crates/tt/src/lib.rs +++ b/src/tools/rust-analyzer/crates/tt/src/lib.rs @@ -817,6 +817,58 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { } } +impl Literal { + pub fn display_no_minus(&self) -> impl fmt::Display { + struct NoMinus<'a, S>(&'a Literal); + impl fmt::Display for NoMinus<'_, S> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let symbol = + self.0.symbol.as_str().strip_prefix('-').unwrap_or(self.0.symbol.as_str()); + match self.0.kind { + LitKind::Byte => write!(f, "b'{}'", symbol), + LitKind::Char => write!(f, "'{}'", symbol), + LitKind::Integer | LitKind::Float | LitKind::Err(_) => write!(f, "{}", symbol), + LitKind::Str => write!(f, "\"{}\"", symbol), + LitKind::ByteStr => write!(f, "b\"{}\"", symbol), + LitKind::CStr => write!(f, "c\"{}\"", symbol), + LitKind::StrRaw(num_of_hashes) => { + let num_of_hashes = num_of_hashes as usize; + write!( + f, + r#"r{0:# { + let num_of_hashes = num_of_hashes as usize; + write!( + f, + r#"br{0:# { + let num_of_hashes = num_of_hashes as usize; + write!( + f, + r#"cr{0:# fmt::Display for Literal { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.kind { From 4f0c28e1dcc07052c1f8ddce686628c1c8f959c8 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 5 May 2025 13:56:24 +0200 Subject: [PATCH 09/51] fix: Fix proc-macro API creating malformed negative literals --- .../src/server_impl/rust_analyzer_span.rs | 40 ++++++++++++++----- .../src/server_impl/token_id.rs | 40 ++++++++++++++----- .../src/server_impl/token_stream.rs | 5 +++ .../crates/proc-macro-srv/src/tests/mod.rs | 25 +++++++----- 4 files changed, 83 insertions(+), 27 deletions(-) diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs index 47555a5db2f7..64b40e7b9437 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs @@ -168,16 +168,38 @@ fn from_token_tree( } bridge::TokenTree::Literal(literal) => { - let literal = tt::Literal { - symbol: literal.symbol, - suffix: literal.suffix, - span: literal.span, - kind: literal_kind_to_internal(literal.kind), - }; + let token_trees = + if let Some((_minus, symbol)) = literal.symbol.as_str().split_once('-') { + let punct = tt::Punct { + spacing: tt::Spacing::Alone, + span: literal.span, + char: '-' as char, + }; + let leaf: tt::Leaf = tt::Leaf::from(punct); + let minus_tree = tt::TokenTree::from(leaf); - let leaf: tt::Leaf = tt::Leaf::from(literal); - let tree = tt::TokenTree::from(leaf); - TokenStream { token_trees: vec![tree] } + let literal = tt::Literal { + symbol: Symbol::intern(symbol), + suffix: literal.suffix, + span: literal.span, + kind: literal_kind_to_internal(literal.kind), + }; + let leaf: tt::Leaf = tt::Leaf::from(literal); + let tree = tt::TokenTree::from(leaf); + vec![minus_tree, tree] + } else { + let literal = tt::Literal { + symbol: literal.symbol, + suffix: literal.suffix, + span: literal.span, + kind: literal_kind_to_internal(literal.kind), + }; + + let leaf: tt::Leaf = tt::Leaf::from(literal); + let tree = tt::TokenTree::from(leaf); + vec![tree] + }; + TokenStream { token_trees } } bridge::TokenTree::Punct(p) => { diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs index c002be4be6ff..24a67bf45c8d 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs @@ -153,16 +153,38 @@ fn from_token_tree( } bridge::TokenTree::Literal(literal) => { - let literal = Literal { - symbol: literal.symbol, - suffix: literal.suffix, - span: literal.span, - kind: literal_kind_to_internal(literal.kind), - }; + let token_trees = + if let Some((_minus, symbol)) = literal.symbol.as_str().split_once('-') { + let punct = tt::Punct { + spacing: tt::Spacing::Alone, + span: literal.span, + char: '-' as char, + }; + let leaf: tt::Leaf = tt::Leaf::from(punct); + let minus_tree = tt::TokenTree::from(leaf); - let leaf = tt::Leaf::from(literal); - let tree = TokenTree::from(leaf); - TokenStream { token_trees: vec![tree] } + let literal = Literal { + symbol: Symbol::intern(symbol), + suffix: literal.suffix, + span: literal.span, + kind: literal_kind_to_internal(literal.kind), + }; + let leaf: tt::Leaf = tt::Leaf::from(literal); + let tree = tt::TokenTree::from(leaf); + vec![minus_tree, tree] + } else { + let literal = Literal { + symbol: literal.symbol, + suffix: literal.suffix, + span: literal.span, + kind: literal_kind_to_internal(literal.kind), + }; + + let leaf: tt::Leaf = tt::Leaf::from(literal); + let tree = tt::TokenTree::from(leaf); + vec![tree] + }; + TokenStream { token_trees } } bridge::TokenTree::Punct(p) => { diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_stream.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_stream.rs index 4946a4f2a621..072557913c2b 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_stream.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_stream.rs @@ -68,6 +68,11 @@ pub(crate) fn into_bridge(self) -> Vec Literals created from negative numbers might not survive round-trips through + // > TokenStream or strings and may be broken into two tokens (- and positive + // > literal). tt::TokenTree::Leaf(tt::Leaf::Literal(lit)) => { result.push(bridge::TokenTree::Literal(bridge::Literal { span: lit.span, diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs index 7a0ac01c50da..36cd675b9a6f 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs @@ -248,13 +248,17 @@ fn test_fn_like_mk_literals() { LITERAL Str string 1 LITERAL CStr cstring 1 LITERAL Float 3.14f64 1 - LITERAL Float -3.14f64 1 + PUNCH - [alone] 1 + LITERAL Float 3.14f64 1 + LITERAL Float 3.14 1 + PUNCH - [alone] 1 LITERAL Float 3.14 1 - LITERAL Float -3.14 1 LITERAL Integer 123i64 1 - LITERAL Integer -123i64 1 + PUNCH - [alone] 1 + LITERAL Integer 123i64 1 LITERAL Integer 123 1 - LITERAL Integer -123 1"#]], + PUNCH - [alone] 1 + LITERAL Integer 123 1"#]], expect![[r#" SUBTREE $$ 42:2@0..100#ROOT2024 42:2@0..100#ROOT2024 @@ -266,13 +270,17 @@ fn test_fn_like_mk_literals() { LITERAL Str string 42:2@0..100#ROOT2024 LITERAL CStr cstring 42:2@0..100#ROOT2024 LITERAL Float 3.14f64 42:2@0..100#ROOT2024 - LITERAL Float -3.14f64 42:2@0..100#ROOT2024 + PUNCH - [alone] 42:2@0..100#ROOT2024 + LITERAL Float 3.14f64 42:2@0..100#ROOT2024 + LITERAL Float 3.14 42:2@0..100#ROOT2024 + PUNCH - [alone] 42:2@0..100#ROOT2024 LITERAL Float 3.14 42:2@0..100#ROOT2024 - LITERAL Float -3.14 42:2@0..100#ROOT2024 LITERAL Integer 123i64 42:2@0..100#ROOT2024 - LITERAL Integer -123i64 42:2@0..100#ROOT2024 + PUNCH - [alone] 42:2@0..100#ROOT2024 + LITERAL Integer 123i64 42:2@0..100#ROOT2024 LITERAL Integer 123 42:2@0..100#ROOT2024 - LITERAL Integer -123 42:2@0..100#ROOT2024"#]], + PUNCH - [alone] 42:2@0..100#ROOT2024 + LITERAL Integer 123 42:2@0..100#ROOT2024"#]], ); } @@ -400,7 +408,6 @@ fn test_fn_like_macro_clone_literals() { ); } - #[test] fn test_fn_like_macro_negative_literals() { assert_expand( From 845106e0f9b3ae70250c8900ae3762239d223117 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 5 May 2025 13:56:24 +0200 Subject: [PATCH 10/51] fix: Fix `move_bounds` assists not working for lifetimes --- .../hir-expand/src/builtin/derive_macro.rs | 13 ++-- .../ide-assists/src/handlers/move_bounds.rs | 52 ++++++++++++---- .../src/server_impl/rust_analyzer_span.rs | 40 +++++++++--- .../src/server_impl/token_id.rs | 40 +++++++++--- .../src/server_impl/token_stream.rs | 5 ++ .../crates/proc-macro-srv/src/tests/mod.rs | 25 +++++--- .../crates/syntax/src/ast/edit_in_place.rs | 61 +++++++++++++++++++ .../crates/syntax/src/ast/make.rs | 3 +- 8 files changed, 194 insertions(+), 45 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/derive_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/derive_macro.rs index 68283b916d74..d135584a0809 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/derive_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/derive_macro.rs @@ -1,5 +1,6 @@ //! Builtin derives. +use either::Either; use intern::sym; use itertools::{Itertools, izip}; use parser::SyntaxKind; @@ -1179,10 +1180,10 @@ fn coerce_pointee_expand( }; new_predicates.push( make::where_pred( - make::ty_path(make::path_from_segments( + Either::Right(make::ty_path(make::path_from_segments( [make::path_segment(new_bounds_target)], false, - )), + ))), new_bounds, ) .clone_for_update(), @@ -1245,7 +1246,9 @@ fn coerce_pointee_expand( substitute_type_in_bound(ty, &pointee_param_name.text(), ADDED_PARAM) }) }); - new_predicates.push(make::where_pred(pred_target, new_bounds).clone_for_update()); + new_predicates.push( + make::where_pred(Either::Right(pred_target), new_bounds).clone_for_update(), + ); } } @@ -1260,10 +1263,10 @@ fn coerce_pointee_expand( // Find the `#[pointee]` parameter and add an `Unsize<__S>` bound to it. where_clause.add_predicate( make::where_pred( - make::ty_path(make::path_from_segments( + Either::Right(make::ty_path(make::path_from_segments( [make::path_segment(make::name_ref(&pointee_param_name.text()))], false, - )), + ))), [make::type_bound(make::ty_path(make::path_from_segments( [ make::path_segment(make::name_ref("core")), diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_bounds.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_bounds.rs index 7e8735bd7a24..a9df6f6fc366 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_bounds.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_bounds.rs @@ -1,3 +1,4 @@ +use either::Either; use syntax::{ ast::{ self, AstNode, HasName, HasTypeBounds, @@ -30,10 +31,11 @@ pub(crate) fn move_bounds_to_where_clause( ) -> Option<()> { let type_param_list = ctx.find_node_at_offset::()?; - let mut type_params = type_param_list.type_or_const_params(); + let mut type_params = type_param_list.generic_params(); if type_params.all(|p| match p { - ast::TypeOrConstParam::Type(t) => t.type_bound_list().is_none(), - ast::TypeOrConstParam::Const(_) => true, + ast::GenericParam::TypeParam(t) => t.type_bound_list().is_none(), + ast::GenericParam::LifetimeParam(l) => l.type_bound_list().is_none(), + ast::GenericParam::ConstParam(_) => true, }) { return None; } @@ -53,20 +55,23 @@ pub(crate) fn move_bounds_to_where_clause( match parent { ast::Fn(it) => it.get_or_create_where_clause(), ast::Trait(it) => it.get_or_create_where_clause(), + ast::TraitAlias(it) => it.get_or_create_where_clause(), ast::Impl(it) => it.get_or_create_where_clause(), ast::Enum(it) => it.get_or_create_where_clause(), ast::Struct(it) => it.get_or_create_where_clause(), + ast::TypeAlias(it) => it.get_or_create_where_clause(), _ => return, } }; - for toc_param in type_param_list.type_or_const_params() { - let type_param = match toc_param { - ast::TypeOrConstParam::Type(x) => x, - ast::TypeOrConstParam::Const(_) => continue, + for generic_param in type_param_list.generic_params() { + let param: &dyn HasTypeBounds = match &generic_param { + ast::GenericParam::TypeParam(t) => t, + ast::GenericParam::LifetimeParam(l) => l, + ast::GenericParam::ConstParam(_) => continue, }; - if let Some(tbl) = type_param.type_bound_list() { - if let Some(predicate) = build_predicate(type_param) { + if let Some(tbl) = param.type_bound_list() { + if let Some(predicate) = build_predicate(generic_param) { where_clause.add_predicate(predicate) } tbl.remove() @@ -76,9 +81,23 @@ pub(crate) fn move_bounds_to_where_clause( ) } -fn build_predicate(param: ast::TypeParam) -> Option { - let path = make::ext::ident_path(¶m.name()?.syntax().to_string()); - let predicate = make::where_pred(make::ty_path(path), param.type_bound_list()?.bounds()); +fn build_predicate(param: ast::GenericParam) -> Option { + let target = match ¶m { + ast::GenericParam::TypeParam(t) => { + Either::Right(make::ty_path(make::ext::ident_path(&t.name()?.to_string()))) + } + ast::GenericParam::LifetimeParam(l) => Either::Left(l.lifetime()?), + ast::GenericParam::ConstParam(_) => return None, + }; + let predicate = make::where_pred( + target, + match param { + ast::GenericParam::TypeParam(t) => t.type_bound_list()?, + ast::GenericParam::LifetimeParam(l) => l.type_bound_list()?, + ast::GenericParam::ConstParam(_) => return None, + } + .bounds(), + ); Some(predicate.clone_for_update()) } @@ -123,4 +142,13 @@ fn move_bounds_to_where_clause_tuple_struct() { r#"struct Pair(T, T) where T: u32;"#, ); } + + #[test] + fn move_bounds_to_where_clause_trait() { + check_assist( + move_bounds_to_where_clause, + r#"trait T<'a: 'static, $0T: u32> {}"#, + r#"trait T<'a, T> where 'a: 'static, T: u32 {}"#, + ); + } } diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs index 47555a5db2f7..64b40e7b9437 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs @@ -168,16 +168,38 @@ fn from_token_tree( } bridge::TokenTree::Literal(literal) => { - let literal = tt::Literal { - symbol: literal.symbol, - suffix: literal.suffix, - span: literal.span, - kind: literal_kind_to_internal(literal.kind), - }; + let token_trees = + if let Some((_minus, symbol)) = literal.symbol.as_str().split_once('-') { + let punct = tt::Punct { + spacing: tt::Spacing::Alone, + span: literal.span, + char: '-' as char, + }; + let leaf: tt::Leaf = tt::Leaf::from(punct); + let minus_tree = tt::TokenTree::from(leaf); - let leaf: tt::Leaf = tt::Leaf::from(literal); - let tree = tt::TokenTree::from(leaf); - TokenStream { token_trees: vec![tree] } + let literal = tt::Literal { + symbol: Symbol::intern(symbol), + suffix: literal.suffix, + span: literal.span, + kind: literal_kind_to_internal(literal.kind), + }; + let leaf: tt::Leaf = tt::Leaf::from(literal); + let tree = tt::TokenTree::from(leaf); + vec![minus_tree, tree] + } else { + let literal = tt::Literal { + symbol: literal.symbol, + suffix: literal.suffix, + span: literal.span, + kind: literal_kind_to_internal(literal.kind), + }; + + let leaf: tt::Leaf = tt::Leaf::from(literal); + let tree = tt::TokenTree::from(leaf); + vec![tree] + }; + TokenStream { token_trees } } bridge::TokenTree::Punct(p) => { diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs index c002be4be6ff..24a67bf45c8d 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs @@ -153,16 +153,38 @@ fn from_token_tree( } bridge::TokenTree::Literal(literal) => { - let literal = Literal { - symbol: literal.symbol, - suffix: literal.suffix, - span: literal.span, - kind: literal_kind_to_internal(literal.kind), - }; + let token_trees = + if let Some((_minus, symbol)) = literal.symbol.as_str().split_once('-') { + let punct = tt::Punct { + spacing: tt::Spacing::Alone, + span: literal.span, + char: '-' as char, + }; + let leaf: tt::Leaf = tt::Leaf::from(punct); + let minus_tree = tt::TokenTree::from(leaf); - let leaf = tt::Leaf::from(literal); - let tree = TokenTree::from(leaf); - TokenStream { token_trees: vec![tree] } + let literal = Literal { + symbol: Symbol::intern(symbol), + suffix: literal.suffix, + span: literal.span, + kind: literal_kind_to_internal(literal.kind), + }; + let leaf: tt::Leaf = tt::Leaf::from(literal); + let tree = tt::TokenTree::from(leaf); + vec![minus_tree, tree] + } else { + let literal = Literal { + symbol: literal.symbol, + suffix: literal.suffix, + span: literal.span, + kind: literal_kind_to_internal(literal.kind), + }; + + let leaf: tt::Leaf = tt::Leaf::from(literal); + let tree = tt::TokenTree::from(leaf); + vec![tree] + }; + TokenStream { token_trees } } bridge::TokenTree::Punct(p) => { diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_stream.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_stream.rs index 4946a4f2a621..072557913c2b 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_stream.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_stream.rs @@ -68,6 +68,11 @@ pub(crate) fn into_bridge(self) -> Vec Literals created from negative numbers might not survive round-trips through + // > TokenStream or strings and may be broken into two tokens (- and positive + // > literal). tt::TokenTree::Leaf(tt::Leaf::Literal(lit)) => { result.push(bridge::TokenTree::Literal(bridge::Literal { span: lit.span, diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs index 7a0ac01c50da..36cd675b9a6f 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs @@ -248,13 +248,17 @@ fn test_fn_like_mk_literals() { LITERAL Str string 1 LITERAL CStr cstring 1 LITERAL Float 3.14f64 1 - LITERAL Float -3.14f64 1 + PUNCH - [alone] 1 + LITERAL Float 3.14f64 1 + LITERAL Float 3.14 1 + PUNCH - [alone] 1 LITERAL Float 3.14 1 - LITERAL Float -3.14 1 LITERAL Integer 123i64 1 - LITERAL Integer -123i64 1 + PUNCH - [alone] 1 + LITERAL Integer 123i64 1 LITERAL Integer 123 1 - LITERAL Integer -123 1"#]], + PUNCH - [alone] 1 + LITERAL Integer 123 1"#]], expect![[r#" SUBTREE $$ 42:2@0..100#ROOT2024 42:2@0..100#ROOT2024 @@ -266,13 +270,17 @@ fn test_fn_like_mk_literals() { LITERAL Str string 42:2@0..100#ROOT2024 LITERAL CStr cstring 42:2@0..100#ROOT2024 LITERAL Float 3.14f64 42:2@0..100#ROOT2024 - LITERAL Float -3.14f64 42:2@0..100#ROOT2024 + PUNCH - [alone] 42:2@0..100#ROOT2024 + LITERAL Float 3.14f64 42:2@0..100#ROOT2024 + LITERAL Float 3.14 42:2@0..100#ROOT2024 + PUNCH - [alone] 42:2@0..100#ROOT2024 LITERAL Float 3.14 42:2@0..100#ROOT2024 - LITERAL Float -3.14 42:2@0..100#ROOT2024 LITERAL Integer 123i64 42:2@0..100#ROOT2024 - LITERAL Integer -123i64 42:2@0..100#ROOT2024 + PUNCH - [alone] 42:2@0..100#ROOT2024 + LITERAL Integer 123i64 42:2@0..100#ROOT2024 LITERAL Integer 123 42:2@0..100#ROOT2024 - LITERAL Integer -123 42:2@0..100#ROOT2024"#]], + PUNCH - [alone] 42:2@0..100#ROOT2024 + LITERAL Integer 123 42:2@0..100#ROOT2024"#]], ); } @@ -400,7 +408,6 @@ fn test_fn_like_macro_clone_literals() { ); } - #[test] fn test_fn_like_macro_negative_literals() { assert_expand( diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs index da0bfd4f37f5..e60243f2c917 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs @@ -109,6 +109,67 @@ fn get_or_create_where_clause(&self) -> ast::WhereClause { } } +impl GenericParamsOwnerEdit for ast::TraitAlias { + fn get_or_create_generic_param_list(&self) -> ast::GenericParamList { + match self.generic_param_list() { + Some(it) => it, + None => { + let position = if let Some(name) = self.name() { + Position::after(name.syntax) + } else if let Some(trait_token) = self.trait_token() { + Position::after(trait_token) + } else { + Position::last_child_of(self.syntax()) + }; + create_generic_param_list(position) + } + } + } + + fn get_or_create_where_clause(&self) -> ast::WhereClause { + if self.where_clause().is_none() { + let position = match self.semicolon_token() { + Some(tok) => Position::before(tok), + None => Position::last_child_of(self.syntax()), + }; + create_where_clause(position); + } + self.where_clause().unwrap() + } +} + +impl GenericParamsOwnerEdit for ast::TypeAlias { + fn get_or_create_generic_param_list(&self) -> ast::GenericParamList { + match self.generic_param_list() { + Some(it) => it, + None => { + let position = if let Some(name) = self.name() { + Position::after(name.syntax) + } else if let Some(trait_token) = self.type_token() { + Position::after(trait_token) + } else { + Position::last_child_of(self.syntax()) + }; + create_generic_param_list(position) + } + } + } + + fn get_or_create_where_clause(&self) -> ast::WhereClause { + if self.where_clause().is_none() { + let position = match self.eq_token() { + Some(tok) => Position::before(tok), + None => match self.semicolon_token() { + Some(tok) => Position::before(tok), + None => Position::last_child_of(self.syntax()), + }, + }; + create_where_clause(position); + } + self.where_clause().unwrap() + } +} + impl GenericParamsOwnerEdit for ast::Struct { fn get_or_create_generic_param_list(&self) -> ast::GenericParamList { match self.generic_param_list() { diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs index 596f73e0b103..fab4cb287c3d 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs @@ -13,6 +13,7 @@ mod quote; +use either::Either; use itertools::Itertools; use parser::{Edition, T}; use rowan::NodeOrToken; @@ -881,7 +882,7 @@ fn from_text(text: &str) -> ast::MatchArmList { } pub fn where_pred( - path: ast::Type, + path: Either, bounds: impl IntoIterator, ) -> ast::WherePred { let bounds = bounds.into_iter().join(" + "); From 63e7c7ece3c7ee5f54edb804cd76fd57e7ac784a Mon Sep 17 00:00:00 2001 From: Vishruth-Thimmaiah Date: Sun, 23 Mar 2025 18:05:45 +0530 Subject: [PATCH 11/51] fix: negative nums in `concat!` expansion --- .../macro_expansion_tests/builtin_fn_macro.rs | 4 +-- .../crates/hir-expand/src/builtin/fn_macro.rs | 35 ++++++++++++++++++- 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs index e21d1415aa29..3027aff3163a 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs @@ -454,13 +454,13 @@ fn test_concat_expand() { #[rustc_builtin_macro] macro_rules! concat {} -fn main() { concat!("fo", "o", 0, r#""bar""#, "\n", false, '"', '\0'); } +fn main() { concat!("fo", "o", 0, r#""bar""#, "\n", false, '"', -4, - 4, '\0'); } "##, expect![[r##" #[rustc_builtin_macro] macro_rules! concat {} -fn main() { "foo0\"bar\"\nfalse\"\u{0}"; } +fn main() { "foo0\"bar\"\nfalse\"-4-4\u{0}"; } "##]], ); } diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs index 621e174cac99..539c72772843 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs @@ -452,7 +452,10 @@ fn concat_expand( Some(_) => (), None => span = Some(s), }; - for (i, mut t) in tt.iter().enumerate() { + + let mut i = 0; + let mut iter = tt.iter(); + while let Some(mut t) = iter.next() { // FIXME: hack on top of a hack: `$e:expr` captures get surrounded in parentheses // to ensure the right parsing order, so skip the parentheses here. Ideally we'd // implement rustc's model. cc https://github.com/rust-lang/rust-analyzer/pull/10623 @@ -504,10 +507,40 @@ fn concat_expand( record_span(id.span); } TtElement::Leaf(tt::Leaf::Punct(punct)) if i % 2 == 1 && punct.char == ',' => (), + // handle negative numbers + TtElement::Leaf(tt::Leaf::Punct(punct)) if i % 2 == 0 && punct.char == '-' => { + let t = match iter.next() { + Some(t) => t, + None => { + err.get_or_insert(ExpandError::other( + call_site, + "unexpected end of input after '-'", + )); + break; + } + }; + + match t { + TtElement::Leaf(tt::Leaf::Literal(it)) + if matches!(it.kind, tt::LitKind::Integer | tt::LitKind::Float) => + { + format_to!(text, "-{}", it.symbol.as_str()); + record_span(punct.span.cover(it.span)); + } + _ => { + err.get_or_insert(ExpandError::other( + call_site, + "expected integer or floating pointer number after '-'", + )); + break; + } + } + } _ => { err.get_or_insert(ExpandError::other(call_site, "unexpected token")); } } + i += 1; } let span = span.unwrap_or_else(|| tt.top_subtree().delimiter.open); ExpandResult { value: quote!(span =>#text), err } From 5620b39a42efda8346d74abcbda9da1650acce90 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 5 May 2025 16:04:57 +0200 Subject: [PATCH 12/51] github: Direct users to discussions instead of issues for questions --- src/tools/rust-analyzer/.github/ISSUE_TEMPLATE/config.yml | 4 ++++ .../.github/ISSUE_TEMPLATE/critical_nightly_regression.md | 2 -- .../rust-analyzer/.github/ISSUE_TEMPLATE/question.md | 8 -------- 3 files changed, 4 insertions(+), 10 deletions(-) create mode 100644 src/tools/rust-analyzer/.github/ISSUE_TEMPLATE/config.yml delete mode 100644 src/tools/rust-analyzer/.github/ISSUE_TEMPLATE/question.md diff --git a/src/tools/rust-analyzer/.github/ISSUE_TEMPLATE/config.yml b/src/tools/rust-analyzer/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 000000000000..466672e3d481 --- /dev/null +++ b/src/tools/rust-analyzer/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,4 @@ +contact_links: + - name: Questions regarding rust-analyzer + url: https://github.com/rust-lang/rust-analyzer/discussions + about: Please ask and answer questions here instead of opening an issue diff --git a/src/tools/rust-analyzer/.github/ISSUE_TEMPLATE/critical_nightly_regression.md b/src/tools/rust-analyzer/.github/ISSUE_TEMPLATE/critical_nightly_regression.md index 23c43443c84f..2b44bdc748f2 100644 --- a/src/tools/rust-analyzer/.github/ISSUE_TEMPLATE/critical_nightly_regression.md +++ b/src/tools/rust-analyzer/.github/ISSUE_TEMPLATE/critical_nightly_regression.md @@ -12,5 +12,3 @@ Troubleshooting guide: https://rust-analyzer.github.io/book/troubleshooting.html Please try to provide information which will help us to fix the issue faster. Minimal reproducible examples with few dependencies are especially lovely <3. --> - -This is a serious regression in nightly and it's important to fix it before the next release. diff --git a/src/tools/rust-analyzer/.github/ISSUE_TEMPLATE/question.md b/src/tools/rust-analyzer/.github/ISSUE_TEMPLATE/question.md deleted file mode 100644 index a90ade882bd9..000000000000 --- a/src/tools/rust-analyzer/.github/ISSUE_TEMPLATE/question.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -name: Support Question -about: A question regarding functionality of rust-analyzer. -title: '' -labels: 'C-support' -assignees: '' - ---- From 941e8f1b155869d4ac3ae7392fc732bb1ebaac45 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 5 May 2025 16:04:57 +0200 Subject: [PATCH 13/51] refactor: Remove unnecessary `AsAny` trait --- .../hir-def/src/macro_expansion_tests/mod.rs | 4 --- .../crates/hir-expand/src/proc_macro.rs | 16 +++------ .../crates/load-cargo/src/lib.rs | 4 --- .../crates/test-fixture/src/lib.rs | 36 ------------------- 4 files changed, 4 insertions(+), 56 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs index fcb4684c9300..dc4334ee0816 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs @@ -380,8 +380,4 @@ fn expand( panic!("got invalid macro input: {:?}", parse.errors()); } } - - fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool { - other.as_any().type_id() == std::any::TypeId::of::() - } } diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/proc_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/proc_macro.rs index 8a1a33d7e3b4..1cd975b980df 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/proc_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/proc_macro.rs @@ -19,18 +19,8 @@ pub enum ProcMacroKind { Attr, } -pub trait AsAny: Any { - fn as_any(&self) -> &dyn Any; -} - -impl AsAny for T { - fn as_any(&self) -> &dyn Any { - self - } -} - /// A proc-macro expander implementation. -pub trait ProcMacroExpander: fmt::Debug + Send + Sync + RefUnwindSafe + AsAny { +pub trait ProcMacroExpander: fmt::Debug + Send + Sync + RefUnwindSafe + Any { /// Run the expander with the given input subtree, optional attribute input subtree (for /// [`ProcMacroKind::Attr`]), environment variables, and span information. fn expand( @@ -44,7 +34,9 @@ fn expand( current_dir: String, ) -> Result; - fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool; + fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool { + other.type_id() == self.type_id() + } } impl PartialEq for dyn ProcMacroExpander { diff --git a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs index 2686a75c7c86..30e2d5416cf6 100644 --- a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs +++ b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs @@ -512,10 +512,6 @@ fn expand( Err(err) => Err(ProcMacroExpansionError::System(err.to_string())), } } - - fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool { - other.as_any().downcast_ref::().is_some_and(|other| self == other) - } } #[cfg(test)] diff --git a/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs b/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs index f6ca5ab6c8c5..96e1301f227e 100644 --- a/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs +++ b/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs @@ -662,10 +662,6 @@ fn expand( ) -> Result { Ok(subtree.clone()) } - - fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool { - other.as_any().type_id() == std::any::TypeId::of::() - } } // Expands to a macro_rules! macro, for issue #18089. @@ -697,10 +693,6 @@ macro_rules! my_macro___ { #subtree }) } - - fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool { - other.as_any().type_id() == std::any::TypeId::of::() - } } // Pastes the attribute input as its output @@ -721,10 +713,6 @@ fn expand( .cloned() .ok_or_else(|| ProcMacroExpansionError::Panic("Expected attribute input".into())) } - - fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool { - other.as_any().type_id() == std::any::TypeId::of::() - } } #[derive(Debug)] @@ -756,10 +744,6 @@ fn expand( top_subtree_delimiter_mut.close = def_site; Ok(result) } - - fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool { - other.as_any().type_id() == std::any::TypeId::of::() - } } #[derive(Debug)] @@ -791,10 +775,6 @@ fn traverse(builder: &mut TopSubtreeBuilder, iter: TtIter<'_>) { traverse(&mut builder, input.iter()); Ok(builder.build()) } - - fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool { - other.as_any().type_id() == std::any::TypeId::of::() - } } // Replaces every literal with an empty string literal and every identifier with its first letter, @@ -835,10 +815,6 @@ fn modify_leaf(leaf: &mut Leaf) { } } } - - fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool { - other.as_any().type_id() == std::any::TypeId::of::() - } } // Reads ident type within string quotes, for issue #17479. @@ -864,10 +840,6 @@ fn expand( #symbol() }) } - - fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool { - other.as_any().type_id() == std::any::TypeId::of::() - } } // Reads ident type within string quotes, for issue #17479. @@ -919,10 +891,6 @@ fn foo() { } }) } - - fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool { - other.as_any().type_id() == std::any::TypeId::of::() - } } // Reads ident type within string quotes, for issue #17479. @@ -950,8 +918,4 @@ fn expand( } Ok(subtree.clone()) } - - fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool { - other.as_any().type_id() == std::any::TypeId::of::() - } } From c3bdb24764ce91e04b0321382cd0af5ed2a387b5 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 5 May 2025 16:53:17 +0200 Subject: [PATCH 14/51] fix: Remove unnecessary token length check for macros in renaming --- src/tools/rust-analyzer/crates/ide-db/src/rename.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-db/src/rename.rs b/src/tools/rust-analyzer/crates/ide-db/src/rename.rs index b8119e1aab36..fa2a46a0f7c2 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/rename.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/rename.rs @@ -390,11 +390,6 @@ pub fn source_edit_from_references( let mut edited_ranges = Vec::new(); for &FileReference { range, ref name, .. } in references { let name_range = name.text_range(); - if name_range.len() != range.len() { - // This usage comes from a different token kind that was downmapped to a NameLike in a macro - // Renaming this will most likely break things syntax-wise - continue; - } let has_emitted_edit = match name { // if the ranges differ then the node is inside a macro call, we can't really attempt // to make special rewrites like shorthand syntax and such, so just rename the node in From d246efe9194cdd56a2c1cc40cd489ee6376ee393 Mon Sep 17 00:00:00 2001 From: Victor Date: Mon, 5 May 2025 22:02:27 +0200 Subject: [PATCH 15/51] Support environment variable CARGO_MANIFEST_PATH. --- .../crates/ide-completion/src/completions/env_vars.rs | 1 + src/tools/rust-analyzer/crates/project-model/src/env.rs | 1 + .../test_data/output/cargo_hello_world_project_model.txt | 5 +++++ ...go_hello_world_project_model_with_selective_overrides.txt | 5 +++++ ...rgo_hello_world_project_model_with_wildcard_overrides.txt | 5 +++++ 5 files changed, 17 insertions(+) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/env_vars.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/env_vars.rs index cd18b3dcfdc2..92cbf411c1e3 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/env_vars.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/env_vars.rs @@ -13,6 +13,7 @@ const CARGO_DEFINED_VARS: &[(&str, &str)] = &[ ("CARGO", "Path to the cargo binary performing the build"), ("CARGO_MANIFEST_DIR", "The directory containing the manifest of your package"), + ("CARGO_MANIFEST_PATH", "The path to the manifest of your package"), ("CARGO_PKG_VERSION", "The full version of your package"), ("CARGO_PKG_VERSION_MAJOR", "The major version of your package"), ("CARGO_PKG_VERSION_MINOR", "The minor version of your package"), diff --git a/src/tools/rust-analyzer/crates/project-model/src/env.rs b/src/tools/rust-analyzer/crates/project-model/src/env.rs index e7293b0b2ef6..450def5461da 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/env.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/env.rs @@ -18,6 +18,7 @@ pub(crate) fn inject_cargo_package_env(env: &mut Env, package: &PackageData) { let manifest_dir = package.manifest.parent(); env.set("CARGO_MANIFEST_DIR", manifest_dir.as_str()); + env.set("CARGO_MANIFEST_PATH", package.manifest.as_str()); env.set("CARGO_PKG_VERSION", package.version.to_string()); env.set("CARGO_PKG_VERSION_MAJOR", package.version.major.to_string()); diff --git a/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model.txt b/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model.txt index 4ef9d8161197..3722e2c72168 100644 --- a/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model.txt +++ b/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model.txt @@ -52,6 +52,7 @@ "CARGO": "$CARGO$", "CARGO_CRATE_NAME": "hello_world", "CARGO_MANIFEST_DIR": "$ROOT$hello-world", + "CARGO_MANIFEST_PATH": "$ROOT$hello-world/Cargo.toml", "CARGO_PKG_AUTHORS": "", "CARGO_PKG_DESCRIPTION": "", "CARGO_PKG_HOMEPAGE": "", @@ -136,6 +137,7 @@ "CARGO": "$CARGO$", "CARGO_CRATE_NAME": "hello_world", "CARGO_MANIFEST_DIR": "$ROOT$hello-world", + "CARGO_MANIFEST_PATH": "$ROOT$hello-world/Cargo.toml", "CARGO_PKG_AUTHORS": "", "CARGO_PKG_DESCRIPTION": "", "CARGO_PKG_HOMEPAGE": "", @@ -220,6 +222,7 @@ "CARGO": "$CARGO$", "CARGO_CRATE_NAME": "an_example", "CARGO_MANIFEST_DIR": "$ROOT$hello-world", + "CARGO_MANIFEST_PATH": "$ROOT$hello-world/Cargo.toml", "CARGO_PKG_AUTHORS": "", "CARGO_PKG_DESCRIPTION": "", "CARGO_PKG_HOMEPAGE": "", @@ -304,6 +307,7 @@ "CARGO": "$CARGO$", "CARGO_CRATE_NAME": "it", "CARGO_MANIFEST_DIR": "$ROOT$hello-world", + "CARGO_MANIFEST_PATH": "$ROOT$hello-world/Cargo.toml", "CARGO_PKG_AUTHORS": "", "CARGO_PKG_DESCRIPTION": "", "CARGO_PKG_HOMEPAGE": "", @@ -384,6 +388,7 @@ "CARGO": "$CARGO$", "CARGO_CRATE_NAME": "libc", "CARGO_MANIFEST_DIR": "$ROOT$.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.98", + "CARGO_MANIFEST_PATH": "$ROOT$.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.98/Cargo.toml", "CARGO_PKG_AUTHORS": "The Rust Project Developers", "CARGO_PKG_DESCRIPTION": "Raw FFI bindings to platform libraries like libc.\n", "CARGO_PKG_HOMEPAGE": "https://github.com/rust-lang/libc", diff --git a/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt b/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt index 4ef9d8161197..3722e2c72168 100644 --- a/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt +++ b/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt @@ -52,6 +52,7 @@ "CARGO": "$CARGO$", "CARGO_CRATE_NAME": "hello_world", "CARGO_MANIFEST_DIR": "$ROOT$hello-world", + "CARGO_MANIFEST_PATH": "$ROOT$hello-world/Cargo.toml", "CARGO_PKG_AUTHORS": "", "CARGO_PKG_DESCRIPTION": "", "CARGO_PKG_HOMEPAGE": "", @@ -136,6 +137,7 @@ "CARGO": "$CARGO$", "CARGO_CRATE_NAME": "hello_world", "CARGO_MANIFEST_DIR": "$ROOT$hello-world", + "CARGO_MANIFEST_PATH": "$ROOT$hello-world/Cargo.toml", "CARGO_PKG_AUTHORS": "", "CARGO_PKG_DESCRIPTION": "", "CARGO_PKG_HOMEPAGE": "", @@ -220,6 +222,7 @@ "CARGO": "$CARGO$", "CARGO_CRATE_NAME": "an_example", "CARGO_MANIFEST_DIR": "$ROOT$hello-world", + "CARGO_MANIFEST_PATH": "$ROOT$hello-world/Cargo.toml", "CARGO_PKG_AUTHORS": "", "CARGO_PKG_DESCRIPTION": "", "CARGO_PKG_HOMEPAGE": "", @@ -304,6 +307,7 @@ "CARGO": "$CARGO$", "CARGO_CRATE_NAME": "it", "CARGO_MANIFEST_DIR": "$ROOT$hello-world", + "CARGO_MANIFEST_PATH": "$ROOT$hello-world/Cargo.toml", "CARGO_PKG_AUTHORS": "", "CARGO_PKG_DESCRIPTION": "", "CARGO_PKG_HOMEPAGE": "", @@ -384,6 +388,7 @@ "CARGO": "$CARGO$", "CARGO_CRATE_NAME": "libc", "CARGO_MANIFEST_DIR": "$ROOT$.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.98", + "CARGO_MANIFEST_PATH": "$ROOT$.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.98/Cargo.toml", "CARGO_PKG_AUTHORS": "The Rust Project Developers", "CARGO_PKG_DESCRIPTION": "Raw FFI bindings to platform libraries like libc.\n", "CARGO_PKG_HOMEPAGE": "https://github.com/rust-lang/libc", diff --git a/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt b/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt index 52089d1dbc2c..7b156ea63a58 100644 --- a/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt +++ b/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt @@ -51,6 +51,7 @@ "CARGO": "$CARGO$", "CARGO_CRATE_NAME": "hello_world", "CARGO_MANIFEST_DIR": "$ROOT$hello-world", + "CARGO_MANIFEST_PATH": "$ROOT$hello-world/Cargo.toml", "CARGO_PKG_AUTHORS": "", "CARGO_PKG_DESCRIPTION": "", "CARGO_PKG_HOMEPAGE": "", @@ -134,6 +135,7 @@ "CARGO": "$CARGO$", "CARGO_CRATE_NAME": "hello_world", "CARGO_MANIFEST_DIR": "$ROOT$hello-world", + "CARGO_MANIFEST_PATH": "$ROOT$hello-world/Cargo.toml", "CARGO_PKG_AUTHORS": "", "CARGO_PKG_DESCRIPTION": "", "CARGO_PKG_HOMEPAGE": "", @@ -217,6 +219,7 @@ "CARGO": "$CARGO$", "CARGO_CRATE_NAME": "an_example", "CARGO_MANIFEST_DIR": "$ROOT$hello-world", + "CARGO_MANIFEST_PATH": "$ROOT$hello-world/Cargo.toml", "CARGO_PKG_AUTHORS": "", "CARGO_PKG_DESCRIPTION": "", "CARGO_PKG_HOMEPAGE": "", @@ -300,6 +303,7 @@ "CARGO": "$CARGO$", "CARGO_CRATE_NAME": "it", "CARGO_MANIFEST_DIR": "$ROOT$hello-world", + "CARGO_MANIFEST_PATH": "$ROOT$hello-world/Cargo.toml", "CARGO_PKG_AUTHORS": "", "CARGO_PKG_DESCRIPTION": "", "CARGO_PKG_HOMEPAGE": "", @@ -380,6 +384,7 @@ "CARGO": "$CARGO$", "CARGO_CRATE_NAME": "libc", "CARGO_MANIFEST_DIR": "$ROOT$.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.98", + "CARGO_MANIFEST_PATH": "$ROOT$.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.98/Cargo.toml", "CARGO_PKG_AUTHORS": "The Rust Project Developers", "CARGO_PKG_DESCRIPTION": "Raw FFI bindings to platform libraries like libc.\n", "CARGO_PKG_HOMEPAGE": "https://github.com/rust-lang/libc", From 26366762a213cac5842f6448c878ead6fbf30cb8 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Wed, 30 Apr 2025 12:50:30 +0300 Subject: [PATCH 16/51] Better manage parallel prime caches To make best use of available cores, and don't waste time waiting for other tasks. See the comments in the code for explanation. --- .../crates/ide-db/src/prime_caches.rs | 320 ++++++++++-------- .../ide-db/src/prime_caches/topologic_sort.rs | 104 ------ 2 files changed, 173 insertions(+), 251 deletions(-) delete mode 100644 src/tools/rust-analyzer/crates/ide-db/src/prime_caches/topologic_sort.rs diff --git a/src/tools/rust-analyzer/crates/ide-db/src/prime_caches.rs b/src/tools/rust-analyzer/crates/ide-db/src/prime_caches.rs index 764b583fb84d..8334198db38c 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/prime_caches.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/prime_caches.rs @@ -2,12 +2,10 @@ //! sometimes is counter productive when, for example, the first goto definition //! request takes longer to compute. This module implements prepopulation of //! various caches, it's not really advanced at the moment. -mod topologic_sort; - -use std::time::Duration; +use std::panic::AssertUnwindSafe; use hir::{Symbol, db::DefDatabase}; -use itertools::Itertools; +use rustc_hash::FxHashMap; use salsa::{Cancelled, Database}; use crate::{ @@ -35,58 +33,112 @@ pub fn parallel_prime_caches( ) { let _p = tracing::info_span!("parallel_prime_caches").entered(); - let mut crates_to_prime = { - // FIXME: We already have the crate list topologically sorted (but without the things - // `TopologicalSortIter` gives us). Maybe there is a way to avoid using it and rip it out - // of the codebase? - let mut builder = topologic_sort::TopologicalSortIter::builder(); - - for &crate_id in db.all_crates().iter() { - builder.add(crate_id, crate_id.data(db).dependencies.iter().map(|d| d.crate_id)); - } - - builder.build() - }; - enum ParallelPrimeCacheWorkerProgress { - BeginCrate { crate_id: Crate, crate_name: Symbol }, - EndCrate { crate_id: Crate }, + BeginCrateDefMap { crate_id: Crate, crate_name: Symbol }, + EndCrateDefMap { crate_id: Crate }, + EndCrateImportMap, + EndModuleSymbols, Cancelled(Cancelled), } - // We split off def map computation from other work, - // as the def map is the relevant one. Once the defmaps are computed - // the project is ready to go, the other indices are just nice to have for some IDE features. - #[derive(PartialOrd, Ord, PartialEq, Eq, Copy, Clone)] - enum PrimingPhase { - DefMap, - ImportMap, - CrateSymbols, - } + // The setup here is a bit complicated. We try to make best use of compute resources. + // The idea is that if we have a def map available to compute, we should do that first. + // This is because def map is a dependency of both import map and symbols. So if we have + // e.g. a def map and a symbols, if we compute the def map we can, after it completes, + // compute the def maps of dependencies, the existing symbols and the symbols of the + // new crate, all in parallel. But if we compute the symbols, after that we will only + // have the def map to compute, and the rest of the CPU cores will rest, which is not + // good. + // However, it's better to compute symbols/import map than to compute a def map that + // isn't ready yet, because one of its dependencies hasn't yet completed its def map. + // Such def map will just block on the dependency, which is just wasted time. So better + // to compute the symbols/import map of an already computed def map in that time. - let (work_sender, progress_receiver) = { + let (reverse_deps, mut to_be_done_deps) = { + let all_crates = db.all_crates(); + let mut reverse_deps = + all_crates.iter().map(|&krate| (krate, Vec::new())).collect::>(); + let mut to_be_done_deps = + all_crates.iter().map(|&krate| (krate, 0u32)).collect::>(); + for &krate in &*all_crates { + for dep in &krate.data(db).dependencies { + reverse_deps.get_mut(&dep.crate_id).unwrap().push(krate); + *to_be_done_deps.get_mut(&krate).unwrap() += 1; + } + } + (reverse_deps, to_be_done_deps) + }; + + let (def_map_work_sender, import_map_work_sender, symbols_work_sender, progress_receiver) = { let (progress_sender, progress_receiver) = crossbeam_channel::unbounded(); - let (work_sender, work_receiver) = crossbeam_channel::unbounded(); + let (def_map_work_sender, def_map_work_receiver) = crossbeam_channel::unbounded(); + let (import_map_work_sender, import_map_work_receiver) = crossbeam_channel::unbounded(); + let (symbols_work_sender, symbols_work_receiver) = crossbeam_channel::unbounded(); let prime_caches_worker = move |db: RootDatabase| { - while let Ok((crate_id, crate_name, kind)) = work_receiver.recv() { - progress_sender - .send(ParallelPrimeCacheWorkerProgress::BeginCrate { crate_id, crate_name })?; + let handle_def_map = |crate_id, crate_name| { + progress_sender.send(ParallelPrimeCacheWorkerProgress::BeginCrateDefMap { + crate_id, + crate_name, + })?; - let cancelled = Cancelled::catch(|| match kind { - PrimingPhase::DefMap => _ = hir::crate_def_map(&db, crate_id), - PrimingPhase::ImportMap => _ = db.import_map(crate_id), - PrimingPhase::CrateSymbols => _ = db.crate_symbols(crate_id.into()), - }); + let cancelled = Cancelled::catch(|| _ = hir::crate_def_map(&db, crate_id)); match cancelled { Ok(()) => progress_sender - .send(ParallelPrimeCacheWorkerProgress::EndCrate { crate_id })?, + .send(ParallelPrimeCacheWorkerProgress::EndCrateDefMap { crate_id })?, Err(cancelled) => progress_sender .send(ParallelPrimeCacheWorkerProgress::Cancelled(cancelled))?, } - } - Ok::<_, crossbeam_channel::SendError<_>>(()) + Ok::<_, crossbeam_channel::SendError<_>>(()) + }; + let handle_import_map = |crate_id| { + let cancelled = Cancelled::catch(|| _ = db.import_map(crate_id)); + + match cancelled { + Ok(()) => { + progress_sender.send(ParallelPrimeCacheWorkerProgress::EndCrateImportMap)? + } + Err(cancelled) => progress_sender + .send(ParallelPrimeCacheWorkerProgress::Cancelled(cancelled))?, + } + + Ok::<_, crossbeam_channel::SendError<_>>(()) + }; + let handle_symbols = |module| { + let cancelled = + Cancelled::catch(AssertUnwindSafe(|| _ = db.module_symbols(module))); + + match cancelled { + Ok(()) => { + progress_sender.send(ParallelPrimeCacheWorkerProgress::EndModuleSymbols)? + } + Err(cancelled) => progress_sender + .send(ParallelPrimeCacheWorkerProgress::Cancelled(cancelled))?, + } + + Ok::<_, crossbeam_channel::SendError<_>>(()) + }; + + loop { + db.unwind_if_revision_cancelled(); + + // Biased because we want to prefer def maps. + crossbeam_channel::select_biased! { + recv(def_map_work_receiver) -> work => { + let Ok((crate_id, crate_name)) = work else { return Ok::<_, crossbeam_channel::SendError<_>>(()) }; + handle_def_map(crate_id, crate_name)?; + } + recv(import_map_work_receiver) -> work => { + let Ok(crate_id) = work else { return Ok(()) }; + handle_import_map(crate_id)?; + } + recv(symbols_work_receiver) -> work => { + let Ok(module) = work else { return Ok(()) }; + handle_symbols(module)?; + } + } + } }; for id in 0..num_worker_threads { @@ -103,138 +155,112 @@ enum PrimingPhase { .expect("failed to spawn thread"); } - (work_sender, progress_receiver) + (def_map_work_sender, import_map_work_sender, symbols_work_sender, progress_receiver) }; - let crates_total = crates_to_prime.pending(); - let mut crates_done = 0; + let crate_def_maps_total = db.all_crates().len(); + let mut crate_def_maps_done = 0; + let (mut crate_import_maps_total, mut crate_import_maps_done) = (0usize, 0usize); + let (mut module_symbols_total, mut module_symbols_done) = (0usize, 0usize); // an index map is used to preserve ordering so we can sort the progress report in order of // "longest crate to index" first let mut crates_currently_indexing = FxIndexMap::with_capacity_and_hasher(num_worker_threads, Default::default()); - let mut additional_phases = vec![]; - - while crates_done < crates_total { - db.unwind_if_revision_cancelled(); - - for krate in &mut crates_to_prime { - let name = krate.extra_data(db).display_name.as_deref().cloned().unwrap_or_else(|| { - Symbol::integer(salsa::plumbing::AsId::as_id(&krate).as_u32() as usize) - }); - let origin = &krate.data(db).origin; - if origin.is_lang() { - additional_phases.push((krate, name.clone(), PrimingPhase::ImportMap)); - } else if origin.is_local() { - // Compute the symbol search index. - // This primes the cache for `ide_db::symbol_index::world_symbols()`. - // - // We do this for workspace crates only (members of local_roots), because doing it - // for all dependencies could be *very* unnecessarily slow in a large project. - // - // FIXME: We should do it unconditionally if the configuration is set to default to - // searching dependencies (rust-analyzer.workspace.symbol.search.scope), but we - // would need to pipe that configuration information down here. - additional_phases.push((krate, name.clone(), PrimingPhase::CrateSymbols)); - } - - work_sender.send((krate, name, PrimingPhase::DefMap)).ok(); + for (&krate, &to_be_done_deps) in &to_be_done_deps { + if to_be_done_deps != 0 { + continue; } - // recv_timeout is somewhat a hack, we need a way to from this thread check to see if the current salsa revision - // is cancelled on a regular basis. workers will only exit if they are processing a task that is cancelled, or - // if this thread exits, and closes the work channel. - let worker_progress = match progress_receiver.recv_timeout(Duration::from_millis(10)) { + let name = crate_name(db, krate); + def_map_work_sender.send((krate, name)).ok(); + } + + while crate_def_maps_done < crate_def_maps_total + || crate_import_maps_done < crate_import_maps_total + || module_symbols_done < module_symbols_total + { + db.unwind_if_revision_cancelled(); + + let progress = ParallelPrimeCachesProgress { + crates_currently_indexing: crates_currently_indexing.values().cloned().collect(), + crates_done: crate_def_maps_done, + crates_total: crate_def_maps_total, + work_type: "Indexing", + }; + + cb(progress); + + // Biased to prefer progress updates (and because it's faster). + let progress = match progress_receiver.recv() { Ok(p) => p, - Err(crossbeam_channel::RecvTimeoutError::Timeout) => { - continue; - } - Err(crossbeam_channel::RecvTimeoutError::Disconnected) => { + Err(crossbeam_channel::RecvError) => { // all our workers have exited, mark us as finished and exit cb(ParallelPrimeCachesProgress { crates_currently_indexing: vec![], - crates_done, - crates_total: crates_done, + crates_done: crate_def_maps_done, + crates_total: crate_def_maps_done, work_type: "Indexing", }); return; } }; - match worker_progress { - ParallelPrimeCacheWorkerProgress::BeginCrate { crate_id, crate_name } => { + + match progress { + ParallelPrimeCacheWorkerProgress::BeginCrateDefMap { crate_id, crate_name } => { crates_currently_indexing.insert(crate_id, crate_name); } - ParallelPrimeCacheWorkerProgress::EndCrate { crate_id } => { + ParallelPrimeCacheWorkerProgress::EndCrateDefMap { crate_id } => { crates_currently_indexing.swap_remove(&crate_id); - crates_to_prime.mark_done(crate_id); - crates_done += 1; + crate_def_maps_done += 1; + + // Fire ready dependencies. + for &dep in &reverse_deps[&crate_id] { + let to_be_done = to_be_done_deps.get_mut(&dep).unwrap(); + *to_be_done -= 1; + if *to_be_done == 0 { + let dep_name = crate_name(db, dep); + def_map_work_sender.send((dep, dep_name)).ok(); + } + } + + let origin = &crate_id.data(db).origin; + if origin.is_lang() { + crate_import_maps_total += 1; + import_map_work_sender.send(crate_id).ok(); + } else if origin.is_local() { + // Compute the symbol search index. + // This primes the cache for `ide_db::symbol_index::world_symbols()`. + // + // We do this for workspace crates only (members of local_roots), because doing it + // for all dependencies could be *very* unnecessarily slow in a large project. + // + // FIXME: We should do it unconditionally if the configuration is set to default to + // searching dependencies (rust-analyzer.workspace.symbol.search.scope), but we + // would need to pipe that configuration information down here. + let modules = hir::Crate::from(crate_id).modules(db); + module_symbols_total += modules.len(); + for module in modules { + symbols_work_sender.send(module).ok(); + } + } } + ParallelPrimeCacheWorkerProgress::EndCrateImportMap => crate_import_maps_done += 1, + ParallelPrimeCacheWorkerProgress::EndModuleSymbols => module_symbols_done += 1, ParallelPrimeCacheWorkerProgress::Cancelled(cancelled) => { // Cancelled::throw should probably be public std::panic::resume_unwind(Box::new(cancelled)); } - }; - - let progress = ParallelPrimeCachesProgress { - crates_currently_indexing: crates_currently_indexing.values().cloned().collect(), - crates_done, - crates_total, - work_type: "Indexing", - }; - - cb(progress); - } - - let mut crates_done = 0; - let crates_total = additional_phases.len(); - for w in additional_phases.into_iter().sorted_by_key(|&(_, _, phase)| phase) { - work_sender.send(w).ok(); - } - - while crates_done < crates_total { - db.unwind_if_revision_cancelled(); - - // recv_timeout is somewhat a hack, we need a way to from this thread check to see if the current salsa revision - // is cancelled on a regular basis. workers will only exit if they are processing a task that is cancelled, or - // if this thread exits, and closes the work channel. - let worker_progress = match progress_receiver.recv_timeout(Duration::from_millis(10)) { - Ok(p) => p, - Err(crossbeam_channel::RecvTimeoutError::Timeout) => { - continue; - } - Err(crossbeam_channel::RecvTimeoutError::Disconnected) => { - // 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 { - ParallelPrimeCacheWorkerProgress::BeginCrate { crate_id, crate_name } => { - crates_currently_indexing.insert(crate_id, crate_name); - } - ParallelPrimeCacheWorkerProgress::EndCrate { crate_id } => { - 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 { - crates_currently_indexing: crates_currently_indexing.values().cloned().collect(), - crates_done, - crates_total, - work_type: "Populating symbols", - }; - - cb(progress); + } } } + +fn crate_name(db: &RootDatabase, krate: Crate) -> Symbol { + krate + .extra_data(db) + .display_name + .as_deref() + .cloned() + .unwrap_or_else(|| Symbol::integer(salsa::plumbing::AsId::as_id(&krate).as_u32() as usize)) +} diff --git a/src/tools/rust-analyzer/crates/ide-db/src/prime_caches/topologic_sort.rs b/src/tools/rust-analyzer/crates/ide-db/src/prime_caches/topologic_sort.rs deleted file mode 100644 index c8a038631036..000000000000 --- a/src/tools/rust-analyzer/crates/ide-db/src/prime_caches/topologic_sort.rs +++ /dev/null @@ -1,104 +0,0 @@ -//! helper data structure to schedule work for parallel prime caches. -use std::{collections::VecDeque, hash::Hash}; - -use crate::FxHashMap; - -pub(crate) struct TopologicSortIterBuilder { - nodes: FxHashMap>, -} - -// this implementation has different bounds on T than would be implied by #[derive(Default)] -impl Default for TopologicSortIterBuilder -where - T: Copy + Eq + PartialEq + Hash, -{ - fn default() -> Self { - Self { nodes: Default::default() } - } -} - -impl TopologicSortIterBuilder -where - T: Copy + Eq + PartialEq + Hash, -{ - fn get_or_create_entry(&mut self, item: T) -> &mut Entry { - self.nodes.entry(item).or_default() - } - - pub(crate) fn add(&mut self, item: T, predecessors: impl IntoIterator) { - let mut num_predecessors = 0; - - for predecessor in predecessors.into_iter() { - self.get_or_create_entry(predecessor).successors.push(item); - num_predecessors += 1; - } - - let entry = self.get_or_create_entry(item); - entry.num_predecessors += num_predecessors; - } - - pub(crate) fn build(self) -> TopologicalSortIter { - let ready = self - .nodes - .iter() - .filter_map( - |(item, entry)| if entry.num_predecessors == 0 { Some(*item) } else { None }, - ) - .collect(); - - TopologicalSortIter { nodes: self.nodes, ready } - } -} - -pub(crate) struct TopologicalSortIter { - ready: VecDeque, - nodes: FxHashMap>, -} - -impl TopologicalSortIter -where - T: Copy + Eq + PartialEq + Hash, -{ - pub(crate) fn builder() -> TopologicSortIterBuilder { - TopologicSortIterBuilder::default() - } - - pub(crate) fn pending(&self) -> usize { - self.nodes.len() - } - - pub(crate) fn mark_done(&mut self, item: T) { - let entry = self.nodes.remove(&item).expect("invariant: unknown item marked as done"); - - for successor in entry.successors { - let succ_entry = self - .nodes - .get_mut(&successor) - .expect("invariant: unknown successor referenced by entry"); - - succ_entry.num_predecessors -= 1; - if succ_entry.num_predecessors == 0 { - self.ready.push_back(successor); - } - } - } -} - -impl Iterator for TopologicalSortIter { - type Item = T; - - fn next(&mut self) -> Option { - self.ready.pop_front() - } -} - -struct Entry { - successors: Vec, - num_predecessors: usize, -} - -impl Default for Entry { - fn default() -> Self { - Self { successors: Default::default(), num_predecessors: 0 } - } -} From 6fa168ac25c21d5b4ea9112a1b52aef071c836e7 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Wed, 30 Apr 2025 12:52:13 +0300 Subject: [PATCH 17/51] Add a `--num-threads` to the `prime-caches` CLI command And make it parallel by default (and remove the `--parallel` flag) to mirror the IDE cache priming. --- .../crates/ide-db/src/prime_caches.rs | 129 +++++++++--------- .../crates/rust-analyzer/src/cli/flags.rs | 6 +- .../rust-analyzer/src/cli/prime_caches.rs | 2 +- 3 files changed, 69 insertions(+), 68 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-db/src/prime_caches.rs b/src/tools/rust-analyzer/crates/ide-db/src/prime_caches.rs index 8334198db38c..22ba48a205e9 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/prime_caches.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/prime_caches.rs @@ -56,14 +56,15 @@ enum ParallelPrimeCacheWorkerProgress { let (reverse_deps, mut to_be_done_deps) = { let all_crates = db.all_crates(); + let to_be_done_deps = all_crates + .iter() + .map(|&krate| (krate, krate.data(db).dependencies.len() as u32)) + .collect::>(); let mut reverse_deps = all_crates.iter().map(|&krate| (krate, Vec::new())).collect::>(); - let mut to_be_done_deps = - all_crates.iter().map(|&krate| (krate, 0u32)).collect::>(); for &krate in &*all_crates { for dep in &krate.data(db).dependencies { reverse_deps.get_mut(&dep.crate_id).unwrap().push(krate); - *to_be_done_deps.get_mut(&krate).unwrap() += 1; } } (reverse_deps, to_be_done_deps) @@ -74,72 +75,72 @@ enum ParallelPrimeCacheWorkerProgress { let (def_map_work_sender, def_map_work_receiver) = crossbeam_channel::unbounded(); let (import_map_work_sender, import_map_work_receiver) = crossbeam_channel::unbounded(); let (symbols_work_sender, symbols_work_receiver) = crossbeam_channel::unbounded(); - let prime_caches_worker = move |db: RootDatabase| { - let handle_def_map = |crate_id, crate_name| { - progress_sender.send(ParallelPrimeCacheWorkerProgress::BeginCrateDefMap { - crate_id, - crate_name, - })?; + let prime_caches_worker = + move |db: RootDatabase| { + let handle_def_map = |crate_id, crate_name| { + progress_sender.send(ParallelPrimeCacheWorkerProgress::BeginCrateDefMap { + crate_id, + crate_name, + })?; - let cancelled = Cancelled::catch(|| _ = hir::crate_def_map(&db, crate_id)); + let cancelled = Cancelled::catch(|| _ = hir::crate_def_map(&db, crate_id)); - match cancelled { - Ok(()) => progress_sender - .send(ParallelPrimeCacheWorkerProgress::EndCrateDefMap { crate_id })?, - Err(cancelled) => progress_sender - .send(ParallelPrimeCacheWorkerProgress::Cancelled(cancelled))?, + match cancelled { + Ok(()) => progress_sender + .send(ParallelPrimeCacheWorkerProgress::EndCrateDefMap { crate_id })?, + Err(cancelled) => progress_sender + .send(ParallelPrimeCacheWorkerProgress::Cancelled(cancelled))?, + } + + Ok::<_, crossbeam_channel::SendError<_>>(()) + }; + let handle_import_map = |crate_id| { + let cancelled = Cancelled::catch(|| _ = db.import_map(crate_id)); + + match cancelled { + Ok(()) => progress_sender + .send(ParallelPrimeCacheWorkerProgress::EndCrateImportMap)?, + Err(cancelled) => progress_sender + .send(ParallelPrimeCacheWorkerProgress::Cancelled(cancelled))?, + } + + Ok::<_, crossbeam_channel::SendError<_>>(()) + }; + let handle_symbols = |module| { + let cancelled = + Cancelled::catch(AssertUnwindSafe(|| _ = db.module_symbols(module))); + + match cancelled { + Ok(()) => progress_sender + .send(ParallelPrimeCacheWorkerProgress::EndModuleSymbols)?, + Err(cancelled) => progress_sender + .send(ParallelPrimeCacheWorkerProgress::Cancelled(cancelled))?, + } + + Ok::<_, crossbeam_channel::SendError<_>>(()) + }; + + loop { + db.unwind_if_revision_cancelled(); + + // Biased because we want to prefer def maps. + crossbeam_channel::select_biased! { + recv(def_map_work_receiver) -> work => { + let Ok((crate_id, crate_name)) = work else { break }; + handle_def_map(crate_id, crate_name)?; + } + recv(import_map_work_receiver) -> work => { + let Ok(crate_id) = work else { break }; + handle_import_map(crate_id)?; + } + recv(symbols_work_receiver) -> work => { + let Ok(module) = work else { break }; + handle_symbols(module)?; + } + } } - Ok::<_, crossbeam_channel::SendError<_>>(()) }; - let handle_import_map = |crate_id| { - let cancelled = Cancelled::catch(|| _ = db.import_map(crate_id)); - - match cancelled { - Ok(()) => { - progress_sender.send(ParallelPrimeCacheWorkerProgress::EndCrateImportMap)? - } - Err(cancelled) => progress_sender - .send(ParallelPrimeCacheWorkerProgress::Cancelled(cancelled))?, - } - - Ok::<_, crossbeam_channel::SendError<_>>(()) - }; - let handle_symbols = |module| { - let cancelled = - Cancelled::catch(AssertUnwindSafe(|| _ = db.module_symbols(module))); - - match cancelled { - Ok(()) => { - progress_sender.send(ParallelPrimeCacheWorkerProgress::EndModuleSymbols)? - } - Err(cancelled) => progress_sender - .send(ParallelPrimeCacheWorkerProgress::Cancelled(cancelled))?, - } - - Ok::<_, crossbeam_channel::SendError<_>>(()) - }; - - loop { - db.unwind_if_revision_cancelled(); - - // Biased because we want to prefer def maps. - crossbeam_channel::select_biased! { - recv(def_map_work_receiver) -> work => { - let Ok((crate_id, crate_name)) = work else { return Ok::<_, crossbeam_channel::SendError<_>>(()) }; - handle_def_map(crate_id, crate_name)?; - } - recv(import_map_work_receiver) -> work => { - let Ok(crate_id) = work else { return Ok(()) }; - handle_import_map(crate_id)?; - } - recv(symbols_work_receiver) -> work => { - let Ok(module) = work else { return Ok(()) }; - handle_symbols(module)?; - } - } - } - }; for id in 0..num_worker_threads { stdx::thread::Builder::new( diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs index 57f95d114d9d..16f351272b69 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs @@ -150,8 +150,8 @@ optional --disable-proc-macros /// Run the proc-macro-srv binary at the specified path. optional --proc-macro-srv path: PathBuf - /// Run cache priming in parallel. - optional --parallel + /// The number of threads to use. Defaults to the number of physical cores. + optional --num-threads num_threads: usize } cmd ssr { @@ -299,7 +299,7 @@ pub struct PrimeCaches { pub disable_build_scripts: bool, pub disable_proc_macros: bool, pub proc_macro_srv: Option, - pub parallel: bool, + pub num_threads: Option, } #[derive(Debug)] diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/prime_caches.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/prime_caches.rs index 46fb701ab426..467d8a53884a 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/prime_caches.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/prime_caches.rs @@ -52,7 +52,7 @@ pub fn run(self) -> anyhow::Result<()> { elapsed.memory.allocated.megabytes() as u64 ); - let threads = if self.parallel { num_cpus::get() } else { 1 }; + let threads = self.num_threads.unwrap_or_else(num_cpus::get_physical); ide_db::prime_caches::parallel_prime_caches(&db, threads, &|_| ()); let elapsed = stop_watch.elapsed(); From bce6763cca5caba6baff36eef28a7241b39e245e Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Tue, 6 May 2025 10:29:53 +0300 Subject: [PATCH 18/51] Notify the user that we're collecting symbols It could be confusing if they see "Indexing n/n" but cache priming does not finish. --- .../rust-analyzer/crates/ide-db/src/prime_caches.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide-db/src/prime_caches.rs b/src/tools/rust-analyzer/crates/ide-db/src/prime_caches.rs index 22ba48a205e9..5356614dce52 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/prime_caches.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/prime_caches.rs @@ -202,7 +202,7 @@ enum ParallelPrimeCacheWorkerProgress { crates_currently_indexing: vec![], crates_done: crate_def_maps_done, crates_total: crate_def_maps_done, - work_type: "Indexing", + work_type: "Done", }); return; } @@ -226,6 +226,15 @@ enum ParallelPrimeCacheWorkerProgress { } } + if crate_def_maps_done == crate_def_maps_total { + cb(ParallelPrimeCachesProgress { + crates_currently_indexing: vec![], + crates_done: crate_def_maps_done, + crates_total: crate_def_maps_done, + work_type: "Collecting Symbols", + }); + } + let origin = &crate_id.data(db).origin; if origin.is_lang() { crate_import_maps_total += 1; From 1ed416de0388493ed9d604522399af5f9fd4eab6 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Tue, 6 May 2025 10:54:35 +0300 Subject: [PATCH 19/51] Don't panic on some weird code --- .../hir-def/src/expr_store/lower/path.rs | 8 +++++++ .../crates/hir-ty/src/tests/simple.rs | 24 +++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/path.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/path.rs index 629d1f2ada71..be006c98a582 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/path.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/path.rs @@ -232,6 +232,14 @@ pub(super) fn lower_path( .with_borrow_mut(|map| map.extend(ast_segments.into_iter().zip(ast_segments_offset..))); } + if let Some(last_segment_args @ Some(GenericArgs { has_self_type: true, .. })) = + generic_args.last_mut() + { + // Well-formed code cannot have `` without an associated item after, + // and this causes panics in hir-ty lowering. + *last_segment_args = None; + } + let mod_path = Interned::new(ModPath::from_segments(kind, segments)); if type_anchor.is_none() && generic_args.is_empty() { return Some(Path::BarePath(mod_path)); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs index eeebe38f1826..08127eeb4632 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs @@ -3902,3 +3902,27 @@ fn main() { "#]], ); } + +#[test] +fn regression_19734() { + check_infer( + r#" +trait Foo { + type Gat<'o>; +} + +trait Bar { + fn baz() -> >; +} + +fn foo() { + T::baz(); +} + "#, + expect![[r#" + 110..127 '{ ...z(); }': () + 116..122 'T::baz': fn baz() -> <{unknown} as Foo>::Gat<'?> + 116..124 'T::baz()': Foo::Gat<'?, {unknown}> + "#]], + ); +} From 546065a3150d4ae438e01c751bfcda6875e141d0 Mon Sep 17 00:00:00 2001 From: Hayashi Mikihiro <34ttrweoewiwe28@gmail.com> Date: Fri, 28 Mar 2025 23:50:26 +0900 Subject: [PATCH 20/51] fix: resolve doc path if outer comments exist on module and replace from cfg_attr bit to doc_place bit Signed-off-by: Hayashi Mikihiro <34ttrweoewiwe28@gmail.com> --- .../crates/hir-expand/src/attrs.rs | 47 +++++++-------- .../rust-analyzer/crates/hir/src/attrs.rs | 17 +++++- .../crates/ide/src/doc_links/tests.rs | 57 +++++++++++++++++++ .../highlight_module_docs_inline.html | 4 +- .../ide/src/syntax_highlighting/tests.rs | 8 +-- 5 files changed, 99 insertions(+), 34 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs b/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs index bb17eb062760..b350a6f8ac08 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs @@ -129,9 +129,8 @@ pub fn merge(&self, other: Self) -> Self { .cloned() .chain(b.slice.iter().map(|it| { let mut it = it.clone(); - it.id.id = (it.id.ast_index() as u32 + last_ast_index) - | ((it.id.cfg_attr_index().unwrap_or(0) as u32) - << AttrId::AST_INDEX_BITS); + let id = it.id.ast_index() as u32 + last_ast_index; + it.id = AttrId::new(id as usize, it.id.is_inner_attr()); it })) .collect::>(); @@ -175,25 +174,21 @@ pub struct AttrId { // FIXME: This only handles a single level of cfg_attr nesting // that is `#[cfg_attr(all(), cfg_attr(all(), cfg(any())))]` breaks again impl AttrId { - const CFG_ATTR_BITS: usize = 7; const AST_INDEX_MASK: usize = 0x00FF_FFFF; - const AST_INDEX_BITS: usize = Self::AST_INDEX_MASK.count_ones() as usize; - const CFG_ATTR_SET_BITS: u32 = 1 << 31; + const INNER_ATTR_BIT: usize = 1 << 31; + + pub fn new(id: usize, is_inner: bool) -> Self { + let id = id & Self::AST_INDEX_MASK; + let id = if is_inner { id | Self::INNER_ATTR_BIT } else { id }; + Self { id: id as u32 } + } pub fn ast_index(&self) -> usize { self.id as usize & Self::AST_INDEX_MASK } - pub fn cfg_attr_index(&self) -> Option { - if self.id & Self::CFG_ATTR_SET_BITS == 0 { - None - } else { - Some(self.id as usize >> Self::AST_INDEX_BITS) - } - } - - pub fn with_cfg_attr(self, idx: usize) -> AttrId { - AttrId { id: self.id | ((idx as u32) << Self::AST_INDEX_BITS) | Self::CFG_ATTR_SET_BITS } + pub fn is_inner_attr(&self) -> bool { + (self.id as usize) & Self::INNER_ATTR_BIT != 0 } } @@ -333,10 +328,7 @@ pub fn expand_cfg_attr( 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 attrs = parts.filter_map(|attr| Attr::from_tt(db, attr, index)); let cfg = TopSubtree::from_token_trees(subtree.top_subtree().delimiter, cfg); let cfg = CfgExpr::parse(&cfg); @@ -467,13 +459,18 @@ fn unescape(s: &str) -> Option> { pub fn collect_attrs( owner: &dyn ast::HasAttrs, ) -> impl Iterator)> { - let inner_attrs = inner_attributes(owner.syntax()).into_iter().flatten(); - let outer_attrs = - ast::AttrDocCommentIter::from_syntax_node(owner.syntax()).filter(|el| match el { + let inner_attrs = + inner_attributes(owner.syntax()).into_iter().flatten().map(|attr| (attr, true)); + let outer_attrs = ast::AttrDocCommentIter::from_syntax_node(owner.syntax()) + .filter(|el| match el { Either::Left(attr) => attr.kind().is_outer(), Either::Right(comment) => comment.is_outer(), - }); - outer_attrs.chain(inner_attrs).enumerate().map(|(id, attr)| (AttrId { id: id as u32 }, attr)) + }) + .map(|attr| (attr, false)); + outer_attrs + .chain(inner_attrs) + .enumerate() + .map(|(id, (attr, is_inner))| (AttrId::new(id, is_inner), attr)) } fn inner_attributes( diff --git a/src/tools/rust-analyzer/crates/hir/src/attrs.rs b/src/tools/rust-analyzer/crates/hir/src/attrs.rs index 2d1727a6e904..38ba8d6a3199 100644 --- a/src/tools/rust-analyzer/crates/hir/src/attrs.rs +++ b/src/tools/rust-analyzer/crates/hir/src/attrs.rs @@ -105,11 +105,13 @@ fn attr_id(self) -> AttrDefId { /// Resolves the item `link` points to in the scope of `def`. pub fn resolve_doc_path_on( db: &dyn HirDatabase, - def: impl HasAttrs, + def: impl HasAttrs + Copy, link: &str, ns: Option, ) -> Option { - resolve_doc_path_on_(db, link, def.attr_id(), ns) + let is_inner = + def.attrs(db).by_key(&intern::sym::doc).attrs().all(|attr| attr.id.is_inner_attr()); + resolve_doc_path_on_(db, link, def.attr_id(), ns, is_inner) } fn resolve_doc_path_on_( @@ -117,9 +119,18 @@ fn resolve_doc_path_on_( link: &str, attr_id: AttrDefId, ns: Option, + is_inner: bool, ) -> Option { let resolver = match attr_id { - AttrDefId::ModuleId(it) => it.resolver(db), + AttrDefId::ModuleId(it) => { + if is_inner { + it.resolver(db) + } else if let Some(parent) = Module::from(it).parent(db) { + parent.id.resolver(db) + } else { + it.resolver(db) + } + } AttrDefId::FieldId(it) => it.parent.resolver(db), AttrDefId::AdtId(it) => it.resolver(db), AttrDefId::FunctionId(it) => it.resolver(db), diff --git a/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs b/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs index 91785be8d8ba..6d56b98e57c8 100644 --- a/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs @@ -575,6 +575,40 @@ fn doc_links_tuple_field_via_self() { ); } +#[test] +fn doc_links_module() { + check_doc_links( + r#" +/// [`M`] +/// [`M::f`] +mod M$0 { + //^ M + #![doc = "inner_item[`M::S`]"] + + pub fn f() {} + //^ M::f + pub struct S; + //^ M::S +} +"#, + ); + + check_doc_links( + r#" +mod M$0 { + //^ super::M + //! [`super::M`] + //! [`super::M::f`] + //! [`super::M::S`] + pub fn f() {} + //^ super::M::f + pub struct S; + //^ super::M::S +} +"#, + ); +} + #[test] fn rewrite_html_root_url() { check_rewrite( @@ -690,6 +724,29 @@ fn main() {} ); } +#[test] +fn rewrite_module() { + check_rewrite( + r#" +//- /main.rs crate:foo +/// [Foo] +pub mod $0Foo{ +}; +"#, + expect![[r#"[Foo](https://docs.rs/foo/*/foo/Foo/index.html)"#]], + ); + + check_rewrite( + r#" +//- /main.rs crate:foo +pub mod $0Foo{ + //! [super::Foo] +}; +"#, + expect![[r#"[super::Foo](https://docs.rs/foo/*/foo/Foo/index.html)"#]], + ); +} + #[test] fn rewrite_intra_doc_link_to_associated_item() { check_rewrite( diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_inline.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_inline.html index 9996a871580f..75d96b422c10 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_inline.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_inline.html @@ -40,9 +40,9 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } -
//! [Struct]
+
//! [foo::Struct]
 //! This is an intra doc injection test for modules
-//! [Struct]
+//! [foo::Struct]
 //! This is an intra doc injection test for modules
 
 pub struct Struct;
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs
index dd359326c61d..a8d953094667 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs
@@ -1072,9 +1072,9 @@ fn test_mod_hl_injection() {
     check_highlighting(
         r##"
 //- /foo.rs
-//! [Struct]
+//! [foo::Struct]
 //! This is an intra doc injection test for modules
-//! [Struct]
+//! [foo::Struct]
 //! This is an intra doc injection test for modules
 
 pub struct Struct;
@@ -1097,9 +1097,9 @@ fn test_mod_hl_injection() {
 /// This is an intra doc injection test for modules
 mod foo;
 //- /foo.rs
-//! [Struct]
+//! [foo::Struct]
 //! This is an intra doc injection test for modules
-//! [Struct]
+//! [foo::Struct]
 //! This is an intra doc injection test for modules
 
 pub struct Struct;

From b9a3e70ccb6f1da30a594ff30833a4b6e0a81e6a Mon Sep 17 00:00:00 2001
From: Hayashi Mikihiro <34ttrweoewiwe28@gmail.com>
Date: Tue, 6 May 2025 16:27:23 +0900
Subject: [PATCH 21/51] Update crates/hir-expand/src/attrs.rs

Co-authored-by: Lukas Wirth 
---
 src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs b/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs
index b350a6f8ac08..9eac5c2c7217 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs
@@ -460,7 +460,7 @@ pub fn collect_attrs(
     owner: &dyn ast::HasAttrs,
 ) -> impl Iterator)> {
     let inner_attrs =
-        inner_attributes(owner.syntax()).into_iter().flatten().map(|attr| (attr, true));
+        inner_attributes(owner.syntax()).into_iter().flatten().zip(iter::repeat(true));
     let outer_attrs = ast::AttrDocCommentIter::from_syntax_node(owner.syntax())
         .filter(|el| match el {
             Either::Left(attr) => attr.kind().is_outer(),

From 674e10e9828ab0701df24c6d5385b6b95e04896f Mon Sep 17 00:00:00 2001
From: Hayashi Mikihiro <34ttrweoewiwe28@gmail.com>
Date: Tue, 6 May 2025 16:27:36 +0900
Subject: [PATCH 22/51] Update crates/hir-expand/src/attrs.rs

Co-authored-by: Lukas Wirth 
---
 src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs b/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs
index 9eac5c2c7217..5fe95f3043ef 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs
@@ -466,7 +466,7 @@ pub fn collect_attrs(
             Either::Left(attr) => attr.kind().is_outer(),
             Either::Right(comment) => comment.is_outer(),
         })
-        .map(|attr| (attr, false));
+        .zip(iter::repeat(false));
     outer_attrs
         .chain(inner_attrs)
         .enumerate()

From a8b8b2be4781ddab3abd5dff647f34992312c508 Mon Sep 17 00:00:00 2001
From: Hayashi Mikihiro <34ttrweoewiwe28@gmail.com>
Date: Tue, 6 May 2025 17:08:12 +0900
Subject: [PATCH 23/51] import std::iter::repeat

Signed-off-by: Hayashi Mikihiro <34ttrweoewiwe28@gmail.com>
---
 src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs b/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs
index 5fe95f3043ef..8b9e1c42c1ae 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs
@@ -1,4 +1,5 @@
 //! A higher level attributes based on TokenTree, with also some shortcuts.
+use std::iter;
 use std::{borrow::Cow, fmt, ops};
 
 use base_db::Crate;

From 7bf0c0034ea191d9cb6e3bbff2c58aaaa437cbb3 Mon Sep 17 00:00:00 2001
From: Hayashi Mikihiro <34ttrweoewiwe28@gmail.com>
Date: Tue, 6 May 2025 17:09:28 +0900
Subject: [PATCH 24/51] Remove ast index mask

Signed-off-by: Hayashi Mikihiro <34ttrweoewiwe28@gmail.com>
---
 .../crates/hir-expand/src/attrs.rs              | 17 ++++++++++-------
 1 file changed, 10 insertions(+), 7 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs b/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs
index 8b9e1c42c1ae..4e519452aa69 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs
@@ -175,21 +175,24 @@ pub struct AttrId {
 // FIXME: This only handles a single level of cfg_attr nesting
 // that is `#[cfg_attr(all(), cfg_attr(all(), cfg(any())))]` breaks again
 impl AttrId {
-    const AST_INDEX_MASK: usize = 0x00FF_FFFF;
-    const INNER_ATTR_BIT: usize = 1 << 31;
+    const INNER_ATTR_SET_BIT: usize = 1 << 31;
 
     pub fn new(id: usize, is_inner: bool) -> Self {
-        let id = id & Self::AST_INDEX_MASK;
-        let id = if is_inner { id | Self::INNER_ATTR_BIT } else { id };
-        Self { id: id as u32 }
+        Self {
+            id: if is_inner {
+                id | Self::INNER_ATTR_SET_BIT
+            } else {
+                id & !Self::INNER_ATTR_SET_BIT
+            } as u32,
+        }
     }
 
     pub fn ast_index(&self) -> usize {
-        self.id as usize & Self::AST_INDEX_MASK
+        self.id as usize & !Self::INNER_ATTR_SET_BIT
     }
 
     pub fn is_inner_attr(&self) -> bool {
-        (self.id as usize) & Self::INNER_ATTR_BIT != 0
+        (self.id as usize) & Self::INNER_ATTR_SET_BIT != 0
     }
 }
 

From 8935d0bdeae3e5165928ebcb31adf7fd5e2a1355 Mon Sep 17 00:00:00 2001
From: Hayashi Mikihiro <34ttrweoewiwe28@gmail.com>
Date: Wed, 7 May 2025 00:36:17 +0900
Subject: [PATCH 25/51] check module path inner or outer

Signed-off-by: Hayashi Mikihiro <34ttrweoewiwe28@gmail.com>
---
 .../rust-analyzer/crates/hir/src/attrs.rs     |  9 +-
 .../rust-analyzer/crates/ide-db/src/defs.rs   | 63 +++++++-----
 .../crates/ide-db/src/documentation.rs        | 83 +++++++++++++---
 .../rust-analyzer/crates/ide/src/doc_links.rs | 96 ++++++++++++-------
 .../crates/ide/src/doc_links/tests.rs         | 56 +++++------
 .../rust-analyzer/crates/ide/src/hover.rs     |  4 +-
 .../crates/ide/src/hover/render.rs            | 46 +++++----
 .../ide/src/syntax_highlighting/inject.rs     | 17 +++-
 .../highlight_module_docs_inline.html         |  4 +-
 .../ide/src/syntax_highlighting/tests.rs      |  8 +-
 10 files changed, 256 insertions(+), 130 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/hir/src/attrs.rs b/src/tools/rust-analyzer/crates/hir/src/attrs.rs
index 38ba8d6a3199..b1cf30b98f5b 100644
--- a/src/tools/rust-analyzer/crates/hir/src/attrs.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/attrs.rs
@@ -108,10 +108,9 @@ pub fn resolve_doc_path_on(
     def: impl HasAttrs + Copy,
     link: &str,
     ns: Option,
+    is_inner_doc: bool,
 ) -> Option {
-    let is_inner =
-        def.attrs(db).by_key(&intern::sym::doc).attrs().all(|attr| attr.id.is_inner_attr());
-    resolve_doc_path_on_(db, link, def.attr_id(), ns, is_inner)
+    resolve_doc_path_on_(db, link, def.attr_id(), ns, is_inner_doc)
 }
 
 fn resolve_doc_path_on_(
@@ -119,11 +118,11 @@ fn resolve_doc_path_on_(
     link: &str,
     attr_id: AttrDefId,
     ns: Option,
-    is_inner: bool,
+    is_inner_doc: bool,
 ) -> Option {
     let resolver = match attr_id {
         AttrDefId::ModuleId(it) => {
-            if is_inner {
+            if is_inner_doc {
                 it.resolver(db)
             } else if let Some(parent) = Module::from(it).parent(db) {
                 parent.id.resolver(db)
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs
index bf4f541ff54c..d5db1c481b69 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs
@@ -6,7 +6,7 @@
 // FIXME: this badly needs rename/rewrite (matklad, 2020-02-06).
 
 use crate::RootDatabase;
-use crate::documentation::{Documentation, HasDocs};
+use crate::documentation::{DocsRangeMap, Documentation, HasDocs};
 use crate::famous_defs::FamousDefs;
 use arrayvec::ArrayVec;
 use either::Either;
@@ -21,7 +21,7 @@
 use span::Edition;
 use stdx::{format_to, impl_from};
 use syntax::{
-    SyntaxKind, SyntaxNode, SyntaxToken,
+    SyntaxKind, SyntaxNode, SyntaxToken, TextSize,
     ast::{self, AstNode},
     match_ast,
 };
@@ -210,29 +210,40 @@ pub fn docs(
         famous_defs: Option<&FamousDefs<'_, '_>>,
         display_target: DisplayTarget,
     ) -> Option {
+        self.docs_with_rangemap(db, famous_defs, display_target).map(|(docs, _)| docs)
+    }
+
+    pub fn docs_with_rangemap(
+        &self,
+        db: &RootDatabase,
+        famous_defs: Option<&FamousDefs<'_, '_>>,
+        display_target: DisplayTarget,
+    ) -> Option<(Documentation, Option)> {
         let docs = match self {
-            Definition::Macro(it) => it.docs(db),
-            Definition::Field(it) => it.docs(db),
-            Definition::Module(it) => it.docs(db),
-            Definition::Crate(it) => it.docs(db),
-            Definition::Function(it) => it.docs(db),
-            Definition::Adt(it) => it.docs(db),
-            Definition::Variant(it) => it.docs(db),
-            Definition::Const(it) => it.docs(db),
-            Definition::Static(it) => it.docs(db),
-            Definition::Trait(it) => it.docs(db),
-            Definition::TraitAlias(it) => it.docs(db),
+            Definition::Macro(it) => it.docs_with_rangemap(db),
+            Definition::Field(it) => it.docs_with_rangemap(db),
+            Definition::Module(it) => it.docs_with_rangemap(db),
+            Definition::Crate(it) => it.docs_with_rangemap(db),
+            Definition::Function(it) => it.docs_with_rangemap(db),
+            Definition::Adt(it) => it.docs_with_rangemap(db),
+            Definition::Variant(it) => it.docs_with_rangemap(db),
+            Definition::Const(it) => it.docs_with_rangemap(db),
+            Definition::Static(it) => it.docs_with_rangemap(db),
+            Definition::Trait(it) => it.docs_with_rangemap(db),
+            Definition::TraitAlias(it) => it.docs_with_rangemap(db),
             Definition::TypeAlias(it) => {
-                it.docs(db).or_else(|| {
+                it.docs_with_rangemap(db).or_else(|| {
                     // docs are missing, try to fall back to the docs of the aliased item.
                     let adt = it.ty(db).as_adt()?;
-                    let docs = adt.docs(db)?;
-                    let docs = format!(
-                        "*This is the documentation for* `{}`\n\n{}",
-                        adt.display(db, display_target),
-                        docs.as_str()
+                    let (docs, range_map) = adt.docs_with_rangemap(db)?;
+                    let header_docs = format!(
+                        "*This is the documentation for* `{}`\n\n",
+                        adt.display(db, display_target)
                     );
-                    Some(Documentation::new(docs))
+                    let offset = TextSize::new(header_docs.len() as u32);
+                    let range_map = range_map.shift_docstring_line_range(offset);
+                    let docs = header_docs + docs.as_str();
+                    Some((Documentation::new(docs), range_map))
                 })
             }
             Definition::BuiltinType(it) => {
@@ -241,17 +252,17 @@ pub fn docs(
                     let primitive_mod =
                         format!("prim_{}", it.name().display(fd.0.db, display_target.edition));
                     let doc_owner = find_std_module(fd, &primitive_mod, display_target.edition)?;
-                    doc_owner.docs(fd.0.db)
+                    doc_owner.docs_with_rangemap(fd.0.db)
                 })
             }
             Definition::BuiltinLifetime(StaticLifetime) => None,
             Definition::Local(_) => None,
             Definition::SelfType(impl_def) => {
-                impl_def.self_ty(db).as_adt().map(|adt| adt.docs(db))?
+                impl_def.self_ty(db).as_adt().map(|adt| adt.docs_with_rangemap(db))?
             }
             Definition::GenericParam(_) => None,
             Definition::Label(_) => None,
-            Definition::ExternCrateDecl(it) => it.docs(db),
+            Definition::ExternCrateDecl(it) => it.docs_with_rangemap(db),
 
             Definition::BuiltinAttr(it) => {
                 let name = it.name(db);
@@ -276,7 +287,8 @@ pub fn docs(
                         name_value_str
                     );
                 }
-                Some(Documentation::new(docs.replace('*', "\\*")))
+
+                return Some((Documentation::new(docs.replace('*', "\\*")), None));
             }
             Definition::ToolModule(_) => None,
             Definition::DeriveHelper(_) => None,
@@ -291,8 +303,9 @@ pub fn docs(
             let trait_ = assoc.implemented_trait(db)?;
             let name = Some(assoc.name(db)?);
             let item = trait_.items(db).into_iter().find(|it| it.name(db) == name)?;
-            item.docs(db)
+            item.docs_with_rangemap(db)
         })
+        .map(|(docs, range_map)| (docs, Some(range_map)))
     }
 
     pub fn label(&self, db: &RootDatabase, display_target: DisplayTarget) -> String {
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/documentation.rs b/src/tools/rust-analyzer/crates/ide-db/src/documentation.rs
index ef2c83992c04..30c355f8b3f9 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/documentation.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/documentation.rs
@@ -34,11 +34,13 @@ fn from(Documentation(string): Documentation) -> Self {
 
 pub trait HasDocs: HasAttrs {
     fn docs(self, db: &dyn HirDatabase) -> Option;
+    fn docs_with_rangemap(self, db: &dyn HirDatabase) -> Option<(Documentation, DocsRangeMap)>;
     fn resolve_doc_path(
         self,
         db: &dyn HirDatabase,
         link: &str,
         ns: Option,
+        is_inner_doc: bool,
     ) -> Option;
 }
 /// A struct to map text ranges from [`Documentation`] back to TextRanges in the syntax tree.
@@ -53,7 +55,7 @@ pub struct DocsRangeMap {
 
 impl DocsRangeMap {
     /// Maps a [`TextRange`] relative to the documentation string back to its AST range
-    pub fn map(&self, range: TextRange) -> Option> {
+    pub fn map(&self, range: TextRange) -> Option<(InFile, AttrId)> {
         let found = self.mapping.binary_search_by(|(probe, ..)| probe.ordering(range)).ok()?;
         let (line_docs_range, idx, original_line_src_range) = self.mapping[found];
         if !line_docs_range.contains_range(range) {
@@ -71,7 +73,7 @@ pub fn map(&self, range: TextRange) -> Option> {
                     text_range.end() + original_line_src_range.start() + relative_range.start(),
                     string.syntax().text_range().len().min(range.len()),
                 );
-                Some(InFile { file_id, value: range })
+                Some((InFile { file_id, value: range }, idx))
             }
             Either::Right(comment) => {
                 let text_range = comment.syntax().text_range();
@@ -82,10 +84,22 @@ pub fn map(&self, range: TextRange) -> Option> {
                         + relative_range.start(),
                     text_range.len().min(range.len()),
                 );
-                Some(InFile { file_id, value: range })
+                Some((InFile { file_id, value: range }, idx))
             }
         }
     }
+
+    pub fn shift_docstring_line_range(self, offset: TextSize) -> DocsRangeMap {
+        let mapping = self
+            .mapping
+            .into_iter()
+            .map(|(buf_offset, id, base_offset)| {
+                let buf_offset = buf_offset.checked_add(offset).unwrap();
+                (buf_offset, id, base_offset)
+            })
+            .collect_vec();
+        DocsRangeMap { source_map: self.source_map, mapping }
+    }
 }
 
 pub fn docs_with_rangemap(
@@ -161,13 +175,20 @@ impl HasDocs for hir::$def {
             fn docs(self, db: &dyn HirDatabase) -> Option {
                 docs_from_attrs(&self.attrs(db)).map(Documentation)
             }
+            fn docs_with_rangemap(
+                self,
+                db: &dyn HirDatabase,
+            ) -> Option<(Documentation, DocsRangeMap)> {
+                docs_with_rangemap(db, &self.attrs(db))
+            }
             fn resolve_doc_path(
                 self,
                 db: &dyn HirDatabase,
                 link: &str,
-                ns: Option
+                ns: Option,
+                is_inner_doc: bool,
             ) -> Option {
-                resolve_doc_path_on(db, self, link, ns)
+                resolve_doc_path_on(db, self, link, ns, is_inner_doc)
             }
         }
     )*};
@@ -184,13 +205,21 @@ impl HasDocs for hir::$variant {
             fn docs(self, db: &dyn HirDatabase) -> Option {
                 hir::$enum::$variant(self).docs(db)
             }
+
+            fn docs_with_rangemap(
+                self,
+                db: &dyn HirDatabase,
+            ) -> Option<(Documentation, DocsRangeMap)> {
+                hir::$enum::$variant(self).docs_with_rangemap(db)
+            }
             fn resolve_doc_path(
                 self,
                 db: &dyn HirDatabase,
                 link: &str,
-                ns: Option
+                ns: Option,
+                is_inner_doc: bool,
             ) -> Option {
-                hir::$enum::$variant(self).resolve_doc_path(db, link, ns)
+                hir::$enum::$variant(self).resolve_doc_path(db, link, ns, is_inner_doc)
             }
         }
     )*};
@@ -207,16 +236,25 @@ fn docs(self, db: &dyn HirDatabase) -> Option {
         }
     }
 
+    fn docs_with_rangemap(self, db: &dyn HirDatabase) -> Option<(Documentation, DocsRangeMap)> {
+        match self {
+            hir::AssocItem::Function(it) => it.docs_with_rangemap(db),
+            hir::AssocItem::Const(it) => it.docs_with_rangemap(db),
+            hir::AssocItem::TypeAlias(it) => it.docs_with_rangemap(db),
+        }
+    }
+
     fn resolve_doc_path(
         self,
         db: &dyn HirDatabase,
         link: &str,
         ns: Option,
+        is_inner_doc: bool,
     ) -> Option {
         match self {
-            hir::AssocItem::Function(it) => it.resolve_doc_path(db, link, ns),
-            hir::AssocItem::Const(it) => it.resolve_doc_path(db, link, ns),
-            hir::AssocItem::TypeAlias(it) => it.resolve_doc_path(db, link, ns),
+            hir::AssocItem::Function(it) => it.resolve_doc_path(db, link, ns, is_inner_doc),
+            hir::AssocItem::Const(it) => it.resolve_doc_path(db, link, ns, is_inner_doc),
+            hir::AssocItem::TypeAlias(it) => it.resolve_doc_path(db, link, ns, is_inner_doc),
         }
     }
 }
@@ -238,13 +276,36 @@ fn docs(self, db: &dyn HirDatabase) -> Option {
         }
         .map(Documentation::new)
     }
+
+    fn docs_with_rangemap(self, db: &dyn HirDatabase) -> Option<(Documentation, DocsRangeMap)> {
+        let crate_docs = docs_with_rangemap(db, &self.resolved_crate(db)?.root_module().attrs(db));
+        let decl_docs = docs_with_rangemap(db, &self.attrs(db));
+        match (decl_docs, crate_docs) {
+            (None, None) => None,
+            (Some(decl_docs), None) => Some(decl_docs),
+            (None, Some(crate_docs)) => Some(crate_docs),
+            (
+                Some((Documentation(mut decl_docs), mut decl_range_map)),
+                Some((Documentation(crate_docs), crate_range_map)),
+            ) => {
+                decl_docs.push('\n');
+                decl_docs.push('\n');
+                let offset = TextSize::new(decl_docs.len() as u32);
+                decl_docs += &crate_docs;
+                let crate_range_map = crate_range_map.shift_docstring_line_range(offset);
+                decl_range_map.mapping.extend(crate_range_map.mapping);
+                Some((Documentation(decl_docs), decl_range_map))
+            }
+        }
+    }
     fn resolve_doc_path(
         self,
         db: &dyn HirDatabase,
         link: &str,
         ns: Option,
+        is_inner_doc: bool,
     ) -> Option {
-        resolve_doc_path_on(db, self, link, ns)
+        resolve_doc_path_on(db, self, link, ns, is_inner_doc)
     }
 }
 
diff --git a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs
index f0247f32d7ec..290420346060 100644
--- a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs
@@ -5,6 +5,8 @@
 
 mod intra_doc_links;
 
+use std::ops::Range;
+
 use pulldown_cmark::{BrokenLink, CowStr, Event, InlineStr, LinkType, Options, Parser, Tag};
 use pulldown_cmark_to_cmark::{Options as CMarkOptions, cmark_resume_with_options};
 use stdx::format_to;
@@ -15,7 +17,7 @@
     RootDatabase,
     base_db::{CrateOrigin, LangCrateOrigin, ReleaseChannel, RootQueryDb},
     defs::{Definition, NameClass, NameRefClass},
-    documentation::{Documentation, HasDocs, docs_with_rangemap},
+    documentation::{DocsRangeMap, Documentation, HasDocs, docs_with_rangemap},
     helpers::pick_best_token,
 };
 use syntax::{
@@ -46,11 +48,17 @@ pub struct DocumentationLinks {
     Options::ENABLE_FOOTNOTES.union(Options::ENABLE_TABLES).union(Options::ENABLE_TASKLISTS);
 
 /// Rewrite documentation links in markdown to point to an online host (e.g. docs.rs)
-pub(crate) fn rewrite_links(db: &RootDatabase, markdown: &str, definition: Definition) -> String {
+pub(crate) fn rewrite_links(
+    db: &RootDatabase,
+    markdown: &str,
+    definition: Definition,
+    range_map: Option,
+) -> String {
     let mut cb = broken_link_clone_cb;
-    let doc = Parser::new_with_broken_link_callback(markdown, MARKDOWN_OPTIONS, Some(&mut cb));
+    let doc = Parser::new_with_broken_link_callback(markdown, MARKDOWN_OPTIONS, Some(&mut cb))
+        .into_offset_iter();
 
-    let doc = map_links(doc, |target, title| {
+    let doc = map_links(doc, |target, title, range| {
         // This check is imperfect, there's some overlap between valid intra-doc links
         // and valid URLs so we choose to be too eager to try to resolve what might be
         // a URL.
@@ -60,7 +68,16 @@ pub(crate) fn rewrite_links(db: &RootDatabase, markdown: &str, definition: Defin
             // Two possibilities:
             // * path-based links: `../../module/struct.MyStruct.html`
             // * module-based links (AKA intra-doc links): `super::super::module::MyStruct`
-            if let Some((target, title)) = rewrite_intra_doc_link(db, definition, target, title) {
+            let text_range =
+                TextRange::new(range.start.try_into().unwrap(), range.end.try_into().unwrap());
+            let is_inner_doc = range_map
+                .as_ref()
+                .and_then(|range_map| range_map.map(text_range))
+                .map(|(_, attr_id)| attr_id.is_inner_attr())
+                .unwrap_or(false);
+            if let Some((target, title)) =
+                rewrite_intra_doc_link(db, definition, target, title, is_inner_doc)
+            {
                 (None, target, title)
             } else if let Some(target) = rewrite_url_link(db, definition, target) {
                 (Some(LinkType::Inline), target, title.to_owned())
@@ -195,22 +212,23 @@ pub(crate) fn resolve_doc_path_for_def(
     def: Definition,
     link: &str,
     ns: Option,
+    is_inner_doc: bool,
 ) -> Option {
     match def {
-        Definition::Module(it) => it.resolve_doc_path(db, link, ns),
-        Definition::Crate(it) => it.resolve_doc_path(db, link, ns),
-        Definition::Function(it) => it.resolve_doc_path(db, link, ns),
-        Definition::Adt(it) => it.resolve_doc_path(db, link, ns),
-        Definition::Variant(it) => it.resolve_doc_path(db, link, ns),
-        Definition::Const(it) => it.resolve_doc_path(db, link, ns),
-        Definition::Static(it) => it.resolve_doc_path(db, link, ns),
-        Definition::Trait(it) => it.resolve_doc_path(db, link, ns),
-        Definition::TraitAlias(it) => it.resolve_doc_path(db, link, ns),
-        Definition::TypeAlias(it) => it.resolve_doc_path(db, link, ns),
-        Definition::Macro(it) => it.resolve_doc_path(db, link, ns),
-        Definition::Field(it) => it.resolve_doc_path(db, link, ns),
-        Definition::SelfType(it) => it.resolve_doc_path(db, link, ns),
-        Definition::ExternCrateDecl(it) => it.resolve_doc_path(db, link, ns),
+        Definition::Module(it) => it.resolve_doc_path(db, link, ns, is_inner_doc),
+        Definition::Crate(it) => it.resolve_doc_path(db, link, ns, is_inner_doc),
+        Definition::Function(it) => it.resolve_doc_path(db, link, ns, is_inner_doc),
+        Definition::Adt(it) => it.resolve_doc_path(db, link, ns, is_inner_doc),
+        Definition::Variant(it) => it.resolve_doc_path(db, link, ns, is_inner_doc),
+        Definition::Const(it) => it.resolve_doc_path(db, link, ns, is_inner_doc),
+        Definition::Static(it) => it.resolve_doc_path(db, link, ns, is_inner_doc),
+        Definition::Trait(it) => it.resolve_doc_path(db, link, ns, is_inner_doc),
+        Definition::TraitAlias(it) => it.resolve_doc_path(db, link, ns, is_inner_doc),
+        Definition::TypeAlias(it) => it.resolve_doc_path(db, link, ns, is_inner_doc),
+        Definition::Macro(it) => it.resolve_doc_path(db, link, ns, is_inner_doc),
+        Definition::Field(it) => it.resolve_doc_path(db, link, ns, is_inner_doc),
+        Definition::SelfType(it) => it.resolve_doc_path(db, link, ns, is_inner_doc),
+        Definition::ExternCrateDecl(it) => it.resolve_doc_path(db, link, ns, is_inner_doc),
         Definition::BuiltinAttr(_)
         | Definition::BuiltinType(_)
         | Definition::BuiltinLifetime(_)
@@ -289,28 +307,39 @@ pub(crate) fn get_definition_with_descend_at(
         let relative_comment_offset = offset - original_start - prefix_len;
 
         sema.descend_into_macros(doc_token).into_iter().find_map(|t| {
-            let (node, descended_prefix_len) = match_ast! {
+            let (node, descended_prefix_len, is_inner) = match_ast!{
                 match t {
-                    ast::Comment(comment) => (t.parent()?, TextSize::try_from(comment.prefix().len()).ok()?),
-                    ast::String(string) => (t.parent_ancestors().skip_while(|n| n.kind() != ATTR).nth(1)?, string.open_quote_text_range()?.len()),
+                    ast::Comment(comment) => {
+                        (t.parent()?, TextSize::try_from(comment.prefix().len()).ok()?, comment.is_inner())
+                    },
+                    ast::String(string) => {
+                        let attr = t.parent_ancestors().find_map(ast::Attr::cast)?;
+                        let attr_is_inner = attr.excl_token().map(|excl| excl.kind() == BANG).unwrap_or(false);
+                        (attr.syntax().parent()?, string.open_quote_text_range()?.len(), attr_is_inner)
+                    },
                     _ => return None,
                 }
             };
             let token_start = t.text_range().start();
             let abs_in_expansion_offset = token_start + relative_comment_offset + descended_prefix_len;
 
-            let (attributes, def) = doc_attributes(sema, &node)?;
+            let (attributes, def) = if is_inner && node.kind() != SOURCE_FILE {
+                let parent = node.parent()?;
+                doc_attributes(sema, &parent).unwrap_or(doc_attributes(sema, &parent.parent()?)?)
+            }else {
+                doc_attributes(sema, &node)?
+            };
             let (docs, doc_mapping) = docs_with_rangemap(sema.db, &attributes)?;
-            let (in_expansion_range, link, ns) =
+            let (in_expansion_range, link, ns, is_inner) =
                 extract_definitions_from_docs(&docs).into_iter().find_map(|(range, link, ns)| {
-                    let mapped = doc_mapping.map(range)?;
-                    (mapped.value.contains(abs_in_expansion_offset)).then_some((mapped.value, link, ns))
+                    let (mapped, idx) = doc_mapping.map(range)?;
+                    (mapped.value.contains(abs_in_expansion_offset)).then_some((mapped.value, link, ns, idx.is_inner_attr()))
                 })?;
             // get the relative range to the doc/attribute in the expansion
             let in_expansion_relative_range = in_expansion_range - descended_prefix_len - token_start;
             // Apply relative range to the original input comment
             let absolute_range = in_expansion_relative_range + original_start + prefix_len;
-            let def = resolve_doc_path_for_def(sema.db, def, &link, ns)?;
+            let def = resolve_doc_path_for_def(sema.db, def, &link, ns, is_inner)?;
             cb(def, node, absolute_range)
         })
     }
@@ -369,6 +398,7 @@ fn rewrite_intra_doc_link(
     def: Definition,
     target: &str,
     title: &str,
+    is_inner_doc: bool,
 ) -> Option<(String, String)> {
     let (link, ns) = parse_intra_doc_link(target);
 
@@ -377,7 +407,7 @@ fn rewrite_intra_doc_link(
         None => (link, None),
     };
 
-    let resolved = resolve_doc_path_for_def(db, def, link, ns)?;
+    let resolved = resolve_doc_path_for_def(db, def, link, ns, is_inner_doc)?;
     let mut url = get_doc_base_urls(db, resolved, None, None).0?;
 
     let (_, file, frag) = filename_and_frag_for_def(db, resolved)?;
@@ -421,8 +451,8 @@ fn mod_path_of_def(db: &RootDatabase, def: Definition) -> Option {
 
 /// Rewrites a markdown document, applying 'callback' to each link.
 fn map_links<'e>(
-    events: impl Iterator>,
-    callback: impl Fn(&str, &str) -> (Option, String, String),
+    events: impl Iterator, Range)>,
+    callback: impl Fn(&str, &str, Range) -> (Option, String, String),
 ) -> impl Iterator> {
     let mut in_link = false;
     // holds the origin link target on start event and the rewritten one on end event
@@ -432,7 +462,7 @@ fn map_links<'e>(
     // `Shortcut` type parsed from Start/End tags doesn't make sense for url links
     let mut end_link_type: Option = None;
 
-    events.map(move |evt| match evt {
+    events.map(move |(evt, range)| match evt {
         Event::Start(Tag::Link(link_type, ref target, _)) => {
             in_link = true;
             end_link_target = Some(target.clone());
@@ -449,7 +479,7 @@ fn map_links<'e>(
         }
         Event::Text(s) if in_link => {
             let (link_type, link_target_s, link_name) =
-                callback(&end_link_target.take().unwrap(), &s);
+                callback(&end_link_target.take().unwrap(), &s, range);
             end_link_target = Some(CowStr::Boxed(link_target_s.into()));
             if !matches!(end_link_type, Some(LinkType::Autolink)) {
                 end_link_type = link_type;
@@ -458,7 +488,7 @@ fn map_links<'e>(
         }
         Event::Code(s) if in_link => {
             let (link_type, link_target_s, link_name) =
-                callback(&end_link_target.take().unwrap(), &s);
+                callback(&end_link_target.take().unwrap(), &s, range);
             end_link_target = Some(CowStr::Boxed(link_target_s.into()));
             if !matches!(end_link_type, Some(LinkType::Autolink)) {
                 end_link_type = link_type;
diff --git a/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs b/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs
index 6d56b98e57c8..6af156fa668f 100644
--- a/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs
@@ -5,7 +5,7 @@
 use ide_db::{
     FilePosition, FileRange, RootDatabase,
     defs::Definition,
-    documentation::{Documentation, HasDocs},
+    documentation::{DocsRangeMap, Documentation, HasDocs},
 };
 use itertools::Itertools;
 use syntax::{AstNode, SyntaxNode, ast, match_ast};
@@ -45,8 +45,8 @@ fn check_external_docs(
 fn check_rewrite(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
     let (analysis, position) = fixture::position(ra_fixture);
     let sema = &Semantics::new(&analysis.db);
-    let (cursor_def, docs) = def_under_cursor(sema, &position);
-    let res = rewrite_links(sema.db, docs.as_str(), cursor_def);
+    let (cursor_def, docs, range) = def_under_cursor(sema, &position);
+    let res = rewrite_links(sema.db, docs.as_str(), cursor_def, Some(range));
     expect.assert_eq(&res)
 }
 
@@ -56,12 +56,14 @@ fn check_doc_links(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
     let (analysis, position, mut expected) = fixture::annotations(ra_fixture);
     expected.sort_by_key(key_fn);
     let sema = &Semantics::new(&analysis.db);
-    let (cursor_def, docs) = def_under_cursor(sema, &position);
+    let (cursor_def, docs, range) = def_under_cursor(sema, &position);
     let defs = extract_definitions_from_docs(&docs);
     let actual: Vec<_> = defs
         .into_iter()
-        .flat_map(|(_, link, ns)| {
-            let def = resolve_doc_path_for_def(sema.db, cursor_def, &link, ns)
+        .flat_map(|(text_range, link, ns)| {
+            let attr = range.map(text_range);
+            let is_inner_attr = attr.map(|(_file, attr)| attr.is_inner_attr()).unwrap_or(false);
+            let def = resolve_doc_path_for_def(sema.db, cursor_def, &link, ns, is_inner_attr)
                 .unwrap_or_else(|| panic!("Failed to resolve {link}"));
             def.try_to_nav(sema.db).unwrap().into_iter().zip(iter::repeat(link))
         })
@@ -78,7 +80,7 @@ fn check_doc_links(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
 fn def_under_cursor(
     sema: &Semantics<'_, RootDatabase>,
     position: &FilePosition,
-) -> (Definition, Documentation) {
+) -> (Definition, Documentation, DocsRangeMap) {
     let (docs, def) = sema
         .parse_guess_edition(position.file_id)
         .syntax()
@@ -89,31 +91,31 @@ fn def_under_cursor(
         .find_map(|it| node_to_def(sema, &it))
         .expect("no def found")
         .unwrap();
-    let docs = docs.expect("no docs found for cursor def");
-    (def, docs)
+    let (docs, range) = docs.expect("no docs found for cursor def");
+    (def, docs, range)
 }
 
 fn node_to_def(
     sema: &Semantics<'_, RootDatabase>,
     node: &SyntaxNode,
-) -> Option, Definition)>> {
+) -> Option, Definition)>> {
     Some(match_ast! {
         match node {
-            ast::SourceFile(it)  => sema.to_def(&it).map(|def| (def.docs(sema.db), Definition::Module(def))),
-            ast::Module(it)      => sema.to_def(&it).map(|def| (def.docs(sema.db), Definition::Module(def))),
-            ast::Fn(it)          => sema.to_def(&it).map(|def| (def.docs(sema.db), Definition::Function(def))),
-            ast::Struct(it)      => sema.to_def(&it).map(|def| (def.docs(sema.db), Definition::Adt(hir::Adt::Struct(def)))),
-            ast::Union(it)       => sema.to_def(&it).map(|def| (def.docs(sema.db), Definition::Adt(hir::Adt::Union(def)))),
-            ast::Enum(it)        => sema.to_def(&it).map(|def| (def.docs(sema.db), Definition::Adt(hir::Adt::Enum(def)))),
-            ast::Variant(it)     => sema.to_def(&it).map(|def| (def.docs(sema.db), Definition::Variant(def))),
-            ast::Trait(it)       => sema.to_def(&it).map(|def| (def.docs(sema.db), Definition::Trait(def))),
-            ast::Static(it)      => sema.to_def(&it).map(|def| (def.docs(sema.db), Definition::Static(def))),
-            ast::Const(it)       => sema.to_def(&it).map(|def| (def.docs(sema.db), Definition::Const(def))),
-            ast::TypeAlias(it)   => sema.to_def(&it).map(|def| (def.docs(sema.db), Definition::TypeAlias(def))),
-            ast::Impl(it)        => sema.to_def(&it).map(|def| (def.docs(sema.db), Definition::SelfType(def))),
-            ast::RecordField(it) => sema.to_def(&it).map(|def| (def.docs(sema.db), Definition::Field(def))),
-            ast::TupleField(it)  => sema.to_def(&it).map(|def| (def.docs(sema.db), Definition::Field(def))),
-            ast::Macro(it)       => sema.to_def(&it).map(|def| (def.docs(sema.db), Definition::Macro(def))),
+            ast::SourceFile(it)  => sema.to_def(&it).map(|def| (def.docs_with_rangemap(sema.db), Definition::Module(def))),
+            ast::Module(it)      => sema.to_def(&it).map(|def| (def.docs_with_rangemap(sema.db), Definition::Module(def))),
+            ast::Fn(it)          => sema.to_def(&it).map(|def| (def.docs_with_rangemap(sema.db), Definition::Function(def))),
+            ast::Struct(it)      => sema.to_def(&it).map(|def| (def.docs_with_rangemap(sema.db), Definition::Adt(hir::Adt::Struct(def)))),
+            ast::Union(it)       => sema.to_def(&it).map(|def| (def.docs_with_rangemap(sema.db), Definition::Adt(hir::Adt::Union(def)))),
+            ast::Enum(it)        => sema.to_def(&it).map(|def| (def.docs_with_rangemap(sema.db), Definition::Adt(hir::Adt::Enum(def)))),
+            ast::Variant(it)     => sema.to_def(&it).map(|def| (def.docs_with_rangemap(sema.db), Definition::Variant(def))),
+            ast::Trait(it)       => sema.to_def(&it).map(|def| (def.docs_with_rangemap(sema.db), Definition::Trait(def))),
+            ast::Static(it)      => sema.to_def(&it).map(|def| (def.docs_with_rangemap(sema.db), Definition::Static(def))),
+            ast::Const(it)       => sema.to_def(&it).map(|def| (def.docs_with_rangemap(sema.db), Definition::Const(def))),
+            ast::TypeAlias(it)   => sema.to_def(&it).map(|def| (def.docs_with_rangemap(sema.db), Definition::TypeAlias(def))),
+            ast::Impl(it)        => sema.to_def(&it).map(|def| (def.docs_with_rangemap(sema.db), Definition::SelfType(def))),
+            ast::RecordField(it) => sema.to_def(&it).map(|def| (def.docs_with_rangemap(sema.db), Definition::Field(def))),
+            ast::TupleField(it)  => sema.to_def(&it).map(|def| (def.docs_with_rangemap(sema.db), Definition::Field(def))),
+            ast::Macro(it)       => sema.to_def(&it).map(|def| (def.docs_with_rangemap(sema.db), Definition::Macro(def))),
             // ast::Use(it) => sema.to_def(&it).map(|def| (Box::new(it) as _, def.attrs(sema.db))),
             _ => return None,
         }
@@ -583,12 +585,12 @@ fn doc_links_module() {
 /// [`M::f`]
 mod M$0 {
   //^ M
-  #![doc = "inner_item[`M::S`]"]
+  #![doc = "inner_item[`S`]"]
 
     pub fn f() {}
          //^ M::f
     pub struct S;
-             //^ M::S
+             //^ S
 }
 "#,
     );
diff --git a/src/tools/rust-analyzer/crates/ide/src/hover.rs b/src/tools/rust-analyzer/crates/ide/src/hover.rs
index 075afcec019f..873e31b4a337 100644
--- a/src/tools/rust-analyzer/crates/ide/src/hover.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/hover.rs
@@ -456,7 +456,7 @@ pub(crate) fn hover_for_definition(
     let notable_traits = def_ty.map(|ty| notable_traits(db, &ty)).unwrap_or_default();
     let subst_types = subst.map(|subst| subst.types(db));
 
-    let markup = render::definition(
+    let (markup, range_map) = render::definition(
         sema.db,
         def,
         famous_defs.as_ref(),
@@ -469,7 +469,7 @@ pub(crate) fn hover_for_definition(
         display_target,
     );
     HoverResult {
-        markup: render::process_markup(sema.db, def, &markup, config),
+        markup: render::process_markup(sema.db, def, &markup, range_map, config),
         actions: [
             show_fn_references_action(sema.db, def),
             show_implementations_action(sema.db, def),
diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs
index 69b83f3b12d8..ad720c8a6274 100644
--- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs
@@ -11,7 +11,7 @@
 use ide_db::{
     RootDatabase,
     defs::Definition,
-    documentation::HasDocs,
+    documentation::{DocsRangeMap, HasDocs},
     famous_defs::FamousDefs,
     generated::lints::{CLIPPY_LINTS, DEFAULT_LINTS, FEATURES},
     syntax_helpers::prettify_macro_expansion,
@@ -21,7 +21,7 @@
     Float,
     ieee::{Half as f16, Quad as f128},
 };
-use span::Edition;
+use span::{Edition, TextSize};
 use stdx::format_to;
 use syntax::{AstNode, AstToken, Direction, SyntaxToken, T, algo, ast, match_ast};
 
@@ -276,13 +276,10 @@ pub(super) fn keyword(
         keyword_hints(sema, token, parent, edition, display_target);
 
     let doc_owner = find_std_module(&famous_defs, &keyword_mod, edition)?;
-    let docs = doc_owner.docs(sema.db)?;
-    let markup = process_markup(
-        sema.db,
-        Definition::Module(doc_owner),
-        &markup(Some(docs.into()), description, None, None, String::new()),
-        config,
-    );
+    let (docs, range_map) = doc_owner.docs_with_rangemap(sema.db)?;
+    let (markup, range_map) =
+        markup(Some(docs.into()), Some(range_map), description, None, None, String::new());
+    let markup = process_markup(sema.db, Definition::Module(doc_owner), &markup, range_map, config);
     Some(HoverResult { markup, actions })
 }
 
@@ -371,11 +368,15 @@ pub(super) fn process_markup(
     db: &RootDatabase,
     def: Definition,
     markup: &Markup,
+    markup_range_map: Option,
     config: &HoverConfig,
 ) -> Markup {
     let markup = markup.as_str();
-    let markup =
-        if config.links_in_hover { rewrite_links(db, markup, def) } else { remove_links(markup) };
+    let markup = if config.links_in_hover {
+        rewrite_links(db, markup, def, markup_range_map)
+    } else {
+        remove_links(markup)
+    };
     Markup::from(markup)
 }
 
@@ -482,7 +483,7 @@ pub(super) fn definition(
     config: &HoverConfig,
     edition: Edition,
     display_target: DisplayTarget,
-) -> Markup {
+) -> (Markup, Option) {
     let mod_path = definition_path(db, &def, edition);
     let label = match def {
         Definition::Trait(trait_) => trait_
@@ -518,7 +519,12 @@ pub(super) fn definition(
         }
         _ => def.label(db, display_target),
     };
-    let docs = def.docs(db, famous_defs, display_target);
+    let (docs, range_map) =
+        if let Some((docs, doc_range)) = def.docs_with_rangemap(db, famous_defs, display_target) {
+            (Some(docs), doc_range)
+        } else {
+            (None, None)
+        };
     let value = || match def {
         Definition::Variant(it) => {
             if !it.parent_enum(db).is_data_carrying(db) {
@@ -807,6 +813,7 @@ pub(super) fn definition(
 
     markup(
         docs.map(Into::into),
+        range_map,
         desc,
         extra.is_empty().not().then_some(extra),
         mod_path,
@@ -1083,11 +1090,12 @@ fn definition_path(db: &RootDatabase, &def: &Definition, edition: Edition) -> Op
 
 fn markup(
     docs: Option,
+    range_map: Option,
     rust: String,
     extra: Option,
     mod_path: Option,
     subst_types: String,
-) -> Markup {
+) -> (Markup, Option) {
     let mut buf = String::new();
 
     if let Some(mod_path) = mod_path {
@@ -1106,9 +1114,15 @@ fn markup(
     }
 
     if let Some(doc) = docs {
-        format_to!(buf, "\n___\n\n{}", doc);
+        format_to!(buf, "\n___\n\n");
+        let offset = TextSize::new(buf.len() as u32);
+        let buf_range_map = range_map.map(|range_map| range_map.shift_docstring_line_range(offset));
+        format_to!(buf, "{}", doc);
+
+        (buf.into(), buf_range_map)
+    } else {
+        (buf.into(), None)
     }
-    buf.into()
 }
 
 fn find_std_module(
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs
index 0998e14c87ba..7f5c2c1ec849 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs
@@ -129,11 +129,18 @@ pub(super) fn doc_comment(
         extract_definitions_from_docs(&docs)
             .into_iter()
             .filter_map(|(range, link, ns)| {
-                doc_mapping.map(range).filter(|mapping| mapping.file_id == src_file_id).and_then(
-                    |InFile { value: mapped_range, .. }| {
-                        Some(mapped_range).zip(resolve_doc_path_for_def(sema.db, def, &link, ns))
-                    },
-                )
+                doc_mapping
+                    .map(range)
+                    .filter(|(mapping, _)| mapping.file_id == src_file_id)
+                    .and_then(|(InFile { value: mapped_range, .. }, attr_id)| {
+                        Some(mapped_range).zip(resolve_doc_path_for_def(
+                            sema.db,
+                            def,
+                            &link,
+                            ns,
+                            attr_id.is_inner_attr(),
+                        ))
+                    })
             })
             .for_each(|(range, def)| {
                 hl.add(HlRange {
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_inline.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_inline.html
index 75d96b422c10..9996a871580f 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_inline.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_inline.html
@@ -40,9 +40,9 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
 .unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
 
-
//! [foo::Struct]
+
//! [Struct]
 //! This is an intra doc injection test for modules
-//! [foo::Struct]
+//! [Struct]
 //! This is an intra doc injection test for modules
 
 pub struct Struct;
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs
index a8d953094667..dd359326c61d 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs
@@ -1072,9 +1072,9 @@ fn test_mod_hl_injection() {
     check_highlighting(
         r##"
 //- /foo.rs
-//! [foo::Struct]
+//! [Struct]
 //! This is an intra doc injection test for modules
-//! [foo::Struct]
+//! [Struct]
 //! This is an intra doc injection test for modules
 
 pub struct Struct;
@@ -1097,9 +1097,9 @@ fn test_mod_hl_injection() {
 /// This is an intra doc injection test for modules
 mod foo;
 //- /foo.rs
-//! [foo::Struct]
+//! [Struct]
 //! This is an intra doc injection test for modules
-//! [foo::Struct]
+//! [Struct]
 //! This is an intra doc injection test for modules
 
 pub struct Struct;

From ecb59e185a8c7170bd77ea7161bd7881be64e5ec Mon Sep 17 00:00:00 2001
From: Hayashi Mikihiro <34ttrweoewiwe28@gmail.com>
Date: Wed, 7 May 2025 00:36:42 +0900
Subject: [PATCH 26/51] add doc link test for goto def

Signed-off-by: Hayashi Mikihiro <34ttrweoewiwe28@gmail.com>
---
 .../crates/ide/src/goto_definition.rs         | 68 +++++++++++++++++++
 1 file changed, 68 insertions(+)

diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs
index b894e857522f..c60ca3562f66 100644
--- a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs
@@ -1922,6 +1922,74 @@ pub fn foo() { }
         )
     }
 
+    #[test]
+    fn goto_def_for_intra_doc_link_outer_same_file() {
+        check(
+            r#"
+/// [`S$0`]
+mod m {
+    //! [`super::S`]
+}
+struct S;
+     //^
+            "#,
+        );
+
+        check(
+            r#"
+/// [`S$0`]
+mod m {}
+struct S;
+     //^
+            "#,
+        );
+
+        check(
+            r#"
+/// [`S$0`]
+fn f() {
+    //! [`S`]
+}
+struct S;
+     //^
+            "#,
+        );
+    }
+
+    #[test]
+    fn goto_def_for_intra_doc_link_inner_same_file() {
+        check(
+            r#"
+/// [`S`]
+mod m {
+    //! [`super::S$0`]
+}
+struct S;
+     //^
+            "#,
+        );
+
+        check(
+            r#"
+mod m {
+    //! [`super::S$0`]
+}
+struct S;
+     //^
+            "#,
+        );
+
+        check(
+            r#"
+fn f() {
+    //! [`S$0`]
+}
+struct S;
+     //^
+            "#,
+        );
+    }
+
     #[test]
     fn goto_def_for_intra_doc_link_inner() {
         check(

From 449d720651381cd5ad46c6e89130baea83de61d2 Mon Sep 17 00:00:00 2001
From: Hayashi Mikihiro <34ttrweoewiwe28@gmail.com>
Date: Wed, 7 May 2025 00:36:52 +0900
Subject: [PATCH 27/51] add doc link test for hover

Signed-off-by: Hayashi Mikihiro <34ttrweoewiwe28@gmail.com>
---
 .../crates/ide/src/hover/tests.rs             | 122 ++++++++++++++++++
 1 file changed, 122 insertions(+)

diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs
index 7b7eef9d5793..06ca24c3ec35 100644
--- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs
@@ -7374,6 +7374,128 @@ pub struct Foo(i32)
     );
 }
 
+#[test]
+fn hover_intra_inner_attr() {
+    check(
+        r#"
+/// outer comment for [`Foo`]
+#[doc = "Doc outer comment for [`Foo`]"]
+pub fn Foo {
+    //! inner comment for [`Foo$0`]
+    #![doc = "Doc inner comment for [`Foo`]"]
+}
+"#,
+        expect![[r#"
+            *[`Foo`]*
+
+            ```rust
+            ra_test_fixture
+            ```
+
+            ```rust
+            pub fn Foo()
+            ```
+
+            ---
+
+            outer comment for [`Foo`](https://docs.rs/ra_test_fixture/*/ra_test_fixture/fn.Foo.html)
+            Doc outer comment for [`Foo`](https://docs.rs/ra_test_fixture/*/ra_test_fixture/fn.Foo.html)
+            inner comment for [`Foo`](https://docs.rs/ra_test_fixture/*/ra_test_fixture/fn.Foo.html)
+            Doc inner comment for [`Foo`](https://docs.rs/ra_test_fixture/*/ra_test_fixture/fn.Foo.html)
+        "#]],
+    );
+
+    check(
+        r#"
+/// outer comment for [`Foo`]
+#[doc = "Doc outer comment for [`Foo`]"]
+pub mod Foo {
+    //! inner comment for [`super::Foo$0`]
+    #![doc = "Doc inner comment for [`super::Foo`]"]
+}
+"#,
+        expect![[r#"
+            *[`super::Foo`]*
+
+            ```rust
+            ra_test_fixture
+            ```
+
+            ```rust
+            pub mod Foo
+            ```
+
+            ---
+
+            outer comment for [`Foo`](https://docs.rs/ra_test_fixture/*/ra_test_fixture/Foo/index.html)
+            Doc outer comment for [`Foo`](https://docs.rs/ra_test_fixture/*/ra_test_fixture/Foo/index.html)
+            inner comment for [`super::Foo`](https://docs.rs/ra_test_fixture/*/ra_test_fixture/Foo/index.html)
+            Doc inner comment for [`super::Foo`](https://docs.rs/ra_test_fixture/*/ra_test_fixture/Foo/index.html)
+        "#]],
+    );
+}
+
+#[test]
+fn hover_intra_outer_attr() {
+    check(
+        r#"
+/// outer comment for [`Foo$0`]
+#[doc = "Doc outer comment for [`Foo`]"]
+pub fn Foo() {
+    //! inner comment for [`Foo`]
+    #![doc = "Doc inner comment for [`Foo`]"]
+}
+"#,
+        expect![[r#"
+            *[`Foo`]*
+
+            ```rust
+            ra_test_fixture
+            ```
+
+            ```rust
+            pub fn Foo()
+            ```
+
+            ---
+
+            outer comment for [`Foo`](https://docs.rs/ra_test_fixture/*/ra_test_fixture/fn.Foo.html)
+            Doc outer comment for [`Foo`](https://docs.rs/ra_test_fixture/*/ra_test_fixture/fn.Foo.html)
+            inner comment for [`Foo`](https://docs.rs/ra_test_fixture/*/ra_test_fixture/fn.Foo.html)
+            Doc inner comment for [`Foo`](https://docs.rs/ra_test_fixture/*/ra_test_fixture/fn.Foo.html)
+        "#]],
+    );
+
+    check(
+        r#"
+/// outer comment for [`Foo$0`]
+#[doc = "Doc outer comment for [`Foo`]"]
+pub mod Foo {
+    //! inner comment for [`super::Foo`]
+    #![doc = "Doc inner comment for [`super::Foo`]"]
+}
+"#,
+        expect![[r#"
+            *[`Foo`]*
+
+            ```rust
+            ra_test_fixture
+            ```
+
+            ```rust
+            pub mod Foo
+            ```
+
+            ---
+
+            outer comment for [`Foo`](https://docs.rs/ra_test_fixture/*/ra_test_fixture/Foo/index.html)
+            Doc outer comment for [`Foo`](https://docs.rs/ra_test_fixture/*/ra_test_fixture/Foo/index.html)
+            inner comment for [`super::Foo`](https://docs.rs/ra_test_fixture/*/ra_test_fixture/Foo/index.html)
+            Doc inner comment for [`super::Foo`](https://docs.rs/ra_test_fixture/*/ra_test_fixture/Foo/index.html)
+        "#]],
+    );
+}
+
 #[test]
 fn hover_intra_generics() {
     check(

From 627fedb423343d3a26147986fdda1d8b18474597 Mon Sep 17 00:00:00 2001
From: Hayashi Mikihiro <34ttrweoewiwe28@gmail.com>
Date: Wed, 7 May 2025 02:56:03 +0900
Subject: [PATCH 28/51] extract function: `doc_attributes` to find def from
 inner doc

Signed-off-by: Hayashi Mikihiro <34ttrweoewiwe28@gmail.com>
---
 .../rust-analyzer/crates/ide/src/doc_links.rs | 34 ++++++++++++++-----
 1 file changed, 26 insertions(+), 8 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs
index 290420346060..2c983287d89c 100644
--- a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs
@@ -12,7 +12,9 @@
 use stdx::format_to;
 use url::Url;
 
-use hir::{Adt, AsAssocItem, AssocItem, AssocItemContainer, HasAttrs, db::HirDatabase, sym};
+use hir::{
+    Adt, AsAssocItem, AssocItem, AssocItemContainer, AttrsWithOwner, HasAttrs, db::HirDatabase, sym,
+};
 use ide_db::{
     RootDatabase,
     base_db::{CrateOrigin, LangCrateOrigin, ReleaseChannel, RootQueryDb},
@@ -322,13 +324,7 @@ pub(crate) fn get_definition_with_descend_at(
             };
             let token_start = t.text_range().start();
             let abs_in_expansion_offset = token_start + relative_comment_offset + descended_prefix_len;
-
-            let (attributes, def) = if is_inner && node.kind() != SOURCE_FILE {
-                let parent = node.parent()?;
-                doc_attributes(sema, &parent).unwrap_or(doc_attributes(sema, &parent.parent()?)?)
-            }else {
-                doc_attributes(sema, &node)?
-            };
+            let (attributes, def) = Self::doc_attributes(sema, &node, is_inner)?;
             let (docs, doc_mapping) = docs_with_rangemap(sema.db, &attributes)?;
             let (in_expansion_range, link, ns, is_inner) =
                 extract_definitions_from_docs(&docs).into_iter().find_map(|(range, link, ns)| {
@@ -343,6 +339,28 @@ pub(crate) fn get_definition_with_descend_at(
             cb(def, node, absolute_range)
         })
     }
+
+    /// When we hover a inner doc item, this find a attached definition.
+    /// ```
+    /// // node == ITEM_LIST
+    /// // node.parent == EXPR_BLOCK
+    /// // node.parent().parent() == FN
+    /// fn f() {
+    ///    //! [`S$0`]
+    /// }
+    /// ```
+    fn doc_attributes(
+        sema: &Semantics<'_, RootDatabase>,
+        node: &SyntaxNode,
+        is_inner_doc: bool,
+    ) -> Option<(AttrsWithOwner, Definition)> {
+        if is_inner_doc && node.kind() != SOURCE_FILE {
+            let parent = node.parent()?;
+            doc_attributes(sema, &parent).or(doc_attributes(sema, &parent.parent()?))
+        } else {
+            doc_attributes(sema, node)
+        }
+    }
 }
 
 fn broken_link_clone_cb(link: BrokenLink<'_>) -> Option<(CowStr<'_>, CowStr<'_>)> {

From db9c18e44ee14c2e54f8dffad7c345e2bf25e24e Mon Sep 17 00:00:00 2001
From: Chayim Refael Friedman 
Date: Thu, 8 May 2025 11:05:39 +0300
Subject: [PATCH 29/51] Still complete parentheses & method call arguments if
 there are existing parentheses, but they are after a newline

---
 .../ide-completion/src/context/analysis.rs    | 31 ++++++++++--
 .../ide-completion/src/tests/expression.rs    | 48 +++++++++++++++++++
 2 files changed, 76 insertions(+), 3 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs
index 391e2379dcd5..284876ffc885 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs
@@ -883,9 +883,10 @@ fn classify_name_ref(
             },
             ast::MethodCallExpr(method) => {
                 let receiver = find_opt_node_in_file(original_file, method.receiver());
+                let has_parens = has_parens(&method);
                 let kind = NameRefKind::DotAccess(DotAccess {
                     receiver_ty: receiver.as_ref().and_then(|it| sema.type_of_expr(it)),
-                    kind: DotAccessKind::Method { has_parens: method.arg_list().is_some_and(|it| it.l_paren_token().is_some()) },
+                    kind: DotAccessKind::Method { has_parens },
                     receiver,
                     ctx: DotAccessExprCtx { in_block_expr: is_in_block(method.syntax()), in_breakable: is_in_breakable(method.syntax()) }
                 });
@@ -1372,7 +1373,7 @@ fn classify_name_ref(
                         }
                     }
 
-                    path_ctx.has_call_parens = it.syntax().parent().is_some_and(|it| ast::CallExpr::can_cast(it.kind()));
+                    path_ctx.has_call_parens = it.syntax().parent().is_some_and(|it| ast::CallExpr::cast(it).is_some_and(|it| has_parens(&it)));
 
                     make_path_kind_expr(it.into())
                 },
@@ -1401,7 +1402,7 @@ fn classify_name_ref(
                         match parent {
                             ast::PathType(it) => make_path_kind_type(it.into()),
                             ast::PathExpr(it) => {
-                                path_ctx.has_call_parens = it.syntax().parent().is_some_and(|it| ast::CallExpr::can_cast(it.kind()));
+                                path_ctx.has_call_parens = it.syntax().parent().is_some_and(|it| ast::CallExpr::cast(it).is_some_and(|it| has_parens(&it)));
 
                                 make_path_kind_expr(it.into())
                             },
@@ -1559,6 +1560,30 @@ fn classify_name_ref(
     Some((NameRefContext { nameref, kind: NameRefKind::Path(path_ctx) }, qualifier_ctx))
 }
 
+/// When writing in the middle of some code the following situation commonly occurs (`|` denotes the cursor):
+/// ```ignore
+/// value.method|
+/// (1, 2, 3)
+/// ```
+/// Here, we want to complete the method parentheses & arguments (if the corresponding settings are on),
+/// but the thing is parsed as a method call with parentheses. Therefore we use heuristics: if the parentheses
+/// are on the next line, consider them non-existent.
+fn has_parens(node: &dyn HasArgList) -> bool {
+    let Some(arg_list) = node.arg_list() else { return false };
+    if arg_list.l_paren_token().is_none() {
+        return false;
+    }
+    let prev_siblings = iter::successors(arg_list.syntax().prev_sibling_or_token(), |it| {
+        it.prev_sibling_or_token()
+    });
+    prev_siblings
+        .take_while(|syntax| syntax.kind().is_trivia())
+        .filter_map(|syntax| {
+            syntax.into_token().filter(|token| token.kind() == SyntaxKind::WHITESPACE)
+        })
+        .all(|whitespace| !whitespace.text().contains('\n'))
+}
+
 fn pattern_context_for(
     sema: &Semantics<'_, RootDatabase>,
     original_file: &SyntaxNode,
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs
index d5137949d42f..3750ad72c8bd 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs
@@ -2126,3 +2126,51 @@ fn main() {
         "#]],
     );
 }
+
+#[test]
+fn call_parens_with_newline() {
+    check_edit(
+        "foo",
+        r#"
+fn foo(v: i32) {}
+
+fn bar() {
+    foo$0
+    ()
+}
+    "#,
+        r#"
+fn foo(v: i32) {}
+
+fn bar() {
+    foo(${1:v});$0
+    ()
+}
+    "#,
+    );
+    check_edit(
+        "foo",
+        r#"
+struct Foo;
+impl Foo {
+    fn foo(&self, v: i32) {}
+}
+
+fn bar() {
+    Foo.foo$0
+    ()
+}
+    "#,
+        r#"
+struct Foo;
+impl Foo {
+    fn foo(&self, v: i32) {}
+}
+
+fn bar() {
+    Foo.foo(${1:v});$0
+    ()
+}
+    "#,
+    );
+}

From 99be5d4d8e896fd878be3ba14d3184fed2e3f6f8 Mon Sep 17 00:00:00 2001
From: Lukas Wirth 
Date: Wed, 7 May 2025 08:44:54 +0200
Subject: [PATCH 30/51] perf: Request cancellation while processing changed
 files

---
 src/tools/rust-analyzer/Cargo.lock            |   1 +
 .../crates/rust-analyzer/src/global_state.rs  | 159 ++++++++++--------
 .../rust-analyzer/crates/stdx/Cargo.toml      |   1 +
 .../crates/stdx/src/thread/pool.rs            |  51 +++++-
 4 files changed, 134 insertions(+), 78 deletions(-)

diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock
index 8d6c8284e44e..34469656dcec 100644
--- a/src/tools/rust-analyzer/Cargo.lock
+++ b/src/tools/rust-analyzer/Cargo.lock
@@ -2228,6 +2228,7 @@ version = "0.0.0"
 dependencies = [
  "backtrace",
  "crossbeam-channel",
+ "crossbeam-utils",
  "itertools 0.14.0",
  "jod-thread",
  "libc",
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs
index 3b3b9c879754..1a31525b46a8 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs
@@ -3,7 +3,7 @@
 //!
 //! Each tick provides an immutable snapshot of the state as `WorldSnapshot`.
 
-use std::{ops::Not as _, time::Instant};
+use std::{ops::Not as _, panic::AssertUnwindSafe, time::Instant};
 
 use crossbeam_channel::{Receiver, Sender, unbounded};
 use hir::ChangeWithProcMacros;
@@ -19,6 +19,7 @@
 use proc_macro_api::ProcMacroClient;
 use project_model::{ManifestPath, ProjectWorkspace, ProjectWorkspaceKind, WorkspaceBuildScripts};
 use rustc_hash::{FxHashMap, FxHashSet};
+use stdx::thread;
 use tracing::{Level, span, trace};
 use triomphe::Arc;
 use vfs::{AbsPathBuf, AnchoredPathBuf, ChangeKind, Vfs, VfsPath};
@@ -78,6 +79,7 @@ pub(crate) struct GlobalState {
 
     pub(crate) task_pool: Handle, Receiver>,
     pub(crate) fmt_pool: Handle, Receiver>,
+    pub(crate) cancellation_pool: thread::Pool,
 
     pub(crate) config: Arc,
     pub(crate) config_errors: Option,
@@ -210,6 +212,7 @@ pub(crate) fn new(sender: Sender, config: Config) -> Global
             let handle = TaskPool::new_with_threads(sender, 1);
             Handle { handle, receiver }
         };
+        let cancellation_pool = thread::Pool::new(1);
 
         let task_queue = {
             let (sender, receiver) = unbounded();
@@ -230,6 +233,7 @@ pub(crate) fn new(sender: Sender, config: Config) -> Global
             req_queue: ReqQueue::default(),
             task_pool,
             fmt_pool,
+            cancellation_pool,
             loader,
             config: Arc::new(config.clone()),
             analysis_host,
@@ -290,7 +294,6 @@ pub(crate) fn new(sender: Sender, config: Config) -> Global
 
     pub(crate) fn process_changes(&mut self) -> bool {
         let _p = span!(Level::INFO, "GlobalState::process_changes").entered();
-
         // We cannot directly resolve a change in a ratoml file to a format
         // that can be used by the config module because config talks
         // in `SourceRootId`s instead of `FileId`s and `FileId` -> `SourceRootId`
@@ -298,66 +301,75 @@ pub(crate) fn process_changes(&mut self) -> bool {
         let mut modified_ratoml_files: FxHashMap =
             FxHashMap::default();
 
-        let (change, modified_rust_files, workspace_structure_change) = {
-            let mut change = ChangeWithProcMacros::default();
-            let mut guard = self.vfs.write();
-            let changed_files = guard.0.take_changes();
-            if changed_files.is_empty() {
-                return false;
-            }
+        let mut change = ChangeWithProcMacros::default();
+        let mut guard = self.vfs.write();
+        let changed_files = guard.0.take_changes();
+        if changed_files.is_empty() {
+            return false;
+        }
 
-            // downgrade to read lock to allow more readers while we are normalizing text
-            let guard = RwLockWriteGuard::downgrade_to_upgradable(guard);
-            let vfs: &Vfs = &guard.0;
+        let (change, modified_rust_files, workspace_structure_change) =
+            self.cancellation_pool.scoped(|s| {
+                // start cancellation in parallel, this will kick off lru eviction
+                // allowing us to do meaningful work while waiting
+                let analysis_host = AssertUnwindSafe(&mut self.analysis_host);
+                s.spawn(thread::ThreadIntent::LatencySensitive, || {
+                    { analysis_host }.0.request_cancellation()
+                });
 
-            let mut workspace_structure_change = None;
-            // A file was added or deleted
-            let mut has_structure_changes = false;
-            let mut bytes = vec![];
-            let mut modified_rust_files = vec![];
-            for file in changed_files.into_values() {
-                let vfs_path = vfs.file_path(file.file_id);
-                if let Some(("rust-analyzer", Some("toml"))) = vfs_path.name_and_extension() {
-                    // Remember ids to use them after `apply_changes`
-                    modified_ratoml_files.insert(file.file_id, (file.kind(), vfs_path.clone()));
-                }
+                // downgrade to read lock to allow more readers while we are normalizing text
+                let guard = RwLockWriteGuard::downgrade_to_upgradable(guard);
+                let vfs: &Vfs = &guard.0;
 
-                if let Some(path) = vfs_path.as_path() {
-                    has_structure_changes |= file.is_created_or_deleted();
-
-                    if file.is_modified() && path.extension() == Some("rs") {
-                        modified_rust_files.push(file.file_id);
+                let mut workspace_structure_change = None;
+                // A file was added or deleted
+                let mut has_structure_changes = false;
+                let mut bytes = vec![];
+                let mut modified_rust_files = vec![];
+                for file in changed_files.into_values() {
+                    let vfs_path = vfs.file_path(file.file_id);
+                    if let Some(("rust-analyzer", Some("toml"))) = vfs_path.name_and_extension() {
+                        // Remember ids to use them after `apply_changes`
+                        modified_ratoml_files.insert(file.file_id, (file.kind(), vfs_path.clone()));
                     }
 
-                    let additional_files = self
-                        .config
-                        .discover_workspace_config()
-                        .map(|cfg| {
-                            cfg.files_to_watch.iter().map(String::as_str).collect::>()
-                        })
-                        .unwrap_or_default();
+                    if let Some(path) = vfs_path.as_path() {
+                        has_structure_changes |= file.is_created_or_deleted();
 
-                    let path = path.to_path_buf();
-                    if file.is_created_or_deleted() {
-                        workspace_structure_change.get_or_insert((path, false)).1 |=
-                            self.crate_graph_file_dependencies.contains(vfs_path);
-                    } else if reload::should_refresh_for_change(
-                        &path,
-                        file.kind(),
-                        &additional_files,
-                    ) {
-                        trace!(?path, kind = ?file.kind(), "refreshing for a change");
-                        workspace_structure_change.get_or_insert((path.clone(), false));
+                        if file.is_modified() && path.extension() == Some("rs") {
+                            modified_rust_files.push(file.file_id);
+                        }
+
+                        let additional_files = self
+                            .config
+                            .discover_workspace_config()
+                            .map(|cfg| {
+                                cfg.files_to_watch.iter().map(String::as_str).collect::>()
+                            })
+                            .unwrap_or_default();
+
+                        let path = path.to_path_buf();
+                        if file.is_created_or_deleted() {
+                            workspace_structure_change.get_or_insert((path, false)).1 |=
+                                self.crate_graph_file_dependencies.contains(vfs_path);
+                        } else if reload::should_refresh_for_change(
+                            &path,
+                            file.kind(),
+                            &additional_files,
+                        ) {
+                            trace!(?path, kind = ?file.kind(), "refreshing for a change");
+                            workspace_structure_change.get_or_insert((path.clone(), false));
+                        }
                     }
-                }
 
-                // Clear native diagnostics when their file gets deleted
-                if !file.exists() {
-                    self.diagnostics.clear_native_for(file.file_id);
-                }
+                    // Clear native diagnostics when their file gets deleted
+                    if !file.exists() {
+                        self.diagnostics.clear_native_for(file.file_id);
+                    }
 
-                let text =
-                    if let vfs::Change::Create(v, _) | vfs::Change::Modify(v, _) = file.change {
+                    let text = if let vfs::Change::Create(v, _) | vfs::Change::Modify(v, _) =
+                        file.change
+                    {
                         String::from_utf8(v).ok().map(|text| {
                             // FIXME: Consider doing normalization in the `vfs` instead? That allows
                             // getting rid of some locking
@@ -367,29 +379,28 @@ pub(crate) fn process_changes(&mut self) -> bool {
                     } else {
                         None
                     };
-                // delay `line_endings_map` changes until we are done normalizing the text
-                // this allows delaying the re-acquisition of the write lock
-                bytes.push((file.file_id, text));
-            }
-            let (vfs, line_endings_map) = &mut *RwLockUpgradableReadGuard::upgrade(guard);
-            bytes.into_iter().for_each(|(file_id, text)| {
-                let text = match text {
-                    None => None,
-                    Some((text, line_endings)) => {
-                        line_endings_map.insert(file_id, line_endings);
-                        Some(text)
-                    }
-                };
-                change.change_file(file_id, text);
+                    // delay `line_endings_map` changes until we are done normalizing the text
+                    // this allows delaying the re-acquisition of the write lock
+                    bytes.push((file.file_id, text));
+                }
+                let (vfs, line_endings_map) = &mut *RwLockUpgradableReadGuard::upgrade(guard);
+                bytes.into_iter().for_each(|(file_id, text)| {
+                    let text = match text {
+                        None => None,
+                        Some((text, line_endings)) => {
+                            line_endings_map.insert(file_id, line_endings);
+                            Some(text)
+                        }
+                    };
+                    change.change_file(file_id, text);
+                });
+                if has_structure_changes {
+                    let roots = self.source_root_config.partition(vfs);
+                    change.set_roots(roots);
+                }
+                (change, modified_rust_files, workspace_structure_change)
             });
-            if has_structure_changes {
-                let roots = self.source_root_config.partition(vfs);
-                change.set_roots(roots);
-            }
-            (change, modified_rust_files, workspace_structure_change)
-        };
 
-        let _p = span!(Level::INFO, "GlobalState::process_changes/apply_change").entered();
         self.analysis_host.apply_change(change);
         if !modified_ratoml_files.is_empty()
             || !self.config.same_source_root_parent_map(&self.local_roots_parent_map)
diff --git a/src/tools/rust-analyzer/crates/stdx/Cargo.toml b/src/tools/rust-analyzer/crates/stdx/Cargo.toml
index 7bda106764b9..b37aded6f68c 100644
--- a/src/tools/rust-analyzer/crates/stdx/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/stdx/Cargo.toml
@@ -17,6 +17,7 @@ jod-thread = "1.0.0"
 crossbeam-channel.workspace = true
 itertools.workspace = true
 tracing.workspace = true
+crossbeam-utils = "0.8.21"
 # Think twice before adding anything here
 
 [target.'cfg(unix)'.dependencies]
diff --git a/src/tools/rust-analyzer/crates/stdx/src/thread/pool.rs b/src/tools/rust-analyzer/crates/stdx/src/thread/pool.rs
index a8de4db624f1..8d76c5fd1fb3 100644
--- a/src/tools/rust-analyzer/crates/stdx/src/thread/pool.rs
+++ b/src/tools/rust-analyzer/crates/stdx/src/thread/pool.rs
@@ -8,6 +8,7 @@
 //! the threading utilities in [`crate::thread`].
 
 use std::{
+    marker::PhantomData,
     panic::{self, UnwindSafe},
     sync::{
         Arc,
@@ -16,8 +17,9 @@
 };
 
 use crossbeam_channel::{Receiver, Sender};
+use crossbeam_utils::sync::WaitGroup;
 
-use super::{Builder, JoinHandle, ThreadIntent};
+use crate::thread::{Builder, JoinHandle, ThreadIntent};
 
 pub struct Pool {
     // `_handles` is never read: the field is present
@@ -79,9 +81,6 @@ pub fn new(threads: usize) -> Self {
         Self { _handles: handles.into_boxed_slice(), extant_tasks, job_sender }
     }
 
-    /// # Panics
-    ///
-    /// Panics if job panics
     pub fn spawn(&self, intent: ThreadIntent, f: F)
     where
         F: FnOnce() + Send + UnwindSafe + 'static,
@@ -97,6 +96,17 @@ pub fn spawn(&self, intent: ThreadIntent, f: F)
         self.job_sender.send(job).unwrap();
     }
 
+    pub fn scoped<'pool, 'scope, F, R>(&'pool self, f: F) -> R
+    where
+        F: FnOnce(&Scope<'pool, 'scope>) -> R,
+    {
+        let wg = WaitGroup::new();
+        let scope = Scope { pool: self, wg, _marker: PhantomData };
+        let r = f(&scope);
+        scope.wg.wait();
+        r
+    }
+
     #[must_use]
     pub fn len(&self) -> usize {
         self.extant_tasks.load(Ordering::SeqCst)
@@ -107,3 +117,36 @@ pub fn is_empty(&self) -> bool {
         self.len() == 0
     }
 }
+
+pub struct Scope<'pool, 'scope> {
+    pool: &'pool Pool,
+    wg: WaitGroup,
+    _marker: PhantomData &'scope ()>,
+}
+
+impl<'scope> Scope<'_, 'scope> {
+    pub fn spawn(&self, intent: ThreadIntent, f: F)
+    where
+        F: 'scope + FnOnce() + Send + UnwindSafe,
+    {
+        let wg = self.wg.clone();
+        let f = Box::new(move || {
+            if cfg!(debug_assertions) {
+                intent.assert_is_used_on_current_thread();
+            }
+            f();
+            drop(wg);
+        });
+
+        let job = Job {
+            requested_intent: intent,
+            f: unsafe {
+                std::mem::transmute::<
+                    Box,
+                    Box,
+                >(f)
+            },
+        };
+        self.pool.job_sender.send(job).unwrap();
+    }
+}

From 322451c7d22167c1ba4c5f34b5fba07b4ed5a8ee Mon Sep 17 00:00:00 2001
From: Chayim Refael Friedman 
Date: Thu, 8 May 2025 11:43:47 +0300
Subject: [PATCH 31/51] Fix postfix snippets duplicating derefs

---
 .../ide-completion/src/completions/postfix.rs | 27 ++++++++++---------
 .../ide-completion/src/tests/expression.rs    | 19 +++++++++++++
 2 files changed, 33 insertions(+), 13 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs
index 54be7d2fbc33..3cdf2112835d 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs
@@ -311,6 +311,8 @@ fn include_references(initial_element: &ast::Expr) -> (ast::Expr, String) {
 
     let mut prefix = String::new();
 
+    let mut found_ref_or_deref = false;
+
     while let Some(parent_deref_element) =
         resulting_element.syntax().parent().and_then(ast::PrefixExpr::cast)
     {
@@ -318,27 +320,26 @@ fn include_references(initial_element: &ast::Expr) -> (ast::Expr, String) {
             break;
         }
 
+        found_ref_or_deref = true;
         resulting_element = ast::Expr::from(parent_deref_element);
 
         prefix.insert(0, '*');
     }
 
-    if let Some(first_ref_expr) = resulting_element.syntax().parent().and_then(ast::RefExpr::cast) {
-        if let Some(expr) = first_ref_expr.expr() {
-            resulting_element = expr;
-        }
+    while let Some(parent_ref_element) =
+        resulting_element.syntax().parent().and_then(ast::RefExpr::cast)
+    {
+        found_ref_or_deref = true;
+        let exclusive = parent_ref_element.mut_token().is_some();
+        resulting_element = ast::Expr::from(parent_ref_element);
 
-        while let Some(parent_ref_element) =
-            resulting_element.syntax().parent().and_then(ast::RefExpr::cast)
-        {
-            let exclusive = parent_ref_element.mut_token().is_some();
-            resulting_element = ast::Expr::from(parent_ref_element);
+        prefix.insert_str(0, if exclusive { "&mut " } else { "&" });
+    }
 
-            prefix.insert_str(0, if exclusive { "&mut " } else { "&" });
-        }
-    } else {
-        // If we do not find any ref expressions, restore
+    if !found_ref_or_deref {
+        // If we do not find any ref/deref expressions, restore
         // all the progress of tree climbing
+        prefix.clear();
         resulting_element = initial_element.clone();
     }
 
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs
index 3750ad72c8bd..e5467767d42c 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs
@@ -2174,3 +2174,22 @@ fn bar() {
     "#,
     );
 }
+
+#[test]
+fn dbg_too_many_asterisks() {
+    check_edit(
+        "dbg",
+        r#"
+fn main() {
+    let x = &42;
+    let y = *x.$0;
+}
+    "#,
+        r#"
+fn main() {
+    let x = &42;
+    let y = dbg!(*x);
+}
+    "#,
+    );
+}

From 656a59e40f792233be25e310d5d533d1d12ed1fd Mon Sep 17 00:00:00 2001
From: Hayashi Mikihiro <34ttrweoewiwe28@gmail.com>
Date: Thu, 8 May 2025 23:37:47 +0900
Subject: [PATCH 32/51] add assert to check ast_index smaller than
 INNER_ATTR_SET_BIT

Signed-off-by: Hayashi Mikihiro <34ttrweoewiwe28@gmail.com>
---
 .../crates/hir-expand/src/attrs.rs            | 22 ++++++++-----------
 1 file changed, 9 insertions(+), 13 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs b/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs
index 4e519452aa69..94c97713f065 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs
@@ -123,15 +123,15 @@ pub fn merge(&self, other: Self) -> Self {
             (None, entries @ Some(_)) => Self { entries },
             (Some(entries), None) => Self { entries: Some(entries.clone()) },
             (Some(a), Some(b)) => {
-                let last_ast_index = a.slice.last().map_or(0, |it| it.id.ast_index() + 1) as u32;
+                let last_ast_index = a.slice.last().map_or(0, |it| it.id.ast_index() + 1);
                 let items = a
                     .slice
                     .iter()
                     .cloned()
                     .chain(b.slice.iter().map(|it| {
                         let mut it = it.clone();
-                        let id = it.id.ast_index() as u32 + last_ast_index;
-                        it.id = AttrId::new(id as usize, it.id.is_inner_attr());
+                        let id = it.id.ast_index() + last_ast_index;
+                        it.id = AttrId::new(id, it.id.is_inner_attr());
                         it
                     }))
                     .collect::>();
@@ -175,24 +175,20 @@ pub struct AttrId {
 // FIXME: This only handles a single level of cfg_attr nesting
 // that is `#[cfg_attr(all(), cfg_attr(all(), cfg(any())))]` breaks again
 impl AttrId {
-    const INNER_ATTR_SET_BIT: usize = 1 << 31;
+    const INNER_ATTR_SET_BIT: u32 = 1 << 31;
 
     pub fn new(id: usize, is_inner: bool) -> Self {
-        Self {
-            id: if is_inner {
-                id | Self::INNER_ATTR_SET_BIT
-            } else {
-                id & !Self::INNER_ATTR_SET_BIT
-            } as u32,
-        }
+        assert!(id <= !Self::INNER_ATTR_SET_BIT as usize);
+        let id = id as u32;
+        Self { id: if is_inner { id | Self::INNER_ATTR_SET_BIT } else { id } }
     }
 
     pub fn ast_index(&self) -> usize {
-        self.id as usize & !Self::INNER_ATTR_SET_BIT
+        (self.id & !Self::INNER_ATTR_SET_BIT) as usize
     }
 
     pub fn is_inner_attr(&self) -> bool {
-        (self.id as usize) & Self::INNER_ATTR_SET_BIT != 0
+        self.id & Self::INNER_ATTR_SET_BIT != 0
     }
 }
 

From 1c53e272142b430e5566bbd069dbf82427c07b8c Mon Sep 17 00:00:00 2001
From: Florian Diebold 
Date: Fri, 9 May 2025 16:15:28 +0200
Subject: [PATCH 33/51] Make diagnostics experimental by default

---
 .../src/handlers/await_outside_of_async.rs      |  1 +
 .../ide-diagnostics/src/handlers/bad_rtn.rs     |  1 +
 .../src/handlers/break_outside_of_loop.rs       |  1 +
 .../src/handlers/elided_lifetimes_in_path.rs    |  2 --
 .../src/handlers/expected_function.rs           |  1 -
 .../src/handlers/generic_args_prohibited.rs     |  1 +
 .../src/handlers/inactive_code.rs               |  1 +
 .../src/handlers/incoherent_impl.rs             |  1 +
 .../src/handlers/incorrect_case.rs              |  1 +
 .../src/handlers/incorrect_generics_len.rs      |  1 -
 .../src/handlers/incorrect_generics_order.rs    |  1 +
 .../src/handlers/invalid_cast.rs                |  3 ++-
 .../src/handlers/invalid_derive_target.rs       |  1 +
 .../ide-diagnostics/src/handlers/macro_error.rs |  2 ++
 .../src/handlers/malformed_derive.rs            |  1 +
 .../src/handlers/mismatched_arg_count.rs        |  2 ++
 .../src/handlers/missing_fields.rs              |  1 +
 .../src/handlers/missing_lifetime.rs            |  1 -
 .../src/handlers/missing_match_arms.rs          |  1 +
 .../src/handlers/missing_unsafe.rs              |  1 +
 .../src/handlers/moved_out_of_ref.rs            |  2 +-
 .../src/handlers/mutability_errors.rs           |  3 ++-
 .../src/handlers/no_such_field.rs               |  2 ++
 .../src/handlers/non_exhaustive_let.rs          |  1 +
 ...renthesized_generic_args_without_fn_trait.rs |  1 +
 .../src/handlers/private_assoc_item.rs          |  1 +
 .../src/handlers/private_field.rs               |  1 +
 .../src/handlers/remove_trailing_return.rs      |  1 +
 .../src/handlers/remove_unnecessary_else.rs     |  1 -
 .../replace_filter_map_next_with_find_map.rs    |  1 +
 .../src/handlers/trait_impl_incorrect_safety.rs |  1 +
 .../handlers/trait_impl_missing_assoc_item.rs   |  1 +
 .../src/handlers/trait_impl_orphan.rs           |  2 --
 .../handlers/trait_impl_redundant_assoc_item.rs |  1 +
 .../src/handlers/type_mismatch.rs               |  4 ++--
 .../ide-diagnostics/src/handlers/typed_hole.rs  |  1 +
 .../src/handlers/undeclared_label.rs            |  1 +
 .../src/handlers/unimplemented_builtin_macro.rs |  1 +
 .../src/handlers/unreachable_label.rs           |  1 +
 .../src/handlers/unresolved_assoc_item.rs       |  1 -
 .../src/handlers/unresolved_extern_crate.rs     |  1 +
 .../src/handlers/unresolved_field.rs            |  1 -
 .../src/handlers/unresolved_ident.rs            |  1 -
 .../src/handlers/unresolved_import.rs           |  1 -
 .../src/handlers/unresolved_macro_call.rs       |  1 -
 .../src/handlers/unresolved_method.rs           |  1 -
 .../src/handlers/unresolved_module.rs           |  1 +
 .../src/handlers/unused_variables.rs            |  3 +--
 .../crates/ide-diagnostics/src/lib.rs           | 17 +++++------------
 49 files changed, 47 insertions(+), 33 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/await_outside_of_async.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/await_outside_of_async.rs
index 92ca7a74184f..2a7b0098edfd 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/await_outside_of_async.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/await_outside_of_async.rs
@@ -14,6 +14,7 @@ pub(crate) fn await_outside_of_async(
         format!("`await` is used inside {}, which is not an `async` context", d.location),
         display_range,
     )
+    .stable()
 }
 
 #[cfg(test)]
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/bad_rtn.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/bad_rtn.rs
index 9ed85f9f208e..ae42a88c313c 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/bad_rtn.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/bad_rtn.rs
@@ -12,6 +12,7 @@ pub(crate) fn bad_rtn(ctx: &DiagnosticsContext<'_>, d: &hir::BadRtn) -> Diagnost
         "return type notation not allowed in this position yet",
         d.rtn.map(Into::into),
     )
+    .stable()
 }
 
 #[cfg(test)]
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs
index c25b0a7bf7d5..cbcaab6c7477 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs
@@ -19,6 +19,7 @@ pub(crate) fn break_outside_of_loop(
         message,
         d.expr.map(|it| it.into()),
     )
+    .stable()
 }
 
 #[cfg(test)]
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/elided_lifetimes_in_path.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/elided_lifetimes_in_path.rs
index 438dd2fdcb6c..b284d9b35103 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/elided_lifetimes_in_path.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/elided_lifetimes_in_path.rs
@@ -15,7 +15,6 @@ pub(crate) fn elided_lifetimes_in_path(
             "implicit elided lifetime not allowed here",
             d.generics_or_segment.map(Into::into),
         )
-        .experimental()
     } else {
         Diagnostic::new_with_syntax_node_ptr(
             ctx,
@@ -23,7 +22,6 @@ pub(crate) fn elided_lifetimes_in_path(
             "hidden lifetime parameters in types are deprecated",
             d.generics_or_segment.map(Into::into),
         )
-        .experimental()
     }
 }
 
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/expected_function.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/expected_function.rs
index a6da0fd9c5e3..7d2ac373dc08 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/expected_function.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/expected_function.rs
@@ -15,7 +15,6 @@ pub(crate) fn expected_function(
         format!("expected function, found {}", d.found.display(ctx.sema.db, ctx.display_target)),
         d.call.map(|it| it.into()),
     )
-    .experimental()
 }
 
 #[cfg(test)]
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/generic_args_prohibited.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/generic_args_prohibited.rs
index b617c0949839..9ae6f013c70d 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/generic_args_prohibited.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/generic_args_prohibited.rs
@@ -21,6 +21,7 @@ pub(crate) fn generic_args_prohibited(
         describe_reason(d.reason),
         d.args.map(Into::into),
     )
+    .stable()
     .with_fixes(fixes(ctx, d))
 }
 
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/inactive_code.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/inactive_code.rs
index 47e1c84fecd0..8611ef653b02 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/inactive_code.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/inactive_code.rs
@@ -33,6 +33,7 @@ pub(crate) fn inactive_code(
         message,
         ctx.sema.diagnostics_display_range(d.node),
     )
+    .stable()
     .with_unused(true);
     Some(res)
 }
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incoherent_impl.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incoherent_impl.rs
index 0b9a2ec9db3d..a0c364b00108 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incoherent_impl.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incoherent_impl.rs
@@ -19,6 +19,7 @@ pub(crate) fn incoherent_impl(ctx: &DiagnosticsContext<'_>, d: &hir::IncoherentI
         "cannot define inherent `impl` for foreign type".to_owned(),
         display_range,
     )
+    .stable()
 }
 
 #[cfg(test)]
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs
index 289a07657325..38f10c778d69 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs
@@ -29,6 +29,7 @@ pub(crate) fn incorrect_case(ctx: &DiagnosticsContext<'_>, d: &hir::IncorrectCas
         ),
         InFile::new(d.file, d.ident.into()),
     )
+    .stable()
     .with_fixes(fixes(ctx, d))
 }
 
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_generics_len.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_generics_len.rs
index 17c7f75880c9..06f357594200 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_generics_len.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_generics_len.rs
@@ -28,7 +28,6 @@ pub(crate) fn incorrect_generics_len(
         message,
         d.generics_or_segment.map(Into::into),
     )
-    .experimental()
 }
 
 #[cfg(test)]
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_generics_order.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_generics_order.rs
index 84496df2d7cf..b71586d6be0b 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_generics_order.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_generics_order.rs
@@ -28,6 +28,7 @@ pub(crate) fn incorrect_generics_order(
         message,
         d.provided_arg.map(Into::into),
     )
+    .stable()
 }
 
 #[cfg(test)]
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs
index d72b21099ce3..7a6e98fe1b54 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs
@@ -100,7 +100,7 @@ pub(crate) fn invalid_cast(ctx: &DiagnosticsContext<'_>, d: &hir::InvalidCast) -
         //     "cannot cast to a pointer of an unknown kind".to_owned(),
         // ),
     };
-    Diagnostic::new(code, message, display_range)
+    Diagnostic::new(code, message, display_range).stable()
 }
 
 // Diagnostic: cast-to-unsized
@@ -113,6 +113,7 @@ pub(crate) fn cast_to_unsized(ctx: &DiagnosticsContext<'_>, d: &hir::CastToUnsiz
         format_ty!(ctx, "cast to unsized type: `{}`", d.cast_ty),
         display_range,
     )
+    .stable()
 }
 
 #[cfg(test)]
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_derive_target.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_derive_target.rs
index ab0f5139f107..8b708f229d00 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_derive_target.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_derive_target.rs
@@ -15,6 +15,7 @@ pub(crate) fn invalid_derive_target(
         "`derive` may only be applied to `struct`s, `enum`s and `union`s",
         display_range,
     )
+    .stable()
 }
 
 #[cfg(test)]
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs
index a2648a1995d7..546512a6cf92 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs
@@ -19,6 +19,7 @@ pub(crate) fn macro_error(ctx: &DiagnosticsContext<'_>, d: &hir::MacroError) ->
         d.message.clone(),
         display_range,
     )
+    .stable()
 }
 
 // Diagnostic: macro-def-error
@@ -33,6 +34,7 @@ pub(crate) fn macro_def_error(ctx: &DiagnosticsContext<'_>, d: &hir::MacroDefErr
         d.message.clone(),
         display_range,
     )
+    .stable()
 }
 
 #[cfg(test)]
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/malformed_derive.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/malformed_derive.rs
index 0e47fff6f93b..701b30b9b593 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/malformed_derive.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/malformed_derive.rs
@@ -14,6 +14,7 @@ pub(crate) fn malformed_derive(
         "malformed derive input, derive attributes are of the form `#[derive(Derive1, Derive2, ...)]`",
         display_range,
     )
+    .stable()
 }
 
 #[cfg(test)]
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs
index 63fd9b4e3f06..25c1e633ba3b 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs
@@ -26,6 +26,7 @@ pub(crate) fn mismatched_tuple_struct_pat_arg_count(
         message,
         invalid_args_range(ctx, d.expr_or_pat, d.expected, d.found),
     )
+    .stable()
 }
 
 // Diagnostic: mismatched-arg-count
@@ -42,6 +43,7 @@ pub(crate) fn mismatched_arg_count(
         message,
         invalid_args_range(ctx, d.call_expr, d.expected, d.found),
     )
+    .stable()
 }
 
 fn invalid_args_range(
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs
index a354d123f5ab..2b76efb1965b 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs
@@ -47,6 +47,7 @@ pub(crate) fn missing_fields(ctx: &DiagnosticsContext<'_>, d: &hir::MissingField
     );
 
     Diagnostic::new_with_syntax_node_ptr(ctx, DiagnosticCode::RustcHardError("E0063"), message, ptr)
+        .stable()
         .with_fixes(fixes(ctx, d))
 }
 
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_lifetime.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_lifetime.rs
index 8cdbb6384ff5..76b30745a04d 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_lifetime.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_lifetime.rs
@@ -13,7 +13,6 @@ pub(crate) fn missing_lifetime(
         "missing lifetime specifier",
         d.generics_or_segment.map(Into::into),
     )
-    .experimental()
 }
 
 #[cfg(test)]
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs
index d3d3c3aa38dc..1fc96b78eda2 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs
@@ -13,6 +13,7 @@ pub(crate) fn missing_match_arms(
         format!("missing match arm: {}", d.uncovered_patterns),
         d.scrutinee_expr.map(Into::into),
     )
+    .stable()
 }
 
 #[cfg(test)]
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs
index 3c36b455ca9d..ff041e183b14 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs
@@ -23,6 +23,7 @@ pub(crate) fn missing_unsafe(ctx: &DiagnosticsContext<'_>, d: &hir::MissingUnsaf
         format!("{operation} is unsafe and requires an unsafe function or block"),
         d.node.map(|it| it.into()),
     )
+    .stable()
     .with_fixes(fixes(ctx, d))
 }
 
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs
index 780271361d72..01cf5e8fa522 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs
@@ -11,7 +11,7 @@ pub(crate) fn moved_out_of_ref(ctx: &DiagnosticsContext<'_>, d: &hir::MovedOutOf
         format!("cannot move `{}` out of reference", d.ty.display(ctx.sema.db, ctx.display_target)),
         d.span,
     )
-    .experimental() // spans are broken, and I'm not sure how precise we can detect copy types
+    // spans are broken, and I'm not sure how precise we can detect copy types
 }
 
 #[cfg(test)]
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs
index 5d25f2c6a90f..8831efa31172 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs
@@ -55,6 +55,7 @@ pub(crate) fn need_mut(ctx: &DiagnosticsContext<'_>, d: &hir::NeedMut) -> Option
             ),
             span,
         )
+        .stable()
         .with_fixes(fixes),
     )
 }
@@ -94,7 +95,7 @@ pub(crate) fn unused_mut(ctx: &DiagnosticsContext<'_>, d: &hir::UnusedMut) -> Op
             "variable does not need to be mutable",
             ast,
         )
-        .experimental() // Not supporting `#[allow(unused_mut)]` in proc macros leads to false positive.
+        // Not supporting `#[allow(unused_mut)]` in proc macros leads to false positive, hence not stable.
         .with_fixes(fixes),
     )
 }
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs
index fa3347aa12e6..84fb467a5ce1 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs
@@ -22,6 +22,7 @@ pub(crate) fn no_such_field(ctx: &DiagnosticsContext<'_>, d: &hir::NoSuchField)
             "field is private",
             node,
         )
+        .stable()
     } else {
         Diagnostic::new_with_syntax_node_ptr(
             ctx,
@@ -32,6 +33,7 @@ pub(crate) fn no_such_field(ctx: &DiagnosticsContext<'_>, d: &hir::NoSuchField)
             "no such field",
             node,
         )
+        .stable()
         .with_fixes(fixes(ctx, d))
     }
 }
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/non_exhaustive_let.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/non_exhaustive_let.rs
index ff1eeb0516a9..35cefd239752 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/non_exhaustive_let.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/non_exhaustive_let.rs
@@ -14,6 +14,7 @@ pub(crate) fn non_exhaustive_let(
         format!("non-exhaustive pattern: {}", d.uncovered_patterns),
         d.pat.map(Into::into),
     )
+    .stable()
 }
 
 #[cfg(test)]
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/parenthesized_generic_args_without_fn_trait.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/parenthesized_generic_args_without_fn_trait.rs
index ccf517234183..68f2b1965702 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/parenthesized_generic_args_without_fn_trait.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/parenthesized_generic_args_without_fn_trait.rs
@@ -14,6 +14,7 @@ pub(crate) fn parenthesized_generic_args_without_fn_trait(
         "parenthesized type parameters may only be used with a `Fn` trait",
         d.args.map(Into::into),
     )
+    .stable()
 }
 
 #[cfg(test)]
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_assoc_item.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_assoc_item.rs
index fe32c590492d..6d33ae0cf9be 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_assoc_item.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_assoc_item.rs
@@ -28,6 +28,7 @@ pub(crate) fn private_assoc_item(
         ),
         d.expr_or_pat.map(Into::into),
     )
+    .stable()
 }
 
 #[cfg(test)]
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_field.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_field.rs
index 237a9b87871c..5b4273a5a627 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_field.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_field.rs
@@ -15,6 +15,7 @@ pub(crate) fn private_field(ctx: &DiagnosticsContext<'_>, d: &hir::PrivateField)
         ),
         d.expr.map(|it| it.into()),
     )
+    .stable()
 }
 
 #[cfg(test)]
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/remove_trailing_return.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/remove_trailing_return.rs
index 6b7864500261..dec7be8b7427 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/remove_trailing_return.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/remove_trailing_return.rs
@@ -31,6 +31,7 @@ pub(crate) fn remove_trailing_return(
             "replace return ; with ",
             display_range,
         )
+        .stable()
         .with_fixes(fixes(ctx, d)),
     )
 }
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/remove_unnecessary_else.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/remove_unnecessary_else.rs
index 8d717b9093b9..7dc5b5b45e5f 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/remove_unnecessary_else.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/remove_unnecessary_else.rs
@@ -36,7 +36,6 @@ pub(crate) fn remove_unnecessary_else(
             "remove unnecessary else block",
             display_range,
         )
-        .experimental()
         .with_fixes(fixes(ctx, d)),
     )
 }
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs
index 6b335c52de75..37ce5f583f93 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs
@@ -21,6 +21,7 @@ pub(crate) fn replace_filter_map_next_with_find_map(
         "replace filter_map(..).next() with find_map(..)",
         InFile::new(d.file, d.next_expr.into()),
     )
+    .stable()
     .with_fixes(fixes(ctx, d))
 }
 
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_incorrect_safety.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_incorrect_safety.rs
index 19ee1caa3e6a..dd142db8590f 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_incorrect_safety.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_incorrect_safety.rs
@@ -33,6 +33,7 @@ pub(crate) fn trait_impl_incorrect_safety(
             },
         ),
     )
+    .stable()
 }
 
 #[cfg(test)]
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_missing_assoc_item.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_missing_assoc_item.rs
index 2d7d78f5d7bd..fa7ba90a756b 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_missing_assoc_item.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_missing_assoc_item.rs
@@ -29,6 +29,7 @@ pub(crate) fn trait_impl_missing_assoc_item(
             &|impl_| impl_.trait_().map(|t| t.syntax().text_range()),
         ),
     )
+    .stable()
 }
 
 #[cfg(test)]
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_orphan.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_orphan.rs
index 35dc9b0fac8a..96911d4781b8 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_orphan.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_orphan.rs
@@ -16,8 +16,6 @@ pub(crate) fn trait_impl_orphan(
             .to_owned(),
         InFile::new(d.file_id, d.impl_.into()),
     )
-    // Not yet checked for false positives
-    .experimental()
 }
 
 #[cfg(test)]
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs
index d5c4bcf768ad..4327b12dce70 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs
@@ -61,6 +61,7 @@ pub(crate) fn trait_impl_redundant_assoc_item(
         format!("{redundant_item_name} is not a member of trait `{trait_name}`"),
         ide_db::FileRange { file_id: file_id.file_id(ctx.sema.db), range },
     )
+    .stable()
     .with_fixes(quickfix_for_redundant_assoc_item(
         ctx,
         d,
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs
index 500c5de791dc..5253734867e8 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs
@@ -53,8 +53,8 @@ pub(crate) fn type_mismatch(ctx: &DiagnosticsContext<'_>, d: &hir::TypeMismatch)
         display_range,
     )
     .with_fixes(fixes(ctx, d));
-    if diag.fixes.is_none() {
-        diag.experimental = true;
+    if diag.fixes.is_some() {
+        diag.experimental = false;
     }
     diag
 }
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/typed_hole.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/typed_hole.rs
index a933f1b42611..1915a88dd002 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/typed_hole.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/typed_hole.rs
@@ -37,6 +37,7 @@ pub(crate) fn typed_hole(ctx: &DiagnosticsContext<'_>, d: &hir::TypedHole) -> Di
     };
 
     Diagnostic::new(DiagnosticCode::RustcHardError("typed-hole"), message, display_range)
+        .stable()
         .with_fixes(fixes)
 }
 
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/undeclared_label.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/undeclared_label.rs
index d16bfb800240..f81d34377da4 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/undeclared_label.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/undeclared_label.rs
@@ -12,6 +12,7 @@ pub(crate) fn undeclared_label(
         format!("use of undeclared label `{}`", name.display(ctx.sema.db, ctx.edition)),
         d.node.map(|it| it.into()),
     )
+    .stable()
 }
 
 #[cfg(test)]
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unimplemented_builtin_macro.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unimplemented_builtin_macro.rs
index 06f176f86f4e..5627393f3181 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unimplemented_builtin_macro.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unimplemented_builtin_macro.rs
@@ -13,4 +13,5 @@ pub(crate) fn unimplemented_builtin_macro(
         "unimplemented built-in macro".to_owned(),
         d.node,
     )
+    .stable()
 }
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unreachable_label.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unreachable_label.rs
index bdff2417ca11..0c9e0d6ce440 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unreachable_label.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unreachable_label.rs
@@ -12,6 +12,7 @@ pub(crate) fn unreachable_label(
         format!("use of unreachable label `{}`", name.display(ctx.sema.db, ctx.edition)),
         d.node.map(|it| it.into()),
     )
+    .stable()
 }
 
 #[cfg(test)]
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_assoc_item.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_assoc_item.rs
index 614057ab52bf..4ae528bf9f28 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_assoc_item.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_assoc_item.rs
@@ -13,7 +13,6 @@ pub(crate) fn unresolved_assoc_item(
         "no such associated item",
         d.expr_or_pat.map(Into::into),
     )
-    .experimental()
 }
 
 #[cfg(test)]
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_extern_crate.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_extern_crate.rs
index 4cd73d46d5f1..7c3eacf7e3ad 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_extern_crate.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_extern_crate.rs
@@ -13,6 +13,7 @@ pub(crate) fn unresolved_extern_crate(
         "unresolved extern crate",
         d.decl.map(|it| it.into()),
     )
+    .stable()
 }
 
 #[cfg(test)]
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_field.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_field.rs
index a4f4813cf5b0..0649c97f8205 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_field.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_field.rs
@@ -52,7 +52,6 @@ pub(crate) fn unresolved_field(
         }),
     )
     .with_fixes(fixes(ctx, d))
-    .experimental()
 }
 
 fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedField) -> Option> {
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_ident.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_ident.rs
index 4f64dabeb52f..801023dabd96 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_ident.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_ident.rs
@@ -13,7 +13,6 @@ pub(crate) fn unresolved_ident(
         range.range = in_node_range + range.range.start();
     }
     Diagnostic::new(DiagnosticCode::RustcHardError("E0425"), "no such value in this scope", range)
-        .experimental()
 }
 
 #[cfg(test)]
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_import.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_import.rs
index 67c7e76a3bc1..0da535d11b4f 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_import.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_import.rs
@@ -18,7 +18,6 @@ pub(crate) fn unresolved_import(
     // - `cfg_if!`-generated code in libstd (we don't load the sysroot correctly)
     // - `core::arch` (we don't handle `#[path = "../"]` correctly)
     // - proc macros and/or proc macro generated code
-    .experimental()
 }
 
 #[cfg(test)]
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs
index 0d1c97750627..a87b8c42ac1d 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs
@@ -16,7 +16,6 @@ pub(crate) fn unresolved_macro_call(
         format!("unresolved macro `{}{bang}`", d.path.display(ctx.sema.db, ctx.edition)),
         display_range,
     )
-    .experimental()
 }
 
 #[cfg(test)]
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs
index 7f07009dc561..00c2a8c4c468 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs
@@ -47,7 +47,6 @@ pub(crate) fn unresolved_method(
         }),
     )
     .with_fixes(fixes(ctx, d))
-    .experimental()
 }
 
 fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedMethodCall) -> Option> {
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_module.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_module.rs
index 599cabe3e4f2..1a409d7e76a2 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_module.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_module.rs
@@ -28,6 +28,7 @@ pub(crate) fn unresolved_module(
         },
         d.decl.map(|it| it.into()),
     )
+    .stable()
     .with_fixes(fixes(ctx, d))
 }
 
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unused_variables.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unused_variables.rs
index 77b1075ea532..e6bbff05f7e8 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unused_variables.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unused_variables.rs
@@ -50,8 +50,7 @@ pub(crate) fn unused_variables(
                 ast.file_id.is_macro(),
                 ctx.edition,
             )
-        }))
-        .experimental(),
+        })),
     )
 }
 
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs
index 607721d611d7..2af14ca949bf 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs
@@ -182,7 +182,7 @@ fn new(
                 DiagnosticCode::Ra(_, s) => s,
             },
             unused: false,
-            experimental: false,
+            experimental: true,
             fixes: None,
             main_node: None,
         }
@@ -198,8 +198,8 @@ fn new_with_syntax_node_ptr(
             .with_main_node(node)
     }
 
-    fn experimental(mut self) -> Diagnostic {
-        self.experimental = true;
+    fn stable(mut self) -> Diagnostic {
+        self.experimental = false;
         self
     }
 
@@ -424,14 +424,11 @@ pub fn semantic_diagnostics(
             AnyDiagnostic::MacroExpansionParseError(d) => {
                 // FIXME: Point to the correct error span here, not just the macro-call name
                 res.extend(d.errors.iter().take(16).map(|err| {
-                    {
                         Diagnostic::new(
                             DiagnosticCode::SyntaxError,
                             format!("Syntax Error in Expansion: {err}"),
                             ctx.resolve_precise_location(&d.node.clone(), d.precise_location),
                         )
-                    }
-                    .experimental()
                 }));
                 continue;
             },
@@ -485,12 +482,8 @@ pub fn semantic_diagnostics(
                 Some(it) => it,
                 None => continue,
             },
-            AnyDiagnostic::GenericArgsProhibited(d) => {
-                handlers::generic_args_prohibited::generic_args_prohibited(&ctx, &d)
-            }
-            AnyDiagnostic::ParenthesizedGenericArgsWithoutFnTrait(d) => {
-                handlers::parenthesized_generic_args_without_fn_trait::parenthesized_generic_args_without_fn_trait(&ctx, &d)
-            }
+            AnyDiagnostic::GenericArgsProhibited(d) => handlers::generic_args_prohibited::generic_args_prohibited(&ctx, &d),
+            AnyDiagnostic::ParenthesizedGenericArgsWithoutFnTrait(d) => handlers::parenthesized_generic_args_without_fn_trait::parenthesized_generic_args_without_fn_trait(&ctx, &d),
             AnyDiagnostic::BadRtn(d) => handlers::bad_rtn::bad_rtn(&ctx, &d),
             AnyDiagnostic::IncorrectGenericsLen(d) => handlers::incorrect_generics_len::incorrect_generics_len(&ctx, &d),
             AnyDiagnostic::IncorrectGenericsOrder(d) => handlers::incorrect_generics_order::incorrect_generics_order(&ctx, &d),

From 0092abd9d7a99432b4d54f91f75b7f17c1543fcc Mon Sep 17 00:00:00 2001
From: gohome001 <3156514693@qq.com>
Date: Sat, 10 May 2025 13:16:34 +0800
Subject: [PATCH 34/51] minor: code review tweak

---
 .../crates/ide/src/highlight_related.rs       | 32 +++++--------------
 1 file changed, 8 insertions(+), 24 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs b/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs
index 90a7c62d71bb..fb8dbcfc7354 100644
--- a/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs
@@ -89,7 +89,7 @@ pub(crate) fn highlight_related(
         T![break] | T![loop] | T![while] | T![continue] if config.break_points => {
             highlight_break_points(sema, token).remove(&file_id)
         }
-        T![unsafe] if token.parent_ancestors().find_map(ast::BlockExpr::cast).is_some() => {
+        T![unsafe] if token.parent().and_then(ast::BlockExpr::cast).is_some() => {
             highlight_unsafe_points(sema, token).remove(&file_id)
         }
         T![|] if config.closure_captures => {
@@ -715,7 +715,7 @@ pub(crate) fn highlight_unsafe_points(
 ) -> FxHashMap> {
     fn hl(
         sema: &Semantics<'_, RootDatabase>,
-        unsafe_token: Option,
+        unsafe_token: &SyntaxToken,
         block_expr: Option,
     ) -> Option>> {
         let mut highlights: FxHashMap> = FxHashMap::default();
@@ -728,27 +728,15 @@ fn hl(
         };
 
         // highlight unsafe keyword itself
-        let unsafe_token = unsafe_token?;
         let unsafe_token_file_id = sema.hir_file_for(&unsafe_token.parent()?);
         push_to_highlights(unsafe_token_file_id, Some(unsafe_token.text_range()));
 
+        // highlight unsafe operations
         if let Some(block) = block_expr {
-            if let Some(node) = block.syntax().ancestors().find(|n| ast::Fn::can_cast(n.kind())) {
-                if let Some(function) = ast::Fn::cast(node) {
-                    // highlight unsafe keyword of the function
-                    if let Some(unsafe_token) = function.unsafe_token() {
-                        push_to_highlights(unsafe_token_file_id, Some(unsafe_token.text_range()));
-                    }
-                    // highlight unsafe operations
-                    if let Some(f) = sema.to_def(&function) {
-                        let unsafe_ops = sema.get_unsafe_ops(f.into());
-                        for unsafe_op in unsafe_ops {
-                            push_to_highlights(
-                                unsafe_op.file_id,
-                                Some(unsafe_op.value.text_range()),
-                            );
-                        }
-                    }
+            if let Some(body) = sema.body_for(InFile::new(unsafe_token_file_id, block.syntax())) {
+                let unsafe_ops = sema.get_unsafe_ops(body);
+                for unsafe_op in unsafe_ops {
+                    push_to_highlights(unsafe_op.file_id, Some(unsafe_op.value.text_range()));
                 }
             }
         }
@@ -756,11 +744,7 @@ fn hl(
         Some(highlights)
     }
 
-    let Some(block_expr) = token.parent().and_then(ast::BlockExpr::cast) else {
-        return FxHashMap::default();
-    };
-
-    hl(sema, Some(token), Some(block_expr)).unwrap_or_default()
+    hl(sema, &token, token.parent().and_then(ast::BlockExpr::cast)).unwrap_or_default()
 }
 
 #[cfg(test)]

From 6274d461cf3005ff1e22afa77c1a96090f63b40f Mon Sep 17 00:00:00 2001
From: Hayashi Mikihiro <34ttrweoewiwe28@gmail.com>
Date: Wed, 14 May 2025 09:59:20 +0900
Subject: [PATCH 35/51] fix: Removing all unused imports removes used imports
 for imports used for Derive macros

Signed-off-by: Hayashi Mikihiro <34ttrweoewiwe28@gmail.com>
---
 src/tools/rust-analyzer/crates/hir/src/lib.rs |   3 +-
 .../rust-analyzer/crates/hir/src/semantics.rs |  24 ++
 .../crates/hir/src/source_analyzer.rs         |  61 ++++-
 .../src/handlers/remove_unused_imports.rs     | 213 +++++++++++++++---
 4 files changed, 256 insertions(+), 45 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs
index c5d37cafd989..3a91050d15fa 100644
--- a/src/tools/rust-analyzer/crates/hir/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs
@@ -97,7 +97,8 @@
     diagnostics::*,
     has_source::HasSource,
     semantics::{
-        PathResolution, Semantics, SemanticsImpl, SemanticsScope, TypeInfo, VisibleTraits,
+        PathResolution, PathResolutionPerNs, Semantics, SemanticsImpl, SemanticsScope, TypeInfo,
+        VisibleTraits,
     },
 };
 
diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs
index 327fe8a75839..7ee72614e220 100644
--- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs
@@ -103,6 +103,26 @@ pub(crate) fn in_type_ns(&self) -> Option {
     }
 }
 
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+pub struct PathResolutionPerNs {
+    pub type_ns: Option,
+    pub value_ns: Option,
+    pub macro_ns: Option,
+}
+
+impl PathResolutionPerNs {
+    pub fn new(
+        type_ns: Option,
+        value_ns: Option,
+        macro_ns: Option,
+    ) -> Self {
+        PathResolutionPerNs { type_ns, value_ns, macro_ns }
+    }
+    pub fn take_path(&self) -> Option {
+        self.type_ns.or(self.value_ns).or(self.macro_ns)
+    }
+}
+
 #[derive(Debug)]
 pub struct TypeInfo {
     /// The original type of the expression or pattern.
@@ -1606,6 +1626,10 @@ pub fn resolve_path(&self, path: &ast::Path) -> Option {
         self.resolve_path_with_subst(path).map(|(it, _)| it)
     }
 
+    pub fn resolve_path_per_ns(&self, path: &ast::Path) -> Option {
+        self.analyze(path.syntax())?.resolve_hir_path_per_ns(self.db, path)
+    }
+
     pub fn resolve_path_with_subst(
         &self,
         path: &ast::Path,
diff --git a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs
index 42da7b942c73..610770d0f31e 100644
--- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs
@@ -10,7 +10,9 @@
 use crate::{
     Adt, AssocItem, BindingMode, BuiltinAttr, BuiltinType, Callable, Const, DeriveHelper, Field,
     Function, GenericSubstitution, Local, Macro, ModuleDef, Static, Struct, ToolModule, Trait,
-    TraitAlias, TupleField, Type, TypeAlias, Variant, db::HirDatabase, semantics::PathResolution,
+    TraitAlias, TupleField, Type, TypeAlias, Variant,
+    db::HirDatabase,
+    semantics::{PathResolution, PathResolutionPerNs},
 };
 use either::Either;
 use hir_def::{
@@ -1159,7 +1161,9 @@ pub(crate) fn resolve_path(
                 prefer_value_ns,
                 name_hygiene(db, InFile::new(self.file_id, path.syntax())),
                 Some(&store),
-            )?;
+                false,
+            )
+            .take_path()?;
             let subst = (|| {
                 let parent = parent()?;
                 let ty = if let Some(expr) = ast::Expr::cast(parent.clone()) {
@@ -1209,6 +1213,26 @@ pub(crate) fn resolve_path(
         }
     }
 
+    pub(crate) fn resolve_hir_path_per_ns(
+        &self,
+        db: &dyn HirDatabase,
+        path: &ast::Path,
+    ) -> Option {
+        let mut collector = ExprCollector::new(db, self.resolver.module(), self.file_id);
+        let hir_path =
+            collector.lower_path(path.clone(), &mut ExprCollector::impl_trait_error_allocator)?;
+        let store = collector.store.finish();
+        Some(resolve_hir_path_(
+            db,
+            &self.resolver,
+            &hir_path,
+            false,
+            name_hygiene(db, InFile::new(self.file_id, path.syntax())),
+            Some(&store),
+            true,
+        ))
+    }
+
     pub(crate) fn record_literal_missing_fields(
         &self,
         db: &'db dyn HirDatabase,
@@ -1532,7 +1556,7 @@ pub(crate) fn resolve_hir_path(
     hygiene: HygieneId,
     store: Option<&ExpressionStore>,
 ) -> Option {
-    resolve_hir_path_(db, resolver, path, false, hygiene, store)
+    resolve_hir_path_(db, resolver, path, false, hygiene, store, false).take_path()
 }
 
 #[inline]
@@ -1554,7 +1578,8 @@ fn resolve_hir_path_(
     prefer_value_ns: bool,
     hygiene: HygieneId,
     store: Option<&ExpressionStore>,
-) -> Option {
+    resolve_per_ns: bool,
+) -> PathResolutionPerNs {
     let types = || {
         let (ty, unresolved) = match path.type_anchor() {
             Some(type_ref) => resolver.generic_def().and_then(|def| {
@@ -1635,9 +1660,31 @@ fn resolve_hir_path_(
             .map(|(def, _)| PathResolution::Def(ModuleDef::Macro(def.into())))
     };
 
-    if prefer_value_ns { values().or_else(types) } else { types().or_else(values) }
-        .or_else(items)
-        .or_else(macros)
+    if resolve_per_ns {
+        PathResolutionPerNs {
+            type_ns: types().or_else(items),
+            value_ns: values(),
+            macro_ns: macros(),
+        }
+    } else {
+        let res = if prefer_value_ns {
+            values()
+                .map(|value_ns| PathResolutionPerNs::new(None, Some(value_ns), None))
+                .unwrap_or_else(|| PathResolutionPerNs::new(types(), None, None))
+        } else {
+            types()
+                .map(|type_ns| PathResolutionPerNs::new(Some(type_ns), None, None))
+                .unwrap_or_else(|| PathResolutionPerNs::new(None, values(), None))
+        };
+
+        if res.take_path().is_some() {
+            res
+        } else if let Some(type_ns) = items() {
+            PathResolutionPerNs::new(Some(type_ns), None, None)
+        } else {
+            PathResolutionPerNs::new(None, None, macros())
+        }
+    }
 }
 
 fn resolve_hir_value_path(
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_imports.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_imports.rs
index 1baf814ca682..fb96882ed428 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_imports.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_imports.rs
@@ -1,6 +1,9 @@
 use std::collections::hash_map::Entry;
 
-use hir::{FileRange, InFile, InRealFile, Module, ModuleSource};
+use hir::{
+    FileRange, InFile, InRealFile, Module, ModuleDef, ModuleSource, PathResolution,
+    PathResolutionPerNs,
+};
 use ide_db::text_edit::TextRange;
 use ide_db::{
     FxHashMap, RootDatabase,
@@ -77,49 +80,52 @@ pub(crate) fn remove_unused_imports(acc: &mut Assists, ctx: &AssistContext<'_>)
             };
 
             // Get the actual definition associated with this use item.
-            let res = match ctx.sema.resolve_path(&path) {
-                Some(x) => x,
-                None => {
+            let res = match ctx.sema.resolve_path_per_ns(&path) {
+                Some(x) if x.take_path().is_some() => x,
+                Some(_) | None => {
                     return None;
                 }
             };
+            match res {
+                PathResolutionPerNs { type_ns: Some(type_ns), .. } if u.star_token().is_some() => {
+                    // Check if any of the children of this module are used
+                    let def_mod = match type_ns {
+                        PathResolution::Def(ModuleDef::Module(module)) => module,
+                        _ => return None,
+                    };
 
-            let def = match res {
-                hir::PathResolution::Def(d) => Definition::from(d),
-                _ => return None,
-            };
-
-            if u.star_token().is_some() {
-                // Check if any of the children of this module are used
-                let def_mod = match def {
-                    Definition::Module(module) => module,
-                    _ => return None,
-                };
-
-                if !def_mod
-                    .scope(ctx.db(), Some(use_module))
-                    .iter()
-                    .filter_map(|(_, x)| match x {
-                        hir::ScopeDef::ModuleDef(d) => Some(Definition::from(*d)),
-                        _ => None,
-                    })
-                    .any(|d| used_once_in_scope(ctx, d, u.rename(), scope))
-                {
-                    return Some(u);
+                    if !def_mod
+                        .scope(ctx.db(), Some(use_module))
+                        .iter()
+                        .filter_map(|(_, x)| match x {
+                            hir::ScopeDef::ModuleDef(d) => Some(Definition::from(*d)),
+                            _ => None,
+                        })
+                        .any(|d| used_once_in_scope(ctx, d, u.rename(), scope))
+                    {
+                        Some(u)
+                    } else {
+                        None
+                    }
                 }
-            } else if let Definition::Trait(ref t) = def {
-                // If the trait or any item is used.
-                if !std::iter::once((def, u.rename()))
-                    .chain(t.items(ctx.db()).into_iter().map(|item| (item.into(), None)))
-                    .any(|(d, rename)| used_once_in_scope(ctx, d, rename, scope))
-                {
-                    return Some(u);
+                PathResolutionPerNs {
+                    type_ns: Some(PathResolution::Def(ModuleDef::Trait(ref t))),
+                    value_ns,
+                    macro_ns,
+                } => {
+                    // If the trait or any item is used.
+                    if is_trait_unused_in_scope(ctx, &u, scope, t) {
+                        let path = [value_ns, macro_ns];
+                        is_path_unused_in_scope(ctx, &u, scope, &path).then_some(u)
+                    } else {
+                        None
+                    }
+                }
+                PathResolutionPerNs { type_ns, value_ns, macro_ns } => {
+                    let path = [type_ns, value_ns, macro_ns];
+                    is_path_unused_in_scope(ctx, &u, scope, &path).then_some(u)
                 }
-            } else if !used_once_in_scope(ctx, def, u.rename(), scope) {
-                return Some(u);
             }
-
-            None
         })
         .peekable();
 
@@ -141,6 +147,33 @@ pub(crate) fn remove_unused_imports(acc: &mut Assists, ctx: &AssistContext<'_>)
     }
 }
 
+fn is_path_unused_in_scope(
+    ctx: &AssistContext<'_>,
+    u: &ast::UseTree,
+    scope: &mut Vec,
+    path: &[Option],
+) -> bool {
+    !path
+        .iter()
+        .filter_map(|path| *path)
+        .filter_map(|res| match res {
+            PathResolution::Def(d) => Some(Definition::from(d)),
+            _ => None,
+        })
+        .any(|def| used_once_in_scope(ctx, def, u.rename(), scope))
+}
+
+fn is_trait_unused_in_scope(
+    ctx: &AssistContext<'_>,
+    u: &ast::UseTree,
+    scope: &mut Vec,
+    t: &hir::Trait,
+) -> bool {
+    !std::iter::once((Definition::Trait(*t), u.rename()))
+        .chain(t.items(ctx.db()).into_iter().map(|item| (item.into(), None)))
+        .any(|(d, rename)| used_once_in_scope(ctx, d, rename, scope))
+}
+
 fn used_once_in_scope(
     ctx: &AssistContext<'_>,
     def: Definition,
@@ -1009,6 +1042,112 @@ fn test(_: Bar) {
     let a = ();
     a.quxx();
 }
+"#,
+        );
+    }
+
+    #[test]
+    fn test_unused_macro() {
+        check_assist(
+            remove_unused_imports,
+            r#"
+//- /foo.rs crate:foo
+#[macro_export]
+macro_rules! m { () => {} }
+
+//- /main.rs crate:main deps:foo
+use foo::m;$0
+fn main() {}
+"#,
+            r#"
+fn main() {}
+"#,
+        );
+
+        check_assist_not_applicable(
+            remove_unused_imports,
+            r#"
+//- /foo.rs crate:foo
+#[macro_export]
+macro_rules! m { () => {} }
+
+//- /main.rs crate:main deps:foo
+use foo::m;$0
+fn main() {
+    m!();
+}
+"#,
+        );
+
+        check_assist_not_applicable(
+            remove_unused_imports,
+            r#"
+//- /foo.rs crate:foo
+#[macro_export]
+macro_rules! m { () => {} }
+
+//- /bar.rs crate:bar deps:foo
+pub use foo::m;
+fn m() {}
+
+
+//- /main.rs crate:main deps:bar
+use bar::m;$0
+fn main() {
+    m!();
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn test_conflict_derive_macro() {
+        check_assist_not_applicable(
+            remove_unused_imports,
+            r#"
+//- proc_macros: derive_identity
+//- minicore: derive
+//- /bar.rs crate:bar
+pub use proc_macros::DeriveIdentity;
+pub trait DeriveIdentity {}
+
+//- /main.rs crate:main deps:bar
+$0use bar::DeriveIdentity;$0
+#[derive(DeriveIdentity)]
+struct S;
+"#,
+        );
+
+        check_assist_not_applicable(
+            remove_unused_imports,
+            r#"
+//- proc_macros: derive_identity
+//- minicore: derive
+//- /bar.rs crate:bar
+pub use proc_macros::DeriveIdentity;
+pub fn DeriveIdentity() {}
+
+//- /main.rs crate:main deps:bar
+$0use bar::DeriveIdentity;$0
+#[derive(DeriveIdentity)]
+struct S;
+"#,
+        );
+
+        check_assist_not_applicable(
+            remove_unused_imports,
+            r#"
+//- proc_macros: derive_identity
+//- minicore: derive
+//- /bar.rs crate:bar
+pub use proc_macros::DeriveIdentity;
+pub fn DeriveIdentity() {}
+
+//- /main.rs crate:main deps:bar
+$0use bar::DeriveIdentity;$0
+fn main() {
+    DeriveIdentity();
+}
 "#,
         );
     }

From de51196bb183ff6bb7ebfdb3bc8093876d5078b8 Mon Sep 17 00:00:00 2001
From: Chayim Refael Friedman 
Date: Wed, 14 May 2025 21:38:40 +0300
Subject: [PATCH 36/51] Don't allow duplicate crates in the all_crates list

For some reason we had them in some projects, I'm not sure why. But this caused cache priming to appear stuck - because it uses a set of crate IDs for the actual work, but for the number of crates to index it just uses `db.all_crates().len()`.
---
 src/tools/rust-analyzer/Cargo.lock             |  1 +
 .../rust-analyzer/crates/base-db/Cargo.toml    |  1 +
 .../rust-analyzer/crates/base-db/src/input.rs  | 18 ++++++++++++------
 3 files changed, 14 insertions(+), 6 deletions(-)

diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock
index 8d6c8284e44e..b35a67c66175 100644
--- a/src/tools/rust-analyzer/Cargo.lock
+++ b/src/tools/rust-analyzer/Cargo.lock
@@ -80,6 +80,7 @@ version = "0.0.0"
 dependencies = [
  "cfg",
  "dashmap",
+ "indexmap",
  "intern",
  "la-arena 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "query-group-macro",
diff --git a/src/tools/rust-analyzer/crates/base-db/Cargo.toml b/src/tools/rust-analyzer/crates/base-db/Cargo.toml
index e2e3253773fe..3b423a86f97a 100644
--- a/src/tools/rust-analyzer/crates/base-db/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/base-db/Cargo.toml
@@ -21,6 +21,7 @@ rustc-hash.workspace = true
 triomphe.workspace = true
 semver.workspace = true
 tracing.workspace = true
+indexmap.workspace = true
 
 # local deps
 cfg.workspace = true
diff --git a/src/tools/rust-analyzer/crates/base-db/src/input.rs b/src/tools/rust-analyzer/crates/base-db/src/input.rs
index 9660e6e87cca..d42d7e5707d3 100644
--- a/src/tools/rust-analyzer/crates/base-db/src/input.rs
+++ b/src/tools/rust-analyzer/crates/base-db/src/input.rs
@@ -14,7 +14,7 @@
 use dashmap::mapref::entry::Entry;
 use intern::Symbol;
 use la_arena::{Arena, Idx, RawIdx};
-use rustc_hash::{FxHashMap, FxHashSet, FxHasher};
+use rustc_hash::{FxBuildHasher, FxHashMap, FxHashSet, FxHasher};
 use salsa::{Durability, Setter};
 use span::Edition;
 use triomphe::Arc;
@@ -24,6 +24,8 @@
 
 pub type ProcMacroPaths = FxHashMap>;
 
+type FxIndexSet = indexmap::IndexSet;
+
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
 pub struct SourceRootId(pub u32);
 
@@ -474,7 +476,9 @@ pub fn add_dep(
     }
 
     pub fn set_in_db(self, db: &mut dyn RootQueryDb) -> CratesIdMap {
-        let mut all_crates = Vec::with_capacity(self.arena.len());
+        // For some reason in some repositories we have duplicate crates, so we use a set and not `Vec`.
+        // We use an `IndexSet` because the list needs to be topologically sorted.
+        let mut all_crates = FxIndexSet::with_capacity_and_hasher(self.arena.len(), FxBuildHasher);
         let mut visited = FxHashMap::default();
         let mut visited_root_files = FxHashSet::default();
 
@@ -494,9 +498,11 @@ pub fn set_in_db(self, db: &mut dyn RootQueryDb) -> CratesIdMap {
             );
         }
 
-        if **old_all_crates != *all_crates {
+        if old_all_crates.len() != all_crates.len()
+            || old_all_crates.iter().any(|&krate| !all_crates.contains(&krate))
+        {
             db.set_all_crates_with_durability(
-                Arc::new(all_crates.into_boxed_slice()),
+                Arc::new(Vec::from_iter(all_crates).into_boxed_slice()),
                 Durability::MEDIUM,
             );
         }
@@ -509,7 +515,7 @@ fn go(
             crates_map: &CratesMap,
             visited: &mut FxHashMap,
             visited_root_files: &mut FxHashSet,
-            all_crates: &mut Vec,
+            all_crates: &mut FxIndexSet,
             source: CrateBuilderId,
         ) -> Crate {
             if let Some(&crate_id) = visited.get(&source) {
@@ -597,7 +603,7 @@ fn go(
                     input
                 }
             };
-            all_crates.push(crate_input);
+            all_crates.insert(crate_input);
             visited.insert(source, crate_input);
             crate_input
         }

From 5adbda4ee5d3013cf9e9a9479268fc8e17d24c40 Mon Sep 17 00:00:00 2001
From: Hayashi Mikihiro <34ttrweoewiwe28@gmail.com>
Date: Thu, 15 May 2025 13:18:30 +0900
Subject: [PATCH 37/51] rename fn name take_path to any

Signed-off-by: Hayashi Mikihiro <34ttrweoewiwe28@gmail.com>
---
 src/tools/rust-analyzer/crates/hir/src/semantics.rs         | 2 +-
 src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs   | 6 +++---
 .../ide-assists/src/handlers/remove_unused_imports.rs       | 2 +-
 3 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs
index 7ee72614e220..caa6700de9f9 100644
--- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs
@@ -118,7 +118,7 @@ pub fn new(
     ) -> Self {
         PathResolutionPerNs { type_ns, value_ns, macro_ns }
     }
-    pub fn take_path(&self) -> Option {
+    pub fn any(&self) -> Option {
         self.type_ns.or(self.value_ns).or(self.macro_ns)
     }
 }
diff --git a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs
index 610770d0f31e..ea21546f9d76 100644
--- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs
@@ -1163,7 +1163,7 @@ pub(crate) fn resolve_path(
                 Some(&store),
                 false,
             )
-            .take_path()?;
+            .any()?;
             let subst = (|| {
                 let parent = parent()?;
                 let ty = if let Some(expr) = ast::Expr::cast(parent.clone()) {
@@ -1556,7 +1556,7 @@ pub(crate) fn resolve_hir_path(
     hygiene: HygieneId,
     store: Option<&ExpressionStore>,
 ) -> Option {
-    resolve_hir_path_(db, resolver, path, false, hygiene, store, false).take_path()
+    resolve_hir_path_(db, resolver, path, false, hygiene, store, false).any()
 }
 
 #[inline]
@@ -1677,7 +1677,7 @@ fn resolve_hir_path_(
                 .unwrap_or_else(|| PathResolutionPerNs::new(None, values(), None))
         };
 
-        if res.take_path().is_some() {
+        if res.any().is_some() {
             res
         } else if let Some(type_ns) = items() {
             PathResolutionPerNs::new(Some(type_ns), None, None)
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_imports.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_imports.rs
index fb96882ed428..dff9a660cf59 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_imports.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_imports.rs
@@ -81,7 +81,7 @@ pub(crate) fn remove_unused_imports(acc: &mut Assists, ctx: &AssistContext<'_>)
 
             // Get the actual definition associated with this use item.
             let res = match ctx.sema.resolve_path_per_ns(&path) {
-                Some(x) if x.take_path().is_some() => x,
+                Some(x) if x.any().is_some() => x,
                 Some(_) | None => {
                     return None;
                 }

From 2c55a78848cebf3bede3339362bbfcbc6c18fa8c Mon Sep 17 00:00:00 2001
From: Hayashi Mikihiro <34ttrweoewiwe28@gmail.com>
Date: Thu, 15 May 2025 14:01:05 +0900
Subject: [PATCH 38/51] check glob

Signed-off-by: Hayashi Mikihiro <34ttrweoewiwe28@gmail.com>
---
 .../src/handlers/remove_unused_imports.rs     | 41 ++++++++++---------
 1 file changed, 21 insertions(+), 20 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_imports.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_imports.rs
index dff9a660cf59..994e7c446cc5 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_imports.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_imports.rs
@@ -86,28 +86,29 @@ pub(crate) fn remove_unused_imports(acc: &mut Assists, ctx: &AssistContext<'_>)
                     return None;
                 }
             };
-            match res {
-                PathResolutionPerNs { type_ns: Some(type_ns), .. } if u.star_token().is_some() => {
-                    // Check if any of the children of this module are used
-                    let def_mod = match type_ns {
-                        PathResolution::Def(ModuleDef::Module(module)) => module,
-                        _ => return None,
-                    };
 
-                    if !def_mod
-                        .scope(ctx.db(), Some(use_module))
-                        .iter()
-                        .filter_map(|(_, x)| match x {
-                            hir::ScopeDef::ModuleDef(d) => Some(Definition::from(*d)),
-                            _ => None,
-                        })
-                        .any(|d| used_once_in_scope(ctx, d, u.rename(), scope))
-                    {
-                        Some(u)
-                    } else {
-                        None
-                    }
+            if u.star_token().is_some() {
+                // Check if any of the children of this module are used
+                let def_mod = match res.type_ns {
+                    Some(PathResolution::Def(ModuleDef::Module(module))) => module,
+                    _ => return None,
+                };
+
+                if !def_mod
+                    .scope(ctx.db(), Some(use_module))
+                    .iter()
+                    .filter_map(|(_, x)| match x {
+                        hir::ScopeDef::ModuleDef(d) => Some(Definition::from(*d)),
+                        _ => None,
+                    })
+                    .any(|d| used_once_in_scope(ctx, d, u.rename(), scope))
+                {
+                    return Some(u);
+                } else {
+                    return None;
                 }
+            }
+            match res {
                 PathResolutionPerNs {
                     type_ns: Some(PathResolution::Def(ModuleDef::Trait(ref t))),
                     value_ns,

From 034d2a2fe778d0effe9532775f08a5652159b207 Mon Sep 17 00:00:00 2001
From: Hayashi Mikihiro <34ttrweoewiwe28@gmail.com>
Date: Thu, 15 May 2025 15:12:31 +0900
Subject: [PATCH 39/51] handle trait in function

Signed-off-by: Hayashi Mikihiro <34ttrweoewiwe28@gmail.com>
---
 .../src/handlers/remove_unused_imports.rs     | 44 ++++++++++---------
 1 file changed, 23 insertions(+), 21 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_imports.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_imports.rs
index 994e7c446cc5..16debc4d7285 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_imports.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_imports.rs
@@ -103,29 +103,12 @@ pub(crate) fn remove_unused_imports(acc: &mut Assists, ctx: &AssistContext<'_>)
                     })
                     .any(|d| used_once_in_scope(ctx, d, u.rename(), scope))
                 {
-                    return Some(u);
+                    Some(u)
                 } else {
-                    return None;
-                }
-            }
-            match res {
-                PathResolutionPerNs {
-                    type_ns: Some(PathResolution::Def(ModuleDef::Trait(ref t))),
-                    value_ns,
-                    macro_ns,
-                } => {
-                    // If the trait or any item is used.
-                    if is_trait_unused_in_scope(ctx, &u, scope, t) {
-                        let path = [value_ns, macro_ns];
-                        is_path_unused_in_scope(ctx, &u, scope, &path).then_some(u)
-                    } else {
-                        None
-                    }
-                }
-                PathResolutionPerNs { type_ns, value_ns, macro_ns } => {
-                    let path = [type_ns, value_ns, macro_ns];
-                    is_path_unused_in_scope(ctx, &u, scope, &path).then_some(u)
+                    None
                 }
+            } else {
+                is_path_per_ns_unused_in_scope(ctx, &u, scope, &res).then_some(u)
             }
         })
         .peekable();
@@ -148,6 +131,25 @@ pub(crate) fn remove_unused_imports(acc: &mut Assists, ctx: &AssistContext<'_>)
     }
 }
 
+fn is_path_per_ns_unused_in_scope(
+    ctx: &AssistContext<'_>,
+    u: &ast::UseTree,
+    scope: &mut Vec,
+    path: &PathResolutionPerNs,
+) -> bool {
+    if let Some(PathResolution::Def(ModuleDef::Trait(ref t))) = path.type_ns {
+        if is_trait_unused_in_scope(ctx, u, scope, t) {
+            let path = [path.value_ns, path.macro_ns];
+            is_path_unused_in_scope(ctx, u, scope, &path)
+        } else {
+            false
+        }
+    } else {
+        let path = [path.type_ns, path.value_ns, path.macro_ns];
+        is_path_unused_in_scope(ctx, u, scope, &path)
+    }
+}
+
 fn is_path_unused_in_scope(
     ctx: &AssistContext<'_>,
     u: &ast::UseTree,

From 66fef16bb6229d61ab32decbe5285d9cf5b7f5c8 Mon Sep 17 00:00:00 2001
From: Berrysoft 
Date: Thu, 15 May 2025 15:14:35 +0800
Subject: [PATCH 40/51] Update Cargo.lock

---
 src/tools/rust-analyzer/Cargo.lock | 102 +++++++++++++++++++----------
 1 file changed, 67 insertions(+), 35 deletions(-)

diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock
index 8d6c8284e44e..6010755f8fa4 100644
--- a/src/tools/rust-analyzer/Cargo.lock
+++ b/src/tools/rust-analyzer/Cargo.lock
@@ -61,9 +61,9 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
 
 [[package]]
 name = "backtrace"
-version = "0.3.74"
+version = "0.3.75"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a"
+checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002"
 dependencies = [
  "addr2line",
  "cfg-if",
@@ -316,9 +316,9 @@ checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
 
 [[package]]
 name = "ctrlc"
-version = "3.4.5"
+version = "3.4.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "90eeab0aa92f3f9b4e87f258c72b139c207d251f9cbc1080a0086b86a8870dd3"
+checksum = "46f93780a459b7d656ef7f071fe699c4d3d2cb201c4b24d085b6ddc505276e73"
 dependencies = [
  "nix",
  "windows-sys 0.59.0",
@@ -1010,9 +1010,9 @@ dependencies = [
 
 [[package]]
 name = "indexmap"
-version = "2.8.0"
+version = "2.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058"
+checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e"
 dependencies = [
  "equivalent",
  "hashbrown 0.15.2",
@@ -1123,12 +1123,12 @@ checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
 
 [[package]]
 name = "libloading"
-version = "0.8.6"
+version = "0.8.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34"
+checksum = "6a793df0d7afeac54f95b471d3af7f0d4fb975699f972341a4b76988d49cdf0c"
 dependencies = [
  "cfg-if",
- "windows-targets 0.52.6",
+ "windows-targets 0.53.0",
 ]
 
 [[package]]
@@ -1358,9 +1358,9 @@ dependencies = [
 
 [[package]]
 name = "nix"
-version = "0.29.0"
+version = "0.30.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46"
+checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6"
 dependencies = [
  "bitflags 2.9.0",
  "cfg-if",
@@ -1640,14 +1640,14 @@ dependencies = [
 
 [[package]]
 name = "process-wrap"
-version = "8.2.0"
+version = "8.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d35f4dc9988d1326b065b4def5e950c3ed727aa03e3151b86cc9e2aec6b03f54"
+checksum = "a3ef4f2f0422f23a82ec9f628ea2acd12871c81a9362b02c43c1aa86acfc3ba1"
 dependencies = [
  "indexmap",
  "nix",
  "tracing",
- "windows 0.59.0",
+ "windows 0.61.1",
 ]
 
 [[package]]
@@ -2750,12 +2750,24 @@ dependencies = [
 
 [[package]]
 name = "windows"
-version = "0.59.0"
+version = "0.61.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7f919aee0a93304be7f62e8e5027811bbba96bcb1de84d6618be56e43f8a32a1"
+checksum = "c5ee8f3d025738cb02bad7868bbb5f8a6327501e870bf51f1b455b0a2454a419"
 dependencies = [
- "windows-core 0.59.0",
- "windows-targets 0.53.0",
+ "windows-collections",
+ "windows-core 0.61.0",
+ "windows-future",
+ "windows-link",
+ "windows-numerics",
+]
+
+[[package]]
+name = "windows-collections"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8"
+dependencies = [
+ "windows-core 0.61.0",
 ]
 
 [[package]]
@@ -2773,15 +2785,25 @@ dependencies = [
 
 [[package]]
 name = "windows-core"
-version = "0.59.0"
+version = "0.61.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "810ce18ed2112484b0d4e15d022e5f598113e220c53e373fb31e67e21670c1ce"
+checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980"
 dependencies = [
- "windows-implement 0.59.0",
- "windows-interface 0.59.0",
- "windows-result 0.3.1",
- "windows-strings 0.3.1",
- "windows-targets 0.53.0",
+ "windows-implement 0.60.0",
+ "windows-interface 0.59.1",
+ "windows-link",
+ "windows-result 0.3.2",
+ "windows-strings 0.4.0",
+]
+
+[[package]]
+name = "windows-future"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a1d6bbefcb7b60acd19828e1bc965da6fcf18a7e39490c5f8be71e54a19ba32"
+dependencies = [
+ "windows-core 0.61.0",
+ "windows-link",
 ]
 
 [[package]]
@@ -2797,9 +2819,9 @@ dependencies = [
 
 [[package]]
 name = "windows-implement"
-version = "0.59.0"
+version = "0.60.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "83577b051e2f49a058c308f17f273b570a6a758386fc291b5f6a934dd84e48c1"
+checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -2819,9 +2841,9 @@ dependencies = [
 
 [[package]]
 name = "windows-interface"
-version = "0.59.0"
+version = "0.59.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cb26fd936d991781ea39e87c3a27285081e3c0da5ca0fcbc02d368cc6f52ff01"
+checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -2830,9 +2852,19 @@ dependencies = [
 
 [[package]]
 name = "windows-link"
-version = "0.1.0"
+version = "0.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6dccfd733ce2b1753b03b6d3c65edf020262ea35e20ccdf3e288043e6dd620e3"
+checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38"
+
+[[package]]
+name = "windows-numerics"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1"
+dependencies = [
+ "windows-core 0.61.0",
+ "windows-link",
+]
 
 [[package]]
 name = "windows-result"
@@ -2845,9 +2877,9 @@ dependencies = [
 
 [[package]]
 name = "windows-result"
-version = "0.3.1"
+version = "0.3.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "06374efe858fab7e4f881500e6e86ec8bc28f9462c47e5a9941a0142ad86b189"
+checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252"
 dependencies = [
  "windows-link",
 ]
@@ -2864,9 +2896,9 @@ dependencies = [
 
 [[package]]
 name = "windows-strings"
-version = "0.3.1"
+version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "87fa48cc5d406560701792be122a10132491cff9d0aeb23583cc2dcafc847319"
+checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97"
 dependencies = [
  "windows-link",
 ]

From 6c21c9fdff5d079f24724ec661fa9d2bc9a2d9f3 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 15 May 2025 15:24:19 +0000
Subject: [PATCH 41/51] Bump undici from 6.21.1 to 6.21.3 in /editors/code

Bumps [undici](https://github.com/nodejs/undici) from 6.21.1 to 6.21.3.
- [Release notes](https://github.com/nodejs/undici/releases)
- [Commits](https://github.com/nodejs/undici/compare/v6.21.1...v6.21.3)

---
updated-dependencies:
- dependency-name: undici
  dependency-version: 6.21.3
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] 
---
 src/tools/rust-analyzer/editors/code/package-lock.json | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/tools/rust-analyzer/editors/code/package-lock.json b/src/tools/rust-analyzer/editors/code/package-lock.json
index 11a37c218f7f..18fb097aad7f 100644
--- a/src/tools/rust-analyzer/editors/code/package-lock.json
+++ b/src/tools/rust-analyzer/editors/code/package-lock.json
@@ -5730,9 +5730,9 @@
             "license": "MIT"
         },
         "node_modules/undici": {
-            "version": "6.21.1",
-            "resolved": "https://registry.npmjs.org/undici/-/undici-6.21.1.tgz",
-            "integrity": "sha512-q/1rj5D0/zayJB2FraXdaWxbhWiNKDvu8naDT2dl1yTlvJp4BLtOcp2a5BvgGNQpYYJzau7tf1WgKv3b+7mqpQ==",
+            "version": "6.21.3",
+            "resolved": "https://registry.npmjs.org/undici/-/undici-6.21.3.tgz",
+            "integrity": "sha512-gBLkYIlEnSp8pFbT64yFgGE6UIB9tAkhukC23PmMDCe5Nd+cRqKxSjw5y54MK2AZMgZfJWMaNE4nYUHgi1XEOw==",
             "dev": true,
             "license": "MIT",
             "engines": {

From ae304db796b6897b6e68486750495898f2d5fec7 Mon Sep 17 00:00:00 2001
From: Chayim Refael Friedman 
Date: Thu, 15 May 2025 23:36:00 +0300
Subject: [PATCH 42/51] Improve asm support

Including:

 - Infer `label {}` and `const` operands.
 - Correctly handle unsafe check inside `label {}`.
 - Fix an embarrassing parser typo that cause labels to never be part of the AST
---
 .../crates/hir-def/src/expr_store.rs          | 18 +++---
 .../hir-ty/src/diagnostics/unsafe_check.rs    | 58 ++++++++++++++-----
 .../crates/hir-ty/src/infer/expr.rs           | 36 +++++++-----
 .../crates/hir-ty/src/tests/macros.rs         |  4 ++
 .../crates/hir-ty/src/tests/simple.rs         | 39 +++++++++++++
 .../src/handlers/missing_unsafe.rs            | 21 +++++++
 .../parser/src/grammar/expressions/atom.rs    |  8 ++-
 .../parser/test_data/generated/runner.rs      |  2 +
 .../test_data/parser/inline/ok/asm_label.rast | 37 ++++++++++++
 .../test_data/parser/inline/ok/asm_label.rs   |  3 +
 10 files changed, 186 insertions(+), 40 deletions(-)
 create mode 100644 src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/asm_label.rast
 create mode 100644 src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/asm_label.rs

diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs
index 09ee286f5cfe..f617c3225ae1 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs
@@ -298,17 +298,16 @@ pub fn walk_child_exprs(&self, expr_id: ExprId, mut f: impl FnMut(ExprId)) {
             Expr::InlineAsm(it) => it.operands.iter().for_each(|(_, op)| match op {
                 AsmOperand::In { expr, .. }
                 | AsmOperand::Out { expr: Some(expr), .. }
-                | AsmOperand::InOut { expr, .. } => f(*expr),
+                | AsmOperand::InOut { expr, .. }
+                | AsmOperand::Const(expr)
+                | AsmOperand::Label(expr) => f(*expr),
                 AsmOperand::SplitInOut { in_expr, out_expr, .. } => {
                     f(*in_expr);
                     if let Some(out_expr) = out_expr {
                         f(*out_expr);
                     }
                 }
-                AsmOperand::Out { expr: None, .. }
-                | AsmOperand::Const(_)
-                | AsmOperand::Label(_)
-                | AsmOperand::Sym(_) => (),
+                AsmOperand::Out { expr: None, .. } | AsmOperand::Sym(_) => (),
             }),
             Expr::If { condition, then_branch, else_branch } => {
                 f(*condition);
@@ -435,17 +434,16 @@ pub fn walk_child_exprs_without_pats(&self, expr_id: ExprId, mut f: impl FnMut(E
             Expr::InlineAsm(it) => it.operands.iter().for_each(|(_, op)| match op {
                 AsmOperand::In { expr, .. }
                 | AsmOperand::Out { expr: Some(expr), .. }
-                | AsmOperand::InOut { expr, .. } => f(*expr),
+                | AsmOperand::InOut { expr, .. }
+                | AsmOperand::Const(expr)
+                | AsmOperand::Label(expr) => f(*expr),
                 AsmOperand::SplitInOut { in_expr, out_expr, .. } => {
                     f(*in_expr);
                     if let Some(out_expr) = out_expr {
                         f(*out_expr);
                     }
                 }
-                AsmOperand::Out { expr: None, .. }
-                | AsmOperand::Const(_)
-                | AsmOperand::Label(_)
-                | AsmOperand::Sym(_) => (),
+                AsmOperand::Out { expr: None, .. } | AsmOperand::Sym(_) => (),
             }),
             Expr::If { condition, then_branch, else_branch } => {
                 f(*condition);
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs
index 6d5aaa34725d..20cf3c781151 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs
@@ -7,7 +7,7 @@
 use hir_def::{
     AdtId, DefWithBodyId, FieldId, FunctionId, VariantId,
     expr_store::{Body, path::Path},
-    hir::{Expr, ExprId, ExprOrPatId, Pat, PatId, Statement, UnaryOp},
+    hir::{AsmOperand, Expr, ExprId, ExprOrPatId, Pat, PatId, Statement, UnaryOp},
     resolver::{HasResolver, ResolveValueResult, Resolver, ValueNs},
     signatures::StaticFlags,
     type_ref::Rawness,
@@ -199,6 +199,17 @@ fn check_call(&mut self, node: ExprId, func: FunctionId) {
         }
     }
 
+    fn with_inside_unsafe_block(
+        &mut self,
+        inside_unsafe_block: InsideUnsafeBlock,
+        f: impl FnOnce(&mut Self) -> R,
+    ) -> R {
+        let old = mem::replace(&mut self.inside_unsafe_block, inside_unsafe_block);
+        let result = f(self);
+        self.inside_unsafe_block = old;
+        result
+    }
+
     fn walk_pats_top(&mut self, pats: impl Iterator, parent_expr: ExprId) {
         let guard = self.resolver.update_to_inner_scope(self.db, self.def, parent_expr);
         pats.for_each(|pat| self.walk_pat(pat));
@@ -303,7 +314,29 @@ fn walk_expr(&mut self, current: ExprId) {
                 self.walk_pats_top(std::iter::once(target), current);
                 self.inside_assignment = old_inside_assignment;
             }
-            Expr::InlineAsm(_) => self.on_unsafe_op(current.into(), UnsafetyReason::InlineAsm),
+            Expr::InlineAsm(asm) => {
+                self.on_unsafe_op(current.into(), UnsafetyReason::InlineAsm);
+                asm.operands.iter().for_each(|(_, op)| match op {
+                    AsmOperand::In { expr, .. }
+                    | AsmOperand::Out { expr: Some(expr), .. }
+                    | AsmOperand::InOut { expr, .. }
+                    | AsmOperand::Const(expr) => self.walk_expr(*expr),
+                    AsmOperand::SplitInOut { in_expr, out_expr, .. } => {
+                        self.walk_expr(*in_expr);
+                        if let Some(out_expr) = out_expr {
+                            self.walk_expr(*out_expr);
+                        }
+                    }
+                    AsmOperand::Out { expr: None, .. } | AsmOperand::Sym(_) => (),
+                    AsmOperand::Label(expr) => {
+                        // Inline asm labels are considered safe even when inside unsafe blocks.
+                        self.with_inside_unsafe_block(InsideUnsafeBlock::No, |this| {
+                            this.walk_expr(*expr)
+                        });
+                    }
+                });
+                return;
+            }
             // rustc allows union assignment to propagate through field accesses and casts.
             Expr::Cast { .. } => self.inside_assignment = inside_assignment,
             Expr::Field { .. } => {
@@ -317,17 +350,16 @@ fn walk_expr(&mut self, current: ExprId) {
                 }
             }
             Expr::Unsafe { statements, .. } => {
-                let old_inside_unsafe_block =
-                    mem::replace(&mut self.inside_unsafe_block, InsideUnsafeBlock::Yes);
-                self.walk_pats_top(
-                    statements.iter().filter_map(|statement| match statement {
-                        &Statement::Let { pat, .. } => Some(pat),
-                        _ => None,
-                    }),
-                    current,
-                );
-                self.body.walk_child_exprs_without_pats(current, |child| self.walk_expr(child));
-                self.inside_unsafe_block = old_inside_unsafe_block;
+                self.with_inside_unsafe_block(InsideUnsafeBlock::Yes, |this| {
+                    this.walk_pats_top(
+                        statements.iter().filter_map(|statement| match statement {
+                            &Statement::Let { pat, .. } => Some(pat),
+                            _ => None,
+                        }),
+                        current,
+                    );
+                    this.body.walk_child_exprs_without_pats(current, |child| this.walk_expr(child));
+                });
                 return;
             }
             Expr::Block { statements, .. } | Expr::Async { statements, .. } => {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs
index 8084b394d044..87b7f3406ff7 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs
@@ -959,8 +959,8 @@ fn infer_expr_inner(
             }
             Expr::OffsetOf(_) => TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(Interner),
             Expr::InlineAsm(asm) => {
-                let mut check_expr_asm_operand = |expr, is_input: bool| {
-                    let ty = self.infer_expr_no_expect(expr, ExprIsRead::Yes);
+                let check_expr_asm_operand = |this: &mut Self, expr, is_input: bool| {
+                    let ty = this.infer_expr_no_expect(expr, ExprIsRead::Yes);
 
                     // If this is an input value, we require its type to be fully resolved
                     // at this point. This allows us to provide helpful coercions which help
@@ -970,18 +970,18 @@ fn infer_expr_inner(
                     // allows them to be inferred based on how they are used later in the
                     // function.
                     if is_input {
-                        let ty = self.resolve_ty_shallow(&ty);
+                        let ty = this.resolve_ty_shallow(&ty);
                         match ty.kind(Interner) {
                             TyKind::FnDef(def, parameters) => {
                                 let fnptr_ty = TyKind::Function(
-                                    CallableSig::from_def(self.db, *def, parameters).to_fn_ptr(),
+                                    CallableSig::from_def(this.db, *def, parameters).to_fn_ptr(),
                                 )
                                 .intern(Interner);
-                                _ = self.coerce(Some(expr), &ty, &fnptr_ty, CoerceNever::Yes);
+                                _ = this.coerce(Some(expr), &ty, &fnptr_ty, CoerceNever::Yes);
                             }
                             TyKind::Ref(mutbl, _, base_ty) => {
                                 let ptr_ty = TyKind::Raw(*mutbl, base_ty.clone()).intern(Interner);
-                                _ = self.coerce(Some(expr), &ty, &ptr_ty, CoerceNever::Yes);
+                                _ = this.coerce(Some(expr), &ty, &ptr_ty, CoerceNever::Yes);
                             }
                             _ => {}
                         }
@@ -990,22 +990,28 @@ fn infer_expr_inner(
 
                 let diverge = asm.options.contains(AsmOptions::NORETURN);
                 asm.operands.iter().for_each(|(_, operand)| match *operand {
-                    AsmOperand::In { expr, .. } => check_expr_asm_operand(expr, true),
+                    AsmOperand::In { expr, .. } => check_expr_asm_operand(self, expr, true),
                     AsmOperand::Out { expr: Some(expr), .. } | AsmOperand::InOut { expr, .. } => {
-                        check_expr_asm_operand(expr, false)
+                        check_expr_asm_operand(self, expr, false)
                     }
                     AsmOperand::Out { expr: None, .. } => (),
                     AsmOperand::SplitInOut { in_expr, out_expr, .. } => {
-                        check_expr_asm_operand(in_expr, true);
+                        check_expr_asm_operand(self, in_expr, true);
                         if let Some(out_expr) = out_expr {
-                            check_expr_asm_operand(out_expr, false);
+                            check_expr_asm_operand(self, out_expr, false);
                         }
                     }
-                    // FIXME
-                    AsmOperand::Label(_) => (),
-                    // FIXME
-                    AsmOperand::Const(_) => (),
-                    // FIXME
+                    AsmOperand::Label(expr) => {
+                        self.infer_expr(
+                            expr,
+                            &Expectation::HasType(self.result.standard_types.unit.clone()),
+                            ExprIsRead::No,
+                        );
+                    }
+                    AsmOperand::Const(expr) => {
+                        self.infer_expr(expr, &Expectation::None, ExprIsRead::No);
+                    }
+                    // FIXME: `sym` should report for things that are not functions or statics.
                     AsmOperand::Sym(_) => (),
                 });
                 if diverge {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs
index 446f0b21a2a1..ea7a113cae3f 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs
@@ -1505,6 +1505,10 @@ fn main() {
             !119..120 'o': i32
             293..294 'o': i32
             308..317 'thread_id': usize
+            !314..320 'OffPtr': usize
+            !333..338 'OffFn': usize
+            !354..355 '0': i32
+            !371..382 'MEM_RELEASE': usize
         "#]],
     )
 }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs
index 08127eeb4632..cf51671afb2b 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs
@@ -3926,3 +3926,42 @@ fn foo() {
         "#]],
     );
 }
+
+#[test]
+fn asm_const_label() {
+    check_infer(
+        r#"
+//- minicore: asm
+const fn bar() -> i32 { 123 }
+fn baz(s: &str) {}
+
+fn foo() {
+    unsafe {
+        core::arch::asm!(
+            "mov eax, {}",
+            "jmp {}",
+            const bar(),
+            label {
+                baz("hello");
+            },
+        );
+    }
+}
+    "#,
+        expect![[r#"
+            22..29 '{ 123 }': i32
+            24..27 '123': i32
+            37..38 's': &'? str
+            46..48 '{}': ()
+            !0..68 'builti...");},)': ()
+            !40..43 'bar': fn bar() -> i32
+            !40..45 'bar()': i32
+            !51..66 '{baz("hello");}': ()
+            !52..55 'baz': fn baz(&'? str)
+            !52..64 'baz("hello")': ()
+            !56..63 '"hello"': &'static str
+            59..257 '{     ...   } }': ()
+            65..255 'unsafe...     }': ()
+        "#]],
+    );
+}
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs
index ff041e183b14..364bead34efa 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs
@@ -894,4 +894,25 @@ fn main() {
         "#,
         );
     }
+
+    #[test]
+    fn asm_label() {
+        check_diagnostics(
+            r#"
+//- minicore: asm
+fn foo() {
+    unsafe {
+        core::arch::asm!(
+            "jmp {}",
+            label {
+                let p = 0xDEADBEAF as *mut u8;
+                *p = 3;
+             // ^^ error: dereference of raw pointer is unsafe and requires an unsafe function or block
+            },
+        );
+    }
+}
+            "#,
+        );
+    }
 }
diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs
index 5faf6fc2759e..8cc332d4633d 100644
--- a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs
@@ -381,10 +381,14 @@ fn parse_asm_expr(p: &mut Parser<'_>, m: Marker) -> Option {
             op.complete(p, ASM_REG_OPERAND);
             op_n.complete(p, ASM_OPERAND_NAMED);
         } else if p.eat_contextual_kw(T![label]) {
+            // test asm_label
+            // fn foo() {
+            //     builtin#asm("", label {});
+            // }
             dir_spec.abandon(p);
             block_expr(p);
-            op.complete(p, ASM_OPERAND_NAMED);
-            op_n.complete(p, ASM_LABEL);
+            op.complete(p, ASM_LABEL);
+            op_n.complete(p, ASM_OPERAND_NAMED);
         } else if p.eat(T![const]) {
             dir_spec.abandon(p);
             expr(p);
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs b/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs
index 24db9478ee56..030d8e0f04dd 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs
+++ b/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs
@@ -21,6 +21,8 @@ fn arb_self_types() {
     #[test]
     fn asm_expr() { run_and_expect_no_errors("test_data/parser/inline/ok/asm_expr.rs"); }
     #[test]
+    fn asm_label() { run_and_expect_no_errors("test_data/parser/inline/ok/asm_label.rs"); }
+    #[test]
     fn assoc_const_eq() {
         run_and_expect_no_errors("test_data/parser/inline/ok/assoc_const_eq.rs");
     }
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/asm_label.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/asm_label.rast
new file mode 100644
index 000000000000..38999c9cd34f
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/asm_label.rast
@@ -0,0 +1,37 @@
+SOURCE_FILE
+  FN
+    FN_KW "fn"
+    WHITESPACE " "
+    NAME
+      IDENT "foo"
+    PARAM_LIST
+      L_PAREN "("
+      R_PAREN ")"
+    WHITESPACE " "
+    BLOCK_EXPR
+      STMT_LIST
+        L_CURLY "{"
+        WHITESPACE "\n    "
+        EXPR_STMT
+          ASM_EXPR
+            BUILTIN_KW "builtin"
+            POUND "#"
+            ASM_KW "asm"
+            L_PAREN "("
+            LITERAL
+              STRING "\"\""
+            COMMA ","
+            WHITESPACE " "
+            ASM_OPERAND_NAMED
+              ASM_LABEL
+                LABEL_KW "label"
+                WHITESPACE " "
+                BLOCK_EXPR
+                  STMT_LIST
+                    L_CURLY "{"
+                    R_CURLY "}"
+            R_PAREN ")"
+          SEMICOLON ";"
+        WHITESPACE "\n"
+        R_CURLY "}"
+  WHITESPACE "\n"
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/asm_label.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/asm_label.rs
new file mode 100644
index 000000000000..996c1c8477b6
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/asm_label.rs
@@ -0,0 +1,3 @@
+fn foo() {
+    builtin#asm("", label {});
+}

From 428279ef6a66bda813fa1c136b232a1b73a6f3b2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= 
Date: Fri, 16 May 2025 11:51:58 +0200
Subject: [PATCH 43/51] Bump zip to 3.0

---
 src/tools/rust-analyzer/Cargo.lock       | 11 ++++-------
 src/tools/rust-analyzer/xtask/Cargo.toml |  2 +-
 2 files changed, 5 insertions(+), 8 deletions(-)

diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock
index 51b6e109d79a..9eff747c21a9 100644
--- a/src/tools/rust-analyzer/Cargo.lock
+++ b/src/tools/rust-analyzer/Cargo.lock
@@ -473,9 +473,9 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
 
 [[package]]
 name = "flate2"
-version = "1.1.0"
+version = "1.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "11faaf5a5236997af9848be0bef4db95824b1d534ebc64d0f0c6cf3e67bd38dc"
+checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece"
 dependencies = [
  "crc32fast",
  "miniz_oxide",
@@ -3263,17 +3263,14 @@ dependencies = [
 
 [[package]]
 name = "zip"
-version = "2.4.2"
+version = "3.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fabe6324e908f85a1c52063ce7aa26b68dcb7eb6dbc83a2d148403c9bc3eba50"
+checksum = "12598812502ed0105f607f941c386f43d441e00148fce9dec3ca5ffb0bde9308"
 dependencies = [
  "arbitrary",
  "crc32fast",
- "crossbeam-utils",
- "displaydoc",
  "flate2",
  "indexmap",
  "memchr",
- "thiserror 2.0.12",
  "time",
 ]
diff --git a/src/tools/rust-analyzer/xtask/Cargo.toml b/src/tools/rust-analyzer/xtask/Cargo.toml
index 6195de5d2025..bb7d83c4b7d9 100644
--- a/src/tools/rust-analyzer/xtask/Cargo.toml
+++ b/src/tools/rust-analyzer/xtask/Cargo.toml
@@ -14,7 +14,7 @@ write-json = "0.1.4"
 xshell.workspace = true
 xflags = "0.3.2"
 time = { version = "0.3", default-features = false }
-zip = { version = "2.4", default-features = false, features = ["deflate-flate2", "flate2", "time"] }
+zip = { version = "3.0", default-features = false, features = ["deflate-flate2", "time"] }
 stdx.workspace = true
 proc-macro2 = "1.0.94"
 quote = "1.0.40"

From 4860712f43a113bbc6c8ebebac39c073b0f750f3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= 
Date: Fri, 16 May 2025 12:03:52 +0200
Subject: [PATCH 44/51] Inline some format args

---
 src/tools/rust-analyzer/crates/tt/src/lib.rs | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/tt/src/lib.rs b/src/tools/rust-analyzer/crates/tt/src/lib.rs
index a511bb0039cb..14574a6456bd 100644
--- a/src/tools/rust-analyzer/crates/tt/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/tt/src/lib.rs
@@ -825,12 +825,12 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
                 let symbol =
                     self.0.symbol.as_str().strip_prefix('-').unwrap_or(self.0.symbol.as_str());
                 match self.0.kind {
-                    LitKind::Byte => write!(f, "b'{}'", symbol),
-                    LitKind::Char => write!(f, "'{}'", symbol),
-                    LitKind::Integer | LitKind::Float | LitKind::Err(_) => write!(f, "{}", symbol),
-                    LitKind::Str => write!(f, "\"{}\"", symbol),
-                    LitKind::ByteStr => write!(f, "b\"{}\"", symbol),
-                    LitKind::CStr => write!(f, "c\"{}\"", symbol),
+                    LitKind::Byte => write!(f, "b'{symbol}'"),
+                    LitKind::Char => write!(f, "'{symbol}'"),
+                    LitKind::Integer | LitKind::Float | LitKind::Err(_) => write!(f, "{symbol}"),
+                    LitKind::Str => write!(f, "\"{symbol}\""),
+                    LitKind::ByteStr => write!(f, "b\"{symbol}\""),
+                    LitKind::CStr => write!(f, "c\"{symbol}\""),
                     LitKind::StrRaw(num_of_hashes) => {
                         let num_of_hashes = num_of_hashes as usize;
                         write!(

From 10f4f6ae3d41c95e56a6ba46dd82d5eb283a9e54 Mon Sep 17 00:00:00 2001
From: Lukas Wirth 
Date: Fri, 16 May 2025 14:52:28 +0200
Subject: [PATCH 45/51] fix: Don't overwrite `RUSTUP_TOOLCHAIN` if it is
 already set

---
 .../rust-analyzer/crates/project-model/src/sysroot.rs      | 7 ++++++-
 .../rust-analyzer/crates/rust-analyzer/src/flycheck.rs     | 6 +++++-
 src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs | 4 ++++
 3 files changed, 15 insertions(+), 2 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs b/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs
index c7c1b0431867..d4055d9a0af9 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs
@@ -137,7 +137,12 @@ pub fn tool(
                 }
 
                 let mut cmd = toolchain::command(tool.prefer_proxy(), current_dir, envs);
-                cmd.env("RUSTUP_TOOLCHAIN", AsRef::::as_ref(root));
+                if !envs.contains_key("RUSTUP_TOOLCHAIN")
+                    && std::env::var_os("RUSTUP_TOOLCHAIN").is_none()
+                {
+                    cmd.env("RUSTUP_TOOLCHAIN", AsRef::::as_ref(root));
+                }
+
                 cmd
             }
             _ => toolchain::command(tool.path(), current_dir, envs),
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs
index fc312439d58c..0e418240db0c 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs
@@ -470,7 +470,11 @@ fn check_command(
                 let mut cmd =
                     toolchain::command(Tool::Cargo.path(), &*self.root, &options.extra_env);
                 if let Some(sysroot_root) = &self.sysroot_root {
-                    cmd.env("RUSTUP_TOOLCHAIN", AsRef::::as_ref(sysroot_root));
+                    if !options.extra_env.contains_key("RUSTUP_TOOLCHAIN")
+                        && std::env::var_os("RUSTUP_TOOLCHAIN").is_none()
+                    {
+                        cmd.env("RUSTUP_TOOLCHAIN", AsRef::::as_ref(sysroot_root));
+                    }
                 }
                 cmd.arg(command);
 
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs
index 55ed1923653f..d7c6aeccf0b5 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs
@@ -659,6 +659,10 @@ pub(crate) fn switch_workspaces(&mut self, cause: Cause) {
                         .chain(
                             ws.sysroot
                                 .root()
+                                .filter(|_| {
+                                    !self.config.extra_env(None).contains_key("RUSTUP_TOOLCHAIN")
+                                        && std::env::var_os("RUSTUP_TOOLCHAIN").is_none()
+                                })
                                 .map(|it| ("RUSTUP_TOOLCHAIN".to_owned(), Some(it.to_string()))),
                         )
                         .collect(),

From 45865f21e3fedd8ba8b0161c126bbc575d1e1e0b Mon Sep 17 00:00:00 2001
From: Lukas Wirth 
Date: Fri, 16 May 2025 16:49:05 +0200
Subject: [PATCH 46/51] ci: Run metrics on the beta channel

This way we can catch regressions from upstream earlier
---
 .../rust-analyzer/.github/workflows/metrics.yaml     | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/src/tools/rust-analyzer/.github/workflows/metrics.yaml b/src/tools/rust-analyzer/.github/workflows/metrics.yaml
index a4146d602185..dc2f432bbc75 100644
--- a/src/tools/rust-analyzer/.github/workflows/metrics.yaml
+++ b/src/tools/rust-analyzer/.github/workflows/metrics.yaml
@@ -18,9 +18,9 @@ jobs:
     steps:
       - name: Install Rust toolchain
         run: |
-          rustup update --no-self-update stable
-          rustup default stable
-          rustup component add --toolchain stable rust-src
+          rustup update --no-self-update beta
+          rustup default beta
+          rustup component add --toolchain beta rust-src
 
       - name: Checkout repository
         uses: actions/checkout@v4
@@ -61,9 +61,9 @@ jobs:
     steps:
       - name: Install Rust toolchain
         run: |
-          rustup update --no-self-update stable
-          rustup default stable
-          rustup component add --toolchain stable rust-src
+          rustup update --no-self-update beta
+          rustup default beta
+          rustup component add --toolchain beta rust-src
 
       - name: Checkout repository
         uses: actions/checkout@v4

From c04873b02d5a5b0723cb7e058bca323a78e6bb70 Mon Sep 17 00:00:00 2001
From: Florian Diebold 
Date: Fri, 16 May 2025 17:01:24 +0200
Subject: [PATCH 47/51] Catch panics in inference in analysis-stats

---
 .../rust-analyzer/src/cli/analysis_stats.rs    | 18 +++++++++++++++++-
 1 file changed, 17 insertions(+), 1 deletion(-)

diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs
index a1e4adf0844a..671e838421f2 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs
@@ -4,6 +4,7 @@
 use std::{
     env, fmt,
     ops::AddAssign,
+    panic::{AssertUnwindSafe, catch_unwind},
     time::{SystemTime, UNIX_EPOCH},
 };
 
@@ -721,6 +722,7 @@ fn run_inference(
         let mut num_pats_unknown = 0;
         let mut num_pats_partially_unknown = 0;
         let mut num_pat_type_mismatches = 0;
+        let mut panics = 0;
         for &body_id in bodies {
             let name = body_id.name(db).unwrap_or_else(Name::missing);
             let module = body_id.module(db);
@@ -774,7 +776,20 @@ fn run_inference(
             }
             bar.set_message(msg);
             let body = db.body(body_id.into());
-            let inference_result = db.infer(body_id.into());
+            let inference_result = catch_unwind(AssertUnwindSafe(|| db.infer(body_id.into())));
+            let inference_result = match inference_result {
+                Ok(inference_result) => inference_result,
+                Err(p) => {
+                    if let Some(s) = p.downcast_ref::<&str>() {
+                        eprintln!("infer panicked for {}: {}", full_name(), s);
+                    } else if let Some(s) = p.downcast_ref::() {
+                        eprintln!("infer panicked for {}: {}", full_name(), s);
+                    }
+                    panics += 1;
+                    bar.inc(1);
+                    continue;
+                }
+            };
             // This query is LRU'd, so actually calling it will skew the timing results.
             let sm = || db.body_with_source_map(body_id.into()).1;
 
@@ -1008,6 +1023,7 @@ fn run_inference(
             percentage(num_pats_partially_unknown, num_pats),
             num_pat_type_mismatches
         );
+        eprintln!("  panics: {}", panics);
         eprintln!("{:<20} {}", "Inference:", inference_time);
         report_metric("unknown type", num_exprs_unknown, "#");
         report_metric("type mismatches", num_expr_type_mismatches, "#");

From e493cb1ad2d5aa1c2e21b8c5c948f68aa9f01222 Mon Sep 17 00:00:00 2001
From: Lukas Wirth 
Date: Sat, 17 May 2025 16:08:09 +0200
Subject: [PATCH 48/51] Debounce workspace fetching for workspace structure
 changes

---
 .../crates/rust-analyzer/src/global_state.rs  | 44 ++++++++++++++++---
 .../crates/rust-analyzer/src/main_loop.rs     | 16 ++++++-
 .../crates/rust-analyzer/src/op_queue.rs      |  2 +-
 .../crates/rust-analyzer/src/reload.rs        |  1 +
 4 files changed, 54 insertions(+), 9 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs
index 1a31525b46a8..a870232d4a0e 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs
@@ -3,7 +3,11 @@
 //!
 //! Each tick provides an immutable snapshot of the state as `WorldSnapshot`.
 
-use std::{ops::Not as _, panic::AssertUnwindSafe, time::Instant};
+use std::{
+    ops::Not as _,
+    panic::AssertUnwindSafe,
+    time::{Duration, Instant},
+};
 
 use crossbeam_channel::{Receiver, Sender, unbounded};
 use hir::ChangeWithProcMacros;
@@ -41,6 +45,7 @@
     test_runner::{CargoTestHandle, CargoTestMessage},
 };
 
+#[derive(Debug)]
 pub(crate) struct FetchWorkspaceRequest {
     pub(crate) path: Option,
     pub(crate) force_crate_graph_reload: bool,
@@ -116,6 +121,11 @@ pub(crate) struct GlobalState {
     pub(crate) discover_sender: Sender,
     pub(crate) discover_receiver: Receiver,
 
+    // Debouncing channel for fetching the workspace
+    // we want to delay it until the VFS looks stable-ish (and thus is not currently in the middle
+    // of a VCS operation like `git switch`)
+    pub(crate) fetch_ws_receiver: Option<(Receiver, FetchWorkspaceRequest)>,
+
     // VFS
     pub(crate) loader: Handle, Receiver>,
     pub(crate) vfs: Arc)>>,
@@ -268,6 +278,8 @@ pub(crate) fn new(sender: Sender, config: Config) -> Global
             discover_sender,
             discover_receiver,
 
+            fetch_ws_receiver: None,
+
             vfs: Arc::new(RwLock::new((vfs::Vfs::default(), Default::default()))),
             vfs_config_version: 0,
             vfs_progress_config_version: 0,
@@ -519,11 +531,7 @@ pub(crate) fn process_changes(&mut self) -> bool {
             if let Some((path, force_crate_graph_reload)) = workspace_structure_change {
                 let _p = span!(Level::INFO, "GlobalState::process_changes/ws_structure_change")
                     .entered();
-
-                self.fetch_workspaces_queue.request_op(
-                    format!("workspace vfs file change: {path}"),
-                    FetchWorkspaceRequest { path: Some(path), force_crate_graph_reload },
-                );
+                self.enqueue_workspace_fetch(path, force_crate_graph_reload);
             }
         }
 
@@ -671,6 +679,30 @@ pub(crate) fn check_workspaces_msrv(&self) -> impl Iterator + '_
             None
         })
     }
+
+    fn enqueue_workspace_fetch(&mut self, path: AbsPathBuf, force_crate_graph_reload: bool) {
+        let already_requested = self.fetch_workspaces_queue.op_requested()
+            && !self.fetch_workspaces_queue.op_in_progress();
+        if self.fetch_ws_receiver.is_none() && already_requested {
+            // Don't queue up a new fetch request if we already have done so
+            // Otherwise we will re-fetch in quick succession which is unnecessary
+            // Note though, that if one is already in progress, we *want* to re-queue
+            // as the in-progress fetch might not have the latest changes in it anymore
+            // FIXME: We should cancel the in-progress fetch here
+            return;
+        }
+
+        self.fetch_ws_receiver = Some((
+            crossbeam_channel::after(Duration::from_millis(100)),
+            FetchWorkspaceRequest { path: Some(path), force_crate_graph_reload },
+        ));
+    }
+
+    pub(crate) fn debounce_workspace_fetch(&mut self) {
+        if let Some((fetch_receiver, _)) = &mut self.fetch_ws_receiver {
+            *fetch_receiver = crossbeam_channel::after(Duration::from_millis(100));
+        }
+    }
 }
 
 impl Drop for GlobalState {
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs
index bd213ffa57a1..0c0438c4b8ff 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs
@@ -8,7 +8,7 @@
     time::{Duration, Instant},
 };
 
-use crossbeam_channel::{Receiver, select};
+use crossbeam_channel::{Receiver, never, select};
 use ide_db::base_db::{SourceDatabase, VfsPath, salsa::Database as _};
 use lsp_server::{Connection, Notification, Request};
 use lsp_types::{TextDocumentIdentifier, notification::Notification as _};
@@ -71,6 +71,7 @@ enum Event {
     Flycheck(FlycheckMessage),
     TestResult(CargoTestMessage),
     DiscoverProject(DiscoverProjectMessage),
+    FetchWorkspaces(FetchWorkspaceRequest),
 }
 
 impl fmt::Display for Event {
@@ -83,6 +84,7 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
             Event::QueuedTask(_) => write!(f, "Event::QueuedTask"),
             Event::TestResult(_) => write!(f, "Event::TestResult"),
             Event::DiscoverProject(_) => write!(f, "Event::DiscoverProject"),
+            Event::FetchWorkspaces(_) => write!(f, "Event::SwitchWorkspaces"),
         }
     }
 }
@@ -150,6 +152,7 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
             }
             _ => (),
         }
+
         match self {
             Event::Lsp(it) => fmt::Debug::fmt(it, f),
             Event::Task(it) => fmt::Debug::fmt(it, f),
@@ -158,6 +161,7 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
             Event::Flycheck(it) => fmt::Debug::fmt(it, f),
             Event::TestResult(it) => fmt::Debug::fmt(it, f),
             Event::DiscoverProject(it) => fmt::Debug::fmt(it, f),
+            Event::FetchWorkspaces(it) => fmt::Debug::fmt(it, f),
         }
     }
 }
@@ -251,7 +255,7 @@ fn register_did_save_capability(&mut self, additional_patterns: impl Iterator,
     ) -> Result, crossbeam_channel::RecvError> {
         // Make sure we reply to formatting requests ASAP so the editor doesn't block
@@ -283,6 +287,10 @@ fn next_event(
 
             recv(self.discover_receiver) -> task =>
                 task.map(Event::DiscoverProject),
+
+            recv(self.fetch_ws_receiver.as_ref().map_or(&never(), |(chan, _)| chan)) -> _instant => {
+                Ok(Event::FetchWorkspaces(self.fetch_ws_receiver.take().unwrap().1))
+            },
         }
         .map(Some)
     }
@@ -412,6 +420,9 @@ fn handle_event(&mut self, event: Event) {
                     self.handle_discover_msg(message);
                 }
             }
+            Event::FetchWorkspaces(req) => {
+                self.fetch_workspaces_queue.request_op("project structure change".to_owned(), req)
+            }
         }
         let event_handling_duration = loop_start.elapsed();
         let (state_changed, memdocs_added_or_removed) = if self.vfs_done {
@@ -830,6 +841,7 @@ fn handle_vfs_msg(&mut self, message: vfs::loader::Message) {
         match message {
             vfs::loader::Message::Changed { files } | vfs::loader::Message::Loaded { files } => {
                 let _p = tracing::info_span!("GlobalState::handle_vfs_msg{changed/load}").entered();
+                self.debounce_workspace_fetch();
                 let vfs = &mut self.vfs.write().0;
                 for (path, contents) in files {
                     let path = VfsPath::from(path);
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/op_queue.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/op_queue.rs
index 709d99bda751..7af5b48bb7a7 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/op_queue.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/op_queue.rs
@@ -36,7 +36,7 @@ fn default() -> Self {
     }
 }
 
-impl OpQueue {
+impl OpQueue {
     /// Request an operation to start.
     pub(crate) fn request_op(&mut self, reason: Cause, args: Args) {
         self.op_requested = Some((reason, args));
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs
index d7c6aeccf0b5..ae9e3e998746 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs
@@ -69,6 +69,7 @@ impl GlobalState {
     /// are ready to do semantic work.
     pub(crate) fn is_quiescent(&self) -> bool {
         self.vfs_done
+            && self.fetch_ws_receiver.is_none()
             && !self.fetch_workspaces_queue.op_in_progress()
             && !self.fetch_build_data_queue.op_in_progress()
             && !self.fetch_proc_macros_queue.op_in_progress()

From 9f82458c2584f6e30dcf715e00dd476f73705ec9 Mon Sep 17 00:00:00 2001
From: Lukas Wirth 
Date: Mon, 19 May 2025 13:08:23 +0200
Subject: [PATCH 49/51] Remote dangling file

---
 .../crates/proc-macro-srv/src/server_impl.rs  |  3 +-
 .../proc-macro-srv/src/server_impl/symbol.rs  | 47 -------------------
 2 files changed, 1 insertion(+), 49 deletions(-)
 delete mode 100644 src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/symbol.rs

diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl.rs
index 3d999421794b..11dbd9200915 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl.rs
@@ -16,9 +16,8 @@
 pub use token_stream::TokenStream;
 
 pub mod rust_analyzer_span;
-// mod symbol;
 pub mod token_id;
-// pub use symbol::*;
+
 use tt::Spacing;
 
 #[derive(Clone)]
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/symbol.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/symbol.rs
deleted file mode 100644
index 6863ce959973..000000000000
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/symbol.rs
+++ /dev/null
@@ -1,47 +0,0 @@
-//! Symbol interner for proc-macro-srv
-
-use std::{cell::RefCell, collections::HashMap, thread::LocalKey};
-
-thread_local! {
-    pub(crate) static SYMBOL_INTERNER: RefCell = Default::default();
-}
-
-// ID for an interned symbol.
-#[derive(Hash, Eq, PartialEq, Copy, Clone)]
-pub struct Symbol(u32);
-
-pub(crate) type SymbolInternerRef = &'static LocalKey>;
-
-impl Symbol {
-    pub(super) fn intern(interner: SymbolInternerRef, data: &str) -> Symbol {
-        interner.with(|i| i.borrow_mut().intern(data))
-    }
-
-    pub(super) fn text(&self, interner: SymbolInternerRef) -> SmolStr {
-        interner.with(|i| i.borrow().get(self).clone())
-    }
-}
-
-#[derive(Default)]
-pub(crate) struct SymbolInterner {
-    idents: HashMap,
-    ident_data: Vec,
-}
-
-impl SymbolInterner {
-    fn intern(&mut self, data: &str) -> Symbol {
-        if let Some(index) = self.idents.get(data) {
-            return Symbol(*index);
-        }
-
-        let index = self.idents.len() as u32;
-        let data = SmolStr::from(data);
-        self.ident_data.push(data.clone());
-        self.idents.insert(data, index);
-        Symbol(index)
-    }
-
-    fn get(&self, sym: &Symbol) -> &SmolStr {
-        &self.ident_data[sym.0 as usize]
-    }
-}

From 1dafeea8cfe587aa55131709a46e21283faaeb20 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= 
Date: Tue, 20 May 2025 10:00:23 +0300
Subject: [PATCH 50/51] Preparing for merge from rust-lang/rust

---
 src/tools/rust-analyzer/rust-version | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/tools/rust-analyzer/rust-version b/src/tools/rust-analyzer/rust-version
index 90e4d65bf961..5b47d1bbaed7 100644
--- a/src/tools/rust-analyzer/rust-version
+++ b/src/tools/rust-analyzer/rust-version
@@ -1 +1 @@
-6e23095adf9209614a45f7f75fea36dad7b92afb
+a8e4c68dcb4dc1e48a0db294c5323cab0227fcb9

From a6674952979445a81a890a936d968f81cf766c61 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= 
Date: Tue, 20 May 2025 10:03:14 +0300
Subject: [PATCH 51/51] Bump rustc crates

---
 src/tools/rust-analyzer/Cargo.lock            | 28 +++++++++----------
 src/tools/rust-analyzer/Cargo.toml            | 15 ++++++----
 .../crates/hir-def/src/hir/format_args.rs     |  3 +-
 .../crates/parser/src/lexed_str.rs            |  5 +++-
 4 files changed, 29 insertions(+), 22 deletions(-)

diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock
index 9eff747c21a9..c4d25284b278 100644
--- a/src/tools/rust-analyzer/Cargo.lock
+++ b/src/tools/rust-analyzer/Cargo.lock
@@ -1750,9 +1750,9 @@ dependencies = [
 
 [[package]]
 name = "ra-ap-rustc_abi"
-version = "0.110.0"
+version = "0.113.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "912228bd8ed3beff1f6f9e5e2d4b37c0827ba3e2070060bf3858a311d0e29e30"
+checksum = "c33b8fa229789975647ca5426be432c7c327ebde89ab15889928185dbcee3230"
 dependencies = [
  "bitflags 2.9.0",
  "ra-ap-rustc_hashes",
@@ -1762,18 +1762,18 @@ dependencies = [
 
 [[package]]
 name = "ra-ap-rustc_hashes"
-version = "0.110.0"
+version = "0.113.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ba520764daf057a9d963fa769f4762eaf87ac5d4900ae76195eeead64cd35afd"
+checksum = "0d68a3e389927002f552938a90b04787f6435f55b46fc5691360470d1cb2e99d"
 dependencies = [
  "rustc-stable-hash",
 ]
 
 [[package]]
 name = "ra-ap-rustc_index"
-version = "0.110.0"
+version = "0.113.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b76b5f9ee55f2d0e5a65bea23f6d738893349ce8d3d17a6720933e647ab04978"
+checksum = "32502273df2838d0ca13f1c67e2a48feef940e591f9771869f07e2db2acede53"
 dependencies = [
  "ra-ap-rustc_index_macros",
  "smallvec",
@@ -1781,9 +1781,9 @@ dependencies = [
 
 [[package]]
 name = "ra-ap-rustc_index_macros"
-version = "0.110.0"
+version = "0.113.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ddd972eb1face2fcaa0d94c01d97862fb955b5561d4f5932003bce8a6cadd8c6"
+checksum = "8a32f081864ae34c7ae6634edfa7a95ab9260ba85015e8b1d347580eda79d14f"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -1792,9 +1792,9 @@ dependencies = [
 
 [[package]]
 name = "ra-ap-rustc_lexer"
-version = "0.110.0"
+version = "0.113.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ba3a9876456fb2521097deef33ddeac1c18260c8eafb68054d986f8b9d6ce9fa"
+checksum = "ed34c51974718c5bd90d876d1364d9725159fc8030c2382b9cb837034152ed68"
 dependencies = [
  "memchr",
  "unicode-properties",
@@ -1803,9 +1803,9 @@ dependencies = [
 
 [[package]]
 name = "ra-ap-rustc_parse_format"
-version = "0.110.0"
+version = "0.113.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e85de58dfcc60a5f9d5ec0157a657e3f84abd8f22c8a0c4d707cfb42c9011f4"
+checksum = "ff0440e5d27facbf4ff13ea651e48c2f6e360b3dbfc56251b41d60719b965fb8"
 dependencies = [
  "ra-ap-rustc_lexer",
  "rustc-literal-escaper",
@@ -1813,9 +1813,9 @@ dependencies = [
 
 [[package]]
 name = "ra-ap-rustc_pattern_analysis"
-version = "0.110.0"
+version = "0.113.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ceadf9db550db67deff7eff2e2765109b860c9d7e5bdfca144863020289c823d"
+checksum = "a6056efa57aba3aa0cc69a0bf1a8281624c23ad25b05748d11ebcd4668037bfc"
 dependencies = [
  "ra-ap-rustc_index",
  "rustc-hash 2.1.1",
diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml
index c4c2fdf34bae..07731bae3f30 100644
--- a/src/tools/rust-analyzer/Cargo.toml
+++ b/src/tools/rust-analyzer/Cargo.toml
@@ -85,11 +85,11 @@ vfs-notify = { path = "./crates/vfs-notify", version = "0.0.0" }
 vfs = { path = "./crates/vfs", version = "0.0.0" }
 edition = { path = "./crates/edition", version = "0.0.0" }
 
-ra-ap-rustc_lexer = { version = "0.110", default-features = false }
-ra-ap-rustc_parse_format = { version = "0.110", default-features = false }
-ra-ap-rustc_index = { version = "0.110", default-features = false }
-ra-ap-rustc_abi = { version = "0.110", default-features = false }
-ra-ap-rustc_pattern_analysis = { version = "0.110", default-features = false }
+ra-ap-rustc_lexer = { version = "0.113", default-features = false }
+ra-ap-rustc_parse_format = { version = "0.113", default-features = false }
+ra-ap-rustc_index = { version = "0.113", default-features = false }
+ra-ap-rustc_abi = { version = "0.113", default-features = false }
+ra-ap-rustc_pattern_analysis = { version = "0.113", default-features = false }
 
 # local crates that aren't published to crates.io. These should not have versions.
 
@@ -132,7 +132,10 @@ pulldown-cmark-to-cmark = "10.0.4"
 pulldown-cmark = { version = "0.9.6", default-features = false }
 rayon = "1.10.0"
 rowan = "=0.15.15"
-salsa = { version = "0.21.1", default-features = false, features = ["rayon","salsa_unstable"] }
+salsa = { 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" }
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/hir/format_args.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir/format_args.rs
index f27a4062a63b..271484da7b9a 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/hir/format_args.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/hir/format_args.rs
@@ -297,7 +297,8 @@ enum ArgRef<'a> {
                     unfinished_literal.clear();
                 }
 
-                let span = parser.arg_places.get(placeholder_index).and_then(|s| to_span(s.clone()));
+                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/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs b/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs
index 0a5c16dc4c49..0fa9a264545d 100644
--- a/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs
@@ -179,7 +179,10 @@ fn extend_token(&mut self, kind: &rustc_lexer::TokenKind, mut token_text: &str)
                     COMMENT
                 }
 
-                rustc_lexer::TokenKind::Frontmatter  { has_invalid_preceding_whitespace, invalid_infostring } => {
+                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 {